Networking

Ver o tópico anterior Ver o tópico seguinte Ir em baixo

Networking

Mensagem por PedroX em Sex 31 Maio 2013, 17:19

Tradução livre do tutorial: http://gmc.yoyogames.com/index.php?showtopic=582029

Networking tutorial, step by step.

Descrição: Tutorial que ensina como o Networking funciona e como criar um servidor e um cliente simples.

Dificuldade: Mediana

Para GM:Studio registrado.
Não requer nada além do Game Maker : Studio.

Crie dois projetos. Um será o servidor e o outro o cliente.

No projeto do cliente, crie um objeto obj_client; no projeto do servidor, crie um objeto obj_server; coloque ambos (em cada projeto) em uma room (que criar).

obj_server.create:
Código:
server = network_create_server(network_socket_tcp, 6510, 32)
buff = buffer_create(16384, buffer_fixed, 1);

obj_client.create:
Código:
client = network_create_socket (network_socket_tcp)
network_connect( client, "127.0.0.1", 6510)
buff = buffer_create( 256, buffer_grow, 1)

O número "6510" é a porta usado pelo jogo para acessar o servidor. O "32" é o número máximo de clientes que o servidor pode gerenciar ao mesmo tempo. O "127.0.0.1" é o ip do servidor; este número indica que o servidor se encontra no mesmo pc.

A função buffer_create() irá criar um buffer tanto no cliente quanto no server. O buffer é como uma folha, em que você escreve as mensagens que irá enviar para os outros jogadores.

O buffer do servidor é maior, pois sua folha irá guardar mensagens de todos os jogadores. O argumento "buffer_grow" indica que o buffer pode crescer.

Adicione isso ao obj_server.create:
Código:
clients_sockets = ds_list_create() //guardará o socket de cada jogador
clients_name = ds_list_create() //guardará o nome de cada jogador
clients_x = ds_list_create() //guardará o valor de x de cada jogador
clients_y = ds_list_create() //guardará o valor de y de cada jogador

E isso ao obj_client.create:
Código:
clients_name = ds_list_create()
clients_x = ds_list_create()
clients_y = ds_list_create()

Para que cada jogador saiba de quem é qual mensagem, o nome de cada um será único.

Adicione isso ao obj_client.create:
Código:
randomize()
setup=1
login=0
name_creation_phrase="Insert your character name :"
alarm[0]=10

Crie dois eventos (alarm0 e alarm1) no obj_client.

Ao alarm0, adicione:
Código:
client_name=get_string(name_creation_phrase, "player"+string(round(random(9999))))
buffer_seek(buff, buffer_seek_start, 0) //garantir que escreverá no inicio da folha
buffer_write(buff , buffer_string, "check_name") //escreve na folha 'buff' uma 'string' com valor "check_name", que é o assunto
buffer_write(buff , buffer_string, client_name)
buffer_size = buffer_tell(buff) //pega o tamanho da mensagem (ou seja, onde ela termina)
network_send_packet( client, buff, buffer_size ) //envia a 'carta'

Importante: podemos enviar mensagens em qualquer evento, mas, para receber, há um evento específico.

Adicione isso a obj_server.networking:
Código:
net_buff = ds_map_find_value(async_load, "buffer")
buff_info = buffer_read(net_buff , buffer_string )  //pega o assunto
sock = ds_map_find_value(async_load, "id") //pega o id do player

if buff_info="check_name" //se o assunto é "check_name"
    {
    b_name=buffer_read(net_buff , buffer_string )
    count=ds_list_size(clients_name)
    name_exists=0 //ainda não há um nome igual ao enviado
for(i=0;i<count;i++)
{
if b_name=ds_list_find_value(clients_name, i) name_exists=1 //se tiver um nome igual, a variável fica com o seu valor igual a 1
}

buffer_seek(buff, buffer_seek_start, 0) 
buffer_write(buff, buffer_string, "name_exists")
buffer_write(buff, buffer_bool, name_exists) //resultado do teste (true ou false)
var buffer_size = buffer_tell(buff);
network_send_packet( sock, buff, buffer_size );
}

"async_load" é um mapa padrão do networking. O evento Networking é disparado quando chega uma nova mensagem ou conexão.

Importante: a parte que você lê do buffer é deletada, então não é possível ler duas vezes a mesma informação (a menos que você tenha a escrito duas ou mais vezes).

Agora adicione isso ao obj_client.networking:
Código:
if ds_map_exists(async_load, "buffer")
    {
net_buff = ds_map_find_value(async_load, "buffer")
    buff_info = buffer_read(net_buff, buffer_string )

if buff_info="name_exists"
        {
        setup=buffer_read(net_buff , buffer_bool )
        if setup = 1
            {
            name_creation_phrase="Name already beeing used, please insert another name :"
            alarm[0]=1
            }
else
            {
            alarm[1]=1
            show_message("Welcome "+client_name+"!")
            }
        }
}

