hudly/server/server.go
2024-10-22 18:49:14 -06:00

224 lines
4.7 KiB
Go

package main
import (
"bufio"
"encoding/json"
"fmt"
"math/rand"
"net"
"strings"
"sync"
"time"
)
type Client struct {
conn net.Conn
username string
room *Room
}
type Room struct {
code string
password string
clients map[*Client]bool
lock sync.Mutex
}
var (
rooms = make(map[string]*Room)
mu sync.Mutex
)
// Helper function to generate a 4-hexadecimal room code
func generateRoomCode() string {
rand.Seed(time.Now().UnixNano())
return fmt.Sprintf("%04x", rand.Intn(0xFFFF))
}
// Handle client connection
func handleClient(client *Client) {
defer client.conn.Close()
reader := bufio.NewReader(client.conn)
for {
line, err := reader.ReadString('\n')
if err != nil {
if client.room != nil {
leaveRoom(client)
}
fmt.Println("Client disconnected:", client.conn.RemoteAddr())
return
}
processCommand(client, strings.TrimSpace(line))
}
}
// Process client commands
func processCommand(client *Client, input string) {
parts := strings.SplitN(input, " ", 2)
if len(parts) < 1 {
return
}
cmd := strings.ToUpper(parts[0])
args := ""
if len(parts) > 1 {
args = parts[1]
}
switch cmd {
case "CREATE":
createRoom(client, args)
case "JOIN":
joinRoom(client, args)
case "LEAVE":
leaveRoom(client)
case "MESSAGE":
sendMessage(client, args)
case "SYNC":
handleSync(client, args)
default:
client.conn.Write([]byte("Unknown command\n"))
}
}
// Create a room with an optional password
func createRoom(client *Client, args string) {
mu.Lock()
defer mu.Unlock()
password := ""
if args != "" {
password = args // Treat all input after CREATE as a password
}
roomCode := generateRoomCode() // Room code is generated automatically
room := &Room{
code: roomCode,
password: password,
clients: make(map[*Client]bool),
}
rooms[roomCode] = room
client.conn.Write([]byte(fmt.Sprintf("Room created with code: %s\n", roomCode)))
}
// Join a room using its 4-hexadecimal code and optional password
func joinRoom(client *Client, args string) {
parts := strings.SplitN(args, " ", 2)
roomCode := parts[0]
password := ""
if len(parts) == 2 {
password = parts[1]
}
mu.Lock()
room, exists := rooms[roomCode]
mu.Unlock()
if !exists {
client.conn.Write([]byte("Room not found\n"))
return
}
if room.password != "" && room.password != password {
client.conn.Write([]byte("Incorrect password\n"))
return
}
room.lock.Lock()
room.clients[client] = true
client.room = room
room.lock.Unlock()
client.conn.Write([]byte(fmt.Sprintf("Joined room: %s\n", roomCode)))
broadcastMessage(client.room, fmt.Sprintf("%s has joined the room\n", client.username))
}
// Leave the current room
func leaveRoom(client *Client) {
if client.room == nil {
client.conn.Write([]byte("You are not in any room\n"))
return
}
client.room.lock.Lock()
delete(client.room.clients, client)
client.room.lock.Unlock()
broadcastMessage(client.room, fmt.Sprintf("%s has left the room\n", client.username))
client.conn.Write([]byte("You have left the room\n"))
client.room = nil
}
// Send a message to all clients in the current room
func sendMessage(client *Client, message string) {
if client.room == nil {
client.conn.Write([]byte("You are not in any room\n"))
return
}
timestamp := time.Now().Format("2006-01-02 15:04:05")
formattedMessage := fmt.Sprintf("[%s] %s: %s\n", timestamp, client.username, message)
broadcastMessage(client.room, formattedMessage)
}
// Broadcast a message to all clients in the room
func broadcastMessage(room *Room, message string) {
room.lock.Lock()
defer room.lock.Unlock()
for client := range room.clients {
client.conn.Write([]byte(message))
}
}
// Handle the SYNC command, expecting a JSON payload
func handleSync(client *Client, payload string) {
var data map[string]interface{}
err := json.Unmarshal([]byte(payload), &data)
if err != nil {
client.conn.Write([]byte("Invalid JSON\n"))
return
}
// You can process the JSON payload here as needed
client.conn.Write([]byte("Sync received\n"))
}
// Start the server
func startServer() {
listener, err := net.Listen("tcp", ":5518")
if err != nil {
fmt.Println("Error starting server:", err)
return
}
defer listener.Close()
fmt.Println("Server started on port 5518")
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting connection:", err)
continue
}
go func() {
conn.Write([]byte("Enter your username: "))
username, _ := bufio.NewReader(conn).ReadString('\n')
username = strings.TrimSpace(username)
client := &Client{
conn: conn,
username: username,
}
conn.Write([]byte(fmt.Sprintf("Welcome %s!\n", username)))
handleClient(client)
}()
}
}
func main() {
startServer()
}