login promptingggggggg

master
Jordan Orelli 4 years ago
parent f7b044d428
commit 30e47e6f7f

@ -13,8 +13,11 @@ type buffer struct {
}
func newBuffer(width, height int) *buffer {
b := &buffer{width: width, height: height}
b.clear()
b := &buffer{
width: width,
height: height,
tiles: make([]tile, width*height),
}
return b
}
@ -43,7 +46,11 @@ func (b *buffer) writeString(s string, start math.Vec, style tcell.Style) {
}
}
func (b *buffer) clear() { b.tiles = make([]tile, b.width*b.height) }
func (b *buffer) clear() {
for i, _ := range b.tiles {
b.tiles[i] = tile{}
}
}
func (b *buffer) blit(s tcell.Screen, offset math.Vec) {
for x := 0; x < b.width; x++ {

@ -0,0 +1,35 @@
package app
import (
"github.com/gdamore/tcell/v2"
"github.com/jordanorelli/astro-domu/internal/math"
)
type form struct {
fields []textField
active int
}
func (f *form) handleEvent(e tcell.Event) change {
switch t := e.(type) {
case *tcell.EventKey:
key := t.Key()
switch key {
case tcell.KeyEnter:
return login{name: f.fields[0].textInput.entered}
}
}
return f.fields[0].handleEvent(e)
}
func (f *form) draw(b *buffer, _ *state) {
for i, field := range f.fields {
b.writeString(field.label, math.Vec{0, i * 2}, tcell.StyleDefault)
b.writeString(field.prompt+field.entered, math.Vec{0, i*2 + 1}, tcell.StyleDefault)
}
}
type textField struct {
label string
textInput
}

@ -3,79 +3,71 @@ package app
import (
"github.com/gdamore/tcell/v2"
"github.com/jordanorelli/astro-domu/internal/sim"
"github.com/jordanorelli/astro-domu/internal/wire"
"github.com/jordanorelli/blammo"
)
type gameView struct {
*blammo.Log
room *wire.Room
me *wire.Entity
inFocus bool
}
func (v *gameView) handleEvent(ui *UI, e tcell.Event) bool {
func (v *gameView) handleEvent(e tcell.Event) change {
switch t := e.(type) {
case *tcell.EventKey:
key := t.Key()
if key == tcell.KeyRune {
switch t.Rune() {
case 'w':
v.move(ui, 0, -1)
return move{0, -1}
case 'a':
v.move(ui, -1, 0)
return move{-1, 0}
case 's':
v.move(ui, 0, 1)
return move{0, 1}
case 'd':
v.move(ui, 1, 0)
return move{1, 0}
}
}
default:
// ui.Debug("screen saw unhandled event of type %T", e)
}
return true
return nil
}
func (v *gameView) move(ui *UI, dx, dy int) {
// fuck lol
go ui.client.Send(sim.Move{dx, dy})
}
func (v *gameView) draw(b *buffer) {
v.drawHeader(b)
func (v *gameView) draw(b *buffer, st *state) {
v.drawHeader(b, st)
// fill in background dots first
for x := 0; x < v.room.Width; x++ {
for y := 0; y < v.room.Height; y++ {
for x := 0; x < st.room.Width; x++ {
for y := 0; y < st.room.Height; y++ {
b.set(x+1, y+2, tile{r: '·', style: tcell.StyleDefault})
}
}
b.set(0, 1, tile{r: '┌'})
b.set(v.room.Width+1, 1, tile{r: '┐'})
b.set(0, v.room.Height+2, tile{r: '└'})
b.set(v.room.Width+1, v.room.Height+2, tile{r: '┘'})
for x := 0; x < v.room.Width; x++ {
b.set(st.room.Width+1, 1, tile{r: '┐'})
b.set(0, st.room.Height+2, tile{r: '└'})
b.set(st.room.Width+1, st.room.Height+2, tile{r: '┘'})
for x := 0; x < st.room.Width; x++ {
b.set(x+1, 1, tile{r: '─'})
b.set(x+1, v.room.Height+2, tile{r: '─'})
b.set(x+1, st.room.Height+2, tile{r: '─'})
}
for y := 0; y < v.room.Height; y++ {
for y := 0; y < st.room.Height; y++ {
b.set(0, y+2, tile{r: '│'})
b.set(v.room.Width+1, y+2, tile{r: '│'})
b.set(st.room.Width+1, y+2, tile{r: '│'})
}
for _, e := range v.room.Entities {
for _, e := range st.room.Entities {
pos := e.Position
b.set(pos.X+1, pos.Y+2, tile{r: e.Glyph, style: tcell.StyleDefault})
}
}
func (v *gameView) drawHeader(b *buffer) {
func (v *gameView) drawHeader(b *buffer, st *state) {
// the first row is the name of the room that we're currently in
var style tcell.Style
style = style.Background(tcell.NewRGBColor(64, 64, 128))
style = style.Foreground(tcell.NewRGBColor(0, 0, 0))
runes := []rune(v.room.Name)
runes := []rune(st.room.Name)
for i := 0; i < b.width; i++ {
if i < len(runes) {
@ -87,3 +79,12 @@ func (v *gameView) drawHeader(b *buffer) {
}
func (v *gameView) setFocus(yes bool) { v.inFocus = yes }
type move struct {
x int
y int
}
func (m move) exec(ui *UI) {
ui.client.Send(sim.Move{m.x, m.y})
}

@ -0,0 +1,46 @@
package app
import (
"github.com/jordanorelli/astro-domu/internal/wire"
)
type login struct {
name string
}
func (l login) exec(ui *UI) {
ui.client = &wire.Client{
Log: ui.Child("client"),
Host: "cdm.jordanorelli.com",
Port: 12805,
}
n, err := ui.client.Dial()
if err != nil {
panic("unable to dial server: " + err.Error())
}
ui.notifications = n
res, err := ui.client.Send(wire.Login{Name: l.name})
if err != nil {
panic("unable to login: " + err.Error())
}
welcome := res.Body.(*wire.Welcome)
ui.Info("cool beans! a login response: %#v", welcome)
ui.state.playerName = l.name
if ui.state.room == nil {
ui.state.room = new(wire.Room)
}
p := welcome.Players[l.name]
// avi := p.Avatar
room := welcome.Rooms[p.Room]
// e := room.Entities[p.Avatar]
ui.state.room = &room
ui.root = &node{
view: &gameView{
Log: ui.Child("game-view"),
},
}
ui.Info("done logging in, we replaced the root view whaduheck")
}

@ -3,43 +3,48 @@ package app
import (
"github.com/gdamore/tcell/v2"
"github.com/jordanorelli/astro-domu/internal/math"
"github.com/jordanorelli/blammo"
)
type menuList struct {
*blammo.Log
choices []string
selected int
choices []menuItem
highlight int
}
func (m *menuList) handleEvent(ui *UI, e tcell.Event) bool {
func (m *menuList) handleEvent(e tcell.Event) change {
switch t := e.(type) {
case *tcell.EventKey:
key := t.Key()
switch key {
case tcell.KeyEnter:
return m.choices[m.highlight].onSelect
case tcell.KeyDown:
m.selected = (m.selected + 1) % len(m.choices)
m.highlight = (m.highlight + 1) % len(m.choices)
return nil
case tcell.KeyUp:
if m.selected == 0 {
m.selected = len(m.choices) - 1
if m.highlight == 0 {
m.highlight = len(m.choices) - 1
} else {
m.selected--
m.highlight--
}
return nil
}
default:
// ui.Debug("screen saw unhandled event of type %T", e)
}
return true
return nil
}
func (m *menuList) draw(b *buffer) {
func (m *menuList) draw(b *buffer, _ *state) {
for i, choice := range m.choices {
if i == m.selected {
b.writeString("▷ "+choice, math.Vec{0, i}, tcell.StyleDefault)
if i == m.highlight {
b.writeString("▷ "+choice.name, math.Vec{0, i}, tcell.StyleDefault)
} else {
b.writeString(" "+choice, math.Vec{0, i}, tcell.StyleDefault)
b.writeString(" "+choice.name, math.Vec{0, i}, tcell.StyleDefault)
}
}
}
func (m *menuList) setFocus(bool) {}
type menuItem struct {
name string
onSelect change
}

@ -0,0 +1,6 @@
package app
type node struct {
view
children []*node
}

@ -0,0 +1,8 @@
package app
import "github.com/jordanorelli/astro-domu/internal/wire"
type state struct {
playerName string
room *wire.Room
}

@ -0,0 +1,41 @@
package app
import (
"fmt"
"github.com/gdamore/tcell/v2"
"github.com/jordanorelli/astro-domu/internal/math"
)
type textInput struct {
prompt string
entered string
}
func (t *textInput) handleEvent(e tcell.Event) change {
switch v := e.(type) {
case *tcell.EventKey:
key := v.Key()
if key == tcell.KeyBackspace || key == tcell.KeyBackspace2 {
line := []rune(t.entered)
if len(line) > 0 {
line = line[:len(line)-1]
}
t.entered = string(line)
break
}
if key == tcell.KeyRune {
t.entered = fmt.Sprintf("%s%c", t.entered, v.Rune())
}
default:
// ui.Debug("screen saw unhandled event of type %T", e)
}
return nil
}
func (t *textInput) draw(b *buffer, _ *state) {
b.writeString(t.prompt+t.entered, math.Vec{0, 0}, tcell.StyleDefault)
}

@ -1,7 +1,7 @@
package app
import (
"fmt"
"time"
"github.com/gdamore/tcell/v2"
"github.com/jordanorelli/astro-domu/internal/exit"
@ -13,87 +13,58 @@ import (
type UI struct {
*blammo.Log
PlayerName string
screen tcell.Screen
room *wire.Room
client *wire.Client
notifications <-chan wire.Response
statusBar *statusBar
gameView *gameView
testList *menuList
chatView *chatView
focussed view
state state
root *node
}
func (ui *UI) Run() {
ui.setupTerminal()
defer ui.clearTerminal()
ui.room = new(wire.Room)
ui.root = mainMenu
if err := ui.connect(); err != nil {
return
}
input := make(chan tcell.Event)
go ui.pollInput(input)
res, err := ui.client.Send(wire.Login{Name: ui.PlayerName})
if err != nil {
ui.Error("login error: %v", err)
return
}
tick := time.Tick(time.Second / time.Duration(30))
welcome, ok := res.Body.(*wire.Welcome)
if !ok {
ui.Error("unexpected initial message of type %t", res.Body)
width, height := ui.screen.Size()
b := newBuffer(width, height)
// b, prev := newBuffer(width, height), newBuffer(width, height)
for {
notify := ui.notifications
select {
case e := <-input:
switch v := e.(type) {
case *tcell.EventKey:
key := v.Key()
if key == tcell.KeyCtrlC {
ui.Info("saw ctrl+c keyboard input, shutting down")
return
}
ui.Info("welcome: %v", welcome)
meta := welcome.Players[ui.PlayerName]
room := welcome.Rooms[meta.Room]
ui.room = &room
ui.gameView = &gameView{
Log: ui.Child("game-view"),
room: &room,
me: &wire.Entity{
ID: meta.Avatar,
Glyph: room.Entities[meta.Avatar].Glyph,
Position: room.Entities[meta.Avatar].Position,
},
}
ui.chatView = &chatView{
Log: ui.Child("chat-view"),
history: make([]sim.ChatMessage, 0, 32),
}
ui.statusBar = &statusBar{}
ui.testList = &menuList{
Log: ui.Child("menu-list"),
choices: []string{"apple", "banana", "orange"},
ui.Info("input event: %v", e)
wants := ui.root.handleEvent(e)
if wants != nil {
wants.exec(ui)
}
ui.focussed = ui.gameView
ui.Info("running ui")
if ui.handleUserInput() {
ui.Info("user requested close")
ui.Info("closing client")
ui.client.Close()
ui.Info("client closed")
ui.Info("finalizing screen")
}
ui.Info("run loop done, shutting down")
}
func (ui *UI) connect() error {
ui.client = &wire.Client{
Log: ui.Child("client"),
Host: "cdm.jordanorelli.com",
Port: 12805,
case <-tick:
b.clear()
ui.root.draw(b, &ui.state)
b.blit(ui.screen, math.Vec{0, 0})
ui.screen.Show()
case n := <-notify:
ui.Info("notification: %v", n)
ui.handleNotification(n.Body)
}
c, err := ui.client.Dial()
if err != nil {
return fmt.Errorf("unable to dial server: %v", err)
}
go ui.handleNotifications(c)
return nil
}
func (ui *UI) setupTerminal() {
@ -116,63 +87,45 @@ func (ui *UI) setupTerminal() {
}
func (ui *UI) clearTerminal() {
ui.statusBar.clearCount++
ui.screen.Clear()
ui.screen.Fini()
}
func (ui *UI) handleNotifications(c <-chan wire.Response) {
for n := range c {
ui.statusBar.msgCount++
if ui.handleNotification(n.Body) {
if ui.gameView != nil {
ui.render()
}
}
}
ui.Info("notifications channel is closed so we must be done")
ui.Info("clearing and finalizing screen from notifications goroutine")
ui.statusBar.clearCount++
ui.screen.Clear()
ui.screen.Fini()
ui.Info("screen finalized")
}
func (ui *UI) handleNotification(v wire.Value) bool {
switch n := v.(type) {
case *wire.Entity:
ui.room.Entities[n.ID] = *n
ui.state.room.Entities[n.ID] = *n
return true
case *wire.Frame:
if ui.room == nil {
ui.room = new(wire.Room)
if ui.state.room == nil {
ui.state.room = new(wire.Room)
}
ui.room.Name = n.RoomName
ui.room.Rect = n.RoomSize
ui.room.Entities = n.Entities
ui.state.room.Name = n.RoomName
ui.state.room.Rect = n.RoomSize
ui.state.room.Entities = n.Entities
return true
case *wire.Delta:
if n.RoomSize != nil {
ui.room.Rect = *n.RoomSize
ui.state.room.Rect = *n.RoomSize
}
if len(n.Entities) > 0 {
for id, e := range n.Entities {
if e != nil {
ui.room.Entities[id] = *e
ui.state.room.Entities[id] = *e
} else {
delete(ui.room.Entities, id)
delete(ui.state.room.Entities, id)
}
}
}
return true
case *sim.ChatMessage:
ui.chatView.history = append(ui.chatView.history, *n)
// ui.chatView.history = append(ui.chatView.history, *n)
return true
default:
@ -198,79 +151,47 @@ func (ui *UI) writeString(x, y int, s string, style tcell.Style) {
}
}
func (ui *UI) handleUserInput() bool {
ui.statusBar.clearCount++
ui.screen.Clear()
ui.render()
func (ui *UI) pollInput(c chan tcell.Event) {
defer close(c)
for {
e := ui.screen.PollEvent()
if e == nil {
ui.Info("run loop sees nil event, breaking out")
// someone else shut us down, so return false
return false
}
switch v := e.(type) {
case *tcell.EventKey:
key := v.Key()
if key == tcell.KeyCtrlC {
ui.Info("saw ctrl+c keyboard input, shutting down")
// we want to shut things down
return true
}
if key == tcell.KeyTab {
ui.Info("saw tab from keyboard input, switching focussed view")
ui.focussed.setFocus(false)
switch ui.focussed {
case ui.gameView:
ui.focussed = ui.chatView
case ui.chatView:
ui.focussed = ui.testList
case ui.testList:
ui.focussed = ui.gameView
}
ui.focussed.setFocus(true)
goto HANDLED
}
}
ui.focussed.handleEvent(ui, e)
ui.statusBar.clearCount++
ui.screen.Clear()
ui.render()
ui.statusBar.showCount++
ui.screen.Show()
HANDLED:
return
}
c <- e
}
func (ui *UI) render() {
width, height := ui.screen.Size()
{
b := newBuffer(width, 1)
ui.statusBar.draw(b)
b.blit(ui.screen, math.Vec{0, 0})
}
gameViewHeight := math.Max((height-1)/2, 8)
{
b := newBuffer(width/2, gameViewHeight)
ui.gameView.draw(b)
b.blit(ui.screen, math.Vec{0, 1})
}
{
b := newBuffer(width/2, gameViewHeight)
ui.testList.draw(b)
b.blit(ui.screen, math.Vec{width / 2, 1})
}
{
b := newBuffer(width, height-gameViewHeight-1)
ui.chatView.draw(b)
b.blit(ui.screen, math.Vec{0, gameViewHeight + 1})
var mainMenu = &node{
view: &menuList{
choices: []menuItem{
menuItem{
name: "join",
onSelect: changeFn(func(ui *UI) {
ui.root = joinForm
}),
},
menuItem{
name: "exit",
onSelect: changeFn(func(ui *UI) {
panic("this is bad programming")
}),
},
},
},
}
ui.statusBar.showCount++
ui.screen.Show()
var joinForm = &node{
view: &form{
fields: []textField{
textField{
label: "What is your name?",
textInput: textInput{
prompt: "> ",
},
},
},
},
}

@ -5,7 +5,18 @@ import (
)
type view interface {
handleEvent(*UI, tcell.Event) bool
draw(*buffer)
handleEvent(tcell.Event) change
draw(*buffer, *state)
}
type focusable interface {
setFocus(bool)
}
type change interface {
exec(*UI)
}
type changeFn func(*UI)
func (f changeFn) exec(ui *UI) { f(ui) }

@ -224,7 +224,7 @@ func (w *world) register(c connect) {
outbox: make(chan wire.Response, 8),
pending: &Request{
From: c.login.Name,
Seq: 1,
Seq: 90,
Wants: &spawnPlayer{},
},
}

@ -164,7 +164,7 @@ func (c *Client) writeLoop() {
case res := <-c.resolved:
p, ok := sent[res.Re]
if !ok {
c.Error("saw response for unknown seq %d")
c.Error("saw response for unknown seq %d", res.Re)
break
}
delete(sent, res.Re)

@ -35,7 +35,7 @@ func main() {
switch os.Args[1] {
case "client":
runClient(os.Args[2])
runClient()
case "server":
s := sim.Server{}
if err := s.Start("cdm.jordanorelli.com", 12805); err != nil {
@ -51,7 +51,7 @@ func main() {
}
}
func runClient(name string) {
func runClient() {
log := newLog("./astro.log").Child("client")
start := time.Now()
@ -64,7 +64,6 @@ func runClient(name string) {
app := app.UI{
Log: log.Child("ui"),
PlayerName: name,
}
app.Run()
}

Loading…
Cancel
Save