Trabalho 3 - Sistemas Distribuídos

Data de entrega: 3/7

Descrição

O trabalho consiste em criar um protótipo de jogo MORPG (Multiplayer Online Role Playing Game), estilo PVP (Player Versus Player), com arquitetura peer-to-peer, em Lua, utilizando a biblioteca DALua.

Cada jogador executará dois processos DALua (em terminais distintos do sistema) para participar do jogo. Um deles será a interface gráfica (textual) e o outro funcionará como o console do jogo, aguardando comandos do teclado. Estes processos fazem parte do daemon ALua criado localmente na máquina para aquele jogador.

Inicialmente o jogo encontra-se no estado desconectado. Sendo assim, o jogador terá a opção de efetuar dois comandos:

O primeiro jogador deverá criar uma nova sessão, enquanto que os demais jogadores se conectarão ao computador que iniciou a sessão ou a algum computador já conectado.

Em seguida, o jogador é apresentado às seguintes opções:

Neste momento, os jogadores podem criar ou entrar em mundos de jogo. Um mundo de jogo é um grupo isolado de jogadores. Cada jogador pode participar de apenas um mundo num dado momento.

Cada mundo de jogo possui um mapa onde os jogadores estão situados. O mapa é um tabuleiro 2D composto por 50x50 posições possíveis. Cada jogador estará localizado em alguma posição do mapa. Quando um mundo é criado, o mapa deve ser carregado de um arquivo de texto onde cada caractere corresponde a uma posição. Os seguintes caracteres são usados no arquivo:

O arquivo mapa.txt contém um exemplo de mapa. Considere que todos os arquivos de mapa terão formato válido e existirão em todos os computadores. No caso de mundos já criados, o jogador recebe o mapa com seu estado atual diretamente de um jogador que já se encontrava no jogo.

Na interface gráfica é exibida a visão de cada jogador. Ela é topográfica, limitada a 11x9 posições do mapa, com o personagem do jogador no centro, ou seja, partindo do centro, é possível visualizar 5 posições para cada lado na direção horizontal e 4 posições na vertical.

Quando um jogador entra no mundo, seu personagem é inserido na posição de spawn do mapa (ou na sua vizinhança imediata, caso já exista alguém naquela posição). Cada personagem é identificado e exibido com números de 1 a 9 (o limite é de 9 jogadores por mundo), atribuídos automaticamente no momento em que o jogador cria ou entra no mundo de jogo. Observe que esses identificadores são únicos dentro de um mundo, porém independentes entre mundos diferentes.

Os obstáculos do mapa são indicados com 'X', enquanto que as bordas (área anterior a [1,1] ou posterior a [50,50]) são preenchidas com '#'. Todo o restante deve ser preenchido com espaço em branco.

Durante a execução do jogo, as ações disponíveis para cada jogador são:

O objetivo do jogo é matar o maior número possível de jogadores enquanto mantém-se vivo. Cada jogador tem uma pontuação, ou nível, que é incrementado quando mata outro jogador, e decrementado quando é morto por alguém. Na tela de cada jogador é exibido o identificador do que tiver a maior pontuação do momento naquele mundo de jogo.

Quando um jogador é morto, seu corpo é adicionado no local da morte, representado pelo caractere '&' (porém não é considerado um obstáculo). Então, ele continua recebendo eventos do jogo, mas é incapaz de efetuar as ações move e attack. Após 5 segundos, seu personagem é automaticamente reinserido na posição de spawn do mapa, e as ações são reativadas.

Na interface, além do mapa são exibidas as 5 últimas mensagens enviadas/recebidas do jogador. Elas podem conter também mensagens informativas, avisando que jogadores entraram ou saíram, ou que alguém foi morto.

Caso o jogador decida sair do mundo de jogo, seu nível será zerado. Se ele for o único jogador no mundo, este será destruído também, podendo ser recriado no futuro.

Não é possível atacar jogadores que estejam na posição de spawn do mapa ou numa área de proteção. Também não é possível mover-se para uma posição com obstáculos, outros jogadores ou para além das bordas do mapa.

Para simplificar, considere que os jogadores nunca fecharão os terminais de jogo e seus respectivos processos (não existe uma opção "disconnect" ou "unlink").

Em termos de implementação:

  1. O processo da interface gráfica cria um daemon ALua na máquina local com dalua.init. O processo do console conecta-se ao mesmo daemon fazendo uma nova chamada a dalua.init com os mesmos parâmetros. Então, os comandos digitados no console são enviados como mensagens para o processo da interface.
  2. Ao criar uma sessão, o processo deve criar também uma aplicação DALua (dalua.app) global da qual participarão todos os jogadores conectados. Ela será útil para obter a lista de mundos criados.
  3. Para se conectar a uma sessão existente, inicialize o DALua e então utilize dalua.link passando o endereço do computador que tem a sessão.
  4. Cada mundo de jogo corresponderá a uma aplicação DALua. Ao criá-la, é útil criar também um objeto de exclusão mútua para a aplicação, que será usado em algumas operações. Quando outros processos entrarem ou saírem da aplicação, o Mutex também deverá ser atualizado (ver documentação do dalua.mutex).
  5. Analise bem os casos em que a exclusão mútua é necessária. Cada jogador mantém uma cópia local do mapa, portanto qualquer alteração nele deve ser sincronizada entre todos os processos. A liberação do Mutex só deve ser realizada quando todos os processos confirmarem que a tarefa desejada foi completada.
  6. Para a reinserção cronometrada do jogador que foi morto, utilize o módulo dalua.timer. Ele também pode ser utilizado para criar testes automatizados, enviando comandos periodicamente à interface.
  7. A interface deve ser atualizada automaticamente sempre que algum evento ocorrer. Utilize o comando os.execute("clear") para limpar a tela e então redesenhe-a por completo.
  8. É possível fazer testes em apenas uma máquina, utilizando para isso portas diferentes para cada jogador durante a inicialização do DALua.