A parte do login não é especial, é apenas um pacote que repassa o nome e a posição do cliente.

Evento obj_client.alarm 1:
Código:
buffer_seek(buff, buffer_seek_start, 0)
buffer_write(buff , buffer_string, "client_setup")
buffer_write(buff , buffer_string, client_name)
buffer_write(buff , buffer_s16, x)
buffer_write(buff , buffer_s16, y)
buffer_size = buffer_tell(buff)
network_send_packet( client, buff, buffer_size )
login=1

Como pode ver, foram escritos quatro dados, e então devem ser lidos quatro dados, na mesma ordem de escrita.

Troque o contéudo de obj_server.networking por isto:
Código:
net_buff = ds_map_find_value(async_load, "buffer")
    buff_info = buffer_read(net_buff , buffer_string )
    sock = ds_map_find_value(async_load, "id")
   
    if buff_info="client_status"//client stats
        {
        index=ds_list_find_index( clients_sockets, sock)
        b_x=buffer_read(net_buff , buffer_s16 )
        b_y=buffer_read(net_buff , buffer_s16 )
        ds_list_replace(clients_x, index, b_x)
        ds_list_replace(clients_y, index, b_y)
        }
   
    else if buff_info="check_name"//check if name exists
        {
        b_name=buffer_read(net_buff , buffer_string )
        count=ds_list_size(clients_name)
        name_exists=0
        for(i=0;i<count;i++)
            {
            if b_name=ds_list_find_value(clients_name, i) name_exists=1
            }
        buffer_seek(buff, buffer_seek_start, 0) 
        buffer_write(buff, buffer_string, "name_exists")
        buffer_write(buff, buffer_bool, name_exists)
        var buffer_size = buffer_tell(buff);
        network_send_packet( sock, buff, buffer_size );
        }
       
       
    else if buff_info="client_setup"//client setup
        {
        b_name=buffer_read(net_buff , buffer_string )
        b_x=buffer_read(net_buff , buffer_s16 )
        b_y=buffer_read(net_buff , buffer_s16 )
        ds_list_add(clients_sockets,sock)
        ds_list_add(clients_name,b_name)
        ds_list_add(clients_x,b_x)
        ds_list_add(clients_y,b_y)
        //show_message(b_name+" joined at pos x:"+string(b_x)+" y:"+string(b_y))
        //show_message("list sizes, socket="+string(ds_list_size(clients_sockets))+" name="+string(ds_list_size(clients_name))+" x="+string(ds_list_size(clients_x))+" y="+string(ds_list_size(clients_y)))
        }

Para enviar o "status" visto acima, adicione no obj_client.step:
Código:
if login=1 //só manda se já logou
    {
    buffer_seek(buff, buffer_seek_start, 0)
    buffer_write(buff , buffer_string, "client_status")
    buffer_write(buff , buffer_s16, mouse_x)
    buffer_write(buff , buffer_s16, mouse_y)
    buffer_size = buffer_tell(buff)
    network_send_packet( client, buff, buffer_size )
    }

Agora que o servidor recebe as mensagens, vamos compartilhá-las com todos os jogadores:

No obj_server.End Step:
Código:
count = ds_list_size(clients_sockets)
if count>0
    {
    buffer_seek(buff, buffer_seek_start, 0)
    buffer_write(buff, buffer_string, "all_clients")
    buffer_write(buff, buffer_s16, count)
    for(i=0;i<count;i++)
        {
        buffer_write(buff, buffer_string, string(ds_list_find_value(clients_name,i)) )//clients_name
        buffer_write(buff, buffer_s16, ds_list_find_value(clients_x,i))//clients_x
        buffer_write(buff, buffer_s16, ds_list_find_value(clients_y,i) )//clients_y
        }
    var buffer_size = buffer_tell(buff);
       
    // Enviar para todos os jogadores
    for(i=0;i<count;i++)
        { 
        sock = ds_list_find_value(clients_sockets,i);
        network_send_packet( sock, buff, buffer_size );
        }
    }

