Support connecting to FIBS via WebSocket proxy

This commit is contained in:
Trevor Slocum 2021-09-07 18:11:56 -07:00
parent 0c1c6ea6c3
commit 8618f9e674
7 changed files with 368 additions and 161 deletions

View File

@ -23,10 +23,14 @@ Run the following command to build a `boxcars` executable:
Run `~/go/bin/boxcars` to play.
## Android
### Android
*Coming soon*
## Gameplay notes
- To bear off a piece, drag it from your home on to the bar.
## Support
Please share issues and suggestions [here](https://code.rocketnine.space/tslocum/boxcars/issues).
@ -35,4 +39,5 @@ Please share issues and suggestions [here](https://code.rocketnine.space/tslocum
- [ebiten](https://github.com/hajimehoshi/ebiten) - 2D game engine
- [draw2d](https://github.com/llgcode/draw2d) - 2D shape drawing
- [kibodo](https://code.rocketnine.space/tslocum/kibodo) - On-screen keyboard
- [resize](https://github.com/nfnt/resize) - Image resizing

13
client_web.go Normal file
View File

@ -0,0 +1,13 @@
// +build js,wasm
package main
import (
"code.rocketnine.space/tslocum/fibs"
)
func init() {
fibs.DefaultProxyAddress = "wss://fibsproxy.rocketnine.space"
AutoWatch = true
}

View File

@ -17,6 +17,7 @@ import (
type stateUpdate struct {
from int
to int
s []string
v []int
}
@ -24,6 +25,8 @@ type board struct {
x, y int
w, h int
innerW, innerH int
op *ebiten.DrawImageOptions
backgroundImage *ebiten.Image
@ -47,13 +50,19 @@ type board struct {
overlapSize int
lastDirection int
V []int
s []string
v []int
moveQueue chan *stateUpdate
drawFrame chan bool
debug int // Print and draw debug information
Client *fibs.Client
dragX, dragY int
}
func NewBoard() *board {
@ -69,7 +78,8 @@ func NewBoard() *board {
},
spaces: make([][]*Sprite, 26),
spaceRects: make([][4]int, 26),
V: make([]int, 42),
s: make([]string, 2),
v: make([]int, 42),
moveQueue: make(chan *stateUpdate, 10),
drawFrame: make(chan bool, 10),
}
@ -270,9 +280,60 @@ func (b *board) draw(screen *ebiten.Image) {
b.op.GeoM.Translate(float64(b.x), float64(b.y))
screen.DrawImage(b.backgroundImage, b.op)
if b.debug == 2 {
b.iterateSpaces(func(space int) {
x, y, w, h := b.spaceRect(space)
spaceImage := ebiten.NewImage(w, h)
if space%2 == 0 {
spaceImage.Fill(color.RGBA{50, 50, 50, 150})
} else {
spaceImage.Fill(color.RGBA{255, 255, 255, 150})
}
x, y = b.offsetPosition(x, y)
b.op.GeoM.Reset()
b.op.GeoM.Translate(float64(x), float64(y))
screen.DrawImage(spaceImage, b.op)
})
}
drawSprite := func(sprite *Sprite) {
x, y := float64(sprite.x), float64(sprite.y)
if !sprite.toStart.IsZero() {
progress := float64(time.Since(sprite.toStart)) / float64(sprite.toTime)
if x == float64(sprite.toX) && y == float64(sprite.toY) {
sprite.toStart = time.Time{}
sprite.x = sprite.toX
sprite.y = sprite.toY
} else {
if x < float64(sprite.toX) {
x += (float64(sprite.toX) - x) * progress
if x > float64(sprite.toX) {
x = float64(sprite.toX)
}
} else if x > float64(sprite.toX) {
x -= (x - float64(sprite.toX)) * progress
if x < float64(sprite.toX) {
x = float64(sprite.toX)
}
}
if y < float64(sprite.toY) {
y += (float64(sprite.toY) - y) * progress
if y > float64(sprite.toY) {
y = float64(sprite.toY)
}
} else if y > float64(sprite.toY) {
y -= (y - float64(sprite.toY)) * progress
if y < float64(sprite.toY) {
y = float64(sprite.toY)
}
}
}
// Schedule another frame
ebiten.ScheduleFrame()
}
b.op.GeoM.Reset()
b.op.GeoM.Translate(float64(sprite.x), float64(sprite.y))
b.op.GeoM.Translate(x, y)
if sprite.colorWhite {
screen.DrawImage(imgCheckerWhite, b.op)
@ -314,6 +375,63 @@ func (b *board) draw(screen *ebiten.Image) {
}
})
// Draw opponent name and dice
borderSize := b.horizontalBorderSize
if borderSize > b.barWidth/2 {
borderSize = b.barWidth / 2
}
playerColor := color.White
opponentColor := color.Black
if b.v[fibs.StatePlayerColor] == -1 {
playerColor = color.Black
opponentColor = color.White
}
if b.v[fibs.StateOpponentDice1] > 0 {
label := fmt.Sprintf("%s %d %d", b.s[fibs.StateOpponentName], b.v[fibs.StateOpponentDice1], b.v[fibs.StateOpponentDice2])
bounds := text.BoundString(mplusNormalFont, label)
img := ebiten.NewImage(bounds.Dx()*2, bounds.Dy()*2)
text.Draw(img, label, mplusNormalFont, 0, bounds.Dy(), opponentColor)
x := ((b.innerW - borderSize) / 4) - (bounds.Dx() / 2)
y := (b.innerH / 2) - (bounds.Dy() / 2)
x, y = b.offsetPosition(x, y)
b.op.GeoM.Reset()
b.op.GeoM.Translate(float64(x), float64(y))
screen.DrawImage(img, b.op)
}
// Draw player name and dice
if b.v[fibs.StatePlayerDice1] > 0 {
label := fmt.Sprintf("%s %d %d", b.s[fibs.StatePlayerName], b.v[fibs.StatePlayerDice1], b.v[fibs.StatePlayerDice2])
bounds := text.BoundString(mplusNormalFont, label)
img := ebiten.NewImage(bounds.Dx()*2, bounds.Dy()*2)
text.Draw(img, label, mplusNormalFont, 0, bounds.Dy(), playerColor)
x := ((b.innerW / 4) * 3) - (bounds.Dx() / 2)
y := (b.innerH / 2) - (bounds.Dy() / 2)
x, y = b.offsetPosition(x, y)
b.op.GeoM.Reset()
b.op.GeoM.Translate(float64(x), float64(y))
screen.DrawImage(img, b.op)
}
/* TODO centering issue
img := ebiten.NewImage(115, 20)
img.Fill(color.White)
x := (((b.innerW - borderSize) / 2) / 2) - (115 / 2)
y := (b.innerH / 2) - (20 / 2)
x = (b.innerW - borderSize) / 4
x, y = b.offsetPosition(x, y)
b.op.GeoM.Reset()
b.op.GeoM.Translate(float64(x), float64(y))
screen.DrawImage(img, b.op)*/
// Draw moving sprite
if b.moving != nil {
drawSprite(b.moving)
@ -328,22 +446,14 @@ func (b *board) draw(screen *ebiten.Image) {
b.iterateSpaces(func(space int) {
x, y, w, h := b.spaceRect(space)
spaceImage := ebiten.NewImage(w, h)
if space%2 == 0 {
spaceImage.Fill(color.RGBA{0, 0, 0, 150})
} else {
spaceImage.Fill(color.RGBA{255, 255, 255, 150})
}
br := ""
if b.bottomRow(space) {
br = "B"
}
ebitenutil.DebugPrint(spaceImage, fmt.Sprintf(" %d %s", space, br))
x, y = b.offsetPosition(x, y)
b.op.GeoM.Reset()
b.op.GeoM.Scale(2, 2)
b.op.GeoM.Translate(float64(x), float64(y))
screen.DrawImage(spaceImage, b.op)
})
@ -390,6 +500,13 @@ func (b *board) setRect(x, y, w, h int) {
b.horizontalBorderSize = 0
}
borderSize := b.horizontalBorderSize
if borderSize > b.barWidth/2 {
borderSize = b.barWidth / 2
}
b.innerW = b.w - (b.horizontalBorderSize * 2)
b.innerH = b.h - (b.verticalBorderSize * 2)
loadAssets(b.spaceWidth)
for i := 0; i < b.Sprites.num; i++ {
@ -422,6 +539,8 @@ func (b *board) positionCheckers() {
s.x += (w - s.w) / 2
}
}
b.ScheduleFrame()
}
func (b *board) spriteAt(x, y int) *Sprite {
@ -447,21 +566,14 @@ func (b *board) spaceAt(x, y int) int {
return -1
}
// TODO move to fibs library
func (b *board) iterateSpaces(f func(space int)) {
if b.V[fibs.StateDirection] == 1 {
for space := 0; space <= 25; space++ {
f(space)
}
return
}
for space := 25; space >= 0; space-- {
for space := 0; space <= 25; space++ {
f(space)
}
}
func (b *board) translateSpace(space int) int {
if b.V[fibs.StateDirection] == -1 {
if b.v[fibs.StateDirection] == -1 {
// Spaces range from 24 - 1.
if space == 0 || space == 25 {
space = 25 - space
@ -526,7 +638,7 @@ func (b *board) bottomRow(space int) bool {
bottomStart := 1
bottomEnd := 12
bottomBar := 25
if b.V[fibs.StateDirection] == 1 {
if b.v[fibs.StateDirection] == 1 {
bottomStart = 13
bottomEnd = 24
bottomBar = 0
@ -562,12 +674,13 @@ func (b *board) stackSpaceRect(space int, stack int) (x, y, w, h int) {
return x, y, w, h
}
func (b *board) SetState(v []int) {
b.moveQueue <- &stateUpdate{-1, -1, v}
func (b *board) SetState(s []string, v []int) {
// TODO setstate include playernames, use string?
b.moveQueue <- &stateUpdate{-1, -1, s, v}
}
func (b *board) ProcessState() {
v := b.V
v := b.v
if b.lastDirection != v[fibs.StateDirection] {
b.setSpaceRects()
@ -578,21 +691,26 @@ func (b *board) ProcessState() {
b.spaces = make([][]*Sprite, 26)
for space := 0; space < 26; space++ {
spaceValue := v[fibs.StateBoardSpace0+space]
if spaceValue == 0 {
continue
}
white := spaceValue > 0
// TODO reverse bar spaces - always?
// TODO take direction into account
if spaceValue == 0 {
white = v[fibs.StatePlayerColor] == 1
}
abs := spaceValue
if abs < 0 {
abs *= -1
}
for i := 0; i < abs; i++ {
var preMovesTo = b.Client.Board.Premoveto[space]
var preMovesFrom = b.Client.Board.Premovefrom[space]
for i := 0; i < (abs+preMovesTo)-preMovesFrom; i++ {
s := b.newSprite(white)
if i >= abs {
s.colorWhite = v[fibs.StatePlayerColor] == 1
// TODO draw special premove overlay?
}
b.spaces[space] = append(b.spaces[space], s)
b.Sprites.sprites = append(b.Sprites.sprites, s)
}
@ -602,9 +720,13 @@ func (b *board) ProcessState() {
b.positionCheckers()
}
func (b *board) _movePiece(sprite *Sprite, from int, to int, speed int) {
moveSize := 1
moveDelay := time.Duration(1/speed) * time.Millisecond
func (b *board) _movePiece(sprite *Sprite, from int, to int, speed int, pause bool) {
//moveSize := 1
//moveDelay := time.Duration(1/speed) * time.Millisecond
moveTime := time.Second / time.Duration(speed)
pauseTime := 750 * time.Millisecond
b.moving = sprite
space := from
for {
@ -616,95 +738,76 @@ func (b *board) _movePiece(sprite *Sprite, from int, to int, speed int) {
space--
}
var diag bool
// Go to bar or home immediately
if from == 0 || from == 25 || to == 0 || to == 25 {
// TODO always enabled
if true || from == 0 || from == 25 || to == 0 || to == 25 {
space = to
diag = true
}
stack := len(b.spaces[space])
if stack == 1 && sprite.colorWhite != b.spaces[space][0].colorWhite {
stack = 0 // Hit
} else if space != to {
stack++
}
x, y, _, _ := b.stackSpaceRect(space, stack)
x, y = b.offsetPosition(x, y)
cy := y
if cy > sprite.y == b.bottomRow(space) {
if cy > sprite.y == b.bottomRow(space) && !diag {
cy = sprite.y
}
if sprite.x != x {
// Center
for {
if sprite.y == cy {
break
}
if sprite.y < cy {
sprite.y += moveSize
if sprite.y > cy {
sprite.y = cy
}
} else if sprite.y > cy {
sprite.y -= moveSize
if sprite.y < cy {
sprite.y = cy
}
}
b.ScheduleFrame()
time.Sleep(moveDelay)
if diag {
sprite.toX = x
sprite.toY = y
sprite.toTime = moveTime
sprite.toStart = time.Now()
ebiten.ScheduleFrame()
time.Sleep(sprite.toTime)
sprite.x = x
sprite.y = y
} else {
if sprite.y != cy {
sprite.toX = sprite.x
sprite.toY = cy
sprite.toTime = moveTime / 4
sprite.toStart = time.Now()
ebiten.ScheduleFrame()
time.Sleep(sprite.toTime)
sprite.toStart = time.Time{}
sprite.y = cy
}
for {
if sprite.x == x {
break
}
if sprite.x < x {
sprite.x += moveSize
if sprite.x > x {
sprite.x = x
}
} else if sprite.x > x {
sprite.x -= moveSize
if sprite.x < x {
sprite.x = x
}
}
b.ScheduleFrame()
time.Sleep(moveDelay / 2)
if sprite.x != x {
sprite.toX = x
sprite.toY = sprite.y
sprite.toTime = moveTime / 4
sprite.toStart = time.Now()
ebiten.ScheduleFrame()
time.Sleep(sprite.toTime)
sprite.toStart = time.Time{}
sprite.x = x
}
}
for {
if sprite.x == x && sprite.y == y {
break
if sprite.x != x || sprite.y != y {
sprite.toX = x
sprite.toY = y
sprite.toTime = moveTime / 4
sprite.toStart = time.Now()
ebiten.ScheduleFrame()
time.Sleep(sprite.toTime)
sprite.toStart = time.Time{}
sprite.x = x
sprite.y = y
}
if sprite.x < x {
sprite.x += moveSize
if sprite.x > x {
sprite.x = x
}
} else if sprite.x > x {
sprite.x -= moveSize
if sprite.x < x {
sprite.x = x
}
}
if sprite.y < y {
sprite.y += moveSize
if sprite.y > y {
sprite.y = y
}
} else if sprite.y > y {
sprite.y -= moveSize
if sprite.y < y {
sprite.y = y
}
}
b.ScheduleFrame()
time.Sleep(moveDelay)
}
}
// TODO do not add bear off pieces
b.spaces[to] = append(b.spaces[to], sprite)
for i, s := range b.spaces[from] {
if s == sprite {
@ -716,13 +819,41 @@ func (b *board) _movePiece(sprite *Sprite, from int, to int, speed int) {
b.ScheduleFrame()
time.Sleep(time.Second)
if !pause {
return
}
time.Sleep(pauseTime)
}
func (b *board) handlePieceMoves() {
for u := range b.moveQueue {
if u.from == -1 || u.to == -1 {
b.V = u.v
same := true
if len(b.s) == len(u.s) {
for i := range b.s {
if b.s[i] != u.s[i] {
same = false
break
}
}
} else {
same = false
}
if len(b.v) == len(u.v) {
for i := range b.v {
if b.v[i] != u.v[i] {
same = false
break
}
}
} else {
same = false
}
if same {
continue
}
b.s = u.s
b.v = u.v
b.ProcessState()
continue
}
@ -743,16 +874,16 @@ func (b *board) handlePieceMoves() {
}
}
b._movePiece(sprite, from, to, 1)
b._movePiece(sprite, from, to, 1, moveAfter == nil)
if moveAfter != nil {
toBar := 0
if b.V[fibs.StateTurn] == b.V[fibs.StatePlayerColor] {
toBar = 25 // TODO how is this determined?
if b.v[fibs.StateDirection] == -1 {
toBar = 25
}
if b.V[fibs.StateDirection] == -1 {
toBar = 25 - toBar
if b.v[fibs.StateTurn] != b.v[fibs.StatePlayerColor] {
toBar = 25 - toBar // TODO how is this determined?
}
b._movePiece(moveAfter, to, toBar, 2)
b._movePiece(moveAfter, to, toBar, 1, true)
}
b.positionCheckers()
@ -761,7 +892,7 @@ func (b *board) handlePieceMoves() {
// Do not call directly
func (b *board) movePiece(from int, to int) {
b.moveQueue <- &stateUpdate{from, to, nil}
b.moveQueue <- &stateUpdate{from, to, nil, nil}
}
func (b *board) update() {
@ -781,6 +912,9 @@ func (b *board) update() {
b.touchIDs = inpututil.AppendJustPressedTouchIDs(b.touchIDs[:0])
for _, id := range b.touchIDs {
x, y := ebiten.TouchPosition(id)
b.dragX, b.dragY = x, y
s := b.spriteAt(x, y)
if s != nil {
b.dragging = s
@ -792,6 +926,12 @@ func (b *board) update() {
x, y := ebiten.CursorPosition()
if b.dragTouchId != -1 {
x, y = ebiten.TouchPosition(b.dragTouchId)
if x != 0 || y != 0 { // 0,0 is returned when the touch is released
b.dragX, b.dragY = x, y
} else {
x, y = b.dragX, b.dragY
}
}
var dropped *Sprite
@ -810,12 +950,16 @@ func (b *board) update() {
index := b.spaceAt(x, y)
if index >= 0 {
for space, pieces := range b.spaces {
for stackIndex, piece := range pieces {
if piece == dropped {
b.spaces[space] = append(b.spaces[space][:stackIndex], b.spaces[space][stackIndex+1:]...)
b.spaces[index] = append(b.spaces[index], dropped)
break
if b.Client != nil {
for space, pieces := range b.spaces {
for _, piece := range pieces {
if piece == dropped {
if space != index {
b.Client.Board.AddPreMove(space, index)
b.ProcessState()
}
break
}
}
}
}

View File

@ -113,6 +113,10 @@ type Sprite struct {
h int
x int
y int
toStart time.Time
toTime time.Duration
toX int
toY int
colorWhite bool
}
@ -147,6 +151,7 @@ type Game struct {
screenW, screenH int
drawBuffer bytes.Buffer
lastDraw time.Time
spinnerIndex int
@ -157,6 +162,7 @@ type Game struct {
usernameConfirmed bool
Watch bool
TV bool
Client *fibs.Client
@ -200,8 +206,12 @@ func NewGame() *Game {
func (g *Game) handleEvents() {
for e := range g.Client.Event {
switch event := e.(type) {
case *fibs.EventBoardState:
g.Board.SetState(event.S, event.V)
case *fibs.EventMove:
g.Board.movePiece(event.From, event.To)
case *fibs.EventDraw:
g.Board.ProcessState()
}
}
}
@ -214,17 +224,21 @@ func (g *Game) Connect() {
address = defaultServerAddress
}
g.Client = fibs.NewClient(address, g.Username, g.Password)
g.Board.Client = g.Client
go g.handleEvents()
c := g.Client
if g.Watch {
if g.TV {
go func() {
time.Sleep(1 * time.Second)
c.WatchRandomGame()
go g.renderLoop()
time.Sleep(time.Second)
c.Out <- []byte("tv")
}()
} else if g.Watch {
go func() {
time.Sleep(time.Second)
c.Out <- []byte("watch")
}()
}
@ -236,15 +250,6 @@ func (g *Game) Connect() {
}()
}
func (g *Game) renderLoop() {
t := time.NewTicker(time.Second)
for range t.C {
gameBoard := g.Board
v := g.Client.Board.GetIntState()
gameBoard.SetState(v)
}
}
func (g *Game) leftTouched() bool {
for _, id := range g.touchIDs {
x, _ := ebiten.TouchPosition(id)
@ -322,7 +327,11 @@ func (g *Game) Update() error { // Called by ebiten only when input occurs
g.inputBuffer += string(input.Rune)
continue
}
if input.Key == ebiten.KeyEnter {
if input.Key == ebiten.KeyBackspace {
if len(g.inputBuffer) > 0 {
g.inputBuffer = g.inputBuffer[:len(g.inputBuffer)-1]
}
} else if input.Key == ebiten.KeyEnter {
g.inputBuffer += "\n"
}
}
@ -381,6 +390,12 @@ func (g *Game) Update() error { // Called by ebiten only when input occurs
}
func (g *Game) Draw(screen *ebiten.Image) {
frameTime := time.Second / 175
if time.Since(g.lastDraw) < frameTime {
time.Sleep(time.Until(g.lastDraw.Add(frameTime)))
}
g.lastDraw = time.Now()
screen.Fill(color.RGBA{0, 102, 51, 255})
// Log in screen

14
go.mod
View File

@ -3,24 +3,24 @@ module code.rocketnine.space/tslocum/boxcars
go 1.17
require (
code.rocketnine.space/tslocum/fibs v0.0.0-00010101000000-000000000000
code.rocketnine.space/tslocum/kibodo v0.0.0-20210830194839-05789279ce56
github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.12.0.20210828073710-0e5dca9453a5
github.com/llgcode/draw2d v0.0.0-20210313082411-577c1ead272a
code.rocketnine.space/tslocum/kibodo v0.0.0-20210908010225-5e1a355e9061
github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.13
github.com/llgcode/draw2d v0.0.0-20210904075650-80aa0a2a901d
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
)
require (
code.rocketnine.space/tslocum/fibs v0.0.0-20210908010953-ee35198ae238 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/klauspost/compress v1.13.5 // indirect
github.com/reiver/go-oi v1.0.0 // indirect
github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e // indirect
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 // indirect
golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55 // indirect
golang.org/x/text v0.3.7 // indirect
nhooyr.io/websocket v1.8.7 // indirect
)
replace code.rocketnine.space/tslocum/fibs => /home/trevor/programming/fibs

63
go.sum
View File

@ -1,17 +1,29 @@
code.rocketnine.space/tslocum/kibodo v0.0.0-20210830194839-05789279ce56 h1:+KVT4Zw9CEQkxdNP81wuTvX3EHRPpPUQFwn6UVLoMFY=
code.rocketnine.space/tslocum/kibodo v0.0.0-20210830194839-05789279ce56/go.mod h1:nWGK8LvmYgMZQcwGMYOOuZ19VsVYL5E1hREEJ2gV46M=
code.rocketnine.space/tslocum/fibs v0.0.0-20210908010953-ee35198ae238 h1:l6mAo+aReaHfL07SF+MWxOAmNVl1ShXhoWTe8HqI/+c=
code.rocketnine.space/tslocum/fibs v0.0.0-20210908010953-ee35198ae238/go.mod h1:f7Cz0zJWOnJ9DW7R/GTuE8z5XWQtkbXkKpksW9SDu2c=
code.rocketnine.space/tslocum/kibodo v0.0.0-20210908010225-5e1a355e9061/go.mod h1:AWfFB/VvYWk+Lugwdpsko9hq8eMmJIgJx2JFSpVewNw=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
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/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-gl/gl v0.0.0-20180407155706-68e253793080/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
github.com/go-gl/glfw v0.0.0-20180426074136-46a8d530c326 h1:QqWaXlVeUGwSH7hO8giZP2Y06Qjl1LWR+FWC22YQsU8=
github.com/go-gl/glfw v0.0.0-20180426074136-46a8d530c326/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be h1:vEIVIuBApEBQTEJt19GfhoU+zFSV+sNTa9E9FdnRYfk=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/hajimehoshi/bitmapfont/v2 v2.1.3 h1:JefUkL0M4nrdVwVq7MMZxSTh6mSxOylm+C4Anoucbb0=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hajimehoshi/bitmapfont/v2 v2.1.3/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs=
github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.12.0.20210828073710-0e5dca9453a5 h1:fUSKz2wvklV02UTmBXXDlNKc6molRGUu5O8b80AvEa4=
github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.12.0.20210828073710-0e5dca9453a5/go.mod h1:B4Cje+Kb1ZjztrKFPaiMWOGXO3Vp8u+zIBdxkZqkyD4=
github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.13/go.mod h1:44O6eBPGyRv8YctRbfzaqUH2sek5UdXh0aLWOP02ELI=
github.com/hajimehoshi/file2byteslice v0.0.0-20200812174855-0e5e8a80490e/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE=
github.com/hajimehoshi/go-mp3 v0.3.2/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
@ -19,35 +31,43 @@ github.com/hajimehoshi/oto/v2 v2.0.0-alpha.2/go.mod h1:rUKQmwMkqmRxe+IAof9+tuYA2
github.com/jakecoffman/cp v1.1.0/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg=
github.com/jfreymuth/oggvorbis v1.0.3/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII=
github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/llgcode/draw2d v0.0.0-20210313082411-577c1ead272a h1:gl2CmDrcVtYJY4YBKfNdpa4Igj3iNIBsaC3m8TQipYw=
github.com/llgcode/draw2d v0.0.0-20210313082411-577c1ead272a/go.mod h1:mVa0dA29Db2S4LVqDYLlsePDzRJLDfdhVZiI15uY0FA=
github.com/llgcode/ps v0.0.0-20150911083025-f1443b32eedb h1:61ndUreYSlWFeCY44JxDDkngVoI7/1MVhEl98Nm0KOk=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.13.5 h1:9O69jUPDcsT9fEm74W92rZL9FQY7rCdaXVneq+yyzl4=
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/llgcode/draw2d v0.0.0-20210904075650-80aa0a2a901d/go.mod h1:mVa0dA29Db2S4LVqDYLlsePDzRJLDfdhVZiI15uY0FA=
github.com/llgcode/ps v0.0.0-20150911083025-f1443b32eedb/go.mod h1:1l8ky+Ew27CMX29uG+a2hNOKpeNYEQjjtiALiBlFQbY=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/reiver/go-oi v1.0.0 h1:nvECWD7LF+vOs8leNGV/ww+F2iZKf3EYjYZ527turzM=
github.com/reiver/go-oi v1.0.0/go.mod h1:RrDBct90BAhoDTxB1fenZwfykqeGvhI6LsNfStJoEkI=
github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e h1:quuzZLi72kkJjl+f5AQ93FMcadG19WkS7MO6TXFOSas=
github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e/go.mod h1:+5vNVvEWwEIx86DB9Ke/+a5wBI464eDRo3eF0LcfpWg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 h1:3In5TnfvnuXTF/uflgpYxSCEGP2NdYT37KsPh3VjZYU=
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554/go.mod h1:jFTmtFYCV0MFtXBU+J5V/+5AUeVS0ON/0WkE/KSrl6E=
golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5/go.mod h1:c4YKU3ZylDmvbw+H/PSvm42vhdWbuxCzbonauEAP9B8=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -55,27 +75,34 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55 h1:rw6UNGRMfarCepjI8qOepea/SXwIBVfTKjztZ5gBbq4=
golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=

11
main.go
View File

@ -18,6 +18,8 @@ const (
screenHeight = 768
)
var AutoWatch bool // WASM only
func main() {
ebiten.SetWindowTitle("Boxcars")
ebiten.SetWindowSize(screenWidth, screenHeight)
@ -38,8 +40,13 @@ func main() {
flag.StringVar(&g.Password, "password", "", "Password")
flag.StringVar(&g.ServerAddress, "address", "fibs.com:4321", "Server address")
flag.BoolVar(&g.Watch, "watch", false, "Watch random game")
flag.BoolVar(&g.TV, "tv", false, "Watch random games continuously")
flag.Parse()
if AutoWatch {
g.Watch = true
}
// Auto-connect
if g.Username != "" && g.Password != "" {
g.Connect()
@ -60,10 +67,6 @@ func main() {
g.Client.Out <- append(scanner.Bytes())
}
if scanner.Err() != nil {
// TODO
}
}()
sigc := make(chan os.Signal, 1)