#import "@preview/sourcerer:0.2.1": code

//Añadir subaparatado pequeño hablando de blockly y relacionandolo con lenguajes de script visuales

Algunos programas necesitan ser extendidos para añadir funcionalidad sin tener que acceder al código fuente. Además, esto permite a usuarios que no sean el desarrollador original añadir funcionalidad al programa. A estos sistemas de extensión se les denomina comúnmente plugins. Un plugin @plugin_architecture es un componente de software que añade una funcionalidad específica a un programa. Los plugins pueden ser utilizados en programas de todo tipo, desde editores de texto hasta videojuegos.

Existen tres formas principales de extender un programa: mediante librerías dinámicas, mediante un archivo de configuración o mediante un lenguaje de scripting. Cada una tiene sus ventajas y desventajas. En este capítulo se explorarán y explicarán estas opciones. 

== Extensión mediante librería dinámica

Una librería dinámica es un archivo que contiene código compilado que puede ser cargado y vinculado a un programa en tiempo de ejecución. Esto significa que el programa no necesita incluir este código en su propio archivo binario, sino que puede cargarlo cuando se necesita.

Las librerías dinámicas son especialmente útiles para proporcionar una interfaz estandarizada a funciones de sistema o a librerías de terceros que pueden cambiar con el tiempo. Al vincular estas funciones en tiempo de ejecución, un programa puede beneficiarse de las actualizaciones y correcciones de errores en la librería sin necesidad de ser recompilado. Al mismo tiempo, si el programa está preparado para ello, las librería dinámicas pueden extender funcionalidad.

Las librerías dinámicas, al ser módulos de código que se cargan y vinculan en tiempo de ejecución, necesitan una forma de comunicarse con el programa principal. Esta comunicación implica saber cómo se llaman las funciones, cómo se pasan los datos y cómo se maneja la memoria. En otras palabras, necesitan un protocolo común que permita al programa y a la librería interactuar de manera efectiva. Aquí es donde entra en juego la Interfaz Binaria de Aplicación, o ABI.

La ABI @understanding_abi es un contrato entre dos piezas de código binario que define cómo deben interactuar entre sí. Incluye detalles como el formato de los datos, la disposición de las llamadas a funciones y el manejo de la memoria.

La ABI es esencial para las librerías dinámicas porque define cómo el código en la librería debe ser llamado y cómo debe interactuar con el código que la llama. Si la ABI de una librería cambia, cualquier código que dependa de ella puede dejar de funcionar correctamente a menos que sea recompilado para usar la nueva ABI. Por esta razón, los desarrolladores de librerías dinámicas suelen esforzarse por mantener la compatibilidad con la ABI para evitar romper los programas existentes. Cada lenguaje tiene su propia ABI, que puede ser o no estable. Esto depende de cómo se haya diseñado el lenguaje y de cómo se haya implementado la ABI. Un lenguaje que tenga poco tiempo de desarrollo no tendrá un ABI estable al tener que cambiar constantemente para añadir nuevas funcionalidades o corregir errores. A pesar de esto, la mayoría de lenguajes con un ABI no estable tienen soporte para usar el ABI de C, que es estable, aunque esto limita las característica que se pueden usar del lenguaje.

Cabe destacar que las librería dinámicas no son un formato universal, sino que cada sistema operativo tiene su propio mecanismo para cargar y vincular librerías dinámicas. Por ejemplo, en Windows se utilizan archivos DLL, en Linux se utilizan archivos SO y en macOS se utilizan archivos dylib. Esto significa que las librerías dinámicas no son necesariamente portables entre sistemas operativos, lo que puede ser una limitación en algunos casos. No obstante, existe un consenso al respecto a su funcionalidad. Por ejemplo, los grandes sistemas operativos actuales, Windows, Linux y MacOS, permiten definir callbacks o funciones para que una librería detecte cuando se ha cargado o descargado por el programa principal.

