Alt-F4 #18 - El camino hacia Clusterio 2.0  18-12-2020

Escrito por Hornwitser, DedlySpyder, editado por stringweasel, Nanogamer7, Conor_, Therenas, nicgarner, Firerazer

Tabla de contenidos

A medida que el año llega a su fin, elegimos dos temas relacionados con las modificaciones para el número 18 de Alt-F4 de esta semana. Primero, Hornwitser nos da una idea del largo progreso de desarrollo de Clusterio 2.0 y los desafíos que plantea. Luego, DedlySpyder habla sobre su proceso de desarrollo de un mod simple y los desafíos de compatibilidad que enfrentan.

El camino a Clusterio 2.0 Hornwitser

Quiero contar la historia de cómo terminé pasando un año desarrollando Clusterio 2.0, que todavía tiene un largo camino por recorrer antes de su lanzamiento. Si no has oído hablar de Clusterio antes, es un software para un servidor de código abierto escrito por Danielv123 (con contribuciones de alrededor de 30 personas más) que permite que los mods interactúen entre servidores. Quizás sea más conocido por el evento Clusterio 60k en 2018, donde se usaron cofres de teletransporte para transferir elementos entre unos 46 servidores de Factorio con el fin de construir una fábrica similar a la versión vainilla que podría hacer 60k de ciencia por minuto. Estos cofres de teletransporte funcionan como cofres de proveedor activo y solicitante; uno elimina elementos del juego y los coloca en un almacenamiento compartido en la nube y el otro toma los elementos solicitados de ese almacenamiento en la nube y los coloca en el juego.

El mineral de hierro se extrae en el servidor de la izquierda y luego se envía al servidor de la derecha a través de los cofres de teletransporte de Clusterio.

Clusterio siempre se ha compuesto de dos partes: las interacciones del juego que se implementan en el código mod y se ejecutan dentro del juego, y la infraestructura del lado del servidor que se ocupa de mover datos entre los distintos servidores del juego. Al principio, el lado del servidor estaba codificado en torno al manejo de los cofres de teletransporte, pero a medida que avanzaba el desarrollo y más y más características se agregaron la idea de lo que Clusterio es cambió de teletransportar cofres a una plataforma modular en el servidor para hacer la conexión entre elementos de los servidores.

En julio de 2019, se llevó a cabo el evento The Gridlock Cluster. En lugar de cofres de teletransporte para transportar elementos entre servidores, había trenes que podían teletransportarse desde el borde de un servidor al borde de otro servidor utilizando paradas de teletransporte de trenes. Godmave implementó el código para teletransportar los trenes como un complemento para Clusterio.

trenes teletransportándose desde el borde de un servidor al borde de otro.

Lamentablemente, el código estaba plagado de problemas, que es donde entro en escena.

Comienzos humildes

Comencé a hackear el código base de Clusterio en julio de 2019 como parte de intentar ayudar al equipo de Gridlock con los muchos problemas que tenían. Los servidores se caían continuamente, los jugadores tenían problemas y parecían surgir nuevos errores y problemas por horas. Fue agitado, pero también hubo mucha diversión. El evento había despertado mi interés en el código detrás de Clusterio, y después del evento me encargué de mejorar este código para el próximo evento. Ese resultó ser un proyecto mucho más grande de lo que posiblemente podría haber imaginado.

He trabajado de manera constante en Clusterio 2.0 durante unos 16 meses y mi estimación de cuándo estará lista sigue siendo la misma “solo un par de meses” con la que comencé. A pesar de esto, mi motivación para seguir trabajando en él sigue siendo fuerte, y una de las cosas que encuentro particularmente motivadora es poner todo este trabajo a prueba organizando mi propio evento Clusterio. He publicado un teaser en Reddit por lo que tengo en mente y ahora mismo el objetivo es ejecutarlo a principios del próximo año . Posiblemente en enero, aunque solo el tiempo dirá cuándo estará todo listo.

