private void threadHandler() //Trata as mensagens que chegam e que são enviadas { networkStream = this.tcpClient.GetStream(); protocolSI = new ProtocolSI(); Boolean trocaPosicao = false; while (protocolSI.GetCmdType() != ProtocolSICmdType.EOT) //Enquanto a thread não receber ordens para terminar { networkStream.Read(protocolSI.Buffer, 0, protocolSI.Buffer.Length); ProtocolSICmdType cmd = protocolSI.GetCmdType(); string msg; byte[] msgByte; switch (protocolSI.GetCmdType()) { case ProtocolSICmdType.PUBLIC_KEY: security.setPublicKey(protocolSI.GetStringFromData()); Console.WriteLine("Recebi uma chave pública"); enviaACK(); simetricKey = protocolSI.Make(ProtocolSICmdType.SYM_CIPHER_DATA, security.getSimetricKey()); networkStream.Write(simetricKey, 0, simetricKey.Length); esperaACK(); IV = protocolSI.Make(ProtocolSICmdType.IV, security.getIV()); networkStream.Write(IV, 0, IV.Length); esperaACK(); break; case ProtocolSICmdType.USER_OPTION_1: //Adquire o nome do jogador connection.WaitOne(); //Caso no qual é feito um broadcast e a thread "errada" recebe o ACK e, portanto connection.ReleaseMutex(); //espera até que a thread "correta" receba o ACK para poder voltar a esperar nova mensagem nomeJogador = security.DecifrarTexto(protocolSI.GetStringFromData()); Console.WriteLine("Jogador {0} - {1}, conectou-se", clientID, nomeJogador); enviaACK(); break; case ProtocolSICmdType.USER_OPTION_2: //Atualiza os jogadores presentes na sala string salaDesejada = security.DecifrarTexto(protocolSI.GetStringFromData()); byte[] newJogador; foreach (room sala in rooms) //Percorre a lista de salas verificando se a sala na qual o cliente deseja conectar-se já existe { if (sala.getName() == salaDesejada) { if (!sala.isFull()) //Verifica se a sala não está cheia { sala.addClientToRoom(this); this.room = sala; break; } else { goto SalaCheia; } } } if (room == null) //Cria a sala caso a mesma não exista { room = new room(salaDesejada); rooms.Add(room); room.addClientToRoom(this); msg = System.DateTime.Now.ToString(); room.writeLog(msg); } Console.WriteLine("{0} entrou na sala {1}", nomeJogador, salaDesejada); enviaACK(); if (room.getClientList().Count == 1) //Se aquele jogador é o único na sala { //Coloca o jogador como o jogador 1 room.setJogador(nomeJogador); msg = String.Format("1/{0}/", nomeJogador); newJogador = protocolSI.Make(ProtocolSICmdType.USER_OPTION_1, security.CifrarTexto(msg)); networkStream.Write(newJogador, 0, newJogador.Length); esperaACK(); } else if (room.getClientList().Count == 2) //Se é o 2º jogador a entrar na sala { int posNovoJogador; room.setJogador(nomeJogador); foreach (ClientHandler client in room.getClientList()) { if (client.clientID != clientID) { posNovoJogador = room.getNomeJogador(2) == nomeJogador ? 2 : 1; //Descobre qual será a posição do novo jogador msg = String.Format("{0}/{1}/{2}", posNovoJogador, nomeJogador, jogadores); newJogador = protocolSI.Make(ProtocolSICmdType.USER_OPTION_1, client.security.CifrarTexto(msg)); broadcast(msg, ProtocolSICmdType.USER_OPTION_1, nomeJogador); //Coloca-se na posição que resta networkStream.Write(newJogador, 0, newJogador.Length); esperaACK(); } else //Envia o nome do jogador que já está na sala para o novo jogador { int posJogadorPresente = room.getNomeJogador(1) != nomeJogador ? 1 : 2; msg = String.Format("{0}/{1}/{2}", posJogadorPresente, room.getNomeJogador(posJogadorPresente), true == room.isMultiplePlayers() ? "true" : "false"); msgByte = protocolSI.Make(ProtocolSICmdType.USER_OPTION_1, security.CifrarTexto(msg)); networkStream.Write(msgByte, 0, msgByte.Length); esperaACK(); } } //Broadcast que informa que há 2 jogadores na sala e, portanto o jogo pode iniciar broadcast(" ", ProtocolSICmdType.USER_OPTION_3); } else //Se a sala já tem 2 jogadores { //Coloca os próximos jogadores na fila room.setJogador(nomeJogador); msg = String.Format("3/{0}/{1}/{2}/{3}/{4}/{5}/{6}", room.getNomeJogador(1), room.getNomeJogador(2), room.getPontos(room.getNomeJogador(1)), room.getPontos(room.getNomeJogador(2)), room.getPontos("empates"), jogadores, room.getProximoJogadores()); newJogador = protocolSI.Make(ProtocolSICmdType.USER_OPTION_1, security.CifrarTexto(msg)); networkStream.Write(newJogador, 0, newJogador.Length); msg = String.Format("4/{0}/{1}", jogadores, room.getProximoJogadores()); broadcast(msg, ProtocolSICmdType.USER_OPTION_1); esperaACK(); } break; SalaCheia: msgByte = protocolSI.Make(ProtocolSICmdType.USER_OPTION_3); networkStream.Write(msgByte, 0, msgByte.Length); esperaACK(); break; case ProtocolSICmdType.DATA: //Transmite o que o jogador disse para o chat msg = $"{System.DateTime.Now.ToString("HH:mm:ss")} - {nomeJogador} : {security.DecifrarTexto(protocolSI.GetStringFromData())}"; Console.WriteLine(msg); broadcast(msg, ProtocolSICmdType.DATA); //Broadcast da mensagem para todos os jogadores room.writeLog(msg); //Escreve para o arquivo de texto as mensagens do chat break; case ProtocolSICmdType.USER_OPTION_3: //Trata da jogada executada utilizando assinaturas digitais //Recebe o movimento cifrado string move = security.DecifrarTexto(protocolSI.GetStringFromData()); //Espera pelo hash assinado do movimento cifrado com a chave privada networkStream.Read(protocolSI.Buffer, 0, protocolSI.Buffer.Length); while (protocolSI.GetCmdType() != ProtocolSICmdType.USER_OPTION_4) { networkStream.Read(protocolSI.Buffer, 0, protocolSI.Buffer.Length); } string moveSign = security.DecifrarTexto(protocolSI.GetStringFromData()); //Verifica a autenticidade do movimento if (security.verifySignData(move, moveSign)) { string[] coordenadas = move.Split('/'); int line = int.Parse(coordenadas[0]); int col = int.Parse(coordenadas[1]); string symbolPlayer = room.getNomeJogador(1) == this.nomeJogador ? "X" : "O"; switch (room.move(line, col, this.nomeJogador)) { case -1: //Movimento é inválido msg = "Movimento inválido, tente novamente!"; byte[] invalid = protocolSI.Make(ProtocolSICmdType.USER_OPTION_4, security.CifrarTexto(msg)); networkStream.Write(invalid, 0, invalid.Length); break; case 0: //Movimento válido broadcast(String.Format("{0}{1}/{2}", line, col, symbolPlayer), ProtocolSICmdType.USER_OPTION_5); break; case 1: //Jogo termina com um ganhador broadcast(String.Format("{0}{1}/{2}", line, col, symbolPlayer), ProtocolSICmdType.USER_OPTION_5); Thread.Sleep(100); broadcast(String.Format("{0}/ganhou!", nomeJogador), ProtocolSICmdType.USER_OPTION_6); Thread.Sleep(100); if (trocaPosicao) { trocaDePosicao(room.isMultiplePlayers() == true ? true : false); trocaPosicao = false; } break; case 2: //Jogo termina em empate broadcast(String.Format("{0}{1}/{2}", line, col, symbolPlayer), ProtocolSICmdType.USER_OPTION_5); Thread.Sleep(100); broadcast(String.Format("/Empate!", nomeJogador), ProtocolSICmdType.USER_OPTION_6); Thread.Sleep(100); if (trocaPosicao) { trocaDePosicao(room.isMultiplePlayers() == true ? true : false); trocaPosicao = false; } break; case 3: //Jogador incorreto tentou fazer o movimento msg = "Espere a sua vez!"; byte[] jogadorIncorreto = protocolSI.Make(ProtocolSICmdType.USER_OPTION_4, security.CifrarTexto(msg)); networkStream.Write(jogadorIncorreto, 0, jogadorIncorreto.Length); break; default: Console.WriteLine("Algo de errado aconteceu ao executar room.move"); break; } } else { Console.WriteLine("Mensagem enviada inválida"); msg = "Ocorreu algum erro, tente novamente!"; byte[] invalid = protocolSI.Make(ProtocolSICmdType.USER_OPTION_7, security.CifrarTexto(msg)); networkStream.Write(invalid, 0, invalid.Length); } break; case ProtocolSICmdType.USER_OPTION_5: //Jogador solicitou troca de posição trocaPosicao = true; if (!room.jogo.jogoComecou()) { trocaDePosicao(); } break; case ProtocolSICmdType.USER_OPTION_6: //Jogador solicitou permitir vários jogadores room.multiplePlayers(); msg = "Múltiplos jogadores habilitado"; broadcast(msg, ProtocolSICmdType.USER_OPTION_8); break; case ProtocolSICmdType.SECRET_KEY: //Recebe a senha do usuário Console.WriteLine("Recebi a senha"); string senha = security.DecifrarTexto(protocolSI.GetStringFromData()); if (security.VerifyLogin(this.nomeJogador, senha)) { //Autentica o jogador Console.WriteLine("{0} autenticado com sucesso", this.nomeJogador); msg = String.Format("{0}/{1}", nomeJogador, security.GetPoints(nomeJogador)); byte[] ack = protocolSI.Make(ProtocolSICmdType.ACK, security.CifrarTexto(msg)); networkStream.Write(ack, 0, ack.Length); jogadores = jogadores + nomeJogador + ',' + security.GetPoints(nomeJogador) + ';'; } else { Console.WriteLine("{0} senha incorreta", this.nomeJogador); byte[] msgConnection = protocolSI.Make(ProtocolSICmdType.USER_OPTION_3); networkStream.Write(msgConnection, 0, msgConnection.Length); esperaACK(); } break; case ProtocolSICmdType.EOT: //Finaliza a sessão do jogador Console.WriteLine("Ending Thread from {0}", nomeJogador); if (room != null) { security.setPoints(nomeJogador, room.getPontos(nomeJogador) + security.GetPoints(nomeJogador)); if (room.getClientList().Count >= 2) { msg = String.Format("Jogador {0} deixou a sala/{1}", nomeJogador, nomeJogador); broadcast(msg, ProtocolSICmdType.USER_OPTION_9, nomeJogador); } } room.novoJogo(); break; case ProtocolSICmdType.ACK: //Caso no qual é feito um broadcast e a thread "errada" recebe o ACK e, portanto connection.WaitOne(); //espera até que a thread "correta" receba o ACK para poder voltar a esperar nova mensagem connection.ReleaseMutex(); break; default: break; } } networkStream.Close(); this.tcpClient.Close(); if (room != null) { this.room.removeClientOfRoom(this); this.room = null; } }