Por último, cabe mencionar que en Windows, las librerías dinámicas pueden contener recursos como texturas, modelos 3D, sonidos, etc. Esto permite, por ejemplo, compartir `mods` de un juego en un solo archivo. @resource_only_dll. Se denomina mod (diminutivo de "modification") a la modificación de un videojuego por parte de los usuarios @poor-2013. Es decir, es un tipo de plugin específico para videojuegos. Las librerías dinámicas y los sistemas descritos en los siguientes apartados pueden ser usados para crear "mods" si el juego lo soporta, puesto que algunos juegos ofrecen soporte oficial de "mods" mientras que en otros casos la comunidad crea herramientas para modificar el juego.

Sin embargo, las librerías dinámicas tienen algunos problema o inconveniencias a tener en cuenta. Por un lado, la compatibilidad entre plataformas ya mencionada. Por otro lado, la compatibilidad entre versiones y el ABI. Además, existen también problemas de seguridad, pues una librería dinámica puede ser modificada por un atacante para lograr la ejecución de código malicioso cierto grado de acceso al sistema. O simplemente un usuario malicioso puede distribuir una librería dinámica que no haga lo que dice hacer.

La mayor ventaja de este sistema es su flexibilidad. Si el API (las funciones que define la librería) está bien diseñado, las posibilidad de extensión pueden llegar a ser infinitas, es el caso de algunos motores de videojuegos como Unity, en el que el código del desarrollador acaba siendo una librería dinámica que usa el motor. Además, una librería dinámica puede tener estado, es decir, sus propios datos. Este estado es global pero ofrece más posibilidades de extensión y mantenimiento. Por ejemplo, aprovechando que las librerías pueden detectar cuando se carga y se descarga, sería posible para un plugin guardar el número de veces que se ha cargado, para ello lo único que hay que hacer es escribir en un fichero la cantidad de veces que se ha cargado y leerlo al cargar la librería. Extendiendo este sistema, un plugin podría persistir todo su estado en un fichero. Esto permite a los sistemas de mods gestionar su propio estado sin tener que depender del programa principal.

== Extensión mediante un archivo de configuración

Un archivo de configuración es una herramienta que ofrece la posibilidad de ajustar el comportamiento de un programa sin necesidad de modificar su código fuente y `sin necesidad de compilar código`. Esta práctica resulta especialmente útil en situaciones donde acceder al código no es una opción viable, ya sea porque no está disponible o porque el usuario carece del conocimiento técnico necesario para modificarlo, o porque el desarrollador o empresa quiera mantener el control sobre el código fuente.

La lectura de este archivo por parte del programa le permite realizar acciones específicas basadas en la información contenida en el mismo. Es como proporcionarle al programa un conjunto de instrucciones adicionales que le dicen cómo comportarse en ciertas circunstancias.

Una de las ventajas más destacadas de este enfoque es que es multiplataforma. Debido a que es un formato específico creado a medida para el programa, no depende de ningún protocolo que indique como cargar símbolos (nombres de funciones) o como gestionar la memoria y datos. Esto facilita la mantención del código.

Además, el uso de archivos de configuración no presenta riesgos de seguridad inherentes. A diferencia de ejecutar código arbitrario, que puede ser potencialmente peligroso, la lectura de un archivo de configuración solo implica que el programa interprete ciertos datos de manera específica. Al mismo tiempo, este es el mayor problema de este foramto. En una librería dinámica, el desarrollador puede realizar las operaciones que quiera dentro de una función, siendo la única limitación las funciones que el programa principal exponga para comunicarse con este. En el caso de un fichero de configuración las limitaciones se extreman al no poder controlar nada que no esté especificado en el formato. Es como si tuvieras una serie de piezas que puedes combinar de forma específica, porque el diseñador de las piezas las creó de tal forma que no todas encajan con todas, además se está limitado a las piezas que el diseñador creó. Por otro lado, al definir una serie de órdenes secuenciales que no pueden conocerse de antemano, el código no puede optimizarse tanto como en una DLL, pues hay que leer el archivo e interpretarlo adecuadamente. Sin embargo las DLLs, una vez cargadas podían ejecutar código directamente.

