ignore new binary

Create new bserv binary

Herein lie the following ideas:

- change package visibility of many things
- try to stash the globals into structs
- the code is far from correct; it merely compiles
This commit is contained in:
Stephen McQuay 2014-03-18 23:38:06 -07:00
parent 62222a9ad9
commit c8efd34080
16 changed files with 146 additions and 126 deletions

2
.gitignore vendored
View File

@ -1,3 +1,3 @@
botserv
bserv/bserv
*.swp
tags

View File

@ -9,6 +9,7 @@ import (
"os"
"runtime/pprof"
"time"
"bitbucket.org/hackerbots/botserv"
"code.google.com/p/go.net/websocket"
)
@ -22,12 +23,6 @@ var config = flag.String("config", "~/.config/hackerbots/config.json", "location
var delta float32
var idg *IdGenerator
var conf Config
// This is the main, global collection of games
var games MapLock
func main() {
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
log.Printf("Starting Server...")
@ -49,10 +44,7 @@ func main() {
log.Println("serving profile info at http://localhost:8667/debug/pprof/")
}
games = MapLock{m: make(map[string]*game)}
idg = NewIdGenerator()
conf, err = loadConfig(*config)
conf, err := botserv.LoadConfig(*config)
if err != nil {
log.Fatal(err)
}
@ -61,14 +53,24 @@ func main() {
sm := http.NewServeMux()
sm.Handle("/", JsonHandler(index))
sm.Handle("/ws/", websocket.Handler(addPlayer))
sm.Handle("/game/start/", JsonHandler(startGame))
sm.Handle("/game/list/", JsonHandler(listGames))
sm.Handle("/game/stats/", JsonHandler(gameStats))
sm.Handle("/game/bw/", JsonHandler(bw))
sm.Handle("/game/stop/", JsonHandler(stopGame))
sm.HandleFunc("/fuck/shit/up/", killServer)
c := botserv.Controller{
Idg: botserv.NewIdGenerator(),
Conf: conf,
Games: botserv.MapLock{
M: make(map[string]*botserv.Game),
},
Memprofile: *mprofile,
Profile: *profile,
}
sm.Handle("/", botserv.JsonHandler(c.Index))
sm.Handle("/ws/", websocket.Handler(c.AddPlayer))
sm.Handle("/game/start/", botserv.JsonHandler(c.StartGame))
sm.Handle("/game/list/", botserv.JsonHandler(c.ListGames))
sm.Handle("/game/stats/", botserv.JsonHandler(c.GameStats))
sm.Handle("/game/bw/", botserv.JsonHandler(c.BW))
sm.Handle("/game/stop/", botserv.JsonHandler(c.StopGame))
sm.HandleFunc("/fuck/shit/up/", c.KillServer)
err = http.ListenAndServe(*addr, sm)
if err != nil {

View File

@ -1,4 +1,4 @@
package main
package botserv
import (
"encoding/json"
@ -31,7 +31,7 @@ const (
DEFAULT_MODE = "deathmatch"
)
func loadConfig(filename string) (Config, error) {
func LoadConfig(filename string) (Config, error) {
c := Config{
Tick: TICK,
Timescale: TIMESCALE,

View File

@ -1,4 +1,4 @@
package main
package botserv
import (
"encoding/json"
@ -19,13 +19,21 @@ func (h JsonHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
h(w, req)
}
func startGame(w http.ResponseWriter, req *http.Request) {
type Controller struct {
Idg *IdGenerator
Conf Config
Games MapLock
Memprofile string
Profile string
}
func (c *Controller) StartGame(w http.ResponseWriter, req *http.Request) {
log.Println("asked to create a game")
requested_game_name := idg.Hash()
width, height := float32(conf.Width), float32(conf.Height)
requested_game_name := c.Idg.Hash()
width, height := float32(c.Conf.Width), float32(c.Conf.Height)
obstacles := 0
maxPoints := conf.MaxPoints
maxPoints := c.Conf.MaxPoints
mode := "deathmatch"
// here we determine if we are going to run with defaults or pick them off
@ -55,10 +63,10 @@ func startGame(w http.ResponseWriter, req *http.Request) {
mode = cfg.Mode
}
g := games.get(requested_game_name)
g := c.Games.get(requested_game_name)
if g == nil {
log.Printf("Game '%s' non-existant; making it now", requested_game_name)
g, err := NewGame(requested_game_name, width, height, obstacles, conf.Tick, maxPoints, mode)
g, err := NewGame(requested_game_name, width, height, obstacles, c.Conf.Tick, maxPoints, mode)
if err != nil {
log.Printf("problem creating game: %s: %s", requested_game_name, err)
b, _ := json.Marshal(NewFailure("game creation failure"))
@ -66,7 +74,7 @@ func startGame(w http.ResponseWriter, req *http.Request) {
return
}
go g.run()
games.add(g)
c.Games.add(g)
} else {
log.Printf("Game '%s' already exists: %p", requested_game_name, g)
b, _ := json.Marshal(NewFailure("game already exists"))
@ -84,10 +92,10 @@ func startGame(w http.ResponseWriter, req *http.Request) {
}
}
func listGames(w http.ResponseWriter, req *http.Request) {
func (c *Controller) ListGames(w http.ResponseWriter, req *http.Request) {
log.Println("games list requested")
games.RLock()
defer games.RUnlock()
c.Games.RLock()
defer c.Games.RUnlock()
type pout struct {
Name string `json:"name"`
Id string `json:"id"`
@ -97,7 +105,7 @@ func listGames(w http.ResponseWriter, req *http.Request) {
Players []pout `json:"players"`
}
ids := make([]gl, 0)
for id, g := range games.m {
for id, g := range c.Games.M {
players := make([]pout, 0)
// TODO - players instead of robots?
for p := range g.players {
@ -118,19 +126,19 @@ func listGames(w http.ResponseWriter, req *http.Request) {
}
}
func gameStats(w http.ResponseWriter, req *http.Request) {
func (c *Controller) GameStats(w http.ResponseWriter, req *http.Request) {
// TODO: wrap this up in something similar to the JsonHandler to verify the
// url? Look at gorilla routing?
key, err := getGameId(req.URL.Path)
key, err := c.getGameId(req.URL.Path)
if err != nil {
b, _ := json.Marshal(NewFailure(err.Error()))
http.Error(w, string(b), http.StatusBadRequest)
return
}
log.Printf("requested stats for game: %s", key)
games.RLock()
g, ok := games.m[key]
games.RUnlock()
c.Games.RLock()
g, ok := c.Games.M[key]
c.Games.RUnlock()
if !ok {
b, _ := json.Marshal(NewFailure("game not found"))
http.Error(w, string(b), http.StatusNotFound)
@ -143,19 +151,19 @@ func gameStats(w http.ResponseWriter, req *http.Request) {
}
}
func bw(w http.ResponseWriter, req *http.Request) {
func (c *Controller) BW(w http.ResponseWriter, req *http.Request) {
// TODO: wrap this up in something similar to the JsonHandler to verify the
// url? Look at gorilla routing?
key, err := getGameId(req.URL.Path)
key, err := c.getGameId(req.URL.Path)
if err != nil {
b, _ := json.Marshal(NewFailure(err.Error()))
http.Error(w, string(b), http.StatusBadRequest)
return
}
log.Printf("requested bandwidth for game: %s", key)
games.RLock()
g, ok := games.m[key]
games.RUnlock()
c.Games.RLock()
g, ok := c.Games.M[key]
c.Games.RUnlock()
if !ok {
b, _ := json.Marshal(NewFailure("game not found"))
http.Error(w, string(b), http.StatusNotFound)
@ -170,16 +178,16 @@ func bw(w http.ResponseWriter, req *http.Request) {
}
}
func stopGame(w http.ResponseWriter, req *http.Request) {
key, err := getGameId(req.URL.Path)
func (c *Controller) StopGame(w http.ResponseWriter, req *http.Request) {
key, err := c.getGameId(req.URL.Path)
if err != nil {
b, _ := json.Marshal(NewFailure(err.Error()))
http.Error(w, string(b), http.StatusBadRequest)
return
}
games.Lock()
g, ok := games.m[key]
defer games.Unlock()
c.Games.Lock()
g, ok := c.Games.M[key]
defer c.Games.Unlock()
if !ok {
http.NotFound(w, req)
return
@ -197,15 +205,15 @@ func stopGame(w http.ResponseWriter, req *http.Request) {
}
}
func killServer(w http.ResponseWriter, req *http.Request) {
if *profile != "" {
func (c *Controller) KillServer(w http.ResponseWriter, req *http.Request) {
if c.Profile != "" {
log.Print("trying to stop cpu profile")
pprof.StopCPUProfile()
log.Print("stopped cpu profile")
}
if *mprofile != "" {
if c.Memprofile != "" {
log.Print("trying to dump memory profile")
f, err := os.Create(*mprofile)
f, err := os.Create(c.Memprofile)
if err != nil {
log.Fatal(err)
}
@ -216,7 +224,7 @@ func killServer(w http.ResponseWriter, req *http.Request) {
log.Fatal("shit got fucked up")
}
func index(w http.ResponseWriter, req *http.Request) {
func (c *Controller) Index(w http.ResponseWriter, req *http.Request) {
log.Println("version requested")
version := struct {
Version string `json:"version"`
@ -230,7 +238,7 @@ func index(w http.ResponseWriter, req *http.Request) {
}
}
func getGameId(path string) (string, error) {
func (c *Controller) getGameId(path string) (string, error) {
var err error
trimmed := strings.Trim(path, "/")
fullPath := strings.Split(trimmed, "/")

View File

@ -1,4 +1,4 @@
package main
package botserv
import (
"log"
@ -7,10 +7,10 @@ import (
type deathmatch struct {
}
func (g *deathmatch) setup(gg *game) {
func (g *deathmatch) setup(gg *Game) {
}
func (g *deathmatch) gameOver(gg *game) (bool, *GameOver) {
func (g *deathmatch) gameOver(gg *Game) (bool, *GameOver) {
over := false
var stats *GameOver
@ -41,5 +41,5 @@ func (g *deathmatch) gameOver(gg *game) (bool, *GameOver) {
return over, stats
}
func (g *deathmatch) tick(gg *game, payload *Boardstate) {
func (g *deathmatch) tick(gg *Game, payload *Boardstate) {
}

51
game.go
View File

@ -1,4 +1,4 @@
package main
package botserv
// delete me
@ -24,23 +24,23 @@ type Scanner struct {
}
type MapLock struct {
m map[string]*game
M map[string]*Game
sync.RWMutex
}
// get is a function that returns a game if found, and creates one if
// not found and force is true. In order to get a hash (rather than use
// the string you pass) send "" for id.
func (ml *MapLock) get(id string) *game {
func (ml *MapLock) get(id string) *Game {
ml.Lock()
g, _ := ml.m[id]
g, _ := ml.M[id]
ml.Unlock()
return g
}
func (ml *MapLock) add(g *game) {
func (ml *MapLock) add(g *Game) {
ml.Lock()
ml.m[g.id] = g
ml.M[g.id] = g
ml.Unlock()
}
@ -64,7 +64,7 @@ type GameStats struct {
sync.RWMutex
}
type game struct {
type Game struct {
id string
players map[*player]bool
projectiles map[*Projectile]bool
@ -87,15 +87,18 @@ type game struct {
stats GameStats
mode GameMode
bw *bandwidth.Bandwidth
Conf Config
Games *MapLock
Verbose bool
}
type GameMode interface {
setup(g *game)
tick(gg *game, payload *Boardstate)
gameOver(gg *game) (bool, *GameOver)
setup(g *Game)
tick(gg *Game, payload *Boardstate)
gameOver(gg *Game) (bool, *GameOver)
}
func NewGame(id string, width, height float32, obstacles, tick, maxPoints int, mode string) (*game, error) {
func NewGame(id string, width, height float32, obstacles, tick, maxPoints int, mode string) (*Game, error) {
bw, err := bandwidth.NewBandwidth(
[]int{1, 10, 60},
1*time.Second,
@ -105,7 +108,7 @@ func NewGame(id string, width, height float32, obstacles, tick, maxPoints int, m
return nil, err
}
go bw.Run()
g := &game{
g := &Game{
id: id,
register: make(chan *player, maxPlayer),
unregister: make(chan *player, maxPlayer),
@ -141,7 +144,7 @@ func NewGame(id string, width, height float32, obstacles, tick, maxPoints int, m
return g, nil
}
func (g *game) tick(payload *Boardstate) {
func (g *Game) tick(payload *Boardstate) {
g.players_remaining = 0
payload.Objects = MinifyObstacles(g.obstacles)
@ -196,7 +199,7 @@ func (g *game) tick(payload *Boardstate) {
}
}
func (g *game) sendUpdate(payload *Boardstate) {
func (g *Game) sendUpdate(payload *Boardstate) {
// Ensure that the robots are always sent in a consistent order
sort.Sort(RobotSorter{Robots: payload.OtherRobots})
sort.Sort(AllRobotSorter{Robots: payload.AllBots})
@ -292,20 +295,20 @@ func (g *game) sendUpdate(payload *Boardstate) {
}
func (g *game) run() {
func (g *Game) run() {
var t0, t1 time.Time
ticker := time.NewTicker(time.Duration(conf.Tick) * time.Millisecond)
ticker := time.NewTicker(time.Duration(g.Conf.Tick) * time.Millisecond)
for {
select {
case <-g.kill:
log.Printf("game %s: received kill signal, dying gracefully", g.id)
close(g.bw.Quit)
games.Lock()
g.Games.Lock()
for player := range g.players {
close(player.send)
}
delete(games.m, g.id)
games.Unlock()
delete(g.Games.M, g.id)
g.Games.Unlock()
return
case p := <-g.register:
g.players[p] = true
@ -330,7 +333,7 @@ func (g *game) run() {
g.turn++
payload.Turn = g.turn
if *verbose {
if g.Verbose {
log.Printf("\033[2JTurn: %v", g.turn)
log.Printf("Players: %v", len(g.players))
log.Printf("Projectiles: %v", len(g.projectiles))
@ -346,7 +349,7 @@ func (g *game) run() {
g.mode.tick(g, payload)
t1 = time.Now()
if *verbose {
if g.Verbose {
log.Printf("Turn Processes %v\n", t1.Sub(t0))
}
@ -354,14 +357,14 @@ func (g *game) run() {
g.sendUpdate(payload)
t1 = time.Now()
if *verbose {
if g.Verbose {
log.Printf("Sent Payload %v\n", t1.Sub(t0))
}
}
}
}
func (g *game) sendGameOver(eg *GameOver) {
func (g *Game) sendGameOver(eg *GameOver) {
log.Printf("sending out game over message: %+v", eg)
for p := range g.players {
p.send <- eg
@ -373,7 +376,7 @@ func (g *game) sendGameOver(eg *GameOver) {
// returns a GameParam object popuplated by info from the game. This is
// used during client/server initial negociation.
func (g *game) gameParam() *GameParam {
func (g *Game) gameParam() *GameParam {
return &GameParam{
BoardSize: BoardSize{
Width: g.width,

2
id.go
View File

@ -1,4 +1,4 @@
package main
package botserv
import (
"crypto/md5"

View File

@ -1,4 +1,4 @@
package main
package botserv
import (
"testing"

View File

@ -1,4 +1,4 @@
package main
package botserv
import (
"log"
@ -9,15 +9,15 @@ type melee struct {
respawn_timer float64
}
func (g *melee) setup(gg *game) {
func (g *melee) setup(gg *Game) {
g.respawn_timer = 5000
}
func (g *melee) gameOver(gg *game) (bool, *GameOver) {
func (g *melee) gameOver(gg *Game) (bool, *GameOver) {
return false, &GameOver{}
}
func (g *melee) tick(gg *game, payload *Boardstate) {
func (g *melee) tick(gg *Game, payload *Boardstate) {
for p := range gg.players {
for _, r := range p.Robots {
_, ok := g.respawn[r]

View File

@ -1,9 +1,10 @@
package main
package botserv
import (
v "bitbucket.org/hackerbots/vector"
"math"
"math/rand"
v "bitbucket.org/hackerbots/vector"
)
type Obstacle struct {

View File

@ -1,4 +1,4 @@
package main
package botserv
import (
"encoding/gob"

View File

@ -1,8 +1,9 @@
package main
package botserv
import (
v "bitbucket.org/hackerbots/vector"
"log"
v "bitbucket.org/hackerbots/vector"
)
type Projectile struct {
@ -13,12 +14,13 @@ type Projectile struct {
Speed float32 `json:"-"`
Damage int `json:"-"`
Owner *Robot `json:"-"`
Delta float32
}
func (p *Projectile) Tick(g *game) {
func (p *Projectile) Tick(g *Game) {
vec := p.MoveTo.Sub(p.Position)
v_norm := vec.Normalize()
v_scaled := v_norm.Scale(p.Speed * delta)
v_scaled := v_norm.Scale(p.Speed * p.Delta)
newPos := p.Position.Add(v_scaled)
hit_player := false

View File

@ -1,4 +1,4 @@
package main
package botserv
import (
"log"
@ -143,7 +143,7 @@ func NewFailure(reason string) *Failure {
}
}
func addPlayer(ws *websocket.Conn) {
func (c *Controller) AddPlayer(ws *websocket.Conn) {
var gid GameID
err := websocket.JSON.Receive(ws, &gid)
if err != nil {
@ -151,16 +151,16 @@ func addPlayer(ws *websocket.Conn) {
return
}
game := games.get(gid.Id)
game := c.Games.get(gid.Id)
if game == nil {
var err error
game, err = NewGame(
gid.Id,
float32(conf.Width),
float32(conf.Height),
conf.Obstacles,
conf.Tick,
conf.MaxPoints,
float32(c.Conf.Width),
float32(c.Conf.Height),
c.Conf.Obstacles,
c.Conf.Tick,
c.Conf.MaxPoints,
"",
)
if err != nil {
@ -169,10 +169,10 @@ func addPlayer(ws *websocket.Conn) {
return
}
go game.run()
games.add(game)
c.Games.add(game)
}
player_id := idg.Hash()
player_id := c.Idg.Hash()
err = websocket.JSON.Send(ws, NewPlayerID(player_id))
if err != nil {
log.Printf("game %s: unable to send player_id to player %s", gid.Id, player_id)
@ -284,7 +284,7 @@ encodingLoops:
convertedStats[name] = dstat
r := Robot{
Stats: dstat,
Id: idg.Hash(),
Id: c.Idg.Hash(),
Name: name,
Health: 10,
Heading: v.Vector2d{1, 0},

View File

@ -1,10 +1,11 @@
package main
package botserv
import (
v "bitbucket.org/hackerbots/vector"
"log"
"math"
"math/rand"
v "bitbucket.org/hackerbots/vector"
)
type Robot struct {
@ -30,6 +31,8 @@ type Robot struct {
Probe *v.Point2d `json:"probe"`
ProbeResult *Collision `json:"probe_result"`
gameStats *BotStats `json:-`
Delta float32 `json:-`
Idg *IdGenerator
}
type Collision struct {
@ -172,7 +175,7 @@ type Instruction struct {
// returns collision, the intersection point, and the robot with whom r has
// collided, if this happened.
func (r *Robot) checkCollisions(g *game, probe v.Vector2d) (bool, *v.Point2d, *Robot) {
func (r *Robot) checkCollisions(g *Game, probe v.Vector2d) (bool, *v.Point2d, *Robot) {
finalCollision := false
collision := false
closest := float32(math.Inf(1))
@ -243,19 +246,19 @@ func (r *Robot) checkCollisions(g *game, probe v.Vector2d) (bool, *v.Point2d, *R
return finalCollision, intersection, finalRobot
}
func (r *Robot) Tick(g *game) {
func (r *Robot) Tick(g *Game) {
r.Collision = nil
r.Hit = false
r.scan(g)
// Adjust Speed
if r.Speed < r.TargetSpeed {
r.Speed += (r.Stats.Acceleration * delta)
r.Speed += (r.Stats.Acceleration * r.Delta)
if r.Speed > r.TargetSpeed {
r.Speed = r.TargetSpeed
}
} else if float32(math.Abs(float64(r.Speed-r.TargetSpeed))) > v.Epsilon {
r.Speed -= (r.Stats.Acceleration * delta)
r.Speed -= (r.Stats.Acceleration * r.Delta)
// Cap reverse to 1/2 speed
if r.Speed < (-0.5 * r.TargetSpeed) {
r.Speed = (-0.5 * r.TargetSpeed)
@ -292,15 +295,15 @@ func (r *Robot) Tick(g *game) {
}
// Max turn radius in this case is in degrees per second
if float32(math.Abs(float64(angle))) > (float32(r.Stats.TurnSpeed) * delta) {
if float32(math.Abs(float64(angle))) > (float32(r.Stats.TurnSpeed) * r.Delta) {
// New heading should be a little less, take current heading and
// rotate by the max turn radius per frame.
rot := (float32(r.Stats.TurnSpeed) * delta) * v.Deg2rad
rot := (float32(r.Stats.TurnSpeed) * r.Delta) * v.Deg2rad
new_heading = current_heading.Rotate(rot * float32(dir))
}
move_vector := new_heading.Scale(r.Speed * delta)
move_vector := new_heading.Scale(r.Speed * r.Delta)
collision, intersection_point, hit_robot := r.checkCollisions(g, move_vector)
if collision {
dmg := int(math.Abs(float64(r.Speed)) / 10.0)
@ -352,7 +355,7 @@ func (r *Robot) Tick(g *game) {
// We only self repair when we're stopped
if math.Abs(float64(r.Speed)) < v.Epsilon && r.RepairCounter > 0 {
r.RepairCounter -= delta
r.RepairCounter -= r.Delta
if r.RepairCounter < 0 {
r.Health += g.repair_hp
if r.Health > r.Stats.Hp {
@ -364,9 +367,9 @@ func (r *Robot) Tick(g *game) {
// We are only allowed to scan when we're stopped
if math.Abs(float64(r.Speed)) < v.Epsilon && r.ActiveScan {
r.ScanCounter += delta * float32(r.Stats.ScannerRadius) * 0.1
r.ScanCounter += r.Delta * float32(r.Stats.ScannerRadius) * 0.1
} else if r.ScanCounter > 0 {
r.ScanCounter -= delta * float32(r.Stats.ScannerRadius) * 0.05
r.ScanCounter -= r.Delta * float32(r.Stats.ScannerRadius) * 0.05
if r.ScanCounter <= 0 {
r.ScanCounter = 0
}
@ -394,7 +397,7 @@ func (r *Robot) Tick(g *game) {
}
}
func (r *Robot) scan(g *game) {
func (r *Robot) scan(g *Game) {
r.Scanners = r.Scanners[:0]
for player := range g.players {
for _, bot := range player.Robots {
@ -440,9 +443,9 @@ func (r *Robot) scan(g *game) {
}
func (r *Robot) fire(g *game) *Projectile {
func (r *Robot) fire(g *Game) *Projectile {
// Throttle the fire rate
time_since_fired := (float32(g.turn) * (delta * 1000)) - (float32(r.LastFired) * (delta * 1000))
time_since_fired := (float32(g.turn) * (r.Delta * 1000)) - (float32(r.LastFired) * (r.Delta * 1000))
if time_since_fired < float32(r.Stats.FireRate) {
return nil
}
@ -451,7 +454,7 @@ func (r *Robot) fire(g *game) *Projectile {
r.gameStats.Shots++
return &Projectile{
Id: idg.Hash(),
Id: r.Idg.Hash(),
Position: r.Position,
MoveTo: *r.FireAt,
Damage: r.Stats.WeaponDamage,
@ -461,7 +464,7 @@ func (r *Robot) fire(g *game) *Projectile {
}
}
func (r *Robot) reset(g *game) {
func (r *Robot) reset(g *Game) {
for {
start_pos := v.Point2d{
X: rand.Float32() * float32(g.width),

View File

@ -1,10 +1,11 @@
package main
package botserv
import (
v "bitbucket.org/hackerbots/vector"
"log"
"math"
"testing"
v "bitbucket.org/hackerbots/vector"
)
func init() {

View File

@ -1,4 +1,4 @@
package main
package botserv
import (
v "bitbucket.org/hackerbots/vector"