O TEMA DO FÓRUM ESTÁ EM MANUTENÇÃO. FEEDBACKS AQUI: ACESSAR

Netplay no RM2000 (Parte 1)

Iniciado por Dr.XGB, 21/10/2015 às 17:10

Multiplayer Online
by Dr.XGB



Autor: Dr.XGB
Plataforma: RPG Maker 2000
Dificuldade: Avançado
Outro Motor: Destiny Patcher 2.0

Você a essa hora deve estar perguntando: Doutor tá maluco! RPG Maker 2000 Online? Haha! Isso 'non ecksixte'!
SIM! Existe sim. Graças ao Destiny é possível fazer Multiplayer Online no RPG Maker 2000. Resumidamente, ele funciona como uma troca de valores entre servidor e cliente. Dali, você consegue realizar sistemas utilizando esse recurso.
Devido a complexidade do sistema, vou dividir este tutorial em 3 partes. A primeira parte será a conexão em si, porém a conexão será feita no mesmo computador, sem precisar de outra máquina ou até uma máquina virtual. Também vamos aprender a enviar e receber dados entre servidor e cliente. A segunda parte já vai ensinar como fazer os movimentos dos jogadores na rede. Já a terceira parte, falaremos melhor como conectar-se a outra máquina. Combinados?
Espero que gostem deste tutorial e que ele seja útil para vocês. O que me motivou a fazer este tutorial foi uma questão que o membro JoãoTS levantou em um tópico que me chamou muita atenção. Dali eu pensei que poderia sim montar um tutorial e uma demo, uma vez que a Demo que o Bananen-Joe fez há muito tempo, se trata da versão 1.0 do Destiny. Logo resolvi fazer uma versão mais otimizada do sistema online no Rm2k.
Vamos nessa? Hora de trabalhar!



Etapa 1: Configurando o Patch

Normalmente eu gosto que as pessoas leiam a Aula 0 que montei toda vez que crio um tutorial que utilize o Destiny, porém dessa vez vou ter que fazer diferente pois há um pequeno detalhe na configuração que será de extrema importância para que o multiplayer online funcione bem.

  • Assim que criar o seu projeto, feche o RPG Maker 2000 e abra o Destiny Patcher 2.0;
  • Logo na primeira aba, marque a opção Use Destiny.dll e mantenha a versão 2.0;
  • Marque a opção MessageLink activated se quiser que as strings apareçam na caixa de mensagem;
  • A aba Title pode ficar configurada da maneira que você quiser;
  • Agora que vem o detalhe mais importante: na aba Window, haverá uma opção chamada Game runs without focus. Marque esta opção. Isso significa que o jogo continuará rodando normalmente mesmo quando você minimiza a janela ou a deixa inativa. Isso para um multiplayer online é muito importante porque o seu jogo precisa estar sempre aberto para receber os dados que o outro jogador envia, da mesma forma que você também consiga enviar os valores para o outro respectivamente.
  • O restante da configuração pode ficar da sua maneira;
  • Assim que terminar tudo, vá em Save e depois Copy Destiny.dll para deixar o seu projeto funcionando com o Destiny;


Etapa 2: Criando o Servidor

Todos nós sabemos que para criar um multiplayer online, é necessário que haja uma comunicação entre as máquinas, obviamente, só que antes elas precisam se organizar para que possam se comunicar. No caso, uma das máquinas será o Servidor e as demais serão os clientes. O Servidor vai se comunicar com todos os clientes e vice-versa, é assim que funcionaria a comunicação entre as máquinas, enviando e recebendo dados entre os jogadores.
Neste tutorial, usaremos somente 3 variáveis, pois faremos uma demo somente com servidor e 1 cliente. Quando fizer com mais clientes, tenha ciência que essa quantidade de variáveis vai aumentar. A quantidade máxima de clientes em um único servidor é de 32, porém aqui usaremos somente 1. No entanto não há mistério para fazer a mesma coisa com mais de um cliente, a lógica é a mesma.
Aqui estão as variáveis usadas neste tutorial:
0001:Server/Client
0002:Temp State
0003:Client1
Para criar o servidor, usaremos o seguinte código na caixinha de comentários do RM:
$
Server.Close();
Server.Listen(1234,SOCK_DESTINY);

Para evitar erros de abrir um servidor que já está aberto, primeiro fechamos o servidor, mesmo que não tenha nenhum aberto. Daí então abriremos o nosso servidor:
Server.Listen(nº da porta, tipo do soquete);

  • nº da porta: valor da porta do servidor. Você pode colocar um número entre 1 a 65535;
  • tipo do soquete: vai determinar qual tipo de protocolo a rede usará. São dois valores: 0 e 1. Todavia, há duas constantes que você pode usar neste parâmetro:

    • SOCKET_DESTINY: protocolo que melhor funciona no RPG Maker;
    • SOCKET_RAW: não há um protocolo definido. Serve para enviar e receber dados brutos, normalmente strings. Geralmente este tipo de soquete é usado para pedir permissões para um determinado site.
