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.

352 lines
8.2 KiB

7 years ago
7 years ago
  1. package botserv
  2. import (
  3. "log"
  4. v "bitbucket.org/hackerbots/vector"
  5. "code.google.com/p/go.net/websocket"
  6. )
  7. // < the name of the game we want to join
  8. type GameID struct {
  9. Id string `json:"id"`
  10. }
  11. // > identify
  12. type PlayerID struct {
  13. Type string `json:"type"`
  14. Hash string `json:"id"`
  15. Failure
  16. }
  17. func NewPlayerID(id string) *PlayerID {
  18. return &PlayerID{
  19. Type: "idreq",
  20. Hash: id,
  21. }
  22. }
  23. // < [robot | spectator], name, client-type, game ID
  24. type ClientID struct {
  25. Type string `json:"type"`
  26. Name string `json:"name"`
  27. Useragent string `json:"useragent"`
  28. }
  29. func (c *ClientID) Valid() (bool, string) {
  30. switch c.Type {
  31. case "robot", "spectator":
  32. return true, ""
  33. }
  34. return false, "useragent must be 'robot' or 'spectator'"
  35. }
  36. type ClientConfig struct {
  37. ID string `json:"id"`
  38. Stats map[string]StatsRequest `json:"stats"`
  39. }
  40. func (config ClientConfig) Valid(max int) bool {
  41. total := 0
  42. for _, s := range config.Stats {
  43. total += (s.Speed +
  44. s.Hp +
  45. s.WeaponRadius +
  46. s.ScannerRadius +
  47. s.Acceleration +
  48. s.TurnSpeed +
  49. s.FireRate +
  50. s.WeaponDamage +
  51. s.WeaponSpeed)
  52. }
  53. if total > max {
  54. return false
  55. }
  56. return true
  57. }
  58. type BoardSize struct {
  59. Width float32 `json:"width"`
  60. Height float32 `json:"height"`
  61. }
  62. type GameParam struct {
  63. // TODO: should have information about max points in here
  64. BoardSize BoardSize `json:"boardsize"`
  65. MaxPoints int `json:"max_points"`
  66. Encoding string `json:"encoding"`
  67. Type string `json:"type"`
  68. }
  69. // > [OK | FULL | NOT AUTH], board size, game params
  70. type Handshake struct {
  71. ID string `json:"id"`
  72. Success bool `json:"success"`
  73. Type string `json:"type"`
  74. }
  75. func NewHandshake(id string, success bool) *Handshake {
  76. return &Handshake{
  77. ID: id,
  78. Success: success,
  79. Type: "handshake",
  80. }
  81. }
  82. type Message interface {
  83. }
  84. type Boardstate struct {
  85. MyRobots []Robot `json:"my_robots"`
  86. OtherRobots []OtherRobot `json:"robots"`
  87. Projectiles []Projectile `json:"projectiles"`
  88. Splosions []Splosion `json:"splosions"`
  89. Objects [][4]int `json:"objects"`
  90. Type string `json:"type"`
  91. Turn int `json:"turn"`
  92. AllBots []BotHealth `json:"all_bots"`
  93. Messages []string `json:"messages"`
  94. }
  95. func NewBoardstate() *Boardstate {
  96. return &Boardstate{
  97. MyRobots: []Robot{},
  98. OtherRobots: []OtherRobot{},
  99. Projectiles: []Projectile{},
  100. Splosions: []Splosion{},
  101. AllBots: []BotHealth{},
  102. Type: "boardstate",
  103. }
  104. }
  105. type GameOver struct {
  106. Winners []string `json:"winners"`
  107. Type string `json:"type"`
  108. }
  109. func NewGameOver() *GameOver {
  110. return &GameOver{
  111. Type: "gameover",
  112. Winners: make([]string, 0),
  113. }
  114. }
  115. type Failure struct {
  116. Reason string `json:"reason"`
  117. Type string `json:"type"`
  118. }
  119. func NewFailure(reason string) *Failure {
  120. return &Failure{
  121. Reason: reason,
  122. Type: "failure",
  123. }
  124. }
  125. func (c *Controller) AddPlayer(ws *websocket.Conn) {
  126. var gid GameID
  127. err := websocket.JSON.Receive(ws, &gid)
  128. if err != nil {
  129. log.Println("problem parsing the requested game id")
  130. return
  131. }
  132. game := c.Games.Get(gid.Id)
  133. if game == nil {
  134. var err error
  135. game, err = NewGame(
  136. gid.Id,
  137. float32(c.Conf.Width),
  138. float32(c.Conf.Height),
  139. c.Conf.Obstacles,
  140. c.Conf.Tick,
  141. c.Conf.MaxPoints,
  142. "",
  143. )
  144. if err != nil {
  145. log.Printf("problem creating game: %s", gid.Id)
  146. websocket.JSON.Send(ws, NewFailure("game creation error"))
  147. return
  148. }
  149. go game.run()
  150. c.Games.Add(game)
  151. }
  152. player_id := c.Idg.Hash()
  153. err = websocket.JSON.Send(ws, NewPlayerID(player_id))
  154. if err != nil {
  155. log.Printf("game %s: unable to send player_id to player %s", gid.Id, player_id)
  156. websocket.JSON.Send(ws, NewFailure("send error"))
  157. return
  158. } else {
  159. log.Printf("game %s: sent player id: %s", gid.Id, player_id)
  160. }
  161. var clientid ClientID
  162. err = websocket.JSON.Receive(ws, &clientid)
  163. if err != nil {
  164. log.Printf("unable to parse ClientID: gid: %s, player: %s", gid.Id, player_id)
  165. websocket.JSON.Send(ws, NewFailure("parse error"))
  166. return
  167. } else {
  168. log.Printf("game %s: recieved: %+v", gid.Id, clientid)
  169. }
  170. if v, msg := clientid.Valid(); !v {
  171. log.Printf("clientid is invalid: %+v", clientid)
  172. websocket.JSON.Send(
  173. ws,
  174. NewFailure(msg),
  175. )
  176. return
  177. }
  178. reqEncs := []string{}
  179. err = websocket.JSON.Receive(ws, &reqEncs)
  180. if err != nil {
  181. log.Printf("%s %s unable to parse requested encodings", gid.Id, player_id)
  182. websocket.JSON.Send(ws, NewFailure("encoding recieve error"))
  183. return
  184. }
  185. prefEncs := []string{
  186. "gob",
  187. "json",
  188. }
  189. var encoding string
  190. encodingLoops:
  191. for _, prefEnc := range prefEncs {
  192. for _, reqEnc := range reqEncs {
  193. if reqEnc == prefEnc {
  194. encoding = prefEnc
  195. log.Println("selected following encoding:", encoding)
  196. break encodingLoops
  197. }
  198. }
  199. }
  200. if encoding == "" {
  201. log.Printf("%s %s unable to negociate encoding", gid.Id, player_id)
  202. websocket.JSON.Send(
  203. ws,
  204. NewFailure("no overlap on supported encodings; I suggest using json"),
  205. )
  206. return
  207. }
  208. gameParam := game.gameParam()
  209. gp := struct {
  210. GameParam
  211. Encoding string `json:"encoding"`
  212. }{
  213. GameParam: *gameParam,
  214. Encoding: encoding,
  215. }
  216. err = websocket.JSON.Send(ws, gp)
  217. if err != nil {
  218. log.Printf("%s %s game param send error", gid.Id, player_id)
  219. websocket.JSON.Send(ws, NewFailure("game param send error"))
  220. return
  221. } else {
  222. log.Printf("%s -> %s: sent %+v", gid.Id, player_id, gameParam)
  223. }
  224. switch clientid.Type {
  225. case "robot":
  226. var conf ClientConfig
  227. for {
  228. log.Printf("%s Waiting for client to send conf ...", player_id)
  229. err = websocket.JSON.Receive(ws, &conf)
  230. log.Printf("%s: conf received: %s", player_id, conf.ID)
  231. if err != nil {
  232. log.Printf("%s %s config parse error", gid.Id, player_id)
  233. websocket.JSON.Send(ws, NewFailure("config parse error"))
  234. return
  235. }
  236. // TODO: verify conf's type
  237. if conf.Valid(game.maxPoints) {
  238. log.Printf("%s -> %s: valid client config", gid.Id, player_id)
  239. _ = websocket.JSON.Send(ws, NewHandshake(player_id, true))
  240. break
  241. } else {
  242. log.Printf("%s: Config is INVALID, abort", player_id)
  243. _ = websocket.JSON.Send(ws, NewFailure("invalid config"))
  244. return
  245. }
  246. }
  247. p := NewPlayer(player_id, ws, game.bw, encoding)
  248. log.Printf("%s: made a player: %s", gid.Id, p.Id)
  249. convertedStats := map[string]Stats{}
  250. for name, stats := range conf.Stats {
  251. dstat := DeriveStats(stats)
  252. convertedStats[name] = dstat
  253. r := Robot{
  254. Stats: dstat,
  255. Id: c.Idg.Hash(),
  256. Name: name,
  257. Health: 10,
  258. Heading: v.Vector2d{1, 0},
  259. Scanners: make([]Scanner, 0),
  260. Delta: c.Conf.Delta,
  261. idg: c.Idg,
  262. }
  263. r.Health = r.Stats.Hp
  264. log.Printf("%s: adding robot: %s", p.Id, r.Id)
  265. r.reset(game)
  266. p.Robots = append(p.Robots, &r)
  267. }
  268. statsPayload := struct {
  269. Stats map[string]Stats `json:"stats"`
  270. Type string `json:"type"`
  271. }{
  272. Stats: convertedStats,
  273. Type: "stats",
  274. }
  275. err = websocket.JSON.Send(ws, &statsPayload)
  276. if err != nil {
  277. log.Printf("error sending convertedStats to client: %s", err)
  278. websocket.JSON.Send(ws, NewFailure("protocol error: convertedStats"))
  279. return
  280. } else {
  281. log.Printf("%s -> %s: sent stats payload", gid.Id, p.Id)
  282. }
  283. log.Printf("%s, %s: about to register this player", gid.Id, p.Id)
  284. game.register <- p
  285. log.Printf("%s, %s: registered player", gid.Id, p.Id)
  286. defer func() {
  287. log.Printf("%s, %s: about to unregister this player", gid.Id, p.Id)
  288. game.unregister <- p
  289. log.Printf("%s, %s: unregistered player", gid.Id, p.Id)
  290. }()
  291. go p.sender()
  292. log.Printf("%s -> %s: p.sender went", gid.Id, p.Id)
  293. p.recv()
  294. log.Printf(
  295. "%s (player): %v (robot) has been disconnected from %s (game)",
  296. p.Id,
  297. p.Robots[0].Id,
  298. gid.Id,
  299. )
  300. case "spectator":
  301. s := NewSpectator(player_id, ws, game.bw, encoding)
  302. log.Printf("%s, %s: about to register this spectator", gid.Id, s.Id)
  303. game.sregister <- s
  304. log.Printf("%s, %s: registered spectator", gid.Id, s.Id)
  305. defer func() {
  306. log.Printf("%s, %s: about to unregister this spectator", gid.Id, s.Id)
  307. game.sunregister <- s
  308. log.Printf("%s, %s: unregistered spectator", gid.Id, s.Id)
  309. }()
  310. go s.sender()
  311. log.Printf("%s -> %s: s.sender went", gid.Id, s.Id)
  312. s.recv()
  313. log.Printf("game %s: spectator %+v has been disconnected from this game", gid.Id, s)
  314. }
  315. log.Printf("exiting AddPlayer")
  316. }