57/100 Dias de Golang - Jogo em UDP - Parte 3
Table of Contents
#
Jogo em UDP - Parte 3
Hoje iremos enviar o grid para cada usuário conectado e receber os inputs de cada usuário. Já temos a função renderArene
que retorna uma string formatada e já temos a lista de playes, o que vamos criar é uma função envia a arena para todos os usuário. Mais o menos nessa estrutura.
arena := renderArena()
sendArena(conn, arena)
A função sendArena vai fazer um for sobre a lista de usuários e enviar para cada usuário o grid no endereço salvo.
func sendArena(conn *net.UDPConn, arena string) {
for _, p := range players {
conn.WriteToUDP([]byte(arena), p.Addr)
}
}
Temos que fazer essa renderização do grid e mandar para o usuário de X e X tempo. Vamos definir aqui uma taxa de atualização de a cada 200ms. E para isso vamos criar um ticker no nosso main com a função de render e sendArena.
ticker := time.NewTicker(200 * time.Millisecond)
for range ticker.C {
arena := renderArena()
sendArena(conn, arena)
}
Caso a taxa de atualização esteja muito alta, ou muito baixa, podemos somente alterar o 200
da função time.NewTicker
. Então já temos uma estrutura básica, o usuário se conecta no servidor, manda os comandos WASD e o servidor retorna o grid. Mas ainda temos que receber esse dado e fazer algo com ele. Para isso vamos criar duas variáveis. Uma variável Comand
que é uma struct do id do player e qual a action e um canal, pois teremos uma goroutine para tratar os dados recebidos. Poderia fazer na mesma função? Provavelmente, mas a ideia aqui é treinar conceitos do Golang então temos que colocar um canal para comunicação.
var commands = make(chan Command, 100)
type Command struct {
PlayerID string
Action string
}
Veja como vai ficar a goroutine:
go func() {
buf := make([]byte, 1024)
for {
n, clientAddr, _ := conn.ReadFromUDP(buf)
msg := strings.TrimSpace(string(buf[:n]))
playerID := clientAddr.String()
if _, exists := players[playerID]; !exists {
players[playerID] = &Player{
ID: playerID,
Char: rune('A' + len(players)%26),
X: 1,
Y: 1,
Addr: clientAddr,
}
fmt.Printf("Novo jogador %s (%c)\n", playerID, players[playerID].Char)
}
commands <- Command{PlayerID: playerID, Action: msg}
}
}()
Ela será um for infinito, que fica recebendo dados da conexão:
n, clientAddr, _ := conn.ReadFromUDP(buf)
msg := strings.TrimSpace(string(buf[:n]))
playerID := clientAddr.String()
Aqui temos que criar uma lógica para verificar se o player existe, caso não exista cria e salva o array de players.
if _, exists := players[playerID]; !exists {
players[playerID] = &Player{
ID: playerID,
Char: rune('A' + len(players)%26),
X: 1,
Y: 1,
Addr: clientAddr,
}
fmt.Printf("Novo jogador %s (%c)\n", playerID, players[playerID].Char)
}
O campo Char
da struct é do tipo rune
e cada player novo vai receber a próxima letra do alfabeto - Aqui temos um “erro”, caso 27 players se conectem, o 27 irá receber um ‘A’ como char, mas não vou corrigir isso. E o X e Y estão sendo iniciado com 1 e 1, pois nas posições 0, 0 temos “paredes”. E o Addr
é o endereço do cliente.
E como criamos o canal, temos que criar o Command
desse player:
commands <- Command{PlayerID: playerID, Action: msg}
Amanhã vamos implantar a função que lida com os Comandos e atualiza o grid.