1
0
mirror of https://github.com/MADTeacher/go_basics.git synced 2025-11-23 21:34:47 +02:00
Files
go_basics/part_8/tic_tac_toe_v7/client/menu.go
2025-06-23 17:12:57 +03:00

201 lines
7.7 KiB
Go

package client
import (
"bufio"
"encoding/json"
"fmt"
"log"
"os"
"strconv"
"strings"
"tic-tac-toe/network"
"time"
)
// Метод для взаимодействия с пользователем
func (c *Client) menu() {
// Создаем буфер для чтения ввода
reader := bufio.NewReader(os.Stdin)
// Создаем энкодер для отправки сообщений
encoder := json.NewEncoder(c.conn)
// Запрашиваем никнейм у пользователя
fmt.Print("Enter your nickname: ")
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
c.playerName = input
var msg network.Message
// Формируем сообщение и отправляем никнейм на сервер
msg.Cmd = network.CmdNickname
payloadData := network.NicknameRequest{Nickname: c.playerName}
jsonPayload, err := json.Marshal(payloadData)
if err != nil {
log.Printf(
"Error marshalling payload for command %s: %v",
msg.Cmd, err,
)
return
}
msg.Payload = jsonPayload
// Отправляем сообщение на сервер
if err := encoder.Encode(msg); err != nil {
log.Printf(
"Failed to send message to server: %v. Disconnecting.",
err,
)
// Выходим из программы, если не удалось отправить сообщение
return
}
for { // Бесконечный цикл
switch c.getState() { // Переключение состояний
case waitNickNameConfirm: // Ожидание подтверждения никнейма
// Ожидаем подтверждения никнейма от сервера
time.Sleep(100 * time.Millisecond)
continue
case mainMenu: // Главное меню
// Переходим в главное меню
c.mainMenu(reader, encoder)
case playerMove: // Ход игрока
// Отрабатываем ход игрока
c.playing(reader, encoder)
case opponentMove: // Ход противника
// Ожидаем данные по ходу противника
time.Sleep(1000 * time.Millisecond)
continue
case endGame: // Конец игры
// Игра завершена. Ждем ее перезапуск от сервера
fmt.Println("\nGame has ended. Restarting in 10 seconds...")
time.Sleep(10 * time.Second)
continue
case waitResponseFromServer: // Ожидание ответа от сервера
time.Sleep(100 * time.Millisecond)
continue
case waitingOpponentInRoom: // Ожидание противника в комнате
// Здесь нам надо учесть ситуацию, сто противник может
// так и не подключиться к комнате. Поэтому, чтобы не
// заставлять игрока страдать в бесконечном цикле ожидания
// мы ограничиваем сообщения 1 раз в 3 секунды и считываем
// ввод пользователя посредством неблокирующего чтения
now := time.Now()
// Если прошло более 3 секунд с момента последнего сообщения
if now.Sub(c.lastMsgTime) > 3*time.Second {
c.lastMsgTime = now
fmt.Println("\nWaiting for opponent to join...")
fmt.Println("Press 'q' and Enter to return to main menu")
fmt.Print("> ")
}
// Проверяем ввод пользователя
var buffer [1]byte
n, _ := os.Stdin.Read(buffer[:])
// Если пользователь нажал 'q' или 'Q',
// то выходим в главное меню
if n > 0 && (buffer[0] == 'q' || buffer[0] == 'Q') {
fmt.Println("Leaving room...")
// Формируем сообщение о выходе из комнаты
// и отправляем на сервер
var msg network.Message
msg.Cmd = network.CmdLeaveRoomRequest
payload := network.LeaveRoomRequest{
RoomName: c.roomName,
PlayerName: c.playerName,
}
jsonPayload, _ := json.Marshal(payload)
msg.Payload = jsonPayload
encoder.Encode(msg) // Отправляем сообщение на сервер
c.setState(mainMenu) // Переходим в главное меню
continue
}
// Быстрый сон для избежания загрузки процессора
time.Sleep(100 * time.Millisecond)
continue
}
}
}
// Главное меню клиента для взаимодействия с пользователем
func (c *Client) mainMenu(reader *bufio.Reader, encoder *json.Encoder) {
var msg network.Message // Создаем буфер для сообщения
// Выводим меню
fmt.Println("Enter command:")
fmt.Println("1 - Get room list")
fmt.Println("2 - Join room")
fmt.Println("3 - Get finished games")
fmt.Println("4 - Get finished game by id")
fmt.Println("5 - Exit")
fmt.Print("> ")
// Считываем ввод пользователя
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
// Преобразуем ввод пользователя в число
command, err := strconv.Atoi(input)
if err != nil {
fmt.Println("Invalid command.")
return
}
// Обрабатываем ввод пользователя
switch command {
case 1: // Получаем список комнат
// Формируем сообщение и отправляем на сервер
msg.Cmd = network.CmdListRoomsRequest
encoder.Encode(msg)
// Переходим в состояние ожидания ответа от сервера
c.setState(waitResponseFromServer)
case 2: // Присоединяемся к комнате
// Запрашиваем имя комнаты у пользователя
fmt.Print("Enter room name: ")
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
// Формируем сообщение и отправляем на сервер
c.roomName = input
msg.Cmd = network.CmdJoinRoomRequest
payload := network.JoinRoomRequest{
RoomName: c.roomName,
PlayerName: c.playerName,
}
jsonPayload, _ := json.Marshal(payload)
msg.Payload = jsonPayload
encoder.Encode(msg) // Отправляем сообщение на сервер
// Переходим в состояние ожидания ответа от сервера
c.setState(waitResponseFromServer)
case 3: // Получаем список завершенных игр
// Формируем сообщение и отправляем на сервер
msg.Cmd = network.CmdFinishedGamesRequest
encoder.Encode(msg)
// Переходим в состояние ожидания ответа от сервера
c.setState(waitResponseFromServer)
case 4: // Получаем завершенную игру по id
// Запрашиваем id игры у пользователя
fmt.Print("Enter game id: ")
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
// Преобразуем ввод пользователя в число
gameId, err := strconv.Atoi(input)
if err != nil {
fmt.Println("Invalid game id.")
return
}
// Формируем сообщение и отправляем на сервер
msg.Cmd = network.CmdFinishedGameByIdRequest
payload := network.GetFinishedGameByIdRequest{GameID: gameId}
jsonPayload, _ := json.Marshal(payload)
msg.Payload = jsonPayload
encoder.Encode(msg) // Отправляем сообщение на сервер
// Переходим в состояние ожидания ответа от сервера
c.setState(waitResponseFromServer)
case 5: // Выходим из программы
os.Exit(0)
default: // Неизвестная команда
fmt.Println("Unknown command.")
return
}
}