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/client.go
2025-06-23 17:12:57 +03:00

145 lines
4.9 KiB
Go

package client
import (
"encoding/json"
"fmt"
"log"
"net"
"sync"
"time"
b "tic-tac-toe/board"
"tic-tac-toe/network"
)
// Объявление структуры клиента
type Client struct {
// подключение к серверу
conn net.Conn
// игровое поле
board *b.Board
// фигура игрока
mySymbol b.BoardField
// фигура игрока, ход которой сейчас
currentPlayer b.BoardField
// никнейм игрока
playerName string
// имя комнаты
roomName string
// текущее состояние клиента
state State
// мьютекс для защиты доступа к данным
mutex sync.RWMutex
// время последнего сообщения
lastMsgTime time.Time
}
// Констукторная функция для создания клиента
func NewClient(addr string) (*Client, error) {
conn, err := net.Dial("tcp", addr)
if err != nil {
return nil, err
}
return &Client{
// подключение к серверу
conn: conn,
// начальное состояние клиента
state: waitNickNameConfirm,
// mySymbol будет установлен при подключении к комнате
mySymbol: b.Empty,
}, nil
}
// Устанавливаем никнейм игрока
func (c *Client) setNickname(nickname string) {
c.playerName = nickname
}
// Получаем текущее состояние клиента
func (c *Client) getState() State {
c.mutex.RLock() // защищаем доступ к данным
defer c.mutex.RUnlock()
return c.state
}
// Устанавливаем текущее состояние клиента
func (c *Client) setState(state State) {
c.mutex.Lock() // защищаем доступ к данным
defer c.mutex.Unlock()
// Если переходим в состояние opponentMove
if state == opponentMove && c.state != opponentMove {
fmt.Println("\nWaiting for opponent's move...")
} else if state == waitingOpponentInRoom &&
c.state != waitingOpponentInRoom {
// Если переходим в состояние waitingOpponentInRoom
fmt.Println("\nWaiting for opponent to join...")
}
c.state = state // устанавливаем новое состояние
}
// Запускаем клиента
func (c *Client) Start() {
defer c.conn.Close()
fmt.Println("Connected to server. ")
// Запускаем горутину для чтения сообщений от сервера
go c.readFromServer()
// Запускаем меню клиента для взаимодействия с пользователем
c.menu()
}
// Читаем сообщения от сервера
func (c *Client) readFromServer() {
decoder := json.NewDecoder(c.conn) // Создаем декодер
for { // Бесконечный цикл
var msg network.Message
if err := decoder.Decode(&msg); err != nil {
log.Printf("Disconnected from server: %v", err)
return // если соединение потеряно, то выходим из горутины
}
switch msg.Cmd {
// Обрабатываем ответ на запрос на присоединение к комнате
case network.CmdRoomJoinResponse:
c.handleRoomJoinResponse(msg.Payload)
// Обрабатываем сообщение об инициализацию игры
case network.CmdInitGame:
c.handleInitGame(msg.Payload)
// Обрабатываем сообщение об обновлении состояния игры
case network.CmdUpdateState:
c.handleUpdateState(msg.Payload)
// Обрабатываем сообщение об окончании игры
case network.CmdEndGame:
c.handleEndGame(msg.Payload)
// Обрабатываем сообщение об ошибке
case network.CmdError:
c.handleError(msg.Payload)
// Обрабатываем сообщение о списке комнат
case network.CmdRoomListResponse:
c.handleRoomListResponse(msg.Payload)
// Обрабатываем сообщение о подтверждении никнейма
case network.CmdNickNameResponse:
c.handleNickNameResponse(msg.Payload)
// Обрабатываем сообщение об отключении оппонента
case network.CmdOpponentLeft:
c.handleOpponentLeft(msg.Payload)
// Обрабатываем сообщение о списке завершенных игр
case network.CmdFinishedGamesResponse:
c.handleFinishedGamesResponse(msg.Payload)
// Обрабатываем сообщение с данными по
// запрошенной завершенной игре
case network.CmdFinishedGameResponse:
c.handleFinishedGameResponse(msg.Payload)
default: // Если пришло неизвестное сообщение
log.Printf(
"Received unhandled message type '%s' "+
"from server. Payload: %s\n> ",
msg.Cmd, string(msg.Payload),
)
}
}
}