Pero volvamos a donde todo comenzó para mí. Estaba instalando Clusterio en mi servidor, tratando de configurar mi propio clúster de prueba para trabajar en las correcciones de los problemas encontrados en The Gridlock Cluster. Una de las primeras cosas que noté fueron los mil paquetes que extrajo como dependencias, ocupando más de 300 MB de espacio en disco. Parecía ridículo que este proyecto necesitara tantas bibliotecas para funcionar. Node.js era nuevo para mí entonces, y aunque ahora he aprendido que esta no es realmente una cantidad tan irrazonable de dependencias para una aplicación Node.js, todavía es mucho. Era una indicación del tipo de estilo de desarrollo que se había utilizado en el proyecto: un enfoque de arriba hacia abajo para agregar características de la manera más simple y rápida de implementar allí mismo.

Este estilo de desarrollo había llevado a la acumulación de mucha deuda técnica, y me refiero a mucha. La deuda técnica es un término que se utiliza a menudo en el desarrollo de software. Es la idea de que elegir atajos en el desarrollo para ahorrar tiempo a menudo genera más trabajo en el futuro al mantener y extender el código base. De alguna manera, se podría decir que Clusterio era más una colección de parches apilados uno encima del otro que un proyecto bien pensado y estructurado. Un ejemplo memorable de esto fue la inclusión y el uso de cuatro clientes HTTP diferentes en el mismo archivo fuente. Por lo general, uno de esos clientes es más que suficiente para todo un proyecto, pero presumiblemente ciertas cosas eran más fáciles de hacer con un cliente en comparación con otros y, a medida que pasaba el tiempo, se acumulaban diferentes clientes.

Entonces, me puse a trabajar para mejorar y limpiar el código base de Clusterio. Una de las primeras cosas que hice fue reducir esas mil dependencias. Resultó que la mayor parte no era realmente necesaria para ejecutar Clusterio. Aproximadamente la mitad eran herramientas de desarrollo que no necesitaban instalarse en un entorno de producción y una cuarta parte eran lo que yo describiría como soluciones rápidas: grandes bibliotecas extraídas para usar una única función de ellas. Muchas de estas bibliotecas fueron triviales de eliminar, ya sea reimplementando la función localmente o usando otra biblioteca que ya era una dependencia del proyecto. Al final logré eliminar la necesidad de unos 700 paquetes (244 MB), aunque debe tenerse en cuenta que la mayoría de estos eran dependencias de dependencias.

El siguiente problema que abordé fueron las pruebas automatizadas. Si no está familiarizado con las pruebas automatizadas, la idea de escribir código es verificar que el código principal funcione como debería y no se rompa con cambios futuros. Las pruebas automatizadas son una especie de piedra angular para escribir código confiable y, si bien hubo pruebas extensas configuradas en algún momento, no funcionaron cuando entré en el proyecto. Es otro ejemplo de deuda técnica asomando su fea cabeza. Mantener las pruebas y agregar nuevas pruebas para cubrir el nuevo código es un trabajo adicional; saltarse ese trabajo es un atajo.

Después de arreglar las pruebas, mi enfoque se centró en limpiar el código en sí. Hacer cosas como arreglar el código roto, eliminar el código obsoleto o no utilizado y refactorizar el código incorrecto en un código ligeramente menos incorrecto. Uno de los cambios que comenzaron a tomar forma fue mover el código para los cofres de teletransporte fuera del código base principal y en un complemento separado. Dado que Clusterio es ante todo el software de servidor que hace posibles las interacciones de mod entre servidores, tener estos cofres de teletransporte también llamados Clusterio es confuso cuando esperamos más y más que Clusterio se use para otras cosas además de esos cofres. Así que también decidimos cambiar el nombre de la función de teletransporte de objetos a través de cofres mágicos como Almacenamiento subespacial. Mientras estaba en eso, también decidí reemplazar esos torcidos cofres del cielo y los sprites de la red de colección con algo más apropiado para el almacenamiento subespacial.

Sprites de marcador de posición de almacenamiento subespacial
Nuevos sprites de marcador de posición para las entradas y salidas de elementos, fluidos y electricidad en el modo de almacenamiento subespacial.

Sin embargo, todavía son más un marcador de posición, ya que no soy un gran artista 3D cuando se trata de texturizar y modelar mecánicamente. Me tomé el tiempo para configurar una cadena de herramientas automatizada con Blender para renderizar, recortar y generar los sprites en el mod. Ya sabes lo que pasa con los programadores: automatiza todas las cosas.

