Por qué reescribir algo que funcionaba
Skript me permitió lanzar rápido. Al final de la primera temporada el servidor corría sobre unos 57 archivos de script, alrededor de 816 KB de código, y sostenía 90 jugadores a la vez. También tenía límites que no podía arreglar desde dentro de Skript: sin tipos reales, sin pruebas, variables globales compartidas para todo, y cada sistema metiéndose en todos los demás.
Antes de migrar nada auditué el código antiguo línea por línea. El registro llegó a 1.038 filas. El peor hallazgo fue el transportador pagando desde dos bucles separados, así que algunos jugadores ganaban unas diez veces lo que la economía estaba balanceada para dar. También había duplicaciones de objetos. Cada fila se convirtió en una corrección en el nuevo código o en una prueba que demuestra que el error no puede volver.
La forma que tiene
La nueva construcción es un plugin núcleo y un jar por función. El núcleo posee lo que cada función necesita, y cada función es su propio plugin pequeño que se puede construir, probar y desactivar por su cuenta.
- lilies-api solo contiene interfaces. Las funciones se comunican entre sí a través del ServicesManager de Bukkit contra esas interfaces, nunca metiéndose directamente en el código de otra.
- LiliesCore encuentra los jars en su carpeta de funciones y los carga a través del propio gestor de plugins de Paper, en orden de dependencia.
- Los jars de función son unos 30 módulos en total. Cada uno trae su propia config, mensajes, objetos y menús.
Todo es un archivo de configuración
Los precios, tasas de drop, diseños de menú y cada línea de texto del chat viven en YAML, una carpeta por
módulo. El staff puede ejecutar /lilies reload para todo o para un solo módulo, y los nuevos
valores se validan antes de reemplazar a los anteriores, así que un error de tipeo deja la configuración
en marcha intacta. Los valores pequeños se pueden cambiar en el juego con /lilies set sin
abrir un archivo.
Rápido con el servidor lleno
- SQLite en modo WAL con un único hilo de escritura asíncrono, así que el hilo principal del juego nunca espera al disco. Los perfiles de jugador se guardan en caché en memoria.
- Los hologramas y objetos en movimiento son visualizaciones solo por paquetes que el cliente interpola, en lugar de entidades teletransportadas cada tick.
- Un único planificador por segundo compartido en lugar de un temporizador por sistema.
Una prueba de carga con 50 jugadores simulados cosechando 20 cultivos por segundo cada uno cuesta 0,37 ms por tick. El servidor tiene 50 ms por tick para gastar, y mantuvo un TPS estable de 20.
Pruebas que atraparon errores reales
La suite tenía 501 pruebas cuando terminó el núcleo y 780 al final de la fase de bucles económicos. La prueba más útil no fue una prueba unitaria en absoluto. Una prueba de arranque inicia un servidor real con cada jar cargado, y atrapó un error que las pruebas unitarias no podían ver.
Cuando un jugador hace clic derecho al aire, Paper dispara el evento de interacción ya marcado como cancelado. Siete manejadores estaban configurados para ignorar eventos cancelados, así que esas funciones ignoraban silenciosamente cada clic al aire. Cada mock en las pruebas unitarias construía el evento sin cancelar, así que todas pasaban. La prueba de arranque usó el servidor real y falló de inmediato.
Eventos sin objetos perdidos
El módulo de eventos ejecuta 14 minijuegos. Unirse a uno toma una instantánea del inventario y el estado del jugador. Cada forma de salir de un evento, ya sea ganando, perdiendo o abandonando, restaura esa instantánea primero. Los premios solo se entregan después de guardar la restauración, así que un fallo en medio no puede entregar un premio y borrar un inventario, ni al revés.
Lo que aprendí
- Auditar antes de migrar hizo que el nuevo código corrigiera los errores antiguos en lugar de copiarlos.
- Los mocks están de acuerdo con tus suposiciones. Una prueba contra el servidor real es la que te contradice.
- Poner cada número en la configuración significa que balancear la economía deja de necesitar a un desarrollador.