Usei o nº da porta como 1234, mas você pode escolher o número que quiser, contanto que esteja entre 1 e 65535.
Beleza, criamos o servidor. Antes de iniciar a conexão, o valor de todos os clientes devem ser passados para -1, que é o valor que indica que aquele cliente não está conectado ao servidor. Como estamos com apenas 1 cliente, mudaremos a variável [0003: Client1] para -1. Depois, mude o valor de [0001: Server/Client para 1, que será a nossa maneira de identificar qual máquina está sendo o servidor e quem está sendo o cliente. Em seguida ative uma switch para indicar que a conexão foi iniciada.
Nosso primeiro comando ficou assim:




Etapa 3: Criando o Cliente

Agora que criamos o servidor, temos que agora criar o cliente. Para todos os clientes que você criar, será o mesmo evento, sem precisar criar um código para cada cliente. Isso veremos mais tarde, até então estamos trabalhando com apenas 1 cliente.
Diferente do servidor que vai receber vários clientes, o cliente terá um único "cliente" que será ele mesmo apenas. O primeiro cliente é o 0. Se podemos ter até 32 clientes, então os clientes vão de 0 até 31.
$
Client[0].Close();
Client[0].Connect("localhost",1234,SOCK_DESTINY);
v[2] = Client[0].State;

Mesmo esquema do servidor, fechamos o cliente antes de abrí-lo para evitar erro, mesmo que não haja nenhum. Daí conectamos ao servidor, procurando o endereço IP, a porta do servidor que está sendo usada e o tipo do soquete daquele servidor. A porta e o soquete deve ser o mesmo que usamos para a etapa anterior para quem ambos possam se conectar. Tente pensar numa casa 65535 salas. Cada sala suporta até 32 pessoas. Para que haja essa conexão, as pessoas devem estar na mesma sala, se porta desta sala não estiver aberta (sem servidor), a pessoa não consegue entrar nessa sala. A lógica da conexão entre servidor e cliente é basicamente essa.
Client[Id do Cliente].Connect(endereço ip, porta, soquete);

  • endereço ip: o IP da máquina que abriu o servidor. Use "localhost" caso queira se conectar com a mesma máquina (também pode ser usado como 127.0.0.1, que é usada para rede local, chamada de loopback);
  • porta: número da porta;
  • soquete: o tipo de soquete que determinará o protocolo. Mesmo caso do servidor.
Depois que a conexão for realizada, usamos a variável [0002: Temp State] para retornar o estado do cliente. O objeto Client[0].State tem 3 valores: -1,0 e 2; que também podem ser usadas as seguintes constantes:

  • STATE_ERROR: quando ocorre um erro na conexão, mesmo encontrando um servidor. Seu valor é -1;
  • STATE_CLOSED: o cliente não conseguiu se conectar. Seu valor é 0;
  • STATE_LISTENING: o cliente está atualmente conectado ao servidor. Seu valor é 2.
Em outras palavras, se a conexão do cliente sofreu algum erro, [0002: Temp State] muda pra -1 (STATE_ERROR); se ele não conseguiu se conectar ao servidor, vai pra 0 (STATE_CLOSED); se o cliente ainda está conectado, o valor de [0002: Temp State] será 2 (STATE_LISTENING).
Depois disso, criamos uma condição por eventos mesmo (Fork) verificando se [0002] está igual a 0, ou seja, se o cliente não conseguiu se conectar. Deixe a opção Else marcada para indicar que a conexão funcionou.
Dentro da condição, feche o cliente e deixe a variável [0001: Server/Client] com o valor de 0. No Else, mude a variável [0001] para 2, indicando que aquele jogador é cliente (de acordo com o nosso método) e ative a switch para indicar que a conexão foi iniciada.
Confira:

Apenas para fixar: usamos a variável [0001: Server/Client] para determinar se você está sendo o servidor ou o cliente desta conexão. Usaremos apenas 3 valores:

  • 0: nem servidor, nem cliente. Modo Offline;
  • 1: Servidor;
  • 2: Cliente
Dessa forma a programação e a conexão ficará mais organizada. O servidor vai exercer uma função na conexão e o cliente fará outra. Por isso que é sempre importante determinar com essa variável.



Etapa 4: Mantendo conexão entre Servidor e Cliente

Depois que criamos o servidor e o cliente, devemos agora estabelecer a conexão entre ambos. O servidor e o cliente deve estar sempre sabendo se um está conectado ou não. Já pensou que loko se um cliente sai do jogo e o servidor continua jogando com ele? Não tem como, né? Então o estado deles devem estar sempre atualizados.
Essa parte podemos deixar nos Eventos Comuns. O primeiro evento será Processo Paralelo com aquela switch que usamos como condição inicial. Tendo em mente que o Servidor e o cliente interagem de forma diferente, vamos criar duas condições: uma para ver se [0001: Server/Client] está igual a 1 e outra se está igual a 2. Na primeira condição, apenas chame outro evento comum, e na segunda, um outro evento comum. Na última linha, deixe um Wait de 0.0s para o processador dar uma pausa e evitar com que a conexão fique com lags.
Veja na screenshot como ficaria:
Vamos agora ao segundo evento comum, que usaremos para a comunicação do Servidor para o cliente.
O que nos resta agora é iniciar a conexão de vez. Voltando ao servidor, lembra que quando o servidor foi criado, mudamos a variável [0003: Client1] para -1? Pois então, agora criaremos uma condição verificando se esta mesma variável está igual a -1, indicando que o cliente ainda não está ativado no jogo, só está conectado, marque a opção Else. Dentro da condição, faremos isso:
$
// Tentar aceitar conexão com um cliente
v[3] = Server.Accept(NEXT_FREE_SOCKET);

Aquela mesma variável que usamos pra verificar o estado do cliente, pode ser aproveitada aqui já que não faremos aquela verificação até estabelecer a conexão. Com Server.Accept(Index do cliente) podemos ver se o cliente já tentou se conectar ao servidor. Usamos a constante NEXT_FREE_SOCKET para encontrar automaticamente um cliente disponível, sem precisar especificar a index.
Depois disso, outra condição para confirmar a verificação desse cliente. Se [0003] for igual a -1, quer dizer que o cliente ainda não está disponível. Portanto a nossa condição será se [0003] for diferente de -1 para que ele seja encontrado. Esta condição é facultativa. Ela serve mesmo só pra aparecer uma mensagem que o cliente se conectou e nada mais.

Agora no Else, como o cliente já existe, ele vai atualizar o estado daquele cliente, assim como fizemos no início da conexão. Se esse estado der -1 (STATE_ERROR), a conexão com esse cliente se encerra, alegando que ele se desconectou do servidor. Fechamos esse cliente e deixamos a variável [0003] como -1 novamente.


Vamos fazer quase o mesmo com o cliente num outro evento comum. A diferença é que com o cliente você não vai precisar verificar se há algum cliente conectado, já vamos verificar o estado do servidor direto (que na verdade estamos usando um cliente para servir de ponte para o servidor).
Na verificação da condição, mude a variável [0001], que determina se é servidor ou cliente, para 0 e desative a switch para indicar que o cliente saiu daquele servidor, voltando para o Modo Offline.



Pronto! Servidor e Cliente já estão conectados.
Mas como haverá interação entre eles? Bem, é o que veremos agora na última etapa desta primeira parte.



Etapa 5: Enviando e recebendo valores

Para que servidor e cliente possam interagir, eles precisam trocar valores constantemente. Inicialmente temos que fazer a recepção, que será feita no mesmo lugar que fizemos os dois eventos comuns. Começando pelo Servidor. Volte para o Evento comum Server Handling, verifique se o cliente existe com uma condição onde [0003: Client1] deve ser diferente de -1, dentro dessa condição faça esse código:
$
// Detectar tipo de valor recebido do Cliente
v[2] = Client[v[3]].GetRecvType();

// Receber valor do cliente
switch(v[2])
  case TYPE_VARIABLE:
    v[Client[v[3]].RecvID()] = Client[v[3]].RecvVariable();
    break;
  case TYPE_SWITCH:
    s[Client[v[3]].RecvID()] = Client[v[3]].RecvSwitch();
    break;
  case TYPE_DWORD:
    d[Client[v[3]].RecvID()] = Client[v[3]].RecvDword();
    break;
  case TYPE_FLOAT:
     f[Client[v[3]].RecvID()] = Client[v[3]].RecvFloat();
     break;
  case TYPE_STRING:
    a[Client[v[3]].RecvID()] = Client[v[3]].RecvString();
    break;
endswitch;

Vamos interpretar esse código: o primeiro é para verificar se você recebeu algum valor de alguma outra máquina. Aproveitamos aquela variável [0002: Temp State] para checar a chegada de algum valor com GetRecvType(). Eis os possíveis valores que esta variável pode receber:

  • 0: não recebeu nenhum valor;
  • 1: TYPE_VARIABLE: Cliente recebeu uma variável;
  • 2: TYPE_SWITCH: recebeu switch (Essa constante também pode ser chamada de TYPE_BOOL);
  • 3: TYPE_DWORD: recebeu Dword (variável com capacidade maior que a variável do RPG Maker);
  • 4: TYPE_FLOAT: recebeu Float (variável com valor decimal, também serve como TYPE_DOUBLE);
  • 5: TYPE_STRING: recebeu String (variáveis em forma de texto);
  • 6: TYPE_BYTE: recebe valor em Byte (variável com capacidade menor, de -128 a 127);
  • 7: TYPE_WORD: recebe valor em Word (variável com capacidade de -32768 a 32767);
Daí então usamos a condição de forma switch para alterar o valor de acordo com o tipo recebido. Para a recepção de dados, usamos essas 3 funções (considerando a recepção de uma variável):

  • GetRecvType() - Ver o tipo do valor que está sendo recebido;
  • RecvID() - ID deste valor recebido (ex: v[1]);
  • RecvVariable() - Valor recebido (ex: v[1] = 14);
Reconhecendo o tipo do valor, as adaptações são feitas. Se o cliente recebeu uma variável, mudará o valor da variável de acordo com a ID recebida também, se recebeu uma switch vai alterar a switch, etc.


Agora vá para o evento comum Client Handling e use este código:
$
// Detectar tipo de valor recebido do Servidor
v[2] = Client[0].GetRecvType();

// Receber valor do Servidor
switch(v[2])
  case TYPE_VARIABLE:
    v[Client[0].RecvID()] = Client[0].RecvVariable();
    break;
  case TYPE_SWITCH:
    s[Client[0].RecvID()] = Client[0].RecvSwitch();
    break;
  case TYPE_DWORD:
    d[Client[0].RecvID()] = Client[0].RecvDword();
    break;
  case TYPE_FLOAT:
     f[Client[0].RecvID()] = Client[0].RecvFloat();
     break;
  case TYPE_STRING:
    a[Client[0].RecvID()] = Client[0].RecvString();
    break;
endswitch;

No código não há diferença em praticamente nada. A única diferença é que ao invés de enviar para a Index do cliente que está no valor da variável [0003], ele vai enviar para um só cliente que está se passando pelo servidor.


Perfeito! Agora cliente e servidor estão prontos para receber dados.
Para enviar algum valor, usamos este código:
Client[Index do Cliente].SendVariable(ID da Variável, Valor da Variável);
Lembrando que se você for o servidor, deve verificar se o cliente está conectado com a condição Fork, se [0003] for diferente de -1, para poder enviar para aquele cliente. Se você for o cliente, a index sempre será 0. Daí é só escolher a Id da variável que será enviada e o valor que essa variável o cliente vai receber lá na outra máquina.

Exemplo:
$
// Enviando Variável para um cliente
if(v[3] != -1) Client[v[3]].SendVariable(10,37); endif;

No caso acima, eu enviei o valor 37 para a variável [0010] do cliente. Você pode enviar Switches, Dwords, Floats ou Strings da mesma maneira com SendSwitch(), SendDword(), SendFloat() e SendString(), respectivamente.



É isso aí!
Um pouco complicado, porém com estudo as coisas ficam bem claras. Se você já tem acompanhado meus tutoriais, creio que não terá dificuldade para fazer o sistema.
Além disso, vou deixar um Demo de como ficou a primeira parte e como aplicar esse tutorial no seu projeto.
Por ser um multiplayer, obviamente vai precisar abrir o jogo duas vezes, fazendo com que a primeira janela escolha ser o servidor e a segunda janela escolher ser o cliente. Como a conexão está em loopback, você pode testar os dois jogos no mesmo computador, sem precisar de máquina virtual ou precisar conectar-se com o computador do coleguinha.
Baixe a Demonstração deste tutorial.


Estude esta demo e aguarde pela parte 2, onde vamos aprender a fazer o movimento dos outros jogadores e passar para os clientes.

Até mais!  :ok:

Nossa!!!!!!!!!! Nem li o tópico mas só de ler o NOme do tópico fico com uma baita alegria em saber que o Maker esquecido por muitos vai ter servidor de Netplay!!! Vou ler e dar um feedback melhor.
O livro traz a vantagem de a gente poder estar só e ao mesmo tempo acompanhado.

------------


Bem, na verdade, após você ler o tópico com mais detalhes, quem estará criando o servidor é você mesmo.
Até então não consegui fazer conexão P2P (Peer to Peer), porém usando algum software feito para isso, como Hamachi, ele funciona muito bem sem atraso no envio dos pacotes.
Além do mais ainda estou pesquisando mais sobre esse lance de conexão e netplay pra ver se eu consigo tornar a conexão independente, como é na maioria dos jogos que possuem netplay =)

Incrivel! Sério nunca esperei ou imaginei isso cara, devo lhe dar os devidos parabéns, nessas limitações você conseguiu trazer um conteúdo desses, simplesmente incrível! Muito bom trabalho  :XD:

That's All~