Guardar los parches

A medida que mi trabajo continuó, la primera mejora importante en la que trabajé fue guardar los parches, pero antes de hablar de ello, quiero dar un contexto al problema que está tratando de resolver. El motor del juego permite modificar el comportamiento del juego con código Lua mediante mods y / o escenarios. Los mods se cargan cuando se inicia el juego y actualizarlos requiere reiniciar el juego. Los escenarios son el código Lua empaquetado con los guardados del juego y cambiar a un código de escenario diferente solo requiere cargar un guardado diferente.

Cuando el comportamiento de cambio de juego se incluye en el código de escenario, a menudo se denomina modificación suave, ya que no es necesario descargar ningún mod y reiniciar Factorio para unirse a un servidor utilizando dicho código de escenario. Si bien es fácil actualizar un mod y continuar con un guardado existente, no es tan sencillo con el código de escenario. Básicamente, hay tres formas de actualizar el código del escenario en un guardado, que enumeraré aproximadamente en el orden de dificultad de implementación:

  • Para escenarios distribuidos a través de un mod, es posible agregar un script de migración en el mod que actualiza el escenario cuando se actualiza el mod. Si bien esto es bastante simple de hacer, tiene el principal inconveniente de requerir que se instale el mod para ejecutar la migración.
  • Puede reemplazar el código de escenario almacenado en el guardado mientras el juego no se está ejecutando. Esto es lo que yo llamo guardar parches y es relativamente sencillo de hacer, ya que los archivos guardados son archivos zip normales y el código Lua se almacena en ellos como archivos de texto ordinarios.
  • También puedes usar la naturaleza dinámica de Lua para cargar y ejecutar código nuevo mientras se ejecuta el juego y el escenario. Esta opción es, con mucho, la más complicada, pero tiene el poder de poder aplicar correcciones al juego mientras se ejecuta un mapa. El inconveniente es que es complicado de implementar y hacerlo bien, lo que aumenta las posibilidades de que algo salga mal. Además, la única forma de enviar datos a un juego en ejecución es a través de comandos, lo que se vuelve problemático cuando son largos.

Para Gridlock Cluster, la tercera opción se realizó a través de un escenario llamado Hotpatch (también conocido como Escenario Multi-mod del lado del servidor). Conceptualmente, Hotpatch es algo genial; te permite cargar un código similar a un mod mientras el juego se está ejecutando, y ejecutará ese código en un entorno que emula el entorno de mod de Factorio. Pero hubo problemas importantes con el uso de Hotpatch: está mal documentado, lo que dificulta su uso correcto; la implementación estaba incompleta y con errores; y el problema más problemático fue que el código de escenario actualizado se envió como comandos largos al inicio. Esto significaba que si los jugadores se unían a un servidor mientras se estaba iniciando y en el proceso de enviar esos largos comandos para actualizar el escenario, las cosas se volvían locas, que es solo una de las muchas formas en que fallaron los servidores de Gridlock.

Si bien muchos de los problemas con Hotpatch se han solucionado, la complejidad y las dificultades de trabajar con él me han enseñado una lección valiosa: tener capacidades avanzadas como poder arreglar código en tiempo de ejecución, o maravillas técnicas de cualquier tipo, no lo hace. Siempre justifique la complejidad y los problemas que enfrentan estos sistemas avanzados. Pude experimentar esto de primera mano cuando trataba de solucionar problemas en los que Hotpatch participó: todos en el equipo (incluido yo mismo) lucharon por comprender el sistema y cómo resolver los problemas con él.

Por esa razón, decidí reemplazar el rol que tenía Hotpatch en Clusterio por algo más simple: guardar parches. Es una solución menos capaz con más limitaciones sobre cómo se escribe el código, pero la simplicidad en la forma en que funciona lo compensa con creces.

Rompiendo Todo

Después de implementar el parche de guardado, quedó claro que se necesitaba una revisión importante del código. Un punto particularmente doloroso de Clusterio ha sido la total falta de administración remota. Si desea iniciar un servidor de Factorio que sea parte del clúster, debe iniciar sesión en la computadora que lo aloja e iniciarlo manualmente a través del administrador de procesos que elija usar, lo mismo ocurre si desea cambiar alguna configuración para eso. servidor. Administrar un clúster de esta manera es doloroso y esa fue una lección aprendida por las malas en el evento Clusterio 60k.

