diff --git a/commands.go b/commands.go index a8b9976..86ecec3 100644 --- a/commands.go +++ b/commands.go @@ -235,7 +235,7 @@ var winCommand = &Command{ name: "win", help: "win the game.", handler: func(conn *Connection, args ...string) { - conn.Win() + conn.Win("win-command") }, } diff --git a/game.go b/game.go index 30a96fa..5899736 100644 --- a/game.go +++ b/game.go @@ -5,6 +5,7 @@ import ( ) type Game struct { + id Id start time.Time end time.Time winner string @@ -13,7 +14,7 @@ type Game struct { func gamesTable() { stmnt := `create table if not exists games ( - id integer not null primary key autoincrement, + id text not null, start text not null, end text, winner text, @@ -26,6 +27,7 @@ func gamesTable() { func NewGame() *Game { game := &Game{ + id: NewId(), start: time.Now(), } if err := game.Create(); err != nil { @@ -37,9 +39,9 @@ func NewGame() *Game { func (g *Game) Create() error { _, err := db.Exec(` insert into games - (start) + (id, start) values - (?) - ;`, g.start) + (?, ?) + ;`, g.id.String(), g.start) return err } diff --git a/global.go b/global.go new file mode 100644 index 0000000..92a44a0 --- /dev/null +++ b/global.go @@ -0,0 +1,29 @@ +package main + +import ( + "crypto/md5" + "os" +) + +var global struct { + hostname string + machineId []byte + pid int + idCounter uint32 +} + +func init() { + global.pid = os.Getpid() + var err error + global.hostname, err = os.Hostname() + if err != nil { + panic("failed to get hostname: " + err.Error()) + } + + hw := md5.New() + if _, err := hw.Write([]byte(global.hostname)); err != nil { + panic("unable to md5 hostname: " + err.Error()) + } + global.machineId = make([]byte, 3) + copy(global.machineId, hw.Sum(nil)) +} diff --git a/id.go b/id.go new file mode 100644 index 0000000..24cf7ae --- /dev/null +++ b/id.go @@ -0,0 +1,55 @@ +package main + +import ( + "encoding/binary" + "fmt" + "sync/atomic" + "time" +) + +// NewObjectId returns a new unique ObjectId. +// This function causes a runtime error if it fails to get the hostname +// of the current machine. +func NewId() Id { + b := make([]byte, 12) + // Timestamp, 4 bytes, big endian + binary.BigEndian.PutUint32(b, uint32(time.Now().Unix())) + b[4] = global.machineId[0] + b[5] = global.machineId[1] + b[6] = global.machineId[2] + // Pid, 2 bytes, specs don't specify endianness, but we use big endian. + b[7] = byte(global.pid >> 8) + b[8] = byte(global.pid) + // Increment, 3 bytes, big endian + i := atomic.AddUint32(&global.idCounter, 1) + b[9] = byte(i >> 16) + b[10] = byte(i >> 8) + b[11] = byte(i) + return Id(b) +} + +// Id is used for tagging each incoming http request for logging +// purposes. The actual implementation is just the ObjectId implementation +// found in launchpad.net/mgo/bson. This will most likely change and evolve +// into its own format. +type Id string + +func (id Id) String() string { + return fmt.Sprintf("%x", string(id)) +} + +// Time returns the timestamp part of the id. +// It's a runtime error to call this method with an invalid id. +func (id Id) Time() time.Time { + secs := int64(binary.BigEndian.Uint32(id.byteSlice(0, 4))) + return time.Unix(secs, 0) +} + +// byteSlice returns byte slice of id from start to end. +// Calling this function with an invalid id will cause a runtime panic. +func (id Id) byteSlice(start, end int) []byte { + if len(id) != 12 { + panic(fmt.Sprintf("Invalid Id: %q", string(id))) + } + return []byte(string(id)[start:end]) +} diff --git a/session.go b/session.go index d038091..2339e3c 100644 --- a/session.go +++ b/session.go @@ -128,7 +128,7 @@ func (c *Connection) NextBomb() time.Duration { func (c *Connection) MadeKill(victim *Connection) { c.kills += 1 if c.kills == 3 { - c.Win() + c.Win("military") } } @@ -163,11 +163,11 @@ func (c *Connection) Withdraw(n int64) { func (c *Connection) Deposit(n int64) { c.money += n if c.money >= 25000 { - c.Win() + c.Win("economic") } } -func (c *Connection) Win() { +func (c *Connection) Win(method string) { for conn, _ := range connected { fmt.Fprintf(conn, "player %s has won.\n", c.PlayerName()) conn.Close()