Troque o conteúdo de obj_client.networking por:
Código:
if ds_map_exists(async_load, "buffer")
    {
    net_buff = ds_map_find_value(async_load, "buffer")
    buff_info = buffer_read(net_buff, buffer_string )
   
    if buff_info="all_clients"
        {
        count=buffer_read(net_buff , buffer_s16 )
        ds_list_clear(clients_name)
        ds_list_clear(clients_x)
        ds_list_clear(clients_y)
        for(i=0;i<count;i++)
            {
            ds_list_add(clients_name,buffer_read(net_buff , buffer_string ))
            ds_list_add(clients_x,buffer_read(net_buff , buffer_s16 ))
            ds_list_add(clients_y,buffer_read(net_buff , buffer_s16 ))
            }
        }
   
    else if buff_info="name_exists"
        {
        setup=buffer_read(net_buff , buffer_bool )
        if setup = 1
            {
            name_creation_phrase="Name already beeing used, please insert another name :"
            alarm[0]=1
            }
        if setup = 0
            {
            alarm[1]=1
            show_message("Welcome "+client_name+"!")
            }
        }
    }

No obj_client.draw:
Código:
count = ds_list_size(clients_name)
if count>0
    {
    for(i=0;i<count;i++)
            {
            name=ds_list_find_value(clients_name,i)//name
            xx=ds_list_find_value(clients_x,i)//x
            yy=ds_list_find_value(clients_y,i)//y
            if name != client_name
                {
                draw_circle(xx,yy,10,true)
                draw_set_halign(fa_center)
                draw_set_valign(fa_middle)
                draw_text(xx,yy-32,name)
                }
            }
    }
draw_circle(mouse_x,mouse_y,10,false)

Troque o conteúdo de obj_server.networking por:
Código:
eventid = ds_map_find_value(async_load, "id")
if server = eventid
    {
    //disconnect of a client
    t = ds_map_find_value(async_load, "type");
    sock = ds_map_find_value(async_load, "socket");


    if( t!=network_type_connect)
        {
        index=ds_list_find_index( clients_sockets, sock)
        //show_message(ds_list_find_value(clients_name, index)+" disconected")
        ds_list_delete(clients_sockets,index)
        ds_list_delete(clients_name, index)//name
        ds_list_delete(clients_x, index )//x
        ds_list_delete(clients_y, index )//y
        }
    }
else
    {
    net_buff = ds_map_find_value(async_load, "buffer")
    buff_info = buffer_read(net_buff , buffer_string )
    sock = ds_map_find_value(async_load, "id")
   
    if buff_info="client_status"//client stats
        {
        index=ds_list_find_index( clients_sockets, sock)
        b_x=buffer_read(net_buff , buffer_s16 )
        b_y=buffer_read(net_buff , buffer_s16 )
        ds_list_replace(clients_x, index, b_x)
        ds_list_replace(clients_y, index, b_y)
        }
   
    else if buff_info="check_name"//check if name exists
        {
        b_name=buffer_read(net_buff , buffer_string )
        count=ds_list_size(clients_name)
        name_exists=0
        for(i=0;i<count;i++)
            {
            if b_name=ds_list_find_value(clients_name, i) name_exists=1
            }
        buffer_seek(buff, buffer_seek_start, 0) 
        buffer_write(buff, buffer_string, "name_exists")
        buffer_write(buff, buffer_bool, name_exists)
        var buffer_size = buffer_tell(buff);
        network_send_packet( sock, buff, buffer_size );
        }
       
       
    else if buff_info="client_setup"//client setup
        {
        b_name=buffer_read(net_buff , buffer_string )
        b_x=buffer_read(net_buff , buffer_s16 )
        b_y=buffer_read(net_buff , buffer_s16 )
        ds_list_add(clients_sockets,sock)
        ds_list_add(clients_name,b_name)
        ds_list_add(clients_x,b_x)
        ds_list_add(clients_y,b_y)
        //show_message(b_name+" joined at pos x:"+string(b_x)+" y:"+string(b_y))
        //show_message("list sizes, socket="+string(ds_list_size(clients_sockets))+" name="+string(ds_list_size(clients_name))+" x="+string(ds_list_size(clients_x))+" y="+string(ds_list_size(clients_y)))
        }       
    }

Ruben -> Artigo original
PedroX -> Tradução (e um pouco de adaptação)

PedroX

Ranking : Nota C
Número de Mensagens : 6034
Idade : 21
Data de inscrição : 26/07/2008
Notas recebidas : C+B
Reputação : 286
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   :
   :
   :

Voltar ao Topo Ir em baixo

Re: Networking

Mensagem por soueunox em Sex 31 Maio 2013, 17:50

Bom, pelo que to vendo é tipo que um jogo online simples.
Com sistemas de nomes e coordenadas do jogadores.

Agora uma pergunta, Quais as mudanças do Client/Server do GM8 para o GM:S
Em relação a esse topico?

soueunox

Número de Mensagens : 112
Idade : 22
Data de inscrição : 17/06/2012
Reputação : 7
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: Networking

