Compare commits

..

21 Commits

Author SHA1 Message Date
Jordan Orelli ae409fd065 list games command 5 years ago
Jordan Orelli d603c74a18 dying should be more of a ceremony 6 years ago
Jordan Orelli 4e1ade8e12 global index is gone! :party_parrot: 6 years ago
Jordan Orelli 209635083d remove old edges table 6 years ago
Jordan Orelli 25d142fb8a get rid of system distances function 6 years ago
Jordan Orelli ce94d3cdd2 scan and broadcast both work without global index 6 years ago
Jordan Orelli dbb2c86f5a slowly getting rid of global index 6 years ago
Jordan Orelli 5ce9a530f8 player name selection no longer blocks new connections 6 years ago
Jordan Orelli 495f4c3fa2 fix nil panic when lobby connections leave 6 years ago
Jordan Orelli 34a984a397 move connection state to its own file 6 years ago
Jordan Orelli 6cd419d013 needs more explanation 6 years ago
Jordan Orelli bdc2783838 uniform status command for all states 6 years ago
Jordan Orelli 15015a9d5b rename command help to summary 6 years ago
Jordan Orelli aa6bd1ca9e travel progress indicator 6 years ago
Jordan Orelli 8cdd7bdaa6 cleaning up enter travel text 6 years ago
Jordan Orelli 2914275282 adding a status command for every player state 6 years ago
Jordan Orelli 50128a64a6 display travel time in output of "nearby" command 6 years ago
Jordan Orelli b0aaa046ad fix broken help command 6 years ago
Jordan Orelli a330d87b4c refactor lobby state 6 years ago
Jordan Orelli 938348ce26 run multiple games at once 6 years ago
Jordan Orelli e629c0b6b6 get rid of crazy id scheme
that was so unecessary wow
6 years ago