Los archivos de configuración, al ser de especificación libre y no estar sujeto a normas, pueden no ser ficheros de texto. Es posible que sean un formato binario no legible para el ser humano. Sin embargo, lo más habitual es que sean ficheros de texto, ya que la intención es que sean fácilmante modificables por el usuario. Además, que sea un fichero de texto elimina la dependencia de una herramienta que pueda leer y escribir el formato del archivo.

Como ejemplo se muestra un archivo de configuración para un sistema bullet hell inspirado en Danmaku:

#code(
  lang: "Danmaku",
```rust
fire circle-blue <0,-5>
  repeat-every 8
    randomize-x -6 6
        fire-particle
            cartesian-velocity
                randomize-time 0 4
                      lerpfromtoback 0.5 1.5 2.5 3.5
                            <0,0.4>
                            <0,0.7>
```
)

Un sistema bullet hell es un tipo de juego de disparos en el que el jugador debe esquivar una gran cantidad de proyectiles en pantalla. En este caso, el archivo de configuración define un patrón de disparo que lanza proyectiles azules en forma de círculo hacia arriba. El patrón se repite cada 8 fotogramas y los proyectiles se mueven aleatoriamente en el eje X entre -6 y 6 unidades. Cada proyectil tiene una velocidad inicial de 0.4 unidades en el eje Y y aumenta a 0.7 unidades a lo largo de su vida útil.

El usuario que consuma esta API está limitado al formato y funciones que el desarrollador ha dado. Además, en sistemas más complejos como el de este ejemplo, esta solución puede ser más difícil de implementar de manera eficiente. Este método de extensión va más alla de un simple archivo de configuración para un sistema con unos parámetros fijos. Esta clase de sistemas puede permitir elegir cierta combinación de parámetros en cierto orden. Este sistema es similar al siguiente que veremos.

== Extensión mediante un lenguaje de scripting

Existe un punto intermedio en cuanto a ventajas y desventajas entre los sistemas ya mencionados: los lenguajes de scripting. Un lenguaje de scripting es un lenguaje de programación que se utiliza para controlar la ejecución de un programa o para extender su funcionalidad. A diferencia de los lenguajes de programación tradicionales, los lenguajes de scripting suelen ser interpretados en lugar de compilados, lo que significa que el código se ejecuta directamente por un intérprete en lugar de ser traducido a código máquina antes de la ejecución.

Un lenguaje de scripting, al igual que otros lenguajes de programación, posee la característica de ser Turing completo. Esto implica que tiene la capacidad de llevar a cabo cualquier operación computacional que sea teóricamente posible. Sin embargo, debido a que los lenguajes de scripting son interpretados en tiempo de ejecución, en lugar de ser compilados previamente, pueden presentar una velocidad de ejecución más lenta en comparación con los lenguajes compilados.

La interpretación en tiempo de ejecución significa que el código se traduce a código máquina mientras el programa se está ejecutando, lo cual puede introducir una sobrecarga adicional que afecta el rendimiento. A pesar de esta desventaja en términos de velocidad, los lenguajes de scripting ofrecen la ventaja de ser multiplataforma.

Ser multiplataforma significa que los scripts escritos en un lenguaje de scripting pueden ser ejecutados en diferentes sistemas operativos o plataformas de hardware. Esto es posible porque el intérprete del lenguaje de scripting, que es el programa que realiza la traducción a código máquina, suele estar disponible para una amplia variedad de plataformas.

Además, los lenguajes de scripting suelen ser más fáciles de aprender y de utilizar que los lenguajes de programación tradicionales. Esto se debe a que los lenguajes de scripting suelen tener una sintaxis más sencilla y menos reglas que los lenguajes de programación compilados. Suelen ser lenguajes que gestionan la memoria automáticamente, es decir, no es necesario liberar la memoria que se ha reservado, lo que facilita la programación.

Uno de los lenguajes de scripting más usados para modificar e incluso desarrollar videojuegos es Lula. Lua es un lenguaje de scripting de alto nivel, multi-paradigma, ligero y eficiente, diseñado principalmente para la incorporación en aplicaciones. Fue creado en 1993 por <NAME>, <NAME> y <NAME>, miembros del Grupo de Tecnología en Computación Gráfica (Tecgraf) de la Pontificia Universidad Católica de Río de Janeiro, Brasil.

Lua es conocido por su simplicidad, eficiencia y flexibilidad. Su diseño se centra en la economía de recursos, tanto en términos de memoria como de velocidad de ejecución. Existe una única estructura de datos, la tabla, que se utiliza para representar tanto arrays como diccionarios. Además, para poder "heredar" funciones de una tabla, Lua define el concepto de metatabla. En Lua, cada tabla puede tener asociada una metatabla. Cuando el desarrollador llama a una función y esta no está definida en la tabla, Lua busca en la metatabla de la tabla para ver si la función está definida ahí. Esto es comparable a los prototipos en JavaScript.

Una de las características más destacadas de Lua es su capacidad para ser embebido en aplicaciones. Esto se debe a su diseño como un lenguaje de scripting, que permite que el código Lua sea llamado desde un programa en C, C++ u otros lenguajes de programación. Esta característica ha llevado a que Lua sea ampliamente utilizado en la industria de los videojuegos, donde se utiliza para controlar la lógica del juego y las interacciones del usuario. Existen motore como Defold que integran Lua como lenguaje de scripting o el popular juego Roblox, que permite a los usuarios crear sus propios juegos utilizando una version modificada de Lua llamada Luau.

Existe una versión más rápida de Lua llamada LuaJIT. El nombre no es arbitrario, JIT hace referencia a un término en inglés que significa "Just In Time", es decir, "Justo a Tiempo". Existen dos estrategias de compilación: la compilación AOT (Ahead Of Time) y la compilación JIT (Just In Time). La compilación AOT consiste en compilar el código fuente a código máquina antes de la ejecución del programa. Tradicionalmente se llaman lenguajes compilados a los lenguajes que compilan con esta estrategia. La compilación JIT, por otro lado, consiste en compilar el código fuente a código máquina durante la ejecución del programa. La compilación JIT tiene la ventaja de ser multiplataforma. LuaJIT es un intérprete de Lua que utiliza la compilación JIT para mejorar el rendimiento de los scripts Lua. Esto significa que el código Lua se compila a código máquina en tiempo de ejecución, lo que puede mejorar significativamente la velocidad de ejecución de los scripts.

Sin embargo, cabe destacar que en los lenguajes JIT, puede llegar a ser importante saber ciertos detalles de su funcionamietno interno para poder aprovechar su potencial. Como se mencionó anteriormente, la única estructura de datos en Lua es la tabla, no existen tipos salvo los primitivos (números, booleanos y cadenas de texto). Esto significa que algunas optimizaciones dependen del uso que el desarrollador haga del lenguajes. Por ejemplo, si una función recibe dos parámetros y esos dos parámetros siempre son números, LuaJIT puede optimizar la función en base a heurísticas. Sin embargo, si durante la ejecución se llama a la función con otro tipo de dato, LuaJIT desactivará la optimización y la función se ejecutará de forma más lenta. Este tipo de optimizaciones son comunes en los lenguajes JIT, por lo que es importante tener en cuenta cómo funcionan para poder aprovechar su potencial. De ser del interés del lector, se recomienda leer al respecto el monomorfismo y polimorfismo de JavaScript, ya que suele ser implementarse con una estrategia JIT y existe documentación adecuada al respecto.

A continuación se explicará de forma general qué es una CPU, que es el multithreading o multihilo y cuales son sus ventajas e inconvenientes. 