first commit
This commit is contained in:
164
backend/internal/hub/hub.go
Normal file
164
backend/internal/hub/hub.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package hub
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
RoomID string
|
||||
Data []byte
|
||||
sender *Client
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
ID string
|
||||
Conn *websocket.Conn
|
||||
send chan []byte
|
||||
hub *Hub
|
||||
roomID string
|
||||
}
|
||||
type Room struct {
|
||||
ID string
|
||||
clients map[*Client]bool
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
type Hub struct {
|
||||
rooms map[string]*Room
|
||||
mu sync.RWMutex
|
||||
Register chan *Client // Exported
|
||||
Unregister chan *Client // Exported
|
||||
Broadcast chan *Message // Exported
|
||||
}
|
||||
func NewHub() *Hub {
|
||||
return &Hub{
|
||||
rooms: make(map[string]*Room),
|
||||
Register: make(chan *Client),
|
||||
Unregister: make(chan *Client),
|
||||
Broadcast: make(chan *Message),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hub) Run() {
|
||||
for {
|
||||
select {
|
||||
case client := <-h.Register:
|
||||
h.registerClient(client)
|
||||
case client := <-h.Unregister:
|
||||
h.unregisterClient(client)
|
||||
case message := <-h.Broadcast:
|
||||
h.broadcastMessage(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hub) registerClient(client *Client) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
room, exists := h.rooms[client.roomID]
|
||||
if !exists {
|
||||
room = &Room{
|
||||
ID: client.roomID,
|
||||
clients: make(map[*Client]bool),
|
||||
}
|
||||
h.rooms[client.roomID] = room
|
||||
log.Printf("Created new room with ID: %s", client.roomID)
|
||||
}
|
||||
room.mu.Lock()
|
||||
room.clients[client] = true
|
||||
room.mu.Unlock()
|
||||
log.Printf("Client %s joined room %s (total clients: %d)", client.ID, client.roomID, len(room.clients))
|
||||
}
|
||||
func (h *Hub) unregisterClient(client *Client) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
room, exists := h.rooms[client.roomID]
|
||||
if !exists {
|
||||
log.Printf("Room %s does not exist for client %s", client.roomID, client.ID)
|
||||
return
|
||||
}
|
||||
room.mu.Lock()
|
||||
if _, ok := room.clients[client]; ok {
|
||||
delete(room.clients, client)
|
||||
close(client.send)
|
||||
log.Printf("Client %s disconnected from room %s", client.ID, client.roomID)
|
||||
}
|
||||
|
||||
room.mu.Unlock()
|
||||
log.Printf("Client %s left room %s (total clients: %d)", client.ID, client.roomID, len(room.clients))
|
||||
|
||||
if len(room.clients) == 0 {
|
||||
delete(h.rooms, client.roomID)
|
||||
log.Printf("Deleted empty room with ID: %s", client.roomID)
|
||||
}
|
||||
}
|
||||
func (h *Hub) broadcastMessage(message *Message) {
|
||||
h.mu.RLock()
|
||||
room, exists := h.rooms[message.RoomID]
|
||||
h.mu.RUnlock()
|
||||
if !exists {
|
||||
log.Printf("Room %s does not exist for broadcasting", message.RoomID)
|
||||
return
|
||||
}
|
||||
|
||||
room.mu.RLock()
|
||||
defer room.mu.RUnlock()
|
||||
for client := range room.clients {
|
||||
if client != message.sender {
|
||||
select {
|
||||
case client.send <- message.Data:
|
||||
default:
|
||||
log.Printf("Failed to send to client %s (channel full)", client.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) ReadPump() {
|
||||
defer func() {
|
||||
c.hub.Unregister <- c
|
||||
c.Conn.Close()
|
||||
}()
|
||||
for {
|
||||
messageType, message, err := c.Conn.ReadMessage()
|
||||
if err != nil {
|
||||
log.Printf("Error reading message from client %s: %v", c.ID, err)
|
||||
break
|
||||
}
|
||||
if messageType == websocket.BinaryMessage {
|
||||
c.hub.Broadcast <- &Message{
|
||||
RoomID: c.roomID,
|
||||
Data: message,
|
||||
sender: c,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) WritePump() {
|
||||
defer func() {
|
||||
c.Conn.Close()
|
||||
}()
|
||||
for message := range c.send {
|
||||
err := c.Conn.WriteMessage(websocket.BinaryMessage, message)
|
||||
if err != nil {
|
||||
log.Printf("Error writing message to client %s: %v", c.ID, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewClient(id string, conn *websocket.Conn, hub *Hub, roomID string) *Client {
|
||||
return &Client{
|
||||
ID: id,
|
||||
Conn: conn,
|
||||
send: make(chan []byte, 256),
|
||||
hub: hub,
|
||||
roomID: roomID,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user