124 lines
3.4 KiB
Go
124 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
// "flag"
|
|
// "fmt"
|
|
"embed"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gin-contrib/gzip"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/gorilla/websocket"
|
|
)
|
|
|
|
//go:embed toe
|
|
var staticFiles embed.FS
|
|
|
|
var waitingPlayers = make(map[string][]*websocket.Conn)
|
|
|
|
func SetCustomContentType() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
requestPath := c.Request.URL.Path
|
|
if strings.Contains(requestPath, ".") {
|
|
c.Header("Cache-Control", "public, max-age=31536000, immutable")
|
|
}
|
|
if strings.HasPrefix(requestPath, "/tic/tac/toe") && !strings.Contains(requestPath, ".") {
|
|
c.Header("Content-Type", "text/html; charset=UTF-8")
|
|
} else if strings.HasSuffix(requestPath, ".ico") {
|
|
c.Header("Content-Type", "image/svg+xml")
|
|
} else if strings.HasSuffix(requestPath, ".js") {
|
|
c.Header("Content-Type", "application/javascript")
|
|
} else if strings.HasSuffix(requestPath, ".css") {
|
|
c.Header("Content-Type", "text/css")
|
|
}
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
func multiplayerHandler(c *gin.Context) {
|
|
if c.GetHeader("Accept") == "*/*" {
|
|
c.String(http.StatusTeapot, "Preload not allowed")
|
|
}
|
|
if c.Query("g") == "" {
|
|
u, err := uuid.NewV7()
|
|
if err != nil {
|
|
fmt.Println("Error:", err)
|
|
return
|
|
}
|
|
c.Redirect(http.StatusPermanentRedirect, "/multiplayer?g="+u.String())
|
|
}
|
|
c.Header("Content-Type", "text/html; charset=UTF-8")
|
|
c.String(http.StatusOK, "<iframe name=\"g\" src=\"/\" width=\"100%\" height=\"100%\"></iframe><script src=\"/tic/tac/toe/w.js\"></script>")
|
|
}
|
|
|
|
var upgrader = websocket.Upgrader{
|
|
CheckOrigin: func(r *http.Request) bool {
|
|
origin := r.Header.Get("Origin")
|
|
host := r.Host
|
|
return origin == "http://"+host || origin == "https://"+host
|
|
},
|
|
}
|
|
|
|
func wsHandler(c *gin.Context) {
|
|
game_id := c.Query("g")
|
|
if game_id == "" {
|
|
c.String(http.StatusTeapot, "No game id")
|
|
return
|
|
}
|
|
|
|
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
|
if err != nil {
|
|
fmt.Println("Upgrade error:", err)
|
|
return
|
|
}
|
|
fmt.Println("New client connected")
|
|
|
|
waitingPlayers[game_id] = append(waitingPlayers[game_id], conn)
|
|
defer func() {
|
|
conn.Close()
|
|
fmt.Println("Client disconnected")
|
|
}()
|
|
|
|
for {
|
|
messageType, message, err := conn.ReadMessage()
|
|
if err != nil {
|
|
fmt.Println("Read error:", err)
|
|
break
|
|
}
|
|
fmt.Printf("Received: %s\n", message)
|
|
|
|
// Рассылка всем клиентам
|
|
for _, c := range waitingPlayers[game_id] {
|
|
if c != conn {
|
|
err := c.WriteMessage(messageType, message)
|
|
if err != nil {
|
|
fmt.Println("Broadcast error:", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var favicon = "<svg width=\"32\" height=\"32\" viewBox=\"0 0 32 32\" xmlns=\"http://www.w3.org/2000/svg\"><path fill=\"none\" stroke=\"#000\" d=\"M10 10h12v12H10z\"/><path d=\"M22 22a8 8 0 0 0-12-12\" fill=\"none\" stroke=\"#f30\" stroke-width=\"2\"/><path stroke=\"#07f\" stroke-width=\"2\" d=\"m8 8 16 16M8 24l8-8\"/><path stroke=\"#000\" d=\"m8.5 7.5 16 16\"/></svg>"
|
|
|
|
func main() {
|
|
embeddedFilesSystem := http.FS(staticFiles)
|
|
|
|
router := gin.Default()
|
|
router.Use(SetCustomContentType())
|
|
router.Use(gzip.Gzip(gzip.DefaultCompression))
|
|
router.GET("/", func(c *gin.Context) {
|
|
c.Redirect(http.StatusMovedPermanently, "/tic/tac/toe/index")
|
|
})
|
|
router.GET("/favicon.ico", func(c *gin.Context) {
|
|
c.String(http.StatusOK, favicon)
|
|
})
|
|
router.GET("/ws", wsHandler)
|
|
router.GET("/multiplayer", multiplayerHandler)
|
|
router.StaticFS("/tic/tac", embeddedFilesSystem)
|
|
router.Run(":8080")
|
|
}
|