Tutorial Projeto Darkstar (parte III)

Nesta parte do tutorial, vamos aprender um pouco sobre a comunicação entre servidor e cliente.

No Darkstar, as mensagens através das quais o servidor e cliente se comunicam têm a forma de um objeto java.nio.ByteBuffer. As mensagens mandadas do servidor para o cliente servem basicamente para coordenar o início e fim de sessões e enviar estados atuais (status) do jogo. Já as mensagens enviadas do cliente ao servidor são em grande parte instruções de jogo, como atacar, mover-se, etc. Há também as mensagens de chat, que estabelem a comunicação entre os jogadores dentro do jogo.

O código utilizado para implementação do protocolo de comunicação encontra-se em pacotes do módulo Project Snowman Common. São eles:


com.sun.darkstar.example.snowman.common.protocol.enumn
com.sun.darkstar.example.snowman.common.protocol.handlers
com.sun.darkstar.example.snowman.common.protocol.messages
com.sun.darkstar.example.snowman.common.protocol.processor


Vamos agora configurar as seguintes mensagens do servidor:

NEWGAME: um pacote NEWGAME é enviado pelo servidor para indicar o início do jogo. Após receber este pacote, o cliente deve visualizar o campo de batalha e esperar um pacote ADDMOB (descrito abaixo) com o status inicial do jogo. O pacote contém:
    - Um inteiro que representa o ID do cliente
    - Uma string que designa o nome do mapa em que a partida será jogada.

STARTGAME: este pacote é enviado pelo servidor para indicar que todos os clientes receberam o status inicial do jogo e que o mesmo pode ser iniciado. O pacote é composto apenas pelo OPCODE.

ADDMOB: como já descrito, este pecote é enviado após o NEWGAME com o satus inical da partida. O pacote é composto por:
    - Um inteiro que representa o ID da entidade a ser adicionada.
    - Um float representando a coordenada X da entidade no campo de batalha.
    - Um float representando a coordenada Y da entidade no campo de batalha.
    - Um inteiro representando o tipo da entidada (jogador, bandeira, etc).
    - Um inteiro representando o time da entidade (Vermelho, Azul, etc).
    - Uma string representando o nome da entidade.


Como dito acima, todas essas mensagens serão transmitidas no formato de ByteBuffer. Portanto, no caso do pacote NEWGAME, por exemplo, o ByteBuffer será composto da seguinte forma:
    - O primeiro byte sempre representará o OPCODE do pacote.
    - O inteiro que represneta o ID do cliente é incluídos nos 4 bytes subsequentes.
    - O valor do comprimento da string que representa o nome do mapa ocupará os 4 bytes subsequentes.
    - A string (convertida para uma sequencia de bytes) ocupará o resto do ByteBuffer.

É hora de por a mão na massa! Acesse o arquivo ServerMessages.java em Project Snowman Common ->  Source Packages -> com.sun.darkstar.example.snowman.common.protocol.messages -> ServerMessages.java.

Neste arquivo você encontrará os métodos createNewGamePkt(..), createStartGamePkt() e createAddMOBPkt(..) aguardando serem implementados. Eis um exemplo de como cada um deles pode ser implementado:


     public static ByteBuffer createNewGamePkt(int myID, String mapname) {
         byte[] bytes = new byte[1 + 4 + 4 + mapname.length()];
         ByteBuffer buffer = ByteBuffer.wrap(bytes);
         buffer.put((byte) EOPCODE.NEWGAME.ordinal());
         buffer.putInt(myID);
         buffer.putInt(mapname.length());
         buffer.put(mapname.getBytes());

         buffer.flip();
         return buffer;
     }


     public static ByteBuffer createStartGamePkt() {
         byte[] bytes = new byte[1];
         ByteBuffer buffer = ByteBuffer.wrap(bytes);
         buffer.put((byte) EOPCODE.STARTGAME.ordinal());

         buffer.flip();
         return buffer;
     }


     public static ByteBuffer createAddMOBPkt(int targetID,
                                              float x,
                                              float y,
                                              EMOBType mobType,
                                              ETeamColor team,
                                              String mobName) {
         byte[] bytes = new byte[1 + 20 + 4 + mobName.length()];
         ByteBuffer buffer = ByteBuffer.wrap(bytes);
         buffer.put((byte) EOPCODE.ADDMOB.ordinal());
         buffer.putInt(targetID);
         buffer.putFloat(x);
         buffer.putFloat(y);
         buffer.putInt(mobType.ordinal());
         buffer.putInt(team.ordinal());
         buffer.putInt(mobName.length());
         buffer.put(mobName.getBytes());

         buffer.flip();
         return buffer;
     }


