first commit

This commit is contained in:
M1ngdaXie
2025-12-29 16:29:24 -08:00
commit 37d89b13b9
48 changed files with 7334 additions and 0 deletions

164
backend/internal/hub/hub.go Normal file
View 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,
}
}