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.

389 lines
8.7KB

  1. package botserv
  2. // delete me
  3. import (
  4. "log"
  5. "sort"
  6. "sync"
  7. "time"
  8. "bitbucket.org/smcquay/bandwidth"
  9. )
  10. const maxPlayer = 128
  11. type BotHealth struct {
  12. RobotId string `json:"robot_id"`
  13. Health int `json:"health"`
  14. }
  15. type Scanner struct {
  16. Id string `json:"id"`
  17. Type string `json:"type"`
  18. }
  19. type MapLock struct {
  20. M map[string]*Game
  21. sync.RWMutex
  22. }
  23. // get is a function that returns a game if found, and creates one if
  24. // not found and force is true. In order to get a hash (rather than use
  25. // the string you pass) send "" for id.
  26. func (ml *MapLock) get(id string) *Game {
  27. ml.Lock()
  28. g, _ := ml.M[id]
  29. ml.Unlock()
  30. return g
  31. }
  32. func (ml *MapLock) add(g *Game) {
  33. ml.Lock()
  34. ml.M[g.id] = g
  35. ml.Unlock()
  36. }
  37. type BotStats struct {
  38. Kills int
  39. Deaths int
  40. Suicides int
  41. Shots int
  42. DirectHits int
  43. Hits int
  44. Wins int
  45. }
  46. type PlayerStats struct {
  47. BotStats map[string]*BotStats
  48. Wins int
  49. }
  50. type GameStats struct {
  51. PlayerStats map[string]*PlayerStats
  52. sync.RWMutex
  53. }
  54. type Game struct {
  55. id string
  56. players map[*player]bool
  57. projectiles map[*Projectile]bool
  58. splosions map[*Splosion]bool
  59. obstacles []Obstacle
  60. obstacle_count int
  61. register chan *player
  62. unregister chan *player
  63. turn int
  64. players_remaining int
  65. width, height float32
  66. maxPoints int
  67. spectators map[*Spectator]bool
  68. sregister chan *Spectator
  69. sunregister chan *Spectator
  70. kill chan bool
  71. repair_hp int
  72. repair_rate float32
  73. tick_duration int
  74. stats GameStats
  75. mode GameMode
  76. bw *bandwidth.Bandwidth
  77. Conf Config
  78. Games *MapLock
  79. Verbose bool
  80. }
  81. type GameMode interface {
  82. setup(g *Game)
  83. tick(gg *Game, payload *Boardstate)
  84. gameOver(gg *Game) (bool, *GameOver)
  85. }
  86. func NewGame(id string, width, height float32, obstacles, tick, maxPoints int, mode string) (*Game, error) {
  87. bw, err := bandwidth.NewBandwidth(
  88. []int{1, 10, 60},
  89. 1*time.Second,
  90. )
  91. if err != nil {
  92. log.Fatal("seriously, what the fuck")
  93. return nil, err
  94. }
  95. go bw.Run()
  96. g := &Game{
  97. id: id,
  98. register: make(chan *player, maxPlayer),
  99. unregister: make(chan *player, maxPlayer),
  100. projectiles: make(map[*Projectile]bool),
  101. splosions: make(map[*Splosion]bool),
  102. obstacles: GenerateObstacles(obstacles, width, height),
  103. obstacle_count: obstacles,
  104. players: make(map[*player]bool),
  105. turn: 0,
  106. width: width,
  107. height: height,
  108. maxPoints: maxPoints,
  109. spectators: make(map[*Spectator]bool),
  110. sregister: make(chan *Spectator),
  111. sunregister: make(chan *Spectator),
  112. kill: make(chan bool, maxPlayer),
  113. repair_hp: 5,
  114. repair_rate: 3.0,
  115. tick_duration: tick,
  116. players_remaining: 2,
  117. stats: GameStats{PlayerStats: make(map[string]*PlayerStats)},
  118. bw: bw,
  119. }
  120. if mode == "melee" {
  121. g.mode = &melee{respawn: make(map[*Robot]float64)}
  122. } else {
  123. g.mode = &deathmatch{}
  124. }
  125. g.mode.setup(g)
  126. return g, nil
  127. }
  128. func (g *Game) tick(payload *Boardstate) {
  129. g.players_remaining = 0
  130. payload.Objects = MinifyObstacles(g.obstacles)
  131. // Update Players
  132. for p := range g.players {
  133. living_robots := 0
  134. for _, r := range p.Robots {
  135. if r.Health > 0 {
  136. living_robots++
  137. r.Tick(g)
  138. }
  139. if len(r.Message) > 0 {
  140. if len(r.Message) > 100 {
  141. r.Message = r.Message[0:99]
  142. }
  143. payload.Messages = append(payload.Messages, r.Message)
  144. }
  145. payload.OtherRobots = append(
  146. payload.OtherRobots,
  147. r.GetTruncatedDetails())
  148. payload.AllBots = append(
  149. payload.AllBots,
  150. BotHealth{RobotId: r.Id, Health: r.Health})
  151. }
  152. if living_robots > 0 {
  153. g.players_remaining++
  154. }
  155. }
  156. // Update Projectiles
  157. for pr := range g.projectiles {
  158. pr.Tick(g)
  159. }
  160. // We do this here, because the tick calls can alter g.projectiles
  161. for pr := range g.projectiles {
  162. payload.Projectiles = append(payload.Projectiles, *pr)
  163. }
  164. // Update Splosions
  165. for s := range g.splosions {
  166. s.Tick()
  167. if !s.Alive() {
  168. delete(g.splosions, s)
  169. }
  170. payload.Splosions = append(payload.Splosions, *s)
  171. }
  172. }
  173. func (g *Game) sendUpdate(payload *Boardstate) {
  174. // Ensure that the robots are always sent in a consistent order
  175. sort.Sort(RobotSorter{Robots: payload.OtherRobots})
  176. sort.Sort(AllRobotSorter{Robots: payload.AllBots})
  177. for p := range g.players {
  178. // Copy the payload but only add the robots in scanner range
  179. player_payload := NewBoardstate()
  180. player_payload.Messages = payload.Messages
  181. player_payload.AllBots = payload.AllBots
  182. player_payload.Turn = payload.Turn
  183. for _, r := range p.Robots {
  184. player_payload.MyRobots = append(player_payload.MyRobots, *r)
  185. // player_payload.OtherRobots = append(
  186. // player_payload.OtherRobots,
  187. // r.GetTruncatedDetails())
  188. }
  189. player_payload.Objects = [][4]int{}
  190. player_payload.Splosions = []Splosion{}
  191. player_payload.Projectiles = []Projectile{}
  192. living_robots := 0
  193. for _, r := range p.Robots {
  194. if r.Health > 0 {
  195. living_robots++
  196. // Filter robots by scanner
  197. for player := range g.players {
  198. for _, scan_entry := range r.Scanners {
  199. for _, r := range player.Robots {
  200. if r.Id == scan_entry.Id {
  201. player_payload.OtherRobots = append(
  202. player_payload.OtherRobots,
  203. r.GetTruncatedDetails())
  204. }
  205. }
  206. }
  207. }
  208. // Filter projectiles
  209. for proj := range g.projectiles {
  210. if proj.Owner == r {
  211. player_payload.Projectiles = append(
  212. player_payload.Projectiles,
  213. *proj)
  214. }
  215. for _, scan_entry := range r.Scanners {
  216. if proj.Id == scan_entry.Id {
  217. player_payload.Projectiles = append(
  218. player_payload.Projectiles,
  219. *proj)
  220. }
  221. }
  222. }
  223. // Filter splosions
  224. for splo := range g.splosions {
  225. for _, scan_entry := range r.Scanners {
  226. if splo.Id == scan_entry.Id {
  227. player_payload.Splosions = append(
  228. player_payload.Splosions,
  229. *splo)
  230. }
  231. }
  232. }
  233. // Filter objects
  234. for _, ob := range g.obstacles {
  235. if ob.distance_from_point(r.Position) < float32(r.Stats.ScannerRadius)+r.ScanCounter {
  236. player_payload.Objects = append(
  237. player_payload.Objects, ob.minify())
  238. }
  239. }
  240. }
  241. }
  242. // if living_robots == 0 {
  243. // player_payload.OtherRobots = payload.OtherRobots
  244. // player_payload.Projectiles = payload.Projectiles
  245. // player_payload.Splosions = payload.Splosions
  246. // player_payload.Objects = payload.Objects
  247. // }
  248. p.send <- player_payload
  249. }
  250. for s := range g.spectators {
  251. s.send <- payload
  252. }
  253. }
  254. func (g *Game) run() {
  255. var t0, t1 time.Time
  256. ticker := time.NewTicker(time.Duration(g.Conf.Tick) * time.Millisecond)
  257. for {
  258. select {
  259. case <-g.kill:
  260. log.Printf("game %s: received kill signal, dying gracefully", g.id)
  261. close(g.bw.Quit)
  262. g.Games.Lock()
  263. for player := range g.players {
  264. close(player.send)
  265. }
  266. delete(g.Games.M, g.id)
  267. g.Games.Unlock()
  268. return
  269. case p := <-g.register:
  270. g.players[p] = true
  271. g.stats.PlayerStats[p.Id] = &PlayerStats{
  272. BotStats: make(map[string]*BotStats),
  273. }
  274. for _, r := range p.Robots {
  275. g.stats.PlayerStats[p.Id].BotStats[r.Name] = &BotStats{}
  276. r.gameStats = g.stats.PlayerStats[p.Id].BotStats[r.Name]
  277. }
  278. case p := <-g.unregister:
  279. delete(g.players, p)
  280. close(p.send)
  281. case s := <-g.sregister:
  282. g.spectators[s] = true
  283. case s := <-g.sunregister:
  284. delete(g.spectators, s)
  285. close(s.send)
  286. case <-ticker.C:
  287. t0 = time.Now()
  288. payload := NewBoardstate()
  289. g.turn++
  290. payload.Turn = g.turn
  291. if g.Verbose {
  292. log.Printf("\033[2JTurn: %v", g.turn)
  293. log.Printf("Players: %v", len(g.players))
  294. log.Printf("Projectiles: %v", len(g.projectiles))
  295. log.Printf("Explosions: %v", len(g.splosions))
  296. }
  297. // UPDATE GAME STATE
  298. if end, data := g.mode.gameOver(g); end {
  299. g.sendGameOver(data)
  300. }
  301. g.tick(payload)
  302. g.mode.tick(g, payload)
  303. t1 = time.Now()
  304. if g.Verbose {
  305. log.Printf("Turn Processes %v\n", t1.Sub(t0))
  306. }
  307. // SEND THE UPDATE TO EACH PLAYER
  308. g.sendUpdate(payload)
  309. t1 = time.Now()
  310. if g.Verbose {
  311. log.Printf("Sent Payload %v\n", t1.Sub(t0))
  312. }
  313. }
  314. }
  315. }
  316. func (g *Game) sendGameOver(eg *GameOver) {
  317. log.Printf("sending out game over message: %+v", eg)
  318. for p := range g.players {
  319. p.send <- eg
  320. }
  321. for s := range g.spectators {
  322. s.send <- eg
  323. }
  324. }
  325. // returns a GameParam object popuplated by info from the game. This is
  326. // used during client/server initial negociation.
  327. func (g *Game) gameParam() *GameParam {
  328. return &GameParam{
  329. BoardSize: BoardSize{
  330. Width: g.width,
  331. Height: g.height,
  332. },
  333. MaxPoints: g.maxPoints,
  334. Type: "gameparam",
  335. }
  336. }