Para Gridlock, el panel de administración del servidor de juegos Pterodactyl se utilizó para administrar los servidores de forma remota; una buena idea que resultó ser la causa de muchos problemas. Pero esa es una historia para otro momento.

Tener la capacidad de administrar de forma remota los servidores de Factorio en Clusterio ha sido una característica deseada durante mucho tiempo y ha habido intentos de implementarla. Sin embargo, esos intentos fueron más una ocurrencia tardía, y debido a la forma en que se estructuraba el código (ejecutando un solo servidor Factorio por proceso Node.js) se volvió muy difícil implementar cualquier administración remota sensata sin hacer una revisión importante del código y romper todo en el proceso.

Entonces, naturalmente, rompí todo e implementé la administración remota.

La forma en que funciona Clusterio 2.0 es que se ejecuta un proceso esclavo en cada computadora en la que desea alojar los servidores de Factorio. Estos servidores de Factorio se denominan instancias en Clusterio y el proceso esclavo se conecta al servidor maestro y escucha los comandos para crear e iniciar instancias. Se pueden ejecutar varias instancias al mismo tiempo en un esclavo, lo que significa que solo necesita configurar un esclavo para cada computadora en la que desea alojar los servidores de Factorio, y solo hay un proceso Node.js para iniciar en estas computadoras.

Otra cosa que tuvo que cambiar fue la forma en que Clusterio se comunicaba entre computadoras. En la versión 1, esto lo maneja en su mayor parte el servidor maestro que aloja un servidor HTTP y responde a las solicitudes en él. Esto tiene el problema de que el servidor principal no puede enviar mensajes a otras computadoras, solo responder a las solicitudes que se le envían desde otras computadoras; así es como funciona HTTP. Para evitar esto, reemplacé HTTP con un protocolo simple basado en WebSocket que usa cargas útiles JSON. WebSocket, a diferencia de HTTP, permite que ambas partes de la conexión se envíen mensajes entre sí en cualquier momento.

Con todo ahora roto, este se convirtió en el punto donde realmente comenzó el desarrollo 2.0. Aproveché esta oportunidad para empezar de cero en muchas cosas en los meses siguientes.

Espero que les haya gustado este vistazo al desarrollo de Clusterio 2.0. Como puede imaginar, hay muchas más cosas sobre 2.0 que han sucedido en los últimos 16 meses, ciertamente suficientes para más artículos sobre el tema. Tenga en cuenta que la 2.0 aún no está listo para su uso en público, aunque si está interesado en el desarrollo y desea probarlo, consulte nuestro servidor Discord y el repositorio de GitHub.

Moddabilidad: El nacimiento de un Mod DedlySpyer

Algo que kovarex dijo en FFF-363 se me quedó grabado:

Este es un ejemplo de una función, que TENÍA QUE HACER, porque una vez que me di cuenta de que la función podría estar allí, casi estaba tratando de usarla y me molestó el hecho de que no estaba allí.

This is an example of a feature, that I just HAD TO DO, because once I realised that the feature could be there, I was almost trying to use it and was annoyed by the fact that it wasn’t there.

— kovarex

He empezado a jugar Factorio durante unos seis años, pero desde que empecé a modificar siempre me ha gustado jugar con el juego. A veces, cuando juego, termino viendo algo nuevo que me molesta un poco y para lo que no hay un mod que lo arregle. Llegaré a un punto en el que terminaré modificándolo yo mismo. Normalmente, esto hace que descarte mi partida actual de Factorio, principalmente porque, para mí, el modding produce el mismo interés que en el juego.

Poco después del lanzamiento de la versión 1.0, esto volvió a sucederme. Cogí la última versión de Krastorio 2, llegué al punto de la servoarmadura en el juego y me preguntaba por qué no podía rotar el equipo. Claro, probablemente podría barajar todo en mi armadura, pero a veces solo quiero presionar “R” y colocar algo en su lugar con un mínimo esfuerzo. Una búsqueda rápida en el portal de mods me mostró que había Rotatable Batteries por GotLag; entonces fue posible, pero no se ha implementado para todo.

