@ agnasg

permafrost-engine review

02-06-2021 11:17 AM

Estaba analizando este código fuente de un juego RTS (real time strategic, o juego de estrategia a tiempo real), y mi opinión en general coincide con los comentarios que aparecen en este hilo de discusión en hackers news (también en reddit, pero hace un año).

Bueno, no exactamente “excelente” pero tiene un buen nivel de calidad. El programador es un profesional. Me llama la atención la forma consistente y repetitiva tanto en hn como en reddit de comentarios como “bravo!”, “Nice and clean code”, “awesome”, “amazing”, en forma uniforme y generalizada. Bien inusual. Más bien raro.

Algunas observaciones:

Buena organización (mencionado en los comentarios y también por el programador). Esto claramente es lo que impacta desde el comienzo. Los juegos son piezas de software bien complicadas, por lo que mantener una consistente y clara forma de organizar los distintos componentes es la clave. He visto docenas de juegos que hacen un genuino intento en lograr esto, fracasando la mayor parte de las veces. En khpx los resultados han sido mixtos y mi apreciación es que al igual del manejo de las estructuras de datos, lograr una organización óptima, eficiente, clara, que facilite el mantenimiento del código no solamente es difícil, sino que consume una inmensa y costosisima cantidad de tiempo. Yo he ido dilatando esta labor pero es algo que hay que atacar a tiempo antes de perder el control del engine.

Uso de goto. Algunos comentadores aplaudieron el uso de “goto” de forma de generar una salida única para las funciones. Es decir, el código está lleno de cosas como esta:

static bool rstate_init (...)
{
    if(!rstate->sq_lock)
        goto fail_sq_lock;
    ...
    if(!rstate->sq_cond)
        goto fail_sq_cond;
    ...
fail_sq_cond:
    SDL_DestroyMutex(rstate->sq_lock);
fail_sq_lock:
    return false;
}

Y la respuesta es que es más fácil de seguir, o que es más facil agregar instrucciones en un punto único de salida. En mi opinión un “goto” nunca es más facil de seguir, y genera rápidamente código espaguetti indescifrable. Además, no se supone que las funciones deben tener unas 30-50 líneas (no más de 100)? Y que las funciones deben tener 1 quizás 2 actividades que realizar? Entonces por qué un único punto de salida es importante si debería haber en una función 1-2 salidas? De acuerdo a mi script bash, en la permafrost-engine hay 667 “goto”s. En khpx hay 0.

Es un proyecto grande, pues tiene 127k líneas de código (sin incluir scripts y utilities). khpx luego de 3 años tiene la mitad, algo más de 60k. En el hilo de reddit mencionan 38KLOC (hace un año), no sé cómo hacen el conteo de líneas de código. Yo hago un conteo crudo con mi script todo propósito / todo terreno en una línea de código Bash:

find . -type f -exec cat {} + | wc -l

Reinvención de la rueda. Esto es algo que yo he estado evadiendo al máximo en khpx, por ello utilizo algunas librerías como Dear Imgui y el STL (el standard template library). Nadie discute que el STL está suficientemente probado y optimizado. No hay razón para utilizar algo diferente. permafrost-engine re-implementa todo, incluyendo vectores, queues, y… ta ta: malloc, su propio sistema de manejo de memoria. Nada más por eso yo descartaría esta engine. Ni idea por qué los aplausos.

Poca documentación, y los pocos comentarios sufren el síndrome de inutilidad:

/* Submit and run the queued batch to completion */
render_thread_start_work();

Uso de macros. No usar macros es una buena práctica. permafrost-engine usa macros masivamente. Hay 1357 “#define”s. No sé, yo voy a seguir “no usándolos”, o usándolos al minimo: hay 170 defines en khpx.

Indirección redirigida. Si buscamos el caracter ‘&’ en permafrost-engine el resultado son 8282 matches. En khpx es 1350. ¿Pero por qué? Porque esta engine usa cosas como “&task->ctx” al máximo. Festival universal ilustrado de apuntadores. No hay nada malo per se en ello. Pero es una mala práctica en 2021.

Extra. Por cierto, ¿recuerdan mi post sobre *scanf? En permafrost-engine hay 53 ocurrencias, en khpx hay 0.

Variables / data structures globales. Excelente manejo. Todas están definidas como “static” y confinadas a su archivo. En khpx las estructuras de datos vuelan por todo el engine sin control de acceso alguno. Este es un punto que en algún momento tengo que corregir, por dificil que me parezca en este momento.

Conclusiones

Como digo al comienzo es un trabajo profesional y la engine es de alta calidad. Salvo los comentarios anteriores, fue una buena experiencia revisar el código y lo recomiendo altamente. De hecho voy a utilizar ideas de esta engine para resolver el problema que tiene khpx al manejar las estructuras de datos.