@ -1,24 +1,5 @@
space-dragons-in-outer-space
============================
there's no url because it's not a web app
it's just a tcp server
you go
like this
`nc 104.236.57.163 9220`
and that is how
you be a dragon
in space
in space
in space
This is a [real-time strategy
game](https://en.wikipedia.org/wiki/Real-time_strategy) in the style of a [MUD](https://en.wikipedia.org/wiki/MUD).

@ -2,6 +2,7 @@ package main
import (
"fmt"
"strings"
"time"
)
@ -32,10 +33,10 @@ func (b *Bomb) Dead() bool {
return b.done
}
func (b *Bomb) Tick(frame int64) {
func (b *Bomb) Tick(game *Game) {
b.fti -= 1
if b.fti <= 0 {
b.target.Bombed(b.profile, frame)
b.target.Bombed(b.profile, game)
b.done = true
log_info("bomb went off on %v", b.target)
}
@ -56,7 +57,6 @@ func MakeBomb(s *System) ConnectionState {
m.CommandSuite = CommandSet{
balCommand,
BroadcastCommand(s),
helpCommand,
NearbyCommand(s),
playersCommand,
}
@ -78,7 +78,23 @@ func (m *MakeBombState) Tick(c *Connection, frame int64) ConnectionState {
return m
}
func (MakeBombState) String() string { return "Making a Bomb" }
func (m *MakeBombState) Exit(c *Connection) {
c.bombs += 1
c.Printf("Done! You now have %v bombs.\n", c.bombs)
}
func (m *MakeBombState) FillStatus(c *Connection, s *status) {
elapsedFrames := c.game.frame - m.start
elapsedDur := framesToDur(elapsedFrames)
desc := fmt.Sprintf(`
Currently making a bomb!
Build time elapsed: %v
Build time remaining: %v
`, elapsedDur, options.makeBombTime-elapsedDur)
s.Description = strings.TrimSpace(desc)
s.Location = m.System.String()
}

@ -9,8 +9,8 @@ type broadcast struct {
start time.Time
origin *System
dist float64
nextHitIndex int
message string
neighborhood Neighborhood
}
func NewBroadcast(from *System, template string, args ...interface{}) *broadcast {
@ -21,20 +21,27 @@ func NewBroadcast(from *System, template string, args ...interface{}) *broadcast
}
}
func (b *broadcast) Tick(frame int64) {
func (b *broadcast) Tick(game *Game) {
if b.neighborhood == nil {
log_info("setting up neighborhood for broadcast: %s", b.message)
b.neighborhood = game.galaxy.Neighborhood(b.origin)
log_info("nearest neighbor: %v", b.neighborhood[0])
}
b.dist += options.lightSpeed
for ; b.nextHitIndex < len(b.origin.Distances()); b.nextHitIndex += 1 {
candidate := b.origin.Distances()[b.nextHitIndex]
if b.dist < candidate.dist {
break
for len(b.neighborhood) > 0 && b.neighborhood[0].distance <= b.dist {
s := game.galaxy.GetSystemByID(b.neighborhood[0].id)
log_info("broadcast %s has reached %s from %s", b.message, s, b.origin)
s.NotifyInhabitants("message received from system %v:\n\t%s\n", b.origin, b.message)
if len(b.neighborhood) > 1 {
b.neighborhood = b.neighborhood[1:]
} else {
b.neighborhood = nil
}
candidate.s.NotifyInhabitants("message received from system %v:\n\t%s\n", b.origin, b.message)
}
}
func (b *broadcast) Dead() bool {
return b.dist > b.origin.Distances()[len(b.origin.Distances())-1].dist
}
func (b *broadcast) Dead() bool { return b.neighborhood == nil }
func (b *broadcast) String() string {
return fmt.Sprintf("[broadcast origin: %v message: %s]", b.origin, b.message)

@ -17,7 +17,6 @@ func MakeColony(c *Connection, sys *System) {
CommandSuite: CommandSet{
balCommand,
BroadcastCommand(sys),
helpCommand,
NearbyCommand(sys),
playersCommand,
},
@ -49,3 +48,7 @@ func (m *MakeColonyState) Exit(c *Connection) {
m.System.colonizedBy = c
c.Printf("Established colony on %v.\n", m.System)
}
func (m *MakeColonyState) FillStatus(c *Connection, s *status) {
s.Location = m.System.String()
}

@ -2,15 +2,45 @@ package main
import (
"fmt"
"sort"
// "strconv"
"strings"
"text/template"
)
var helpTemplate = template.Must(template.New("help").Parse(`
{{.Name}} Command Reference
Summary: {{.Summary}}
{{- if .Usage}}
Usage: {{.Usage}}
{{end}}
{{- if .Description}}
Details:
{{.Description}}
{{end}}
`))
func printHelp(conn *Connection, cmd *Command) {
desc := strings.ReplaceAll(strings.TrimSpace(cmd.help), "\n", "\n ")
helpTemplate.Execute(conn, struct {
Name string
Summary string
Usage string
Description string
}{
Name: cmd.name,
Summary: cmd.summary,
Usage: cmd.usage,
Description: desc,
})
}
var commandRegistry map[string]*Command
type Command struct {
name string
summary string
usage string
help string
arity int
variadic bool
@ -37,6 +67,14 @@ func (c Command) Commands() []Command {
type CommandSet []Command
func (c CommandSet) GetCommand(name string) *Command {
switch name {
case "help":
return &helpCommand
case "commands":
return &commandsCommand
case "status":
return &statusCommand
}
for _, cmd := range c {
if cmd.name == name {
return &cmd
@ -46,104 +84,147 @@ func (c CommandSet) GetCommand(name string) *Command {
}
func (c CommandSet) Commands() []Command {
return []Command(c)
return append([]Command(c), statusCommand, helpCommand, commandsCommand)
}
var helpCommand = Command{
name: "help",
help: "helpful things to help you",
summary: "explains how to play the game",
usage: "help [command-name]",
help: `
help explains the usage of various commands in Exocolonus. On its own, the help
command displays some basic info about how the game is played. If given an
argument of a command name, the help command displays the detailed usage of the
specified command.
`,
handler: func(conn *Connection, args ...string) {
msg := `
Star Dragons is a stupid name, but it's the name that Brian suggested. It has
nothing to do with Dragons.
Anyway, Star Dragons is a game of cunning text-based, real-time strategy. You
play as some kind of space-faring entity, faring space in your inspecific
space-faring vessel. If you want a big one, it's big; if you want a small one,
it's small. If you want a pink one, it's pink, if you want a black one, it's
black. And so on, and so forth. It is the space craft of your dreams. Or
perhaps you are one of those insect-like alien races and you play as the queen.
Yeah, that's the ticket! You're the biggest baddest queen bug in space.
In Star Dragons, you issue your spacecraft (which is *not* called a Dragon)
textual commands to control it. The objective of the game is to be the first
person or alien or bug or magical space ponycorn to eradicate three enemy
species. Right now that is the only win condition.
All of the systems present in Star Dragons are named and positioned after known
exoplanet systems. When attempting to communicate from one star system to
another, it takes time for the light of your message to reach the other star
systems. Star systems that are farther away take longer to communicate with.
Exocolonus is a game of cunning text-based, real-time strategy. You play as
some kind of space-faring entity, faring space in your inspecific space-faring
vessel. If you want a big one, it's big; if you want a small one, it's small.
If you want a pink one, it's pink, if you want a black one, it's black. And so
on, and so forth. It is the space craft of your dreams. Or perhaps you are
one of those insect-like alien races and you play as the queen. Yeah, that's
the ticket! You're the biggest baddest queen bug in space.
In Exocolonus, you issue your spacecraft textual commands to control it. The
objective of the game is to be the first person or alien or bug or magical
space ponycorn to eradicate three enemy species. Right now that is the only
win condition.
Exocolonus deals with relativity with respect to observation. When an effect
takes place, knowledge of that effect travels throughougt the galaxy at the
speed of light. If a system is bombed, the closest systems to it will know
first. When you broadcast messages, they travel at the speed of light.
All of the systems present in Exocolonus are named and positioned after known
exoplanet systems. Each star system in Exocolonus is a real star system that
has been researched by astronomers, and the number of planets in each system
corresponds to the number of known exoplanets in those systems. When
attempting to communicate from one star system to another, it takes time for
the light of your message to reach the other star systems. Star systems that
are farther away take longer to communicate with.
`
if len(args) == 0 {
msg = strings.TrimSpace(msg)
fmt.Fprintln(conn, msg)
if len(args) == 0 {
fmt.Fprint(conn, "\n")
conn.Line()
fmt.Fprint(conn, "\n")
fmt.Fprintln(conn, `use the "commands" command for a list of commands.`)
fmt.Fprintln(conn, `use "help [command-name]" to get info for a specific command.`)
return
}
for _, cmdName := range args {
cmd, ok := commandRegistry[cmdName]
if !ok {
cmd := conn.GetCommand(cmdName)
if cmd == nil {
conn.Printf("no such command: %v\n", cmdName)
continue
}
conn.Printf("%v: %v\n", cmdName, cmd.help)
printHelp(conn, cmd)
}
},
}
var commandsCommand = Command{
name: "commands",
help: "gives you a handy list of commands",
type status struct {
State string
GameCode string
Balance int
Bombs int
Kills int
Location string
Description string
}
var statusTemplate = template.Must(template.New("status").Parse(`
Current State: {{.State}}
--------------------------------------------------------------------------------
{{- if .GameCode}}
Current Game: {{.GameCode}}
Balance: {{.Balance}}
Bombs: {{.Bombs}}
Kills: {{.Kills}}
Location: {{.Location}}
{{end}}
{{.Description}}
`))
var statusCommand = Command{
name: "status",
summary: "display your current status",
handler: func(conn *Connection, args ...string) {
names := make([]string, 0, len(commandRegistry))
for name, _ := range commandRegistry {
names = append(names, name)
s := status{
State: conn.ConnectionState.String(),
}
sort.Strings(names)
fmt.Fprintln(conn, "--------------------------------------------------------------------------------")
for _, name := range names {
cmd := commandRegistry[name]
conn.Printf("%-16s %s\n", name, cmd.help)
conn.ConnectionState.FillStatus(conn, &s)
if conn.game != nil {
s.GameCode = conn.game.id
s.Balance = conn.money
s.Bombs = conn.bombs
s.Kills = conn.kills
}
fmt.Fprintln(conn, "--------------------------------------------------------------------------------")
statusTemplate.Execute(conn, s)
},
}
// this isn't a real command it just puts command in the list of commands, this
// is weird and circular, this is a special case.
var commandsCommand = Command{
name: "commands",
summary: "lists currently available commands",
}
func BroadcastCommand(sys *System) Command {
return Command{
name: "broadcast",
help: "broadcast a message for all systems to hear",
summary: "broadcast a message for all systems to hear",
handler: func(c *Connection, args ...string) {
msg := strings.Join(args, " ")
b := NewBroadcast(sys, msg)
log_info("player %s send broadcast from system %v: %v\n", c.Name(), sys, msg)
currentGame.Register(b)
c.game.Register(b)
},
}
}
func NearbyCommand(sys *System) Command {
handler := func(c *Connection, args ...string) {
neighbors, err := sys.Nearby(25)
if err != nil {
log_error("unable to get neighbors: %v", err)
return
}
c.Printf("--------------------------------------------------------------------------------\n")
c.Printf("%-4s %-20s %s\n", "id", "name", "distance")
c.Printf("--------------------------------------------------------------------------------\n")
for _, neighbor := range neighbors {
other := index[neighbor.id]
c.Printf("%-4d %-20s %-5.6v\n", other.id, other.name, neighbor.distance)
neighbors := c.game.galaxy.Neighborhood(sys)
c.Line()
c.Printf("%-4s %-20s %-12s %s\n", "id", "name", "distance", "trip time")
c.Line()
for _, neighbor := range neighbors[:25] {
other := c.game.galaxy.GetSystemByID(neighbor.id)
dur := NewTravel(c, sys, other).(*TravelState).tripTime()
c.Printf("%-4d %-20s %-12.6vpc %v\n", other.id, other.name, neighbor.distance, dur)
}
c.Printf("--------------------------------------------------------------------------------\n")
c.Line()
}
return Command{
name: "nearby",
help: "list nearby star systems",
summary: "list nearby star systems",
arity: 0,
handler: handler,
}
@ -151,7 +232,7 @@ func NearbyCommand(sys *System) Command {
var winCommand = Command{
name: "win",
help: "win the game.",
summary: "win the game.",
debug: true,
handler: func(conn *Connection, args ...string) {
conn.Win("win-command")
@ -160,9 +241,9 @@ var winCommand = Command{
var playersCommand = Command{
name: "players",
help: "lists the connected players",
summary: "lists the connected players",
handler: func(conn *Connection, args ...string) {
for other, _ := range currentGame.connections {
for other, _ := range conn.game.connections {
conn.Printf("%v\n", other.Name())
}
},
@ -170,7 +251,7 @@ var playersCommand = Command{
var balCommand = Command{
name: "bal",
help: "displays your current balance in space duckets",
summary: "displays your current balance in space duckets",
handler: func(conn *Connection, args ...string) {
fmt.Fprintln(conn, conn.money)
},

@ -5,6 +5,7 @@ import (
"fmt"
"io"
"net"
"runtime"
"sort"
"strings"
"time"
@ -12,6 +13,7 @@ import (
type Connection struct {
*bufio.Reader
game *Game
net.Conn
ConnectionState
bombs int
@ -30,69 +32,63 @@ func NewConnection(conn net.Conn) *Connection {
bombs: options.startBombs,
money: options.startMoney,
}
c.SetState(SpawnRandomly())
currentGame.Join(c)
c.SetState(EnterLobby())
return c
}
func (c *Connection) Login() {
for {
c.Printf("what is your name, adventurer?\n")
name, err := c.ReadString('\n')
if err == nil {
name = strings.TrimSpace(name)
} else {
log_error("player failed to connect: %v", err)
return
}
if !ValidName(name) {
c.Printf("that name is illegal.\n")
continue
}
log_info("player connected: %v", name)
profile, err := loadProfile(name)
if err != nil {
log_error("could not read profile: %v", err)
profile = &Profile{name: name}
if err := profile.Create(); err != nil {
log_error("unable to create profile record: %v", err)
}
c.Printf("you look new around these parts, %s.\n", profile.name)
c.Printf(`if you'd like a description of how to play, type the "help" command\n`)
c.profile = profile
} else {
c.profile = profile
c.Printf("welcome back, %s.\n", profile.name)
}
break
}
currentGame.Register(c)
}
func (c *Connection) Dead() bool {
return false
}
func (c *Connection) Tick(frame int64) {
func (c *Connection) Tick(game *Game) {
if c.ConnectionState == nil {
log_error("connected client has nil state.")
c.Printf("somehow you have a nil state. I don't know what to do so I'm going to kick you off.")
c.Close()
return
}
c.SetState(c.ConnectionState.Tick(c, frame))
c.SetState(c.ConnectionState.Tick(c, game.frame))
}
func (c *Connection) RunCommand(name string, args ...string) {
defer func() {
if r := recover(); r != nil {
c.Printf("something is broken. Log this as a ticket!\n")
c.Printf("recovered: %v\n", r)
c.Printf("(something is broken)")
c.Printf("ERROR: %v\n", r)
callers := make([]uintptr, 40)
n := runtime.Callers(5, callers)
callers = callers[:n]
frames := runtime.CallersFrames(callers)
log_error("recovered: %v", r)
for {
frame, more := frames.Next()
if !more {
break
}
log_error(" %s +%d (%s)\n", frame.File, frame.Line, frame.Function)
}
}
}()
switch name {
case "commands":
c.ListCommands()
return
}
cmd := c.GetCommand(name)
if cmd == nil {
c.Printf("No such command: %v\n", name)
return
}
cmd.handler(c, args...)
}
func (c *Connection) ListCommands() {
c.Printf("\n")
c.Line()
c.Printf("- Available Commands in state: %s\n", c.ConnectionState.String())
c.Line()
commands := c.Commands()
names := make([]string, len(commands))
@ -102,18 +98,9 @@ func (c *Connection) RunCommand(name string, args ...string) {
sort.Strings(names)
for _, name := range names {
cmd := c.GetCommand(name)
c.Printf("%-20s%s\n", name, cmd.help)
c.Printf("%-20s%s\n", name, cmd.summary)
}
c.Line()
return
}
cmd := c.GetCommand(name)
if cmd == nil {
c.Printf("No such command: %v\n", name)
return
}
cmd.handler(c, args...)
c.Printf("\n")
}
func (c *Connection) SetState(s ConnectionState) {
@ -126,8 +113,8 @@ func (c *Connection) SetState(s ConnectionState) {
c.ConnectionState.Exit(c)
}
log_info("enter state: %v", s)
s.Enter(c)
c.ConnectionState = s
s.Enter(c)
}
func (c *Connection) ReadLines(out chan []string) {
@ -162,7 +149,9 @@ func (c *Connection) Printf(template string, args ...interface{}) (int, error) {
func (c *Connection) Close() error {
log_info("player disconnecting: %s", c.Name())
currentGame.Quit(c)
if c.game != nil {
c.game.Quit(c)
}
if c.Conn != nil {
return c.Conn.Close()
}
@ -177,17 +166,17 @@ func (c *Connection) Name() string {
}
func (c *Connection) RecordScan() {
c.Printf("scanning known systems for signs of life\n")
c.Printf("Scanning known systems for signs of life\n")
c.lastScan = time.Now()
time.AfterFunc(options.scanTime, func() {
c.Printf("scanner ready\n")
c.Printf("Scanner ready\n")
})
}
func (c *Connection) RecordBomb() {
c.lastBomb = time.Now()
time.AfterFunc(15*time.Second, func() {
fmt.Fprintln(c, "bomb arsenal reloaded")
fmt.Fprintln(c, "Bomb arsenal reloaded")
})
}
@ -226,37 +215,9 @@ func (c *Connection) Deposit(n int) {
}
func (c *Connection) Win(method string) {
currentGame.Win(c, method)
c.game.Win(c, method)
}
func (c *Connection) Die(frame int64) {
c.SetState(NewDeadState(frame))
}
type ConnectionState interface {
CommandSuite
String() string
Enter(c *Connection)
Tick(c *Connection, frame int64) ConnectionState
Exit(c *Connection)
}
// No-op enter struct, for composing connection states that have no interesitng
// Enter mechanic.
type NopEnter struct{}
func (n NopEnter) Enter(c *Connection) {}
// No-op exit struct, for composing connection states that have no interesting
// Exit mechanic.
type NopExit struct{}
func (n NopExit) Exit(c *Connection) {}
func SpawnRandomly() ConnectionState {
sys, err := randomSystem()
if err != nil {
return NewErrorState(fmt.Errorf("unable to create idle state: %v", err))
}
return Idle(sys)
}

@ -0,0 +1,39 @@
package main
type ConnectionState interface {
// commands available while in this state
CommandSuite
// human-readable description of the state
String() string
// fills a status struct to be printed by the status command. The
// ConnectionState only needs to fill in things that are unique to the
// state itself, the common things on the connection are filled in
// automatically
FillStatus(*Connection, *status)
// Triggered once when the state is entered
Enter(c *Connection)
// Triggered every frame in which the state is the connection's current
// state. Returning a different ConnectionState transitions between states.
Tick(c *Connection, frame int64) ConnectionState
// Triggered once when this state has finished for that connection
Exit(c *Connection)
}
// No-op enter struct, for composing connection states that have no interesitng
// Enter mechanic.
type NopEnter struct{}
func (n NopEnter) Enter(c *Connection) {}
// No-op exit struct, for composing connection states that have no interesting
// Exit mechanic.
type NopExit struct{}
func (n NopExit) Exit(c *Connection) {}

53
db.go

@ -49,63 +49,12 @@ func planetsData() {
planet.Store(db)
}
}
indexSystems()
}
func edgesTable() {
stmnt := `create table if not exists edges (
id_1 integer,
id_2 integer,
distance real
);`
if _, err := db.Exec(stmnt); err != nil {
log_error("couldn't create distance table: %v", err)
}
// indexSystems()
}
func setupDb() {
planetsTable()
planetsData()
edgesTable()
profilesTable()
gamesTable()
fillEdges()
}
func fillEdges() {
row := db.QueryRow(`select count(*) from edges;`)
var n int
if err := row.Scan(&n); err != nil {
log_error("couldn't get number of edges: %v", err)
return
}
if n > 0 {
return
}
for i := 0; i < len(index); i++ {
for j := 0; j < len(index); j++ {
if i == j {
continue
}
if index[i] == nil {
log_error("wtf there's nil shit in here for id %d", i)
continue
}
if index[j] == nil {
log_error("wtf there's nil shit in here 2 for id %d", j)
continue
}
dist := index[i].DistanceTo(index[j])
log_info("distance from %s to %s: %v", index[i].name, index[j].name, dist)
_, err := db.Exec(`
insert into edges
(id_1, id_2, distance)
values
(?, ?, ?)
;`, i, j, dist)
if err != nil {
log_error("unable to write edge to db: %v", err)
}
}
}
}

@ -1,6 +1,9 @@
package main
import ()
import (
"strings"
"time"
)
type DeadState struct {
CommandSuite
@ -8,16 +11,71 @@ type DeadState struct {
}
func NewDeadState(died int64) ConnectionState {
return &DeadState{start: died}
return &DeadState{
start: died,
CommandSuite: CommandSet{},
}
}
func (d *DeadState) Enter(c *Connection) {
c.Printf("You are dead.\n")
msg := `
Y88b d88P d8888
Y88b d88P d88888
Y88o88P d88P888
Y888P .d88b. 888 888 d88P 888 888d888 .d88b.
888 d88""88b 888 888 d88P 888 888P" d8P Y8b
888 888 888 888 888 d88P 888 888 88888888
888 Y88..88P Y88b 888 d8888888888 888 Y8b.
888 "Y88P" "Y88888 d88P 888 888 "Y8888
____
__,---' '--.__
,-' ; '.
,' '--.'--.
,' '._ '-.
; ; '-- ;
,-'-_ _,-~~-. ,-- '.
;; '-,; ,'~'.__ ,;;; ; ;
;; ;,' ,;; ', ;;; '. ;
': ,' ':; __/ '.; ; ;
;~~^. '. '---'~~ ;; ; ;
',' '. '. .;;; ;'
,',^. '. '._ __ ':; ,'
'-' '--' ~'--'~~'--. ~ ,'
/;'-;_ ; ;. /. / ; ~~'-. ;
-._ ; ; ; ',;'-;__;---; '----'
'--.__ ''-'-;__;: ; ;__;
... '--.__ '-- '-'
'--.:::... '--.__ ____
'--:::::--. '--.__ __,--' '.
'--:::';.... '--' ___ '.
'--'-:::... __ ) ;
~'-:::... '---. ( ,'
~'-:::::::::'--. '-.
~'-::::::::'. ;
~'--:::,' ,'
~~'--'~
8888888b. 8888888888 d8888 8888888b.
888 "Y88b 888 d88888 888 "Y88b
888 888 888 d88P888 888 888
888 888 8888888 d88P 888 888 888
888 888 888 d88P 888 888 888
888 888 888 d88P 888 888 888
888 .d88P 888 d8888888888 888 .d88P
8888888P" 8888888888 d88P 888 8888888P"
`
lines := strings.Split(msg, "\n")
for _, line := range lines {
c.Write([]byte(line + "\n"))
time.Sleep(20 * time.Millisecond)
}
}
func (d *DeadState) Tick(c *Connection, frame int64) ConnectionState {
if frame-d.start > options.respawnFrames {
return SpawnRandomly()
return c.game.SpawnPlayer()
}
return d
}
@ -26,6 +84,55 @@ func (d *DeadState) Exit(c *Connection) {
c.Printf("You're alive again.\n")
}
func (d *DeadState) String() string {
return "dead"
func (d *DeadState) String() string { return "dead" }
func (d *DeadState) FillStatus(c *Connection, s *status) {
s.Description = `
Y88b d88P d8888
Y88b d88P d88888
Y88o88P d88P888
Y888P .d88b. 888 888 d88P 888 888d888 .d88b.
888 d88""88b 888 888 d88P 888 888P" d8P Y8b
888 888 888 888 888 d88P 888 888 88888888
888 Y88..88P Y88b 888 d8888888888 888 Y8b.
888 "Y88P" "Y88888 d88P 888 888 "Y8888
____
__,---' '--.__
,-' ; '.
,' '--.'--.
,' '._ '-.
; ; '-- ;
,-'-_ _,-~~-. ,-- '.
;; '-,; ,'~'.__ ,;;; ; ;
;; ;,' ,;; ', ;;; '. ;
': ,' ':; __/ '.; ; ;
;~~^. '. '---'~~ ;; ; ;
',' '. '. .;;; ;'
,',^. '. '._ __ ':; ,'
'-' '--' ~'--'~~'--. ~ ,'
/;'-;_ ; ;. /. / ; ~~'-. ;
-._ ; ; ; ',;'-;__;---; '----'
'--.__ ''-'-;__;: ; ;__;
... '--.__ '-- '-'
'--.:::... '--.__ ____
'--:::::--. '--.__ __,--' '.
'--:::';.... '--' ___ '.
'--'-:::... __ ) ;
~'-:::... '---. ( ,'
~'-:::::::::'--. '-.
~'-::::::::'. ;
~'--:::,' ,'
~~'--'~
8888888b. 8888888888 d8888 8888888b.
888 "Y88b 888 d88888 888 "Y88b
888 888 888 d88P888 888 888
888 888 8888888 d88P 888 888 888
888 888 888 d88P 888 888 888
888 888 888 d88P 888 888 888
888 .d88P 888 d8888888888 888 .d88P
8888888P" 8888888888 d88P 888 8888888P"
`
}

@ -11,7 +11,6 @@ const (
E_No_DB
E_No_Port
E_Bad_Duration
E_Missing_Slack_OAuth_Token
)
type errorGroup []error
@ -63,3 +62,5 @@ func (e *ErrorState) String() string {
func (e *ErrorState) RunCommand(c *Connection, name string, args ...string) ConnectionState {
return e
}
func (e *ErrorState) FillStatus(c *Connection, s *status) {}

@ -0,0 +1,85 @@
package main
import (
"math/rand"
"sort"
"strconv"
)
// Galaxy is a collection of systems
type Galaxy struct {
systems map[int]*System
names map[string]int
}
func NewGalaxy() *Galaxy {
g := &Galaxy{
systems: make(map[int]*System),
names: make(map[string]int),
}
g.indexSystems()
return g
}
func (g *Galaxy) indexSystems() {
rows, err := db.Query(`select * from planets`)
if err != nil {
log_error("unable to select all planets: %v", err)
return
}
defer rows.Close()
for rows.Next() {
s := System{}
if err := rows.Scan(&s.id, &s.name, &s.x, &s.y, &s.z, &s.planets); err != nil {
log_info("unable to scan planet row: %v", err)
continue
}
g.systems[s.id] = &s
g.names[s.name] = s.id
s.money = int64(rand.NormFloat64()*options.moneySigma + options.moneyMean)
}
}
// GetSystem gets a system by either ID or name. If the provided string
// contains an integer, we assume the lookup is intended to be by ID.
func (g *Galaxy) GetSystem(s string) *System {
id, err := strconv.Atoi(s)
if err == nil {
return g.GetSystemByID(id)
}
return g.GetSystemByName(s)
}
func (g *Galaxy) GetSystemByID(id int) *System {
return g.systems[id]
}
func (g *Galaxy) GetSystemByName(name string) *System {
id := g.SystemID(name)
if id == 0 {
return nil
}
return g.GetSystemByID(id)
}
func (g *Galaxy) SystemID(name string) int { return g.names[name] }
// Neighborhood generates the neighborhood for a given system.
func (g *Galaxy) Neighborhood(sys *System) Neighborhood {
neighbors := make(Neighborhood, 0, len(g.systems))
for id, sys2 := range g.systems {
if id == sys.id {
continue
}
neighbors = append(neighbors, Neighbor{id: id, distance: sys.DistanceTo(sys2)})
}
sort.Sort(neighbors)
return neighbors
}
func (g *Galaxy) randomSystem() *System {
id := rand.Intn(len(g.systems))
return g.GetSystemByID(id)
}

@ -2,11 +2,12 @@ package main
import (
"fmt"
"math/rand"
"time"
)
type Game struct {
id Id
id string
start time.Time
end time.Time
done chan interface{}
@ -15,6 +16,7 @@ type Game struct {
connections map[*Connection]bool
frame int64
elems map[GameElement]bool
galaxy *Galaxy
}
func gamesTable() {
@ -30,28 +32,32 @@ func gamesTable() {
}
}
func init() { rand.Seed(time.Now().UnixNano()) }
func newID() string {
chars := []rune("ABCDEEEEEEEEFGHJJJJJJJKMNPQQQQQQQRTUVWXXXXXYZZZZZ234677777789")
id := make([]rune, 0, 4)
for i := 0; i < cap(id); i++ {
id = append(id, chars[rand.Intn(len(chars))])
}
return string(id)
}
func NewGame() *Game {
game := &Game{
id: NewId(),
id: newID(),
start: time.Now(),
done: make(chan interface{}),
connections: make(map[*Connection]bool, 32),
elems: make(map[GameElement]bool, 32),
galaxy: NewGalaxy(),
}
if err := game.Create(); err != nil {
log_error("unable to create game: %v", err)
}
for _, system := range index {
for _, system := range game.galaxy.systems {
game.Register(system)
}
if currentGame != nil {
log_info("passing %d connections...", len(currentGame.connections))
for conn, _ := range currentGame.connections {
log_info("moving player %s to new game", conn.Name())
currentGame.Quit(conn)
game.Join(conn)
}
}
return game
}
@ -61,7 +67,7 @@ func (g *Game) Create() error {
(id, start)
values
(?, ?)
;`, g.id.String(), g.start)
;`, g.id, g.start)
if err != nil {
return fmt.Errorf("error writing sqlite insert statement to create game: %v")
}
@ -78,7 +84,12 @@ func (g *Game) Store() error {
}
func (g *Game) Join(conn *Connection) {
log_info("Player %s has joined game %s", conn.Name(), g.id)
for there, _ := range g.connections {
there.Printf("Player %s has joined the game", conn.Name())
}
g.connections[conn] = true
g.Register(conn)
}
func (g *Game) Quit(conn *Connection) {
@ -97,6 +108,8 @@ func (g *Game) Win(winner *Connection, method string) {
for conn, _ := range g.connections {
conn.Printf("player %s has won by %s victory.\n", winner.Name(), method)
}
gm.Remove(g)
}
func (g *Game) Reset() {
@ -128,7 +141,7 @@ func (g *Game) Register(elem GameElement) {
func (g *Game) tick() {
g.frame += 1
for elem := range g.elems {
elem.Tick(g.frame)
elem.Tick(g)
}
for elem := range g.elems {
if elem.Dead() {
@ -138,7 +151,11 @@ func (g *Game) tick() {
}
}
func (g *Game) SpawnPlayer() ConnectionState {
return Idle(g.galaxy.randomSystem())
}
type GameElement interface {
Tick(frame int64)
Tick(*Game)
Dead() bool
}

@ -0,0 +1,41 @@
package main
import (
"sync"
)
var gm *GameManager
func init() {
gm = &GameManager{
games: make(map[string]*Game, 32),
}
}
type GameManager struct {
games map[string]*Game
sync.Mutex
}
func (g *GameManager) NewGame() *Game {
g.Lock()
defer g.Unlock()
game := NewGame()
g.games[game.id] = game
return game
}
func (g *GameManager) Get(id string) *Game {
g.Lock()
defer g.Unlock()
return g.games[id]
}
func (g *GameManager) Remove(game *Game) {
g.Lock()
defer g.Unlock()
delete(g.games, game.id)
}

@ -2,86 +2,4 @@ module github.com/jordanorelli/exo
go 1.12
require (
github.com/OpenPeeDeeP/depguard v0.0.0-20181229194401-1f388ab2d810 // indirect
github.com/StackExchange/wmi v0.0.0-20181212234831-e0a55b97c705 // indirect
github.com/alecthomas/gometalinter v3.0.0+incompatible // indirect
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect
github.com/coreos/etcd v3.3.12+incompatible // indirect
github.com/cosiner/argv v0.0.1 // indirect
github.com/cpuguy83/go-md2man v1.0.10 // indirect
github.com/davidrjenni/reftools v0.0.0-20180914123528-654d0ba4f96d // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/fatih/gomodifytags v0.0.0-20180914191908-141225bf62b6 // indirect
github.com/fatih/motion v0.0.0-20180408211639-218875ebe238 // indirect
github.com/go-critic/go-critic v0.3.4 // indirect
github.com/go-delve/delve v1.2.0 // indirect
github.com/go-ole/go-ole v1.2.4 // indirect
github.com/go-toolsmith/astcast v1.0.0 // indirect
github.com/go-toolsmith/astcopy v1.0.0 // indirect
github.com/go-toolsmith/astfmt v1.0.0 // indirect
github.com/go-toolsmith/astp v1.0.0 // indirect
github.com/go-toolsmith/pkgload v1.0.0 // indirect
github.com/go-toolsmith/typep v1.0.0 // indirect
github.com/gogo/protobuf v1.2.1 // indirect
github.com/golang/mock v1.2.0 // indirect
github.com/golang/protobuf v1.3.1 // indirect
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 // indirect
github.com/golangci/go-tools v0.0.0-20190124090046-35a9f45a5db0 // indirect
github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d // indirect
github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98 // indirect
github.com/golangci/golangci-lint v1.16.0 // indirect
github.com/golangci/gosec v0.0.0-20180901114220-8afd9cbb6cfb // indirect
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219 // indirect
github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039 // indirect
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf // indirect
github.com/jordanorelli/moon v0.0.1
github.com/josharian/impl v0.0.0-20180228163738-3d0f908298c4 // indirect
github.com/jstemmer/gotags v1.4.1 // indirect
github.com/keegancsmith/rpc v1.1.0 // indirect
github.com/kisielk/errcheck v1.2.0 // indirect
github.com/klauspost/asmfmt v1.2.0 // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/koron/iferr v0.0.0-20180615142939-bb332a3b1d91 // indirect
github.com/kr/pty v1.1.4 // indirect
github.com/mattn/go-colorable v0.1.1 // indirect
github.com/mattn/go-isatty v0.0.7 // indirect
github.com/mattn/go-runewidth v0.0.4 // indirect
github.com/mattn/go-sqlite3 v1.10.0
github.com/mdempsky/gocode v0.0.0-20190203001940-7fb65232883f // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d // indirect
github.com/nicksnyder/go-i18n v1.10.0 // indirect
github.com/onsi/ginkgo v1.8.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
github.com/pelletier/go-toml v1.3.0 // indirect
github.com/peterh/liner v1.1.0 // indirect
github.com/pkg/errors v0.8.1 // indirect
github.com/pkg/profile v1.3.0 // indirect
github.com/rogpeppe/go-internal v1.3.0 // indirect
github.com/rogpeppe/godef v1.1.1 // indirect
github.com/russross/blackfriday v2.0.0+incompatible // indirect
github.com/shirou/gopsutil v2.18.12+incompatible // indirect
github.com/shurcooL/go v0.0.0-20190330031554-6713ea532688 // indirect
github.com/sirupsen/logrus v1.4.1 // indirect
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cobra v0.0.3 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/viper v1.3.2 // indirect
github.com/stamblerre/gocode v0.0.0-20190327203809-810592086997 // indirect
github.com/stretchr/testify v1.3.0 // indirect
github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 // indirect
github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a // indirect
golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c // indirect
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5 // indirect
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 // indirect
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 // indirect
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 // indirect
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 // indirect
golang.org/x/tools v0.0.0-20190406165526-0d5674b3111a // indirect
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c // indirect
honnef.co/go/tools v0.0.0-20190404041852-d36bf9040906 // indirect
mvdan.cc/unparam v0.0.0-20190310220240-1b9ccfa71afe // indirect
sourcegraph.com/sqs/pbtypes v1.0.0 // indirect
)
require github.com/mattn/go-sqlite3 v1.10.0

346
go.sum

@ -1,348 +1,2 @@
9fans.net/go v0.0.0-20181112161441-237454027057 h1:OcHlKWkAMJEF1ndWLGxp5dnJQkYM/YImUOvsBoz6h5E=
9fans.net/go v0.0.0-20181112161441-237454027057/go.mod h1:diCsxrliIURU9xsYtjCp5AbpQKqdhKmf0ujWDUSkfoY=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OpenPeeDeeP/depguard v0.0.0-20180806142446-a69c782687b2/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o=
github.com/OpenPeeDeeP/depguard v0.0.0-20181229194401-1f388ab2d810 h1:pFks+oaqVWDFq0KsLQZFB2pQGB5Us0HfMSBENtieXOs=
github.com/OpenPeeDeeP/depguard v0.0.0-20181229194401-1f388ab2d810/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/StackExchange/wmi v0.0.0-20181212234831-e0a55b97c705/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/alecthomas/gometalinter v3.0.0+incompatible h1:e9Zfvfytsw/e6Kd/PYd75wggK+/kX5Xn8IYDUKyc5fU=
github.com/alecthomas/gometalinter v3.0.0+incompatible/go.mod h1:qfIpQGGz3d+NmgyPBqv+LSh50emm1pt72EtcX2vKYQk=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.12+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ=
github.com/cosiner/argv v0.0.1 h1:2iAFN+sWPktbZ4tvxm33Ei8VY66FPCxdOxpncUGpAXE=
github.com/cosiner/argv v0.0.1/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ=
github.com/cpuguy83/go-md2man v1.0.8/go.mod h1:N6JayAiVKtlHSnuTCeuLSQVs75hb8q+dYQLjr7cDsKY=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davidrjenni/reftools v0.0.0-20180914123528-654d0ba4f96d h1:aRvyac5PN1NEfcANJ1tfs8GMs5I9OXsVeg0FJkpXOys=
github.com/davidrjenni/reftools v0.0.0-20180914123528-654d0ba4f96d/go.mod h1:8o/GRMvsb9VyFbSEZGXfa0dkSXml4G23W0D/h9FksWM=
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/gomodifytags v0.0.0-20180914191908-141225bf62b6 h1:iXJdM8Uob6EPOG/PFr5q0J124ysiZdJfACHqICBb3b8=
github.com/fatih/gomodifytags v0.0.0-20180914191908-141225bf62b6/go.mod h1:p2/x7bnOQsbq/deXsDIlj2yLiKFGPkD2nuoYqwn8R4Y=
github.com/fatih/motion v0.0.0-20180408211639-218875ebe238 h1:Qo4RxRMFag+fvDqQ6A3MblYBormptQUZ1ssOtV+EeQ8=
github.com/fatih/motion v0.0.0-20180408211639-218875ebe238/go.mod h1:pseIrV+t9A4+po+KJ1LheSnYH8m1qs6WhKx2zFiGi9I=
github.com/fatih/structtag v1.0.0 h1:pTHj65+u3RKWYPSGaU290FpI/dXxTaHdVwVwbcPKmEc=
github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-critic/go-critic v0.0.0-20181204210945-ee9bf5809ead/go.mod h1:3MzXZKJdeXqdU9cj+rvZdNiN7SZ8V9OjybF8loZDmHU=
github.com/go-critic/go-critic v0.3.4 h1:FYaiaLjX0Nqei80KPhm4CyFQUBbmJwSrHxQ73taaGBc=
github.com/go-critic/go-critic v0.3.4/go.mod h1:AHR42Lk/E/aOznsrYdMYeIQS5RH10HZHSqP+rD6AJrc=
github.com/go-delve/delve v1.2.0 h1:uwGyfYO0WsWqbnDWvxCBKOr2qFLpii3tLxwM+fTJs70=
github.com/go-delve/delve v1.2.0/go.mod h1:yP+LD36s/ud5nm4lsQY0TwNhYu2PAwk6xItz+442j74=
github.com/go-lintpack/lintpack v0.5.1/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0TuRN0=
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-toolsmith/astcast v0.0.0-20181028201508-b7a89ed70af1/go.mod h1:TEo3Ghaj7PsZawQHxT/oBvo4HK/sl1RcuUHDKTTju+o=
github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g=
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
github.com/go-toolsmith/astcopy v0.0.0-20180903214859-79b422d080c4/go.mod h1:c9CPdq2AzM8oPomdlPniEfPAC6g1s7NqZzODt8y6ib8=
github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8=
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ=
github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg=
github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k=
github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk=
github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg=
github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks=
github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
github.com/go-toolsmith/strparse v0.0.0-20180903215201-830b6daa1241/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4=
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
github.com/go-toolsmith/typep v0.0.0-20181030061450-d63dc7650676/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
github.com/go-toolsmith/typep v1.0.0 h1:zKymWyA1TRYvqYrYDrfEMZULyrhcnGY3x7LDKU2XQaA=
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
github.com/golangci/errcheck v0.0.0-20181003203344-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 h1:YYWNAGTKWhKpcLLt7aSj/odlKrSrelQwlovBpDuf19w=
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw=
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
github.com/golangci/go-tools v0.0.0-20180109140146-af6baa5dc196/go.mod h1:unzUULGw35sjyOYjUt0jMTXqHlZPpPc6e+xfO4cd6mM=
github.com/golangci/go-tools v0.0.0-20190124090046-35a9f45a5db0 h1:MRhC9XbUjE6XDOInSJ8pwHuPagqsyO89QDU9IdVhe3o=
github.com/golangci/go-tools v0.0.0-20190124090046-35a9f45a5db0/go.mod h1:unzUULGw35sjyOYjUt0jMTXqHlZPpPc6e+xfO4cd6mM=
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3 h1:pe9JHs3cHHDQgOFXJJdYkK6fLz2PWyYtP4hthoCMvs8=
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o=
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d h1:pXTK/gkVNs7Zyy7WKgLXmpQ5bHTrq5GDsp8R9Qs67g0=
github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
github.com/golangci/gofmt v0.0.0-20181105071733-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98 h1:0OkFarm1Zy2CjCiDKfK9XHgmc2wbDlRMD2hD8anAJHU=
github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
github.com/golangci/golangci-lint v1.16.0 h1:PcWAN9JHflZzJQaZVY1JXZE0Tgjq+jO2v4QLqJ/Azvw=
github.com/golangci/golangci-lint v1.16.0/go.mod h1:uySrAxrUmZYnxyccYSnwuAEm+3144Zg5IAUueIW8+fA=
github.com/golangci/gosec v0.0.0-20180901114220-66fb7fc33547/go.mod h1:0qUabqiIQgfmlAmulqxyiGkkyF6/tOGSnY2cnPVwrzU=
github.com/golangci/gosec v0.0.0-20180901114220-8afd9cbb6cfb h1:Bi7BYmZVg4C+mKGi8LeohcP2GGUl2XJD4xCkJoZSaYc=
github.com/golangci/gosec v0.0.0-20180901114220-8afd9cbb6cfb/go.mod h1:ON/c2UR0VAAv6ZEAFKhjCLplESSmRFfZcDLASbI1GWo=
github.com/golangci/ineffassign v0.0.0-20180808204949-2ee8f2867dde h1:qEGp3ZF1Qw6TkbWKn6GdJ12Ssu/CpJBaBcJ4hrUjrSo=
github.com/golangci/ineffassign v0.0.0-20180808204949-2ee8f2867dde/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
github.com/golangci/lint-1 v0.0.0-20180610141402-4bf9709227d1/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219 h1:utua3L2IbQJmauC5IXdEA547bcoU5dozgQAfc8Onsg4=
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA=
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770 h1:EL/O5HGrF7Jaq0yNhBLucz9hTuRzj2LdwGBOaENgxIk=
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 h1:leSNB7iYzLYSSx3J/s5sVf4Drkc68W2wm4Ixh/mr0us=
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI=
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039 h1:XQKc8IYQOeRwVs36tDrEmTgDgP88d5iEURwpmtiAlOM=
github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys=
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jordanorelli/moon v0.0.1 h1:PuBQFItFrvtYpnB+Mv77HM/su+PkeWoSsnAu9q+l/zs=
github.com/jordanorelli/moon v0.0.1/go.mod h1:8oB/ghqOmK3i35ZVK4eXthJ0vBM2yjgEyZIQGqTBHjM=
github.com/josharian/impl v0.0.0-20180228163738-3d0f908298c4 h1:gmIVMdGlVf5e6Yo6+ZklxdOrvtOvyrAjJyXAbmOznyo=
github.com/josharian/impl v0.0.0-20180228163738-3d0f908298c4/go.mod h1:t4Tr0tn92eq5ISef4cS5plFAMYAqZlAXtgUcKE6y8nw=
github.com/jstemmer/gotags v1.4.1 h1:aWIyXsU3lTDqhsEC49MP85p2cUUWr2ptvdGNqqGA3r4=
github.com/jstemmer/gotags v1.4.1/go.mod h1:b6J3X0bsLbR4C5SgSx3V3KjuWTtmRzcmWPbTkWZ49PA=
github.com/keegancsmith/rpc v1.1.0 h1:bXVRk3EzbtrEegTGKxNTc+St1lR7t/Z1PAO8misBnCc=
github.com/keegancsmith/rpc v1.1.0/go.mod h1:Xow74TKX34OPPiPCdz6x1o9c0SCxRqGxDuKGk7ZOo8s=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0 h1:reN85Pxc5larApoH1keMBiu2GWtPqXQ1nc9gx+jOU+E=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/asmfmt v1.2.0 h1:zwsyBYgEdabg32alMful/5pRtMTcR5C5w1LKNg9OD78=
github.com/klauspost/asmfmt v1.2.0/go.mod h1:RAoUvqkWr2rUa2I19qKMEVZQe4BVtcHGTMCUOcCU2Lg=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/koron/iferr v0.0.0-20180615142939-bb332a3b1d91 h1:hunjgdb3b21ZdRmzDPXii0EcnHpjH7uCP+kODoE1JH0=
github.com/koron/iferr v0.0.0-20180615142939-bb332a3b1d91/go.mod h1:C2tFh8w3I6i4lnUJfoBx2Hwku3mgu4wPNTtUNp1i5KI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/mdempsky/gocode v0.0.0-20190203001940-7fb65232883f h1:ee+twVCignaZjt7jpbMSLxAeTN/Nfq9W/nm91E7QO1A=
github.com/mdempsky/gocode v0.0.0-20190203001940-7fb65232883f/go.mod h1:hltEC42XzfMNgg0S1v6JTywwra2Mu6F6cLR03debVQ8=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/nicksnyder/go-i18n v1.10.0 h1:5AzlPKvXBH4qBzmZ09Ua9Gipyruv6uApMcrNZdo96+Q=
github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.3.0 h1:e5+lF2E4Y2WCIxBefVowBuB0iHrUH4HZ8q+6mGF7fJc=
github.com/pelletier/go-toml v1.3.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/peterh/liner v1.1.0 h1:f+aAedNJA6uk7+6rXsYBnhdo4Xux7ESLe+kcuVUF5os=
github.com/peterh/liner v1.1.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v0.0.0-20170413231811-06b906832ed0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/profile v1.3.0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.1/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/godef v1.1.1 h1:NujOtt9q9vIClRTB3sCZpavac+NMRaIayzrcz1h4fSE=
github.com/rogpeppe/godef v1.1.1/go.mod h1:oEo1eMy1VUEHUzUIX4F7IqvMJRiz9UId44mvnR8oPlQ=
github.com/russross/blackfriday v0.0.0-20180428102519-11635eb403ff/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go v0.0.0-20190330031554-6713ea532688/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/sirupsen/logrus v0.0.0-20180523074243-ea8897e79973/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stamblerre/gocode v0.0.0-20190327203809-810592086997 h1:LF81AGV63kJoxjmSgQPT8FARAMHeY46CYQ4TNoVDWHM=
github.com/stamblerre/gocode v0.0.0-20190327203809-810592086997/go.mod h1:EM2T8YDoTCvGXbEpFHxarbpv7VE26QD1++Cb1Pbh7Gs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a h1:00UFliGZl2UciXe8o/2iuEsRQ9u7z0rzDTVzuj6EYY0=
github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a/go.mod h1:ofmGw6LrMypycsiWcyug6516EXpIxSbZ+uI9ppGypfY=
golang.org/x/arch v0.0.0-20171004143515-077ac972c2e4/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8=
golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c h1:Rx/HTKi09myZ25t1SOlDHmHOy/mKxNAcu0hP1oPX9qM=
golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/crypto v0.0.0-20180614174826-fd5f17ee7299/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180614134839-8883426083c0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 h1:1Fzlr8kkDLQwqMP8GxrhptBLqZG/EDpiATneiZHY998=
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180824175216-6c1c5e93cdc1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181120060634-fc4f04983f62/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181130195746-895048a75ecf/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181205014116-22934f0fdb62/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181207195948-8634b1ecd393/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190213192042-740235f6c0d8/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190314010720-f0bfdbff1f9c/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190406165526-0d5674b3111a h1:c1oK9xVrpJp+44t5dM2cXiplsgGYQnKb05svZBQOMns=
golang.org/x/tools v0.0.0-20190406165526-0d5674b3111a/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c h1:vTxShRUnK60yd8DZU+f95p1zSLj814+5CuEh7NjF2/Y=
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170407172122-cd8b52f8269e/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190404041852-d36bf9040906 h1:eCa7DriK+Q8ESfZJadXOQpNNXzaM3BHdFcxwJmlYUs4=
honnef.co/go/tools v0.0.0-20190404041852-d36bf9040906/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190124213536-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY=
mvdan.cc/unparam v0.0.0-20190310220240-1b9ccfa71afe h1:Ekmnp+NcP2joadI9pbK4Bva87QKZSeY7le//oiMrc9g=
mvdan.cc/unparam v0.0.0-20190310220240-1b9ccfa71afe/go.mod h1:BnhuWBAqxH3+J5bDybdxgw5ZfS+DsVd4iylsKQePN8o=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
sourcegraph.com/sourcegraph/go-diff v0.5.1-0.20190210232911-dee78e514455 h1:qoQ5Kt+Zm+GXBtz49YwD3juBhr/E0U25jO6bBzxW6NI=
sourcegraph.com/sourcegraph/go-diff v0.5.1-0.20190210232911-dee78e514455/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
sourcegraph.com/sqs/pbtypes v1.0.0 h1:f7lAwqviDEGvON4kRv0o5V7FT/IQK+tbkF664XMbP3o=
sourcegraph.com/sqs/pbtypes v1.0.0/go.mod h1:3AciMUv4qUuRHRHhOG4TZOB+72GdPVz5k+c648qsFS4=

55
id.go

@ -1,55 +0,0 @@
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])
}

@ -15,43 +15,37 @@ func Idle(sys *System) ConnectionState {
i := &IdleState{System: sys}
i.CommandSuite = CommandSet{
balCommand,
helpCommand,
playersCommand,
BroadcastCommand(sys),
NearbyCommand(sys),
Command{
name: "goto",
help: "travel between star systems",
summary: "travel between star systems",
arity: 1,
handler: i.travelTo,
},
Command{
name: "bomb",
help: "bomb another star system",
summary: "bomb another star system",
arity: 1,
usage: "bomb [system-name or system-id]",
handler: i.bomb,
},
Command{
name: "mine",
help: "mine the current system for resources",
summary: "mine the current system for resources",
arity: 0,
handler: i.mine,
},
Command{
name: "info",
help: "gives you information about the current star system",
arity: 0,
handler: i.info,
},
Command{
name: "scan",
help: "scans the galaxy for signs of life",
summary: "scans the galaxy for signs of life",
arity: 0,
handler: i.scan,
},
Command{
name: "make",
help: "makes things",
summary: "makes things",
handler: i.maek,
},
}
@ -71,9 +65,9 @@ func (i *IdleState) Tick(c *Connection, frame int64) ConnectionState {
}
func (i *IdleState) travelTo(c *Connection, args ...string) {
dest, err := GetSystem(args[0])
if err != nil {
c.Printf("%v\n", err)
dest := c.game.galaxy.GetSystem(args[0])
if dest == nil {
c.Printf("no such system: %s", args[0])
return
}
c.SetState(NewTravel(c, i.System, dest))
@ -89,37 +83,36 @@ func (i *IdleState) bomb(c *Connection, args ...string) {
return
}
target, err := GetSystem(args[0])
if err != nil {
c.Printf("Cannot send bomb: %v\n", err)
target := c.game.galaxy.GetSystem(args[0])
if target == nil {
c.Printf("Cannot send bomb: no such system: %v\n", args[0])
return
}
c.bombs -= 1
c.lastBomb = time.Now()
bomb := NewBomb(c, i.System, target)
currentGame.Register(bomb)
c.game.Register(bomb)
}
func (i *IdleState) mine(c *Connection, args ...string) {
c.SetState(Mine(i.System))
}
func (i *IdleState) info(c *Connection, args ...string) {
c.Printf("Currently idle on system %v\n", i.System)
c.Printf("Space duckets available: %v\n", i.money)
}
func (i *IdleState) scan(c *Connection, args ...string) {
if time.Since(c.lastScan) < 1*time.Minute {
return
}
c.Printf("Scanning the galaxy for signs of life...\n")
currentGame.Register(NewScan(i.System))
c.game.Register(NewScan(i.System, c.game.galaxy.Neighborhood(i.System)))
}
// "make" is already a keyword
func (i *IdleState) maek(c *Connection, args ...string) {
if len(args) != 1 {
c.Printf("not sure what to do! Expecting a command like this: make [thing]\ne.g.:\nmake bomb\nmake colony")
return
}
switch args[0] {
case "bomb":
if c.money < options.bombCost {
@ -136,3 +129,8 @@ func (i *IdleState) maek(c *Connection, args ...string) {
c.Printf("I don't know how to make a %v.\n", args[0])
}
}
func (i *IdleState) FillStatus(c *Connection, s *status) {
s.Location = i.System.String()
s.Description = "Just hanging out, enjoying outer space."
}

@ -0,0 +1,185 @@
package main
import (
"strings"
"time"
)
var banner = `
##############################################################################################
/$$$$$$$$ /$$
| $$_____/ | $$
| $$ /$$ /$$ /$$$$$$ /$$$$$$$ /$$$$$$ | $$ /$$$$$$ /$$$$$$$ /$$ /$$ /$$$$$$$
| $$$$$ | $$ /$$/ /$$__ $$ /$$_____/ /$$__ $$| $$ /$$__ $$| $$__ $$| $$ | $$ /$$_____/
| $$__/ \ $$$$/ | $$ \ $$| $$ | $$ \ $$| $$| $$ \ $$| $$ \ $$| $$ | $$| $$$$$$
| $$ >$$ $$ | $$ | $$| $$ | $$ | $$| $$| $$ | $$| $$ | $$| $$ | $$ \____ $$
| $$$$$$$$ /$$/\ $$| $$$$$$/| $$$$$$$| $$$$$$/| $$| $$$$$$/| $$ | $$| $$$$$$/ /$$$$$$$/
|________/|__/ \__/ \______/ \_______/ \______/ |__/ \______/ |__/ |__/ \______/ |_______/
~+
* +
' |
() .-.,="''"=. - o -
'=/_ \ |
* | '=._ |
\ '=./', '
. '=.__.=' '=' *
+ +
O * ' .
A game of dark cunning in the vast unknown of space by Jordan Orelli.
##############################################################################################
`
type LobbyState struct {
CommandSuite
NopExit
}
func EnterLobby() ConnectionState {
return &LobbyState{
CommandSuite: CommandSet{
newGameCommand,
joinGameCommand,
listGamesCommand,
},
}
}
func (st *LobbyState) String() string { return "Lobby" }
func (st *LobbyState) Enter(c *Connection) {
c.Printf(strings.TrimSpace(banner))
time.Sleep(1 * time.Second)
for {
c.Printf("\n\nWhat is your name, adventurer?\n")
name, err := c.ReadString('\n')
if err == nil {
name = strings.TrimSpace(name)
} else {
log_error("player failed to connect: %v", err)
return
}
if !ValidName(name) {
c.Printf("that name is illegal.\n")
continue
}
log_info("player connected: %v", name)
profile, err := loadProfile(name)
if err != nil {
log_error("could not read profile: %v", err)
profile = &Profile{name: name}
if err := profile.Create(); err != nil {
log_error("unable to create profile record: %v", err)
}
c.Printf("you look new around these parts, %s.\n", profile.name)
c.Printf(`if you'd like a description of how to play, type the "help" command\n`)
c.profile = profile
} else {
c.profile = profile
c.Printf("Welcome back, %s.\n", profile.name)
}
break
}
c.ListCommands()
}
func (st *LobbyState) Tick(c *Connection, frame int64) ConnectionState { return st }
func (st *LobbyState) FillStatus(c *Connection, s *status) {
s.Description = strings.TrimSpace(`
Currently in the Lobby, waiting for you to issue a "new" command to start a new
game, or a "join" command to join an existing game.
`)
}
var newGameCommand = Command{
name: "new",
summary: "starts a new game",
arity: 0,
variadic: false,
handler: func(c *Connection, args ...string) {
c.Printf("Starting a new game...\n")
game := gm.NewGame()
log_info("%s Created game: %s", c.profile.name, game.id)
go game.Run()
c.game = game
c.Printf("Now playing in game: %s\n\n", game.id)
c.Line()
c.game.Join(c)
c.SetState(game.SpawnPlayer())
},
debug: false,
}
var joinGameCommand = Command{
name: "join",
summary: "joins an existing game",
usage: "join [game-code]",
arity: 1,
variadic: false,
handler: func(c *Connection, args ...string) {
if len(args) == 0 {
gm.Lock()
defer gm.Unlock()
if len(gm.games) == 1 {
for _, game := range gm.games {
c.game = game
log_info("%s Joining game: %s", c.profile.name, c.game.id)
c.Printf("You have joined game %s\n", game.id)
c.SetState(game.SpawnPlayer())
c.game.Join(c)
return
}
}
c.Printf(strings.TrimLeft(`
Missing game code! When a player starts a game, they will be given a code to
identify their game. Use this game to join the other player's game.
Usage: join [game-code]`, " \n\t"))
return
}
id := args[0]
game := gm.Get(id)
c.game = game
log_info("%s Joining game: %s", c.profile.name, c.game.id)
c.Printf("You have joined game %s\n", game.id)
c.SetState(game.SpawnPlayer())
c.game.Join(c)
},
debug: false,
}
var listGamesCommand = Command{
name: "list",
summary: "lists game lobbies that can be joined",
usage: "list",
arity: 0,
variadic: false,
handler: func(c *Connection, args ...string) {
gm.Lock()
defer gm.Unlock()
c.Line()
c.Printf("%-8s %-20s\n", "Game", "Player")
c.Line()
for id, game := range gm.games {
c.Printf("%-8s %-20s\n", id, "")
for conn, _ := range game.connections {
if conn.profile != nil {
c.Printf("%-8s %-20s\n", "", conn.profile.name)
}
}
c.Printf("--------------------\n")
}
},
}

@ -7,9 +7,7 @@ import (
"math/rand"
"net"
"os"
"strings"
"time"
// "golang.org/x/oauth2"
)
var options struct {
@ -20,13 +18,12 @@ var options struct {
frameLength time.Duration
colonyCost int
frameRate int
lightSpeed float64
lightSpeed float64 // the distance that light travels in one tick
makeBombTime time.Duration
makeColonyTime time.Duration
makeShieldTime time.Duration
moneyMean float64
moneySigma float64
oauthToken string
playerSpeed float64
respawnFrames int64
respawnTime time.Duration
@ -39,7 +36,6 @@ var options struct {
var (
info_log *log.Logger
error_log *log.Logger
currentGame *Game
)
func log_error(template string, args ...interface{}) {
@ -51,10 +47,6 @@ func log_info(template string, args ...interface{}) {
}
func bail(status int, template string, args ...interface{}) {
if !strings.HasSuffix(template, "\n") {
template += "\n"
}
if status == 0 {
fmt.Fprintf(os.Stdout, template, args...)
} else {
@ -63,9 +55,9 @@ func bail(status int, template string, args ...interface{}) {
os.Exit(status)
}
func handleConnection(conn *Connection) {
func handleConnection(sock net.Conn) {
conn := NewConnection(sock)
defer conn.Close()
conn.Login()
c := make(chan []string)
go conn.ReadLines(c)
@ -86,10 +78,6 @@ func framesToDur(frames int64) time.Duration {
func main() {
flag.Parse()
if options.oauthToken == "" {
bail(E_Missing_Slack_OAuth_Token, "missing slack oauth token")
}
dbconnect()
options.frameLength = time.Second / time.Duration(options.frameRate)
options.respawnFrames = durToFrames(options.respawnTime)
@ -99,18 +87,12 @@ func main() {
error_log = log.New(os.Stderr, "[ERROR] ", 0)
setupDb()
listener, err := net.Listen("tcp", ":9220")
addr := ":9220"
listener, err := net.Listen("tcp", addr)
if err != nil {
bail(E_No_Port, "unable to start server: %v", err)
}
go func() {
for {
log_info("starting new game")
currentGame = NewGame()
currentGame.Run()
}
}()
log_info("listening on %s", addr)
for {
conn, err := listener.Accept()
@ -118,7 +100,7 @@ func main() {
log_error("error accepting connection: %v", err)
continue
}
go handleConnection(NewConnection(conn))
go handleConnection(conn)
}
}
@ -141,5 +123,4 @@ func init() {
flag.IntVar(&options.startMoney, "start-money", 1000, "amount of money a player has to start")
flag.DurationVar(&options.makeShieldTime, "shield-time", 15*time.Second, "time it takes to make a shield")
flag.DurationVar(&options.scanTime, "scan-recharge", 1*time.Minute, "time it takes for scanners to recharge")
flag.StringVar(&options.oauthToken, "oauth-token", "", "slack bot user oauth access token")
}

@ -2,6 +2,7 @@ package main
import (
"fmt"
"strings"
)
type MiningState struct {
@ -14,22 +15,15 @@ func Mine(sys *System) ConnectionState {
m := &MiningState{System: sys}
m.CommandSuite = CommandSet{
balCommand,
helpCommand,
playersCommand,
BroadcastCommand(sys),
NearbyCommand(sys),
Command{
name: "stop",
help: "stops mining",
summary: "stops mining",
arity: 0,
handler: m.stop,
},
Command{
name: "info",
help: "gives you information about the current mining operation",
arity: 0,
handler: m.info,
},
}
return m
}
@ -66,8 +60,11 @@ func (m *MiningState) stop(c *Connection, args ...string) {
c.SetState(Idle(m.System))
}
func (m *MiningState) info(c *Connection, args ...string) {
c.Printf("Currently mining system %v\n", m.System)
c.Printf("Mined so far: %v\n", m.mined)
c.Printf("Remaining space duckets on %v: %v\n", m.System, m.money)
func (m *MiningState) FillStatus(c *Connection, s *status) {
s.Location = m.System.String()
s.Description = strings.TrimSpace(fmt.Sprintf(`
Currently mining on system: %s
Mined so far: %d
Available space duckets: %d
`, m.System.String(), m.mined, m.money))
}

@ -12,6 +12,7 @@ type scan struct {
nextHitIndex int
nextEchoIndex int
results []scanResult
neighborhood Neighborhood
}
type scanResult struct {
@ -38,36 +39,40 @@ func (r *scanResult) playerNames() []string {
return names
}
func NewScan(origin *System) *scan {
func NewScan(origin *System, n Neighborhood) *scan {
return &scan{
origin: origin,
start: time.Now(),
results: make([]scanResult, 0, len(origin.Distances())),
results: make([]scanResult, 0, len(n)),
neighborhood: n,
}
}
func (s *scan) Tick(frame int64) {
func (s *scan) Tick(game *Game) {
s.dist += options.lightSpeed
s.hits()
s.hits(game)
s.echos()
}
func (s *scan) Dead() bool {
return s.nextEchoIndex >= len(s.origin.Distances())
return s.neighborhood == nil
}
func (s *scan) String() string {
return fmt.Sprintf("[scan origin: %s start_time: %v]", s.origin.name, s.start)
}
func (s *scan) hits() {
for ; s.nextHitIndex < len(s.origin.Distances()); s.nextHitIndex += 1 {
candidate := s.origin.Distances()[s.nextHitIndex]
if s.dist < candidate.dist {
break
func (s *scan) hits(game *Game) {
for len(s.neighborhood) > 0 && s.neighborhood[0].distance <= s.dist {
sys := game.galaxy.GetSystemByID(s.neighborhood[0].id)
s.results = append(s.results, s.hitSystem(sys, s.neighborhood[0].distance))
log_info("scan hit %v. Traveled %v in %v", sys.name, s.neighborhood[0].distance, time.Since(s.start))
if len(s.neighborhood) > 1 {
s.neighborhood = s.neighborhood[1:]
} else {
s.neighborhood = nil
}
s.results = append(s.results, s.hitSystem(candidate.s, candidate.dist))
log_info("scan hit %v. Traveled %v in %v", candidate.s.name, candidate.dist, time.Since(s.start))
}
}

@ -10,7 +10,6 @@ func MakeShield(c *Connection, s *System) {
CommandSuite: CommandSet{
balCommand,
BroadcastCommand(s),
helpCommand,
NearbyCommand(s),
playersCommand,
},
@ -47,11 +46,15 @@ func (m *MakeShieldState) String() string {
return fmt.Sprintf("Making shield on %v", m.System)
}
func (m *MakeShieldState) FillStatus(c *Connection, s *status) {
s.Location = m.System.String()
}
type Shield struct {
energy float64
}
func (s *Shield) Tick(frame int64) {
func (s *Shield) Tick() {
if s.energy < 1000 {
s.energy += (1000 - s.energy) * 0.0005
}

@ -4,16 +4,9 @@ import (
"database/sql"
"fmt"
"math"
"math/rand"
"strconv"
"time"
)
var (
index map[int]*System
nameIndex map[string]*System
)
type System struct {
*Shield
id int
@ -26,29 +19,13 @@ type System struct {
money int64
}
func GetSystem(id string) (*System, error) {
idNum, err := strconv.Atoi(id)
if err == nil {
sys, ok := index[idNum]
if !ok {
return nil, fmt.Errorf("No such system: %v", idNum)
}
return sys, nil
}
sys, ok := nameIndex[id]
if !ok {
return nil, fmt.Errorf("No such system: %v", id)
}
return sys, nil
}
func (s *System) Tick(frame int64) {
func (s *System) Tick(game *Game) {
if s.colonizedBy != nil && s.money > 0 {
s.colonizedBy.Deposit(1)
s.money -= 1
}
if s.Shield != nil {
s.Shield.Tick(frame)
s.Shield.Tick()
}
}
@ -62,7 +39,6 @@ func (s *System) Reset() {
}
func (s *System) Arrive(conn *Connection) {
// conn.SetSystem(s)
if s.players[conn] {
return
}
@ -80,7 +56,6 @@ func (s *System) Arrive(conn *Connection) {
func (s *System) Leave(p *Connection) {
delete(s.players, p)
// p.location = nil
}
func (s *System) NotifyInhabitants(template string, args ...interface{}) {
@ -138,38 +113,7 @@ type Ray struct {
dist float64 // distance in parsecs
}
func (s *System) Distances() []Ray {
if s.distances == nil {
s.distances = make([]Ray, 0, 551)
rows, err := db.Query(`
select edges.id_2, edges.distance
from edges
where edges.id_1 = ?
order by distance
;`, s.id)
if err != nil {
log_error("unable to query for system distances: %v", err)
return nil
}
for rows.Next() {
var (
r Ray
id int
dist float64
)
if err := rows.Scan(&id, &dist); err != nil {
log_error("unable to unpack Ray from sql result: %v", err)
continue
}
r.s = index[id]
r.dist = dist
s.distances = append(s.distances, r)
}
}
return s.distances
}
func (s *System) Bombed(bomber *Connection, frame int64) {
func (s *System) Bombed(bomber *Connection, game *Game) {
if s.Shield != nil {
if s.Shield.Hit() {
s.EachConn(func(conn *Connection) {
@ -182,7 +126,7 @@ func (s *System) Bombed(bomber *Connection, frame int64) {
}
s.EachConn(func(conn *Connection) {
conn.Die(frame)
conn.Die(game.frame)
s.Leave(conn)
bomber.MadeKill(conn)
})
@ -191,21 +135,20 @@ func (s *System) Bombed(bomber *Connection, frame int64) {
s.colonizedBy = nil
}
for id, _ := range index {
for id, other := range game.galaxy.systems {
if id == s.id {
continue
}
delay := s.LightTimeTo(index[id])
id2 := id
delay := s.LightTimeTo(game.galaxy.systems[id])
from := s
to := other
time.AfterFunc(delay, func() {
bombNotice(id2, s.id)
bombNotice(to, from)
})
}
}
func bombNotice(to_id, from_id int) {
to := index[to_id]
from := index[from_id]
func bombNotice(to, from *System) {
to.EachConn(func(conn *Connection) {
conn.Printf("a bombing has been observed on %s\n", from.name)
})
@ -215,36 +158,17 @@ func (s System) String() string {
return fmt.Sprintf("%s (id: %v)", s.name, s.id)
}
type Neighborhood []Neighbor
func (n Neighborhood) Len() int { return len(n) }
func (n Neighborhood) Less(i, j int) bool { return n[i].distance < n[j].distance }
func (n Neighborhood) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
type Neighbor struct {
id int
distance float64
}
func (e *System) Nearby(n int) ([]Neighbor, error) {
rows, err := db.Query(`
select planets.id, edges.distance
from edges
join planets on edges.id_2 = planets.id
where edges.id_1 = ?
order by distance
limit ?
;`, e.id, n)
if err != nil {
log_error("unable to get nearby systems for %s: %v", e.name, err)
return nil, err
}
neighbors := make([]Neighbor, 0, n)
for rows.Next() {
var neighbor Neighbor
if err := rows.Scan(&neighbor.id, &neighbor.distance); err != nil {
log_error("error unpacking row from nearby neighbors query: %v", err)
continue
}
neighbors = append(neighbors, neighbor)
}
return neighbors, nil
}
func countSystems() (int, error) {
row := db.QueryRow(`select count(*) from planets`)
@ -261,35 +185,25 @@ func dist3d(x1, y1, z1, x2, y2, z2 float64) float64 {
return math.Sqrt(sq(x1-x2) + sq(y1-y2) + sq(z1-z2))
}
func indexSystems() map[int]*System {
rows, err := db.Query(`select * from planets`)
if err != nil {
log_error("unable to select all planets: %v", err)
return nil
}
defer rows.Close()
index = make(map[int]*System, 551)
nameIndex = make(map[string]*System, 551)
for rows.Next() {
p := System{}
if err := rows.Scan(&p.id, &p.name, &p.x, &p.y, &p.z, &p.planets); err != nil {
log_info("unable to scan planet row: %v", err)
continue
}
index[p.id] = &p
nameIndex[p.name] = &p
p.money = int64(rand.NormFloat64()*options.moneySigma + options.moneyMean)
log_info("seeded system %v with %v monies", p, p.money)
}
return index
}
func randomSystem() (*System, error) {
n := len(index)
if n == 0 {
return nil, fmt.Errorf("no planets are known to exist")
}
pick := rand.Intn(n)
sys := index[pick]
return sys, nil
}
// func indexSystems() map[int]*System {
// rows, err := db.Query(`select * from planets`)
// if err != nil {
// log_error("unable to select all planets: %v", err)
// return nil
// }
// defer rows.Close()
// index = make(map[int]*System, 551)
// nameIndex = make(map[string]*System, 551)
// for rows.Next() {
// p := System{}
// if err := rows.Scan(&p.id, &p.name, &p.x, &p.y, &p.z, &p.planets); err != nil {
// log_info("unable to scan planet row: %v", err)
// continue
// }
// index[p.id] = &p
// nameIndex[p.name] = &p
// p.money = int64(rand.NormFloat64()*options.moneySigma + options.moneyMean)
// // log_info("seeded system %v with %v monies", p, p.money)
// }
// return index
// }

@ -1,7 +1,9 @@
package main
import (
"bytes"
"fmt"
"text/template"
"time"
)
@ -20,39 +22,76 @@ func NewTravel(c *Connection, start, dest *System) ConnectionState {
dist: start.DistanceTo(dest),
}
t.CommandSuite = CommandSet{
helpCommand,
playersCommand,
balCommand,
Command{
name: "progress",
help: "displays how far you are along your travel",
summary: "displays how far you are along your travel",
arity: 0,
handler: t.progress,
},
Command{
name: "eta",
help: "displays estimated time of arrival",
summary: "displays estimated time of arrival",
arity: 0,
handler: func(c *Connection, args ...string) {
c.Printf("Remaining: %v\n", t.remaining())
c.Printf("Current time: %v\n", time.Now())
c.Printf("ETA: %v\n", t.eta())
c.Printf("%v\n", t.remaining())
},
},
}
return t
}
var enterTravelTemplate = template.Must(template.New("enter-travel").Parse(`
Departing: {{.Departing}}
Destination: {{.Destination}}
Total Trip Time: {{.Duration}}
`))
func (t *TravelState) Enter(c *Connection) {
c.Printf("Leaving %v, bound for %v.\n", t.start, t.dest)
c.Printf("Trip duration: %v\n", t.tripTime())
c.Printf("Current time: %v\n", time.Now())
c.Printf("ETA: %v\n", t.eta())
enterTravelTemplate.Execute(c, struct {
Departing *System
Destination *System
Duration time.Duration
}{
t.start,
t.dest,
t.tripTime(),
})
t.start.Leave(c)
}
func (t *TravelState) Tick(c *Connection, frame int64) ConnectionState {
t.travelled += options.playerSpeed * options.lightSpeed
dt := options.playerSpeed * options.lightSpeed
segmentLength := t.dist / 18
x := t.travelled
for x > segmentLength {
x -= segmentLength
}
if x < dt {
c.Printf("%v", t.start.name)
var buf bytes.Buffer
segment := int(t.travelled / t.dist * 18)
buf.WriteRune('|')
for i := 0; i < 18; i++ {
switch {
case i == segment:
buf.WriteRune('>')
case i == segment-1:
buf.WriteRune('=')
case i < segment:
buf.WriteRune('-')
default:
buf.WriteRune(' ')
}
}
buf.WriteRune('|')
c.Write(buf.Bytes())
c.Printf("at %v in %v\n", t.dest.name, t.remaining())
}
t.travelled += dt
if t.travelled >= t.dist {
return Idle(t.dest)
}
@ -68,6 +107,10 @@ func (t *TravelState) String() string {
return fmt.Sprintf("Traveling from %v to %v", t.start, t.dest)
}
func (t *TravelState) FillStatus(c *Connection, s *status) {
s.Location = fmt.Sprintf("between %s and %s", t.start, t.dest)
}
func (t *TravelState) progress(c *Connection, args ...string) {
c.Printf("%v\n", t.travelled/t.dist)
}

Loading…
Cancel
Save