Programando un módulo para Minetest
Hace poco hablé de Minetest y comenté que lo estaba valorando como una posible herramienta educativa... todo consiste en programar lo que necesitas para poder mostrarlo en un entorno ya pensado para poder interactuar con él. Me preguntaba entonces, en aquel artículo, si sería muy difícil programar tus propios módulos para adaptar la plataforma a tus necesidades. Bien, he comenzado la exploración y tras algunos tropiezos voy encontrando la manera de hacerlo. En este artículo vengo a contar mis primeros pasitos. ¿Qué hace el módulo de prueba que quiero desarrollar? Un espejo mágico. Ya sabéis mi debilidad con las conversacionales y yo quiero un objeto o un algo con el que conversar: una estupidez artificial es lo más adecuado.
Montar un mundo
Lo primero que hice fue un mundo de pruebas, sólo para ver si era complicado y trasteé un poco con los módulos, cuáles se cargan, cuáles no... usé módulos hechos sólo por probar1 y el mundo quedó resultón.
En la pantalla se puede ver que podemos crear un mundo simplemente
pulsando el botón donde dice Nuevo
. Eso hará que nos aparezca la
pantalla de crear un nuevo mundo.
Otro aspecto importante es que si habilitamos las opción Hospedar
servidor
desde cualquier ordenador conectado a la misma red, se puede
compartir el mundo y jugar juntos2 desde los equipos conectados a
la misma.
Echado un primer vistazo al juego en general, ya vemos que podemos utilizarlo en modo monousuario, como si fuera un lego virtual; poner un servidor en una red local y jugar con los usuarios de dicha red, algunas pruebas he hecho con mi hija conectados ambos en el mismo mundo local3 y por último, también podemos conectarnos a un servidor de los muchos que hay por Internet y jugar a un mundo que ha hecho otro4.
El diálogo anterior nos permite generar un mundo con las opciones que tenemos ahí. Ponerle un nombre, modificar la semilla aleatoria sobre la que se generará el mapa, etc. Como lo que necesito es un mundo para hacer pruebas tampoco hace falta mucha historia, pero ya puestos hice un mundo con todo (cavernas, valles, y todas las opciones posibles). Lo llamé Kvintero por otro proyecto con el que ando, pero tampoco es importante a estas alturas el nombre. El caso es que funciona y lo puedo recorrer, crear objetos y disponerlos en él como me parezca.
Es decir, ahora tenemos un terreno muy bonito, pero sigue siendo un mundo por construir. Hay montañas, valles, cuevas, ríos... todo en automático y sin esfuerzo. Como inicio, ya es bastante prometedor. Puedes moverte e interactuar, pero si quieres tener cosas más interesantes es aconsejable cargar algunos módulos, como he dicho.
En esa lista, al seleccionar el nombre de uno de los módulos nos mostrará la información del módulo:
Como podemos observar, nos permite activarlo con el selector de
arriba, ver las dependencias (incluso las opcionales), etc.
Además, otra de las funciones recomendables de este diálogo lo
proporciona el botón que hay en la parte inferior derecha: Encontrar
más mods
.
Podemos obtener información más detallada de los módulos pulsando en
su botón de opción (el de más a la derecha) o incorporarlo al mundo
pulsando sobre el botón +
. Si lo hacemos, el programa se conecta con
el repositorio de módulos y descarga el código en tu máquina y lo
guarda en el directorio ~/.minetest/mods/
La estructura del directorio .minetest
es buena tenerla en
cuenta... En ese lugar también guarda el sistema información que nos
viene muy bien, un fichero llamado debug.txt
donde nos mostrará
avisos que nos pueden servir para depurar nuestro código.
También he descubierto, que lanzando Minetest desde la consola, nos aparece información adicional que nos va declarando todas las acciones que se llevan a cabo en el mundo. He utilizado el siguiente comando:
minetest > servidor.log 2> acciones.log
Después podemos observar el contenido de dichos archivos. Por ejemplo,
servidor.log
puede contener algo así:
Loaded texture: /usr/share/minetest/games/minetest_game/menu/header.png Loaded texture: /home/notxor/.minetest/games/Lord-of-the-Test-1.1.0/menu/icon.png Loaded texture: /usr/share/minetest/games/devtest/menu/icon.png Loaded texture: /usr/share/minetest/games/minetest_game/menu/icon.png Loaded texture: /usr/share/minetest/textures/base/pack/plus.png (T@mobs_animal)[MOD] Mobs Redo Animals loaded
El fichero de acciones podría ser algo así:
2021-05-28 19:08:21: [Main]: Automatically selecting world at [/home/notxor/.minetest/worlds/Kvintero] [ALSOFT] (EE) Failed to set real-time priority for thread: Operation not permitted (1) [ALSOFT] (EE) Failed to set real-time priority for thread: Operation not permitted (1) 2021-05-28 19:08:29: ACTION[Main]: [MOD] Mobs Redo loaded .__ __ __ _____ |__| ____ _____/ |_ ____ _______/ |_ / \| |/ \_/ __ \ __\/ __ \ / ___/\ __\ | Y Y \ | | \ ___/| | \ ___/ \___ \ | | |__|_| /__|___| /\___ >__| \___ >____ > |__| \/ \/ \/ \/ \/ 2021-05-28 19:08:29: ACTION[Main]: World at [/home/notxor/.minetest/worlds/Kvintero] 2021-05-28 19:08:29: ACTION[Main]: Server for gameid="minetest" listening on 0.0.0.0:30000. 2021-05-28 19:08:32: ACTION[Server]: Notxor [127.0.0.1] joins game. List of players: Notxor 2021-05-28 19:08:32: WARNING[Main]: Irrlicht: Could not open file of texture: character.png 2021-05-28 19:08:34: ACTION[Server]: Notxor uses espejito:node, pointing at [node under=61,12,320 above=61,13,320] 2021-05-28 19:08:37: ACTION[Server]: Notxor pregunta al espejito: Hola 2021-05-28 19:08:41: ACTION[Server]: Notxor uses espejito:node, pointing at [node under=61,12,320 above=61,13,320] 2021-05-28 19:08:44: ACTION[Server]: Notxor pregunta al espejito: asdfasd 2021-05-28 19:08:49: ACTION[Server]: Notxor uses espejito:node, pointing at [node under=61,12,320 above=61,13,320] 2021-05-28 19:08:58: ACTION[Server]: Notxor pregunta al espejito: Contenido de la pregunta 2021-05-28 19:09:01: WARNING[Main]: Irrlicht: Could not open file of texture: mobs_chicken_white.png 2021-05-28 19:09:06: ACTION[Server]: Notxor uses espejito:node, pointing at [node under=61,12,321 above=61,13,321] 2021-05-28 19:09:09: ACTION[Server]: Notxor pregunta al espejito: asdf asd 2021-05-28 19:09:14: ACTION[Server]: Notxor digs default:dirt_with_grass at (62,12,321) 2021-05-28 19:09:16: ACTION[Server]: Notxor digs default:dirt_with_grass at (62,12,322) 2021-05-28 19:09:17: ACTION[Server]: Notxor places node default:dirt_with_grass at (62,12,322) 2021-05-28 19:09:18: ACTION[Server]: Notxor places node default:dirt_with_grass at (62,12,321) 2021-05-28 19:09:19: ACTION[Server]: Notxor digs default:dirt at (63,12,320) 2021-05-28 19:09:19: ACTION[Server]: Notxor digs default:dirt_with_grass at (62,12,320) 2021-05-28 19:09:20: ACTION[Server]: Notxor places node default:dirt_with_grass at (62,12,320) 2021-05-28 19:09:21: ACTION[Server]: Notxor places node default:dirt_with_grass at (63,12,320) 2021-05-28 19:09:32: ACTION[Main]: Server: Shutting down
Se puede observar, que he realizado algunas acciones sólo para ver cómo se muestran en la salida. El redireccionar las salidas a ficheros lo he hecho sólo a efectos de tener algo que cortar y pegar en el artículo, pero no me parece una mala política si quieres tener un seguimiento de lo que hacen los usuarios en tu servidor.
Programar un módulo
Esto es sólo una prueba, pero no me ha costado demasiado hacerlo, más allá de algún pequeño atasco por falta de información, no porque haya poca, sino porque me he tirado al ruedo sin leer el manual de instrucciones y me ha pillado todo el asunto muy poco leído e instruido.
El caso es que estuve pensando durante algún tiempo qué tipo de módulo podía escribir, algo que fuera relativamente fácil de modelar, que fuera, como mucho, un cubo para mostrarse en un voxel... pero sí tenía claro que quería programar un diálogo de entrada de datos y también una salida que fuera de texto a la consola. Todas esas condiciones se cumplían en el intento de hacer un espejo mágico al que hacer preguntas y que conteste.
Preparar el proyecto, fue muy sencillo: creé un directorio llamado
espejito
dentro del directorio de módulos, es decir:
~./.minetest/mods/espejito
El único contenido que puse en el primer paso fue el fichero
mod.conf
con el siguiente contenido:
name = espejito description = Proporciona un espejo mágico con el que hablar depends = default author = Notxor
Como se puede ver es una configuración muy sencilla: proporciona un nombre, una descripción, las dependencias de otros módulos y el autor. Es decir, la información mínima para funcionar, incluso algunos de los campos no son necesarios para que Minetest lo procese como un módulo.
También creé un fichero llamado init.lua
, al principio vacío.
Crear el objeto
Lo primero, y quizá lo más fácil, fue introducir un objeto nuevo en el módulo. Sólo hay que registrar el nodo:
minetest.register_node( "espejito:node", { description = "Espejito mágico", -- drawtype = "glasslike", drawtype = "signlike", paramtype2 = "wallmounted", selection_box = { type = "wallmounted", }, tiles = {"espejito_textura.png"}, on_use = function(itemstack, player, pointed_thing) minetest.show_formspec(player:get_player_name(), "pregunta_espejo", get_pregunta_formspec()) end, sounds = default.node_sound_glass_defaults(), groups = {cracky = 3}, })
Este es el código tal y como está funcionando ahora, ha habido varias
evoluciones según miraba la documentación. De primeras, por ejemplo,
lo definí como un objeto drawtype
de cristal, sin embargo, después
de mirar la documentación me pareció más adecuado que se dibujara como
un cartel o señal, además seleccioné el tipo de selección como
montado en la pared que es donde debería colgarse un espejo.
También, proporcioné una textura que se encuentra en el siguiente
path:
~/.minetest/mods/espejito/textures/espejito_textura.png
Es un PNG
de \(16 \times 16\) creado para la ocasión y, como se puede
apreciar, no hace falta especificar el path, porque Minetest
entiende que debe estar en el lugar estándar.
Podemos ver también, que se especifican los sonidos de cristal. Es
decir, cuando lo rompemos, lo golpeamos o realizamos una acción con él
se utilizarán los sonidos que hay para cristal en el módulo
default
.
También llamará la atención la aparición de on_use
donde se
establece una función que se ejecutará cuando queramos usarlo, sin
embargo, lo explicaré más detenidamente más adelante, porque es donde
está el meollo del código. Pero antes de meternos en código quiero
comentar algunos aspectos interesantes de la declaración de los
objetos, por ejemplo el de cómo crear un espejito mágico:
minetest.register_craft( { type = "shaped", output = "espejito:node", recipe = { {"default:glass", "default:diamond", "default:glass"} }, })
El formato de la creación de un espejito es sencillo de entender: es
un tipo forma, se genera una salida (output = "espejito:node"
) con
una receta (recipe
)5.
Simplemente con esto ya aparece el objeto en el mundo. Aunque no se puede hacer mucho con él: podemos crearlo, destruirlo, pero poco más.
Dotar al objeto de acciones
Como dije antes, al declarar el objeto se ha metido una función que
responderá a la acción on_use
. ¿Qué hace esa función? Mostrar un
formulario, veamos el siguiente código:
local function get_pregunta_formspec() return "size[5.75,3.5]".. "label[1,0.25;¿Qué desea vuestra excelencia?]".. "field[0.5,1.5;5.25,1;pregunta;Pregunta: ;]".. "button_exit[1.5,2.3;3,0.8;preguntar;Preguntar]" end local function conversacion() local frases = { "Cuñaaaaao...", "No puedorrr, no puedorrr...", "¡¿Que si quiere bolsa?!" } return frases[math.random(#frases)] end
como vimos en on_use
se llamaba a show_formspec
, se obtiene el
nombre del jugador, se pone nombre al formulario (pregunta_espejo
) y
se llama a la función get_pregunta_fromspec()
.
Para capturar el contenido del formulario:
minetest.register_on_player_receive_fields(function(player, formname, fields) local nombre_jugador = player:get_player_name() if fields.pregunta ~= nil then -- información de depuración a la consola minetest.log("action", nombre_jugador .. " pregunta al espejito: " .. fields.pregunta) if (formname == "pregunta_espejo" and fields.pregunta ~= "") then minetest.chat_send_player(nombre_jugador, conversacion()) end end end)
Como se puede observar, independientemente de lo que le preguntemos al
espejo, lo que hace la función conversacion
es enviar al azar una
frase de las que hay en su lista de frases. Vale, que no son muchas ni
muy inteligentes... pero es una estupidez artificial, que es como
una inteligencia artificial pero sin intentar engañar al usuario.
Conclusiones
Es el primer módulo para Minecraft que hago y aún no está terminado. Mi intención es hacer un bot conversacional.
Los siguientes pasos serán mejorar la funcionalidad de conversacion
,
primero haciendo una especie de Eliza o algo similar. El objetivo
no es crear algo usable para el juego como tal, aunque si es así,
tampoco pasaría nada. El objetivo último es aprender cómo funciona la
creación de módulos y profundizar hasta tener una idea más precisa de
lo que es factible a la hora de diseñar un juego para esta
plataforma.
Para más información podéis consultar un libro introductorio sobre la programación de módulos para Minetest y la API de la plataforma en los siguientes enlaces:
Footnotes:
Y por tener código que mirar, copias y plagiar si fuera necesario. Algo que más que necesario es imprescindible cuando no sabes muy bien qué estás haciendo, como es el caso.
Recuerda abrir en el cortafuegos el puerto 30000, o el que utilices para el juego.
Es una experiencia bastante más agradable, no hay lag y todos los jugadores son amigables (depende de con quién te juntes, claro)
Si hace poco que te has interesado por este mundillo, seguramente el mundo que diseñe otro para jugar será mucho mejor del que puedes crear tú en el primer momento... como me sucede a mí.
La estructura de la receta es conveniente mirarla en la documentación. No es complicado de entender, pero necesita una explicación larga, especialmente si no has jugado nunca a este juego.
Comentarios