(suprimir el último párrafo del apartado 3.) 3.1. El mapa del código Para no perdernos en el proceloso mar de líneas de código (actualmente los ficheros fuente de Minë ocupan cerca de 10.000 líneas), mantenemos a modo de mapa un diagrama de clases, hecho con UML. La última versión está disponible en la página electrónica de Minë, http://eutherpe.org/desarrollo.html. Los diagramas muestran el diseño de alto nivel del programa, y son imprescindibles para conocer su estructura, antes de empezar a leer las clases de una en una. Hay tres grandes bloques: - Modelo de datos-> una representación abstracta del mundo y sus habitantes, independiente del interfaz (debería poder usarse tal cual si cambiáramos el interfaz, por ejemplo a uno gráfico). - Control-> el interfaz de usuario. Aquí se interactúa con los jugadores y se controla (o sea, se da órdenes) a los PJ y PNJ. - Infraestructura-> algunas clases no relacionadas entre sí, con utilidades genéricas usadas por el resto de módulos. [insertar el diagrama mine.png] 3.1.1. Modelo de datos Este es el bloque más significativo, que contiene el diseño "conceptual". Cada clase de aquí representa una entidad del juego tal como lo ven los usuarios (personajes, salas, objetos...). [insertar el diagrama mine-modelo.png] El diagrama se lee mejor de arriba a abajo. Al principio parece un poco confuso, pero todos los elementos tienen un sentido intuitivo. El Mundo es único y representa eso, todo el mundo de Minë y lo que hay en él. Contiene una lista de Salas (el rombo en UML significa contener), y cada Sala, a su vez, contiene personajes: los personajes que se encuentran dentro de esa sala. Los Objetos ahora no están modelados del todo, pero en principio podrán estar dentro de una Sala o de un Personaje. Un Personaje, a su vez, puede ser PersonajeJugador o PersonajeNoJugador (la línea de puntos con un triángulo, en UML, significa subclasificación). Todos los personajes conocen de 1 a 5 idiomas, y los jugadores tienen además una Raza y una Profesión. Finalmente, todo Personaje es manejado por un objeto Controlador, que es quien le da las ordenes de lo que debe hacer, y a quien informa de las cosas que le ocurren. Controlador no es una clase, sino un interfaz, lo que significa que no contiene código, sino sólo la declaración de qué métodos debe tener una clase que pueda controlar Personajes. Las clases que implementan ese interfaz pertenecen al siguiente bloque del diagrama. Hay algunas asociaciones más entre clases, que se pueden entender observando el código fuente. Las clases que tienen un único rectángulo (por ejemplo ConnectionHandler) no pertenecen a este diagrama, se muestran aquí sólo para poder ver las asociaciones. 3.1.2. Control. Este bloque es un poco más abstracto. Contiene las clases que llevan el control del programa y la interacción con el jugador. [insertar el diagrama mine-control.png] La clase Mine es el programa en sí. Es el fichero que se ejecuta al inicio y contiene el bucle principal. Inicializa el Mundo y los InterfacesPJ (las flechas punteadas en UML representan una asociación no permanente). Un InterfazPJ es el objeto que controla a un PersonajeJugador. Acepta conexiones telnet para recibir comandos de un jugador y enviarle sus respuestas, y también da órdenes al personaje y recibe los "sucesos" o "eventos" que éste genera. La funcionalidad de un InterfazPJ, al ser bastante complicada, está repartida en diferentes Dialogos. Cada Dialogo es una "máquina de estados" que representa una situación de diálogo con el jugador (por ejemplo, creación de un personaje nuevo, juego normal, modo combate, etc.). Una máquina de estados es un objeto que tiene un "estado" en cada momento, y responde a "sucesos" que le ocurren bien cambiando a otro estado o realizando una acción. Por ejemplo, en el dialogo "crear un pj", al entrar en estado 1 pregunta el nombre del pj, y espera que ocurra algo. Si el jugador teclea un nombre correcto, lo registra y pasa al estado 2. Si teclea un nombre incorrecto (que ya existe, por ejemplo), devuelve un mensaje de error y continúa en estado 1. Un InterfazPNJ tiene la misma misión, pero esta vez sobre un PersonajeNoJugador. La diferencia es que no tiene ningún socket telnet, sino que las órdenes a ejecutar las decide un programa de inteligencia artificial (más o menos "inteligente"). 3.1.3. Infraestructura En este bloque tenemos clases que representan conceptos genéricos que tienen más relación con el sistema operativo que con un juego MUD. [insertar el diagrama mine-infraestructura.png] ConnectionHandler y Connection representan "sockets" TCP/IP, es decir, los canales de comunicación que implementan el protocolo telnet, usado para hablar con los jugadores. TimerHandler y Timer son temporizadores, o cronómetros, útiles para medir tiempos. Utils es un módulo "cajón de sastre" para meter funciones sencillitas y variadas. 3.2. Entendiendo el código Hemos seguido unas reglas básicas de codificación, para mantener un estilo coherente en distintos ficheros hechos por diferentes programadores. En primer lugar, tenemos un fichero fuente por cada clase, con el mismo nombre de la clase en minúsculas (p. ej. la clase DialogoCrear está en dialogocrear.py). Todos los ficheros tienen al principio la nota de "CopyLeft", el comentario que indica su contenido, los import, y la clase. Todas las clases tienen un bloque de comentarios explicando la clase con detalle. Al final se incluye un trozo de código para pruebas unitarias del módulo. Al escribir el código se siguen unas normas de diseño, que se pueden consultar en el fichero documentos/diseño.txt. Al leer el código es fundamental leer las clases siguiendo el diagrama, de arriba a abajo, y leer y entender en primer lugar la cabecera donde se explica qué representa, y cuales son sus atributos. El bucle principal del programa está en mine.py, pero casi no hace falta ni que lo mires. El programa se comporta como si todos los objetos del sistema estuvieran "vivos" y funcionando al mismo tiempo. Cuando ocurre cualquier cosa, el objeto correspondiente recibe un mensaje de "evento", es decir, "alguien" llama a un método de un objeto diciéndole que ha ocurrido algo (al principio no es necesario que sepas quién es ese alguien). Por ejemplo, si un jugador escribe algo desde su terminal, el objeto de tipo InterfazPJ correspondiente a ese jugador recibe una llamada al método comando_recibido().