224 lines
4.7 KiB
Go
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()
|
||
|
}
|