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

148 lines
4.9 KiB
Go

package server
import (
"encoding/json"
"log"
"net"
"sync"
db "tic-tac-toe/database"
g "tic-tac-toe/game"
"tic-tac-toe/network"
"tic-tac-toe/player"
"tic-tac-toe/room"
)
type Server struct {
// Слушатель для подключения клиентов
listener net.Listener
// Интерфейс для сохранения завершенных игр в базе данных
repository db.IRepository
// Карта комнат
rooms map[string]*room.Room
// Карта игроков
players map[string]player.IPlayer
// Мьютекс для защиты доступа к данным
mutex sync.RWMutex
}
func NewServer(addr string, repository db.IRepository) (*Server, error) {
listener, err := net.Listen("tcp", addr) // Создаем слушатель
if err != nil {
return nil, err
}
// Создаем экземпляр структуры Server
server := &Server{
listener: listener,
repository: repository,
rooms: make(map[string]*room.Room),
players: make(map[string]player.IPlayer),
}
// Создаем комнаты и добавляем их в карту rooms по ключу,
// который является именем комнаты
server.rooms["room1"] = room.NewRoom(
"room1", server.repository, 3, g.PvP, g.None,
)
server.rooms["room2"] = room.NewRoom(
"room2", server.repository, 3, g.PvC, g.Easy,
)
server.rooms["room3"] = room.NewRoom(
"room3", server.repository, 5, g.PvC, g.Medium,
)
server.rooms["room4"] = room.NewRoom(
"room4", server.repository, 6, g.PvC, g.Hard,
)
// Возвращаем экземпляр созданного сервера
return server, nil
}
// Запускаем прослушивание подключений и обработку сообщений от клиентов
func (s *Server) Start() {
log.Printf("Server started, listening on %s", s.listener.Addr())
defer s.listener.Close() // Закрываем слушателя при завершении
// Запускаем бесконечный цикл обработки подключений
for {
// Принимаем подключение
conn, err := s.listener.Accept()
if err != nil { // Если ошибка, то пропускаем итерацию
log.Printf("Error accepting connection: %v", err)
continue
}
// Чтобы не блокировать основной поток
// передаем сокетное подключение в отдельную горутину, где и
// будем обрабатывать сообщения от клиента по данному подключению
go s.handleConnection(conn)
}
}
// Обрабатываем подключение клиента
func (s *Server) handleConnection(conn net.Conn) {
log.Printf("New client connected: %s", conn.RemoteAddr())
defer conn.Close() // Закрываем подключение при завершении
// Создаем декодер для чтения сообщений от клиента
decoder := json.NewDecoder(conn)
for { // Бесконечный цикл обработки сообщений
// Создаем переменную для хранения сообщения
var msg network.Message
// Декодируем сообщение от клиента
if err := decoder.Decode(&msg); err != nil { // Если ошибка
log.Printf(
"Client %s disconnected: %v", conn.RemoteAddr(),
err,
)
// Обрабатываем отключение клиента
s.disconnectedClientHandler(conn)
return
}
// Обрабатываем сообщение от клиента
s.handleCommand(conn, &msg)
}
}
// Обрабатываем отключение клиента
func (s *Server) disconnectedClientHandler(conn net.Conn) {
var player player.IPlayer // Создаем переменную для хранения игрока
// Проходим по всем комнатам
for _, room := range s.rooms {
// Если игрок 1 в комнате
if room.Player1 != nil {
// Если игрок 1 подключился по этому сокету
if room.Player1.CheckSocket(conn) {
player = room.Player1
// Удаляем игрока из комнаты
room.RemovePlayer(room.Player1)
break
}
}
// Если игрок 2 в комнате
if room.Player2 != nil {
// Если игрок 2 подключился по этому сокету
if room.Player2.CheckSocket(conn) {
player = room.Player2
// Удаляем игрока из комнаты
room.RemovePlayer(room.Player2)
break
}
}
}
// Если игрок не найден
if player == nil {
log.Printf(
"Client %s disconnected: player not found",
conn.RemoteAddr(),
)
return
}
// Удаляем игрока из карты, предварительно защищая доступ к ней
s.mutex.Lock()
delete(s.players, player.GetNickname())
s.mutex.Unlock()
}