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

148 lines
4.9 KiB
Go
Raw Normal View History

2025-06-21 13:28:09 +03:00
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 {
2025-06-23 17:12:57 +03:00
// Слушатель для подключения клиентов
listener net.Listener
// Интерфейс для сохранения завершенных игр в базе данных
2025-06-21 13:28:09 +03:00
repository db.IRepository
2025-06-23 17:12:57 +03:00
// Карта комнат
rooms map[string]*room.Room
// Карта игроков
players map[string]player.IPlayer
// Мьютекс для защиты доступа к данным
mutex sync.RWMutex
2025-06-21 13:28:09 +03:00
}
func NewServer(addr string, repository db.IRepository) (*Server, error) {
2025-06-23 17:12:57 +03:00
listener, err := net.Listen("tcp", addr) // Создаем слушатель
2025-06-21 13:28:09 +03:00
if err != nil {
return nil, err
}
2025-06-23 17:12:57 +03:00
// Создаем экземпляр структуры Server
2025-06-21 13:28:09 +03:00
server := &Server{
listener: listener,
repository: repository,
rooms: make(map[string]*room.Room),
players: make(map[string]player.IPlayer),
}
2025-06-23 17:12:57 +03:00
// Создаем комнаты и добавляем их в карту rooms по ключу,
// который является именем комнаты
2025-06-21 13:28:09 +03:00
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,
)
2025-06-23 17:12:57 +03:00
// Возвращаем экземпляр созданного сервера
2025-06-21 13:28:09 +03:00
return server, nil
}
2025-06-23 17:12:57 +03:00
// Запускаем прослушивание подключений и обработку сообщений от клиентов
2025-06-21 13:28:09 +03:00
func (s *Server) Start() {
log.Printf("Server started, listening on %s", s.listener.Addr())
2025-06-23 17:12:57 +03:00
defer s.listener.Close() // Закрываем слушателя при завершении
2025-06-21 13:28:09 +03:00
2025-06-23 17:12:57 +03:00
// Запускаем бесконечный цикл обработки подключений
2025-06-21 13:28:09 +03:00
for {
2025-06-23 17:12:57 +03:00
// Принимаем подключение
2025-06-21 13:28:09 +03:00
conn, err := s.listener.Accept()
2025-06-23 17:12:57 +03:00
if err != nil { // Если ошибка, то пропускаем итерацию
2025-06-21 13:28:09 +03:00
log.Printf("Error accepting connection: %v", err)
continue
}
2025-06-23 17:12:57 +03:00
// Чтобы не блокировать основной поток
// передаем сокетное подключение в отдельную горутину, где и
// будем обрабатывать сообщения от клиента по данному подключению
2025-06-21 13:28:09 +03:00
go s.handleConnection(conn)
}
}
2025-06-23 17:12:57 +03:00
// Обрабатываем подключение клиента
2025-06-21 13:28:09 +03:00
func (s *Server) handleConnection(conn net.Conn) {
log.Printf("New client connected: %s", conn.RemoteAddr())
2025-06-23 17:12:57 +03:00
defer conn.Close() // Закрываем подключение при завершении
2025-06-21 13:28:09 +03:00
2025-06-23 17:12:57 +03:00
// Создаем декодер для чтения сообщений от клиента
2025-06-21 13:28:09 +03:00
decoder := json.NewDecoder(conn)
2025-06-23 17:12:57 +03:00
for { // Бесконечный цикл обработки сообщений
// Создаем переменную для хранения сообщения
2025-06-21 13:28:09 +03:00
var msg network.Message
2025-06-23 17:12:57 +03:00
// Декодируем сообщение от клиента
if err := decoder.Decode(&msg); err != nil { // Если ошибка
log.Printf(
"Client %s disconnected: %v", conn.RemoteAddr(),
err,
)
2025-06-23 17:12:57 +03:00
// Обрабатываем отключение клиента
2025-06-21 13:28:09 +03:00
s.disconnectedClientHandler(conn)
return
}
2025-06-23 17:12:57 +03:00
// Обрабатываем сообщение от клиента
2025-06-21 13:28:09 +03:00
s.handleCommand(conn, &msg)
}
}
2025-06-23 17:12:57 +03:00
// Обрабатываем отключение клиента
2025-06-21 13:28:09 +03:00
func (s *Server) disconnectedClientHandler(conn net.Conn) {
2025-06-23 17:12:57 +03:00
var player player.IPlayer // Создаем переменную для хранения игрока
// Проходим по всем комнатам
2025-06-21 13:28:09 +03:00
for _, room := range s.rooms {
2025-06-23 17:12:57 +03:00
// Если игрок 1 в комнате
2025-06-21 13:28:09 +03:00
if room.Player1 != nil {
2025-06-23 17:12:57 +03:00
// Если игрок 1 подключился по этому сокету
2025-06-21 13:28:09 +03:00
if room.Player1.CheckSocket(conn) {
player = room.Player1
2025-06-23 17:12:57 +03:00
// Удаляем игрока из комнаты
2025-06-21 13:28:09 +03:00
room.RemovePlayer(room.Player1)
break
}
}
2025-06-23 17:12:57 +03:00
// Если игрок 2 в комнате
2025-06-21 13:28:09 +03:00
if room.Player2 != nil {
2025-06-23 17:12:57 +03:00
// Если игрок 2 подключился по этому сокету
2025-06-21 13:28:09 +03:00
if room.Player2.CheckSocket(conn) {
player = room.Player2
2025-06-23 17:12:57 +03:00
// Удаляем игрока из комнаты
2025-06-21 13:28:09 +03:00
room.RemovePlayer(room.Player2)
break
}
}
}
2025-06-23 17:12:57 +03:00
// Если игрок не найден
2025-06-21 13:28:09 +03:00
if player == nil {
log.Printf(
"Client %s disconnected: player not found",
conn.RemoteAddr(),
)
return
}
2025-06-23 17:12:57 +03:00
// Удаляем игрока из карты, предварительно защищая доступ к ней
2025-06-21 13:28:09 +03:00
s.mutex.Lock()
delete(s.players, player.GetNickname())
s.mutex.Unlock()
}