Mensagem por PedroX em Sex 31 Maio 2013, 18:04

Quais as mudanças do Client/Server do GM8 para o GM:S
Muitas. Agora você não precisa de DLLs. Há sistemas de buffers e sockets mais personalizáveis. Os tipos de dados também são mais abrangentes. Não pega no HTML5 (pena).

PedroX

Ranking : Nota C
Número de Mensagens : 6034
Idade : 21
Data de inscrição : 26/07/2008
Notas recebidas : C+B
Reputação : 286
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   :
   :
   :

Voltar ao Topo Ir em baixo

Re: Networking

Mensagem por Misterioso em Sex 31 Maio 2013, 19:11

Sockets e mais sockets mas no final sempre tem uma situação complicada, o IP da pessoa teria que ser único na rede de internet dela, e que as portas do seu Modem e do seu Firewall fossem abertas. E no meio dessa confusão, Hamachi não seria uma boa opção, e alem do mais mesmo que haja uma pessoa sendo o servidor, uma hora ou outra ela vai ter que desligar o computador, Pra jogos desse tipo é recomendado VirtualServer só que não é de graça, seria legal um sistema tipo requisições sem lag, né PedroX.

Flw!

Misterioso

Ranking : Sem avaliações
Número de Mensagens : 43
Idade : 19
Data de inscrição : 01/08/2012
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: Networking

Mensagem por soueunox em Sex 31 Maio 2013, 20:32

PedroX escreveu:
Quais as mudanças do Client/Server do GM8 para o GM:S
Muitas. Agora você não precisa de DLLs. Há sistemas de buffers e sockets mais personalizáveis. Os tipos de dados também são mais abrangentes. Não pega no HTML5 (pena).

Bom, pena mesmo que não pegue no HTML5.
Mais o melhor modulo e tecnica facil de ultizar é a dll mesmo ne?
Pois ja tem as scripts e tudo mais...
a não ser que as script possam ser executadas sem a dll também :p
Muito bom, não sabia dessa função do GM:S
Ela pega em android, iOS?

soueunox

Número de Mensagens : 112
Idade : 22
Data de inscrição : 17/06/2012
Reputação : 7
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: Networking

Mensagem por PedroX em Sex 31 Maio 2013, 22:20

@Kimimaro
Sim. Mas se for um jogo muito 'ativo', requests não adiantariam muito. Mesmo assim, seu projeto sobre isso poderia ser postado aqui para estudos...

@soueunox
Sim, pega em todos menos HTML5 (segundo o que já li até o momento).

Melhor para GM:S: o modo desse tutorial (interno)
Melhor para GM:8: DLLs.

PedroX

Ranking : Nota C
Número de Mensagens : 6034
Idade : 21
Data de inscrição : 26/07/2008
Notas recebidas : C+B
Reputação : 286
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   :
   :
   :

Voltar ao Topo Ir em baixo

Re: Networking

Mensagem por machosupremo em Sab 01 Jun 2013, 13:58

Uma pergunta sobre a estrutura ai abordada.
O desenvolvedor da engine escolheu uma série de listas (ds_list_create) para cada tipo de informação.

Eu ja aprendi a usar objetos para cada novo jogador e a este objeto eu atribuiria as devidas variáveis.

Qual método seria mais adequado quanto a performance e/ou facilidade no desenvolvimento.

machosupremo

Número de Mensagens : 99
Idade : 26
Data de inscrição : 10/10/2012
Reputação : 2
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0

http://www.dinoup.com

Voltar ao Topo Ir em baixo

Re: Networking

Mensagem por PedroX em Sab 01 Jun 2013, 14:28

facilidade no desenvolvimento
Qual acha mais fácil? Acho que é uma questão de resposta pessoal.

performance
Usar listas gasta menos memória e talvez seja mais rápido. Instâncias gastam mais memória, por terem muitas informações (image_index, por exemplo, vai ficar aumentando automaticamente) e talvez sejam mais lentas.

Minha opinião:

No server, usar listas. No cliente, usar instâncias (desde que essas sejam visíveis, pois o GM por padrão desenha mais rápido do que códigos do Draw).

PedroX

Ranking : Nota C
Número de Mensagens : 6034
Idade : 21
Data de inscrição : 26/07/2008
Notas recebidas : C+B
Reputação : 286
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   :
   :
   :

Voltar ao Topo Ir em baixo

Re: Networking

Mensagem por Conteúdo patrocinado Hoje à(s) 03:46


Conteúdo patrocinado


Voltar ao Topo Ir em baixo

Ver o tópico anterior Ver o tópico seguinte Voltar ao Topo

- Tópicos similares

 
Permissão deste fórum:
Você não pode responder aos tópicos neste fórum