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.

160 lines
3.3 KiB

  1. package main
  2. import (
  3. "errors"
  4. "log"
  5. "math/rand"
  6. "net/http"
  7. "os"
  8. Cache "tictactoe-api/cache"
  9. "time"
  10. "github.com/gin-contrib/multitemplate"
  11. "github.com/gin-gonic/autotls"
  12. "github.com/gin-gonic/gin"
  13. )
  14. func setupRender() multitemplate.Renderer {
  15. r := multitemplate.NewRenderer()
  16. r.AddFromFiles("index", "dist/index.html")
  17. r.AddFromFiles("join", "dist/join.html")
  18. return r
  19. }
  20. const charBytes = "abcdefghijkmnopqrstuvwxyz023456789"
  21. func generateRandom(n int) string {
  22. b := make([]byte, n)
  23. for i := range b {
  24. b[i] = charBytes[rand.Int63()%int64(len(charBytes))]
  25. }
  26. return string(b)
  27. }
  28. func main() {
  29. hub := newHub()
  30. go hub.run()
  31. cache := Cache.NewCache(time.Minute*10, time.Minute)
  32. router := gin.Default()
  33. router.HTMLRender = setupRender()
  34. router.Static("/static", "dist")
  35. router.GET("/favicon.ico", func(c *gin.Context) {
  36. c.File("dist/favicon.ico")
  37. })
  38. router.GET("/ping", func(c *gin.Context) {
  39. c.String(http.StatusOK, "pong")
  40. })
  41. router.Any("/ws", func(c *gin.Context) {
  42. serveWs(hub, c.Writer, c.Request)
  43. })
  44. //
  45. //
  46. //
  47. router.GET("/join", func(c *gin.Context) {
  48. c.HTML(http.StatusOK, "join", gin.H{
  49. "title": "Play Game",
  50. })
  51. })
  52. router.GET("/game", func(c *gin.Context) {
  53. c.HTML(http.StatusOK, "index", gin.H{
  54. "title": "Start New Game",
  55. })
  56. })
  57. router.POST("/game", func(c *gin.Context) {
  58. var game Cache.Game
  59. err := c.BindJSON(&game)
  60. if err != nil {
  61. log.Printf("Error Binding Request %s\n", err.Error())
  62. }
  63. count := 0
  64. generate:
  65. game.ID = generateRandom(6)
  66. _, found := cache.Get(game.ID)
  67. if found {
  68. count = count + 1
  69. log.Printf("GAME FOUND, trying again: %s\n", game.ID)
  70. if count >= 3 {
  71. err = errors.New("Could not generate a new game (too many games in progress)")
  72. } else {
  73. goto generate
  74. }
  75. }
  76. if err != nil {
  77. c.JSON(http.StatusConflict, gin.H{
  78. "error": err.Error(),
  79. })
  80. } else {
  81. game.Turn = &game.Player1.UUID
  82. cache.Set(game.ID, game, 0)
  83. c.JSON(http.StatusOK, game)
  84. }
  85. })
  86. router.GET("/game/:gameID", func(c *gin.Context) {
  87. gameID := c.Params.ByName("gameID")
  88. game, found := cache.Get(gameID)
  89. if !found {
  90. c.Status(http.StatusNotFound)
  91. return
  92. }
  93. c.JSON(http.StatusOK, game)
  94. })
  95. router.POST("/game/:gameID", func(c *gin.Context) {
  96. gameID := c.Params.ByName("gameID")
  97. game, found := cache.Get(gameID)
  98. if !found {
  99. c.Status(http.StatusNotFound)
  100. return
  101. }
  102. var updateGame Cache.Game
  103. err := c.BindJSON(&updateGame)
  104. if err != nil {
  105. log.Printf("Error: %s\n", err)
  106. }
  107. if updateGame.ID != "" {
  108. game.ID = updateGame.ID
  109. }
  110. if updateGame.Player1 != nil {
  111. game.Player1 = updateGame.Player1
  112. }
  113. if updateGame.Player2 != nil {
  114. game.Player2 = updateGame.Player2
  115. }
  116. if updateGame.Turn != nil {
  117. game.Turn = updateGame.Turn
  118. }
  119. if (Cache.Matrix{}) != updateGame.Matrix {
  120. game.Matrix = updateGame.Matrix
  121. }
  122. // if updateGame.Draw != nil {
  123. // game.Draw = updateGame.Draw
  124. // }
  125. // if updateGame.Winner != nil {
  126. // game.Winner = updateGame.Winner
  127. // }
  128. // if updateGame.Tally != nil {
  129. // game.Tally = append(game.Tally, updateGame.Tally[0])
  130. // }
  131. cache.Set(gameID, game, 0)
  132. c.JSON(http.StatusOK, game)
  133. })
  134. //
  135. //
  136. //
  137. if os.Getenv("GIN_MODE") == "release" {
  138. log.Fatal(autotls.Run(router, "tictactoe.l3vi.co"))
  139. } else {
  140. log.Fatal(router.Run(":80"))
  141. }
  142. }