Hacer que un mod funcione en todas las circunstancias puede ser a veces un montón de trabajo. La forma segura de manejar cada caso es codificar los cambios para cada situación. Eso definitivamente funcionará, pero requiere un monitoreo constante. Habiendo hecho algo como esto en el pasado, sé que puede volverse bastante difícil de manejar leer. Además, si uno de esos otros mods cambia algo, mi implementación se rompe por completo o es inconsistente con el mod “admitido”. Por lo tanto, recientemente me convertí en un gran fanático de tratar de hacer que mis mods sean lo más dinámicos posible para evitar esto. En teoría, también debería ahorrar mucho tiempo, pero esto no siempre funciona.

xkcd Automation
Creditos: xkcd #1319

Sin embargo, esa realidad es lo que disfruto en Factorio; el “Oh, pero necesito hacer esto”. Eso no es divertido si solo se trata de agregar otra dependencia de mod al agregar una cadena a una lista. Entonces, para comenzar un nuevo mod con el objetivo de poder rotar cualquier equipo sin la necesidad de mantenerlo constantemente, necesitaba apoyarme en cómo Factorio carga los mods.

En otros juegos en los que deseas agregar modificaciones, tienes algún tipo de lista de orden de modificaciones. Tú, el jugador, o un programa creado por modders, necesitas decirle al juego en qué orden se cargarán los mods, para asegurarte de que todo encaja lo suficientemente bien como para no explotar. Factorio logra este orden a través de dependencias de mod, pero también va un paso más allá. Factorio no solo carga todos los mods en orden una vez, los carga en orden tres veces.

¿Tres veces? Parece excesivo, ¿verdad? De hecho, es una idea fantástica. La wiki explica esto con mucho más detalle, pero lo explicaré rápidamente aquí. Cada mod, el orden de carga, tiene una etapa de configuración, luego una etapa de datos. La etapa de configuración es bastante autoexplicativa, y la etapa de datos es para datos de prototipos, como elementos, entidades y recetas. Este ciclo luego se repite dos veces más. Los mods especifican qué cargar en cada iteración del ciclo. Las convenciones de modificación recomiendan que todos los prototipos se agreguen lo antes posible en este proceso. Esto permite que los mods que quieran depender implícitamente de otros mods lo hagan sin necesidad de saber que existen. Por ejemplo, el mod de Factorio básico hace esto para barriles de líquidos.

Así es como la comunidad tiene los mods de revisión gigantes que reelaboran por completo todas las recetas; simplemente lo hacen en una etapa de datos posterior para cada receta del juego. No hay listas grandes de modificaciones que necesiten mantenimiento, ni “Esta modificación necesita cargarse en último lugar”.

Así es como puedo hacer que mi mod pueda rotar cualquier equipo. Puedo mover mis comprobaciones de equipo que necesita una versión rotada a una etapa de datos posterior, y debería cubrir implícitamente cualquier equipo en el juego. No es necesario que nombre los mods X, Y y Z como dependencias, o que el jugador administre cualquier cosa al final; simplemente funciona. No hay una gestión constante para los cambios de nombre, a menos que haya un problema más complejo que disfrutaré rastreando.

Con todo eso en mente, a los pocos días, y abandonado un guardado de Krastorio a medio terminar, nació Rotatable Equipment.

Equipo rotado
Equipo vainilla y variantes rotadas.

Contribuyendo

Como siempre, buscamos personas que quieran contribuir con Alt-F4, ya sea enviando un artículo o ayudando con la traducción. Si tienes algo interesante en mente que quieres compartir con la comunidad de una manera pulida, este es el lugar para hacerlo. Si no estás muy seguro, con gusto te ayudaremos discutiendo ideas de contenido y preguntas sobre la estructura. Si eso te gusta, únete a [Discord] (https://discord.gg/nxnCFkb) para comenzar.

Como el próximo viernes cae en el día de Navidad, decidimos no publicar un número esa semana, lo que significa que este es el último número de Alt-F4 de este año. Haremos nuestro glorioso regreso el primer día de enero con un episodio especial que repasa cómo se ha desarrollado el proyecto hasta ahora, con las perspectivas de varios miembros del equipo sobre el trabajo que han estado haciendo. Debería ser divertido.