Feito isso, construa o módulo Project Snowman Common:

Acabamos de estabelecer uma linguagem entre servidor e cliente, vamos agora entender como ela funciona:

1. O servidor envia um NEWGAME a todos os clientes conectados.
2. O servidor envia um ADDMOB para cada jogador e bandeira no jogo.
3. O servidor envia um READY para cada cliente.
4. Cada cliente envia um READY para o servidor.
5. O servidor envia um STARTGAME para cada cliente.

Precisamos agora configurar o servidor para agir de acordo com esse padrão de comunicação. Iremos utilizar a classe ServerMessages, que implementamos parcialmente acima, e o método send(..) da interface ClientSession para o envio das mensagens.


Acesse o arquivo SnowmanServer.java em Project Snowman Server ->  Source Packages -> com.sun.darkstar.example.snowman.server -> SnowmanServer.java. Antes de tudo, inclua na classe o atributo:


private EntityFactory entityFactory;

Vamos agora modificar os já conhecidos métodos loggedIn(..) e initialize(..) para:

     public void initialize(Properties props) {
         this.entityFactory = new EntityFactoryImpl();
     }

     public ClientSessionListener loggedIn(ClientSession session) {
         session.send(ServerMessages.createNewGamePkt(1, "default_map"));
         session.send(ServerMessages.createAddMOBPkt(1,
                                                     10,
                                                     10,
                                                     EMOBType.SNOWMAN,
                                                     ETeamColor.Blue,
                                                     session.getName()));
         session.send(ServerMessages.createReadyPkt());

         return new SnowmanPlayerListener(
                 entityFactory.createSnowmanPlayer(session));
     }


Após essas modificações, você deverá incluir alguns imports. O Netbeans faz isso automaticamente. Basta acessar o menu Código-fonte e clicar em Corrigir Importações (ou pressionar Ctrl+Shift+I).

Precisamos também fazer com que o servidor identifique o READY enviado pelo cliente no passo 4 da rotina de comunicação descrita anteriormente. Para isso, acesse a classe SnowmanPlayerListener em Project Snowman Server -> Source Packages -> com.sun.darkstar.example.snowman.server ->  SnowmanPlayerListener.java e modifique o método receivedMessage(..) para:


     public void receivedMessage(ByteBuffer message) {
         if (EOPCODE.READY == EOPCODE.values()[message.get()]) {
             playerRef.get().getSession().send(ServerMessages.createStartGamePkt());
         }
     }


Aqui também será necessário corrigir as importações.

Vamos testar nossas modificações. Rode o servidor, inicie um cliente e tente uma conexão. Você deve conseguir agora logar e visualizar o jogo, embora ainda sem bandeiras e com apenas um jogador:


No próximo post começaremos a lidar com partidas multi-player. Até lá!

Comments:

Post a Comment:
  • HTML Syntax: NOT allowed
About

Thiago Sá é estudante de graduação de Engenharia de Teleinformática na Universidade Federal do Ceará e atualmente exerce a função de Embaixador de Campus da Sun Microsystems em Fortaleza, CE.

Search

Archives
« Abril 2014
SegTerQuaQuiSexSábDom
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Today