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.

385 lines
8.6 KiB

7 years ago
7 years ago
7 years ago
  1. package main
  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. }
  78. type GameMode interface {
  79. setup(g *game)
  80. tick(gg *game, payload *Boardstate)
  81. gameOver(gg *game) (bool, *GameOver)
  82. }
  83. func NewGame(id string, width, height float32, obstacles, tick, maxPoints int, mode string) (*game, error) {
  84. bw, err := bandwidth.NewBandwidth(
  85. []int{1, 10, 60},
  86. 1*time.Second,
  87. )
  88. if err != nil {
  89. log.Fatal("seriously, what the fuck")
  90. return nil, err
  91. }
  92. go bw.Run()
  93. g := &game{
  94. id: id,
  95. register: make(chan *player, maxPlayer),
  96. unregister: make(chan *player, maxPlayer),
  97. projectiles: make(map[*Projectile]bool),
  98. splosions: make(map[*Splosion]bool),
  99. obstacles: GenerateObstacles(obstacles, width, height),
  100. obstacle_count: obstacles,
  101. players: make(map[*player]bool),
  102. turn: 0,
  103. width: width,
  104. height: height,
  105. maxPoints: maxPoints,
  106. spectators: make(map[*Spectator]bool),
  107. sregister: make(chan *Spectator),
  108. sunregister: make(chan *Spectator),
  109. kill: make(chan bool, maxPlayer),
  110. repair_hp: 5,
  111. repair_rate: 3.0,
  112. tick_duration: tick,
  113. players_remaining: 2,
  114. stats: GameStats{PlayerStats: make(map[string]*PlayerStats)},
  115. bw: bw,
  116. }
  117. if mode == "melee" {
  118. g.mode = &melee{respawn: make(map[*Robot]float64)}
  119. } else {
  120. g.mode = &deathmatch{}
  121. }
  122. g.mode.setup(g)
  123. return g, nil
  124. }
  125. func (g *game) tick(payload *Boardstate) {
  126. g.players_remaining = 0
  127. payload.Objects = MinifyObstacles(g.obstacles)
  128. // Update Players
  129. for p := range g.players {
  130. living_robots := 0
  131. for _, r := range p.Robots {
  132. if r.Health > 0 {
  133. living_robots++
  134. r.Tick(g)
  135. }
  136. if len(r.Message) > 0 {
  137. if len(r.Message) > 100 {
  138. r.Message = r.Message[0:99]
  139. }
  140. payload.Messages = append(payload.Messages, r.Message)
  141. }
  142. payload.OtherRobots = append(
  143. payload.OtherRobots,
  144. r.GetTruncatedDetails())
  145. payload.AllBots = append(
  146. payload.AllBots,
  147. BotHealth{RobotId: r.Id, Health: r.Health})
  148. }
  149. if living_robots > 0 {
  150. g.players_remaining++
  151. }
  152. }
  153. // Update Projectiles
  154. for pr := range g.projectiles {
  155. pr.Tick(g)
  156. }
  157. // We do this here, because the tick calls can alter g.projectiles
  158. for pr := range g.projectiles {
  159. payload.Projectiles = append(payload.Projectiles, *pr)
  160. }
  161. // Update Splosions
  162. for s := range g.splosions {
  163. s.Tick()
  164. if !s.Alive() {
  165. delete(g.splosions, s)
  166. }
  167. payload.Splosions = append(payload.Splosions, *s)
  168. }
  169. }
  170. func (g *game) sendUpdate(payload *Boardstate) {
  171. // Ensure that the robots are always sent in a consistent order
  172. sort.Sort(RobotSorter{Robots: payload.OtherRobots})
  173. sort.Sort(AllRobotSorter{Robots: payload.AllBots})
  174. for p := range g.players {
  175. // Copy the payload but only add the robots in scanner range
  176. player_payload := NewBoardstate()
  177. player_payload.Messages = payload.Messages
  178. player_payload.AllBots = payload.AllBots
  179. player_payload.Turn = payload.Turn
  180. for _, r := range p.Robots {
  181. player_payload.MyRobots = append(player_payload.MyRobots, *r)
  182. // player_payload.OtherRobots = append(
  183. // player_payload.OtherRobots,
  184. // r.GetTruncatedDetails())
  185. }
  186. player_payload.Objects = [][4]int{}
  187. player_payload.Splosions = []Splosion{}
  188. player_payload.Projectiles = []Projectile{}
  189. living_robots := 0
  190. for _, r := range p.Robots {
  191. if r.Health > 0 {
  192. living_robots++
  193. // Filter robots by scanner
  194. for player := range g.players {
  195. for _, scan_entry := range r.Scanners {
  196. for _, r := range player.Robots {
  197. if r.Id == scan_entry.Id {
  198. player_payload.OtherRobots = append(
  199. player_payload.OtherRobots,
  200. r.GetTruncatedDetails())
  201. }
  202. }
  203. }
  204. }
  205. // Filter projectiles
  206. for proj := range g.projectiles {
  207. if proj.Owner == r {
  208. player_payload.Projectiles = append(
  209. player_payload.Projectiles,
  210. *proj)
  211. }
  212. for _, scan_entry := range r.Scanners {
  213. if proj.Id == scan_entry.Id {
  214. player_payload.Projectiles = append(
  215. player_payload.Projectiles,
  216. *proj)
  217. }
  218. }
  219. }
  220. // Filter splosions
  221. for splo := range g.splosions {
  222. for _, scan_entry := range r.Scanners {
  223. if splo.Id == scan_entry.Id {
  224. player_payload.Splosions = append(
  225. player_payload.Splosions,
  226. *splo)
  227. }
  228. }
  229. }
  230. // Filter objects
  231. for _, ob := range g.obstacles {
  232. if ob.distance_from_point(r.Position) < float32(r.Stats.ScannerRadius)+r.ScanCounter {
  233. player_payload.Objects = append(
  234. player_payload.Objects, ob.minify())
  235. }
  236. }
  237. }
  238. }
  239. // if living_robots == 0 {
  240. // player_payload.OtherRobots = payload.OtherRobots
  241. // player_payload.Projectiles = payload.Projectiles
  242. // player_payload.Splosions = payload.Splosions
  243. // player_payload.Objects = payload.Objects
  244. // }
  245. p.send <- player_payload
  246. }
  247. for s := range g.spectators {
  248. s.send <- payload
  249. }
  250. }
  251. func (g *game) run() {
  252. var t0, t1 time.Time
  253. ticker := time.NewTicker(time.Duration(conf.Tick) * time.Millisecond)
  254. for {
  255. select {
  256. case <-g.kill:
  257. log.Printf("game %s: received kill signal, dying gracefully", g.id)
  258. close(g.bw.Quit)
  259. games.Lock()
  260. for player := range g.players {
  261. close(player.send)
  262. }
  263. delete(games.m, g.id)
  264. games.Unlock()
  265. return
  266. case p := <-g.register:
  267. g.players[p] = true
  268. g.stats.PlayerStats[p.Id] = &PlayerStats{
  269. BotStats: make(map[string]*BotStats),
  270. }
  271. for _, r := range p.Robots {
  272. g.stats.PlayerStats[p.Id].BotStats[r.Name] = &BotStats{}
  273. r.gameStats = g.stats.PlayerStats[p.Id].BotStats[r.Name]
  274. }
  275. case p := <-g.unregister:
  276. delete(g.players, p)
  277. close(p.send)
  278. case s := <-g.sregister:
  279. g.spectators[s] = true
  280. case s := <-g.sunregister:
  281. delete(g.spectators, s)
  282. close(s.send)
  283. case <-ticker.C:
  284. t0 = time.Now()
  285. payload := NewBoardstate()
  286. g.turn++
  287. payload.Turn = g.turn
  288. if *verbose {
  289. log.Printf("\033[2JTurn: %v", g.turn)
  290. log.Printf("Players: %v", len(g.players))
  291. log.Printf("Projectiles: %v", len(g.projectiles))
  292. log.Printf("Explosions: %v", len(g.splosions))
  293. }
  294. // UPDATE GAME STATE
  295. if end, data := g.mode.gameOver(g); end {
  296. g.sendGameOver(data)
  297. }
  298. g.tick(payload)
  299. g.mode.tick(g, payload)
  300. t1 = time.Now()
  301. if *verbose {
  302. log.Printf("Turn Processes %v\n", t1.Sub(t0))
  303. }
  304. // SEND THE UPDATE TO EACH PLAYER
  305. g.sendUpdate(payload)
  306. t1 = time.Now()
  307. if *verbose {
  308. log.Printf("Sent Payload %v\n", t1.Sub(t0))
  309. }
  310. }
  311. }
  312. }
  313. func (g *game) sendGameOver(eg *GameOver) {
  314. log.Printf("sending out game over message: %+v", eg)
  315. for p := range g.players {
  316. p.send <- eg
  317. }
  318. for s := range g.spectators {
  319. s.send <- eg
  320. }
  321. }
  322. // returns a GameParam object popuplated by info from the game. This is
  323. // used during client/server initial negociation.
  324. func (g *game) gameParam() *GameParam {
  325. return &GameParam{
  326. BoardSize: BoardSize{
  327. Width: g.width,
  328. Height: g.height,
  329. },
  330. MaxPoints: g.maxPoints,
  331. Type: "gameparam",
  332. }
  333. }