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

201 lines
7.7 KiB
Go
Raw Normal View History

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