"{Jogador} ganhou"
para encerrar o jogo e salvar a vitória em um armazenamento.A aposta blind é *y*
sem ter que recarregar o navegador. Para facilitar isso, podemos usar WebSockets.WebSocket é uma tecnologia que permite a comunicação bidirecional por canais full-duplex sobre um único socket TCP (Transmission Control Protocol)
/jogo
.200
quando acessamos o GET /jogo
.jogo
:novaRequisicaoDeJogo
para fazer a requisição para /jogo
. Tente escrever essa função você mesmo.verificaStatus
para aceitar resposta
ao invés de resposta.Code
já que parece combinar melhor.WebSocket
é integrado na maioria dos navegadores modernos, logo não precisamos nos preocupar em instalar bibliotecas. A página web não vai funcionar em navegadores antigos, mas para nosso caso tá tudo bem.jogo.html
.html/template
é um pacote do Go para criar HTML. No nosso caso, chamamos template.ParseFiles
enviando o caminho do nosso arquivo HTML. Presumindo que não há nenhum erro, chamamos a função Execute
para "executar" o template, que o escreve para um ìo.Writer
. No nosso caso, esperamos que o template seja escrito na internet, então enviamos o nosso http.ResponseWriter
.cmd/webserver
e execute o arquivo main.go
. Visite http://localhost:5000/jogo
.`jogo.html
no diretório cmd/webserver
. Eu escolho criar um symlink (ln -s ../../jogo.html jogo.html
) para o arquivo dentro da raiz do projeto para caso eu faça alterações, elas reflitam quando o servidor estiver sendo executado.go get github.com/gorilla/websocket
.websocket
. Minha IDE fez isso automaticamente para mim e a sua deve fazer o mesmo.httptest.NewServer
, que recebe um http.Handler
que vai esperar conexões.websocket.DefaultDialer.Dial
, tentamos conectar no nosso servidor para então enviar uma mensagem com nosso vencedor
./ws
, então ainda não estamos apertando as mãos.webSocket
:Upgrade
para atualizar a requisição. Agora, se você executar o teste novamente, o próximo erro deve aparecer.conexão.ReadMessage()
bloqueia a espera por uma mensagem na conexão. Quando obtivermos uma, vamos usá-la para GravarVitoria
. Isso finalmente fecharia a conexão WebSocket.time.Sleep
curto antes da verificação final.upgrader
para um valor privado dentro do nosso pacote porque não precisamos redeclará-lo em toda requisição na conexão com o WebSocket.template.ParseFiles("jogo.html")
vai ser executada a cada GET /jogo
, o que significa que vamos usar o sistema de arquivo a cada requisição apesar de não ser necessário parsear o template novamente. Vamos refatorar o código para que possamos fazer o parse do template uma vez em NovoServidorJogador
ao invés disso. Vamos ter que fazer isso para que nossa função possa retornar um erro caso tenhamos problema ao obter o template do disco ou fazer parse dele.ServidorJogador
:NovoServidorJogador
, agora temos problemas de compilação. Tente corrigir por si só ou olhe para o código fonte caso para ver a solução.deveFazerServidorJogador(t *testing.T, armazenamento ArmazenamentoJogador) *ServidorJogador
para que eu possa esconder o erro dos testes.deveConectarAoWebSocket
para que eu possa esconder um erro ao criar uma conexão de WebSocket./jogo
. Devemos vê-los gravados em /liga
. Lembre-se que sempre que tivermos um vencedor, vamos fechar a conexão, e você vai precisar atualizar a página para abrir a conexão novamente.jogo.html
para atualizar o código do lado do cliente para os novos requerimentos:conexão.onmessage
, presumimos ser alertas de blind e então definimos o blindContainer.innerText
de acordo.Jogo
para que nosso código CLI possa chamar um Jogo
e todo o restante se responsabilizaria por agendar os alertas de blind. Isso acabou até sendo uma boa separação de responsabilidades.Começar
a jogo, o que ativaria os alertas de blind, e quando o usuario declarava o vencedor, isso iria Terminar
. Esses sã os mesmos requerimentos que temos agora, só que a obtenção das entradas era diferente; logo, só precisamos reutilizar esse conceito aonde possível.Jogo
é TexasHoldem
:AlertadorDeBlind
, o TexasHoldem
pode agendar alertas de blind para enviar para qualquer lugar.AlertadorDeBlind
que usamos na CLI.os.Stdout
, mas isso não vai funcionar no nosso servidor web. Para cada requisição, obtemos um novo http.ResponseWriter
que então melhoramos para uma *websocket.Conn
. Logo, não odemos saber quando construímos nossas dependências para onde nossos alertas precisam ir.AlertadorDeBlind.AgendarAlertaPara
para que ele receba um destino paara os alertas para que possamos reutiliza-lo no nosso servidor web.AlertadorDeBlind.go
e adicione o parâmetro para io.Writer`:SaidaAlertador
não encaixa bem no nosso modelo, então vamos apenas renomeá-lo para Alertador
:TexasHoldem
porque estamos chamando AgendarAlertaPara
sem uma descrição. Só para deixar tudo compilando novamente, vamos escrevê-lo para os.Stdout
.AlertadorDeBlindEspiao
não implementa mais o AlertadorDeBlind
. Corrija isso atualizando a assinatura de AgendarAlertaPara
, execute os testes e todos devem estar passando.TexasHoldem
saiba para onde enviar os alertas de blind. Agora, vamos atualizar o Jogo
para que quando você começa uma jogo, declare para onde os alertas devem ir.TexasHoldem
para que implemente Jogo
corretamenteCLI
, quando começamos a jogo, preciamos passar nosssa propriedade saida
(cli.jogo.Começar(numeroDeJogadores, cli.saida
)TexasHoldem
, precisamos usar jogo.Começar(5, ioutil.Discard)
para corrigir o problema de compilação e configurar a saída do alerta para ser descartada Jogo
dentro do Servidor
.CLI
e Servidor
são os mesmos! É apenas o mecanismo de entrega que é diferente.CLI
para inspiração.JogoEspiao
.Jogo
e passamos para o `deveFazerServidorJogador
(certifique-se de atualizar a função auxiliar para isso).deveFazerServidorJogador
em outros testes. Crie uma variável não exportada jogoTosco
e use-a em todos os testes que não estão compilando:Jogo
, pois NovoServidorJogador
ainda não o suporta:Jogo
como campo para ServidorJogador
para que possamos usá-lo quando ele obtiver requisições.jogo
, então é só renomeá-lo para jogarJogo
)Jogo
dentro de webSocket
.jogo.Começar
, enviamos os dados para ioutil.Discard
que vai apenar descartar qualquer mensagem escrita nele.`main.go
para passar um Jogo
para o ServidorJogador
:Jogo
com ServidorJogador
e ele toma conta dos detalhes. Quando descobrirmos como enviar mensagens de blind atraves de web sockets ao invés de descartá-las, tudo deve ficar pronto.io.Writer
para a jogo para ter aonde escrever os alertas be blind.websocketServidorJogador
de antes? É o nosso wrapper em torno do nosso WebSocket, então parece que devemos ser capazes de enviá-lo para que nosso Jogo
seja capaz de enviar mensagens para ele.websocketServidorJogador
implementa o io.Writer
. Para fazer isso, precisamos usar do *websocket.Conn
para ussar a escrita de mensagem WriteMessage
para enviar a mensagem para o websocket.TexasHoldem
para que o tempo de incremento do blind seja mais curto para que você possa ver as coisas em ação:websocketServidorJogador
para ComeçarJogo
no lugar do ioutil.Discard
, então isso faz parecer que tenhamos que espionar a chamada para verificar se ela funciona.JogoEspiao
não envia nenhum dado para a saida
quando você chama Começar
. Devemos alterar isso para que possamos configurá-lo para enviar uma mensagem e então verificar se a mensagem é enviada para o websocket. Isso deve nos dar confiança que configuramos as coisas corretamente enquanto ainda exercitamos o comportamento real do que esperamos.AlertaDeBlind
.Começar
do JogoEspiao
para enviar a mensagem para a saída
.ServidorJogador
, quando ele tentar Começar
o jogo, deve acabar enviando mensagens pelo websocket se as coisas estiverem funcionando direito.alertaDeBlindEsperado
e configuramos nosso JogoEspiao
para enviá-lo para a saida
se Começar
for chamado.ws.ReadMessage()
para esperar por uma mensagem ser enviada e então verificamos se é aquela que esperamos.`ws.ReadMessage()
vai bloqueá-lo até obter a mensagem, que nunca vai chegar.