A simple TicTacToe app with Golang backend and WebSockets gluing it all together.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

128 lines
2.7 KiB

  1. package main
  2. import (
  3. "log"
  4. "net/http"
  5. "os"
  6. "time"
  7. cache "tictactoe-api/cache"
  8. "github.com/gin-contrib/multitemplate"
  9. "github.com/gin-gonic/autotls"
  10. "github.com/gin-gonic/gin"
  11. uuid "github.com/satori/go.uuid"
  12. )
  13. func setupRender() multitemplate.Renderer {
  14. r := multitemplate.NewRenderer()
  15. r.AddFromFiles("index", "dist/index.html")
  16. r.AddFromFiles("join", "dist/join.html")
  17. return r
  18. }
  19. func main() {
  20. hub := newHub()
  21. go hub.run()
  22. cache := cache.NewCache(time.Minute*10, time.Minute)
  23. router := gin.Default()
  24. router.HTMLRender = setupRender()
  25. router.Static("/static", "dist")
  26. router.GET("/favicon.ico", func(c *gin.Context) {
  27. c.File("dist/favicon.ico")
  28. })
  29. router.GET("/ping", func(c *gin.Context) {
  30. c.String(http.StatusOK, "pong")
  31. })
  32. router.Any("/ws", func(c *gin.Context) {
  33. serveWs(hub, c.Writer, c.Request)
  34. })
  35. //
  36. //
  37. //
  38. router.GET("/join/:gameUUID", func(c *gin.Context) {
  39. c.HTML(http.StatusOK, "join", gin.H{
  40. "title": "Play Game",
  41. })
  42. })
  43. router.GET("/game", func(c *gin.Context) {
  44. c.HTML(http.StatusOK, "index", gin.H{
  45. "title": "Start New Game",
  46. })
  47. })
  48. router.POST("/game", func(c *gin.Context) {
  49. var game Game
  50. err := c.BindJSON(&game)
  51. if err != nil {
  52. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
  53. return
  54. }
  55. game.Turn = game.Players[0].UUID
  56. cache.Set(game.UUID, game, 0)
  57. c.JSON(http.StatusOK, game)
  58. })
  59. router.GET("/game/:gameUUID", func(c *gin.Context) {
  60. gameUUID, err := uuid.FromString(c.Params.ByName("gameUUID"))
  61. if err != nil {
  62. log.Printf("UUID Parse Failed: %s\n", err)
  63. }
  64. game, found := cache.Get(gameUUID)
  65. if !found {
  66. c.Status(http.StatusNotFound)
  67. return
  68. }
  69. c.JSON(http.StatusOK, game)
  70. })
  71. router.POST("/game/:gameUUID/player/:playerUUID", func(c *gin.Context) {
  72. gameUUID, err := uuid.FromString(c.Params.ByName("gameUUID"))
  73. if err != nil {
  74. log.Printf("UUID Game Parse Failed: %s\n", err)
  75. }
  76. playerUUID, err := uuid.FromString(c.Params.ByName("playerUUID"))
  77. if err != nil {
  78. log.Printf("UUID Player Parse Failed: %s\n", err)
  79. }
  80. game, found := cache.Get(gameUUID)
  81. if !found {
  82. c.Status(http.StatusNotFound)
  83. return
  84. }
  85. _game := game.(Game)
  86. var player Player
  87. err = c.BindJSON(&player)
  88. if err != nil {
  89. log.Printf("Error: %s\n", err)
  90. }
  91. if len(_game.Players) > 1 {
  92. c.JSON(http.StatusBadRequest, gin.H{"error": "TicTacToe can only be played with two players."})
  93. return
  94. }
  95. _game.Players = append(_game.Players, &Player{
  96. UUID: &playerUUID,
  97. Name: player.Name,
  98. })
  99. cache.Set(gameUUID, _game, 0)
  100. c.JSON(http.StatusOK, _game)
  101. })
  102. //
  103. //
  104. //
  105. if os.Getenv("GIN_MODE") == "release" {
  106. log.Fatal(autotls.Run(router, "tictactoe.l3vi.co"))
  107. } else {
  108. log.Fatal(router.Run(":80"))
  109. }
  110. }