From 5ad36dbe69b204332eeaba7a562a3cca82e017d2 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 31 Oct 2020 15:37:03 +0000 Subject: [PATCH] working on a major refactor --- internal/app/game_view.go | 82 +++++++++++++++++++++++++++++++++++ internal/app/ui.go | 23 +++++----- internal/app/view.go | 73 ------------------------------- internal/math/vec.go | 26 +++++++++++ internal/server/session.go | 12 +---- internal/server/sim/entity.go | 16 +++---- internal/server/sim/player.go | 69 ++++++++++------------------- internal/server/sim/room.go | 4 +- internal/server/sim/tile.go | 2 +- internal/server/sim/world.go | 5 ++- internal/wire/entity.go | 15 +++++++ internal/wire/login.go | 11 +++++ internal/wire/welcome.go | 20 +++++++++ 13 files changed, 200 insertions(+), 158 deletions(-) create mode 100644 internal/app/game_view.go create mode 100644 internal/math/vec.go create mode 100644 internal/wire/login.go create mode 100644 internal/wire/welcome.go diff --git a/internal/app/game_view.go b/internal/app/game_view.go new file mode 100644 index 0000000..709814b --- /dev/null +++ b/internal/app/game_view.go @@ -0,0 +1,82 @@ +package app + +import ( + "github.com/gdamore/tcell/v2" + "github.com/jordanorelli/astro-domu/internal/server/sim" + "github.com/jordanorelli/astro-domu/internal/wire" + "github.com/jordanorelli/blammo" +) + +type gameView struct { + *blammo.Log + width int + height int + // entities map[int]wire.Entity +} + +func (v *gameView) handleEvent(ui *UI, e tcell.Event) bool { + switch t := e.(type) { + case *tcell.EventKey: + key := t.Key() + if key == tcell.KeyRune { + switch t.Rune() { + case 'w': + v.move(ui, 0, -1) + case 'a': + v.move(ui, -1, 0) + case 's': + v.move(ui, 0, 1) + case 'd': + v.move(ui, 1, 0) + } + } + default: + // ui.Debug("screen saw unhandled event of type %T", e) + } + return true +} + +func (v *gameView) notify(wv wire.Value) { + v.Error("ignoring notifications at the moment: %v", wv) + // if e, ok := v.(*wire.Entity); ok { + // m.entities[e.ID] = *e + // } +} + +func (v *gameView) move(ui *UI, dx, dy int) { + _, err := ui.client.Send(sim.Move{dx, dy}) + if err != nil { + return + } + // e := reply.Body.(*wire.Entity) + // v.entities[e.ID] = *e +} + +func (v *gameView) draw(ui *UI) { + offset := point{1, 1} + + // fill in background dots first + for x := 0; x < v.width; x++ { + for y := 0; y < v.height; y++ { + ui.screen.SetContent(x+offset.x, y+offset.y, '·', nil, tcell.StyleDefault) + } + } + + // frame it + ui.screen.SetContent(offset.x-1, offset.y-1, '┌', nil, tcell.StyleDefault) + ui.screen.SetContent(offset.x+v.width, offset.y-1, '┐', nil, tcell.StyleDefault) + ui.screen.SetContent(offset.x-1, offset.y+v.height, '└', nil, tcell.StyleDefault) + ui.screen.SetContent(offset.x+v.width, offset.y+v.height, '┘', nil, tcell.StyleDefault) + for x := 0; x < v.width; x++ { + ui.screen.SetContent(x+offset.x, offset.y-1, '─', nil, tcell.StyleDefault) + ui.screen.SetContent(x+offset.x, offset.y+v.height, '─', nil, tcell.StyleDefault) + } + for y := 0; y < v.height; y++ { + ui.screen.SetContent(offset.x-1, y+offset.y, '│', nil, tcell.StyleDefault) + ui.screen.SetContent(offset.x+v.width, y+offset.y, '│', nil, tcell.StyleDefault) + } + + // for _, e := range v.entities { + // ui.screen.SetContent(e.Position[0]+offset.x, e.Position[1]+offset.y, e.Glyph, nil, tcell.StyleDefault) + // } +} diff --git a/internal/app/ui.go b/internal/app/ui.go index 91a40e0..a355468 100644 --- a/internal/app/ui.go +++ b/internal/app/ui.go @@ -5,8 +5,6 @@ import ( "github.com/gdamore/tcell/v2" "github.com/jordanorelli/astro-domu/internal/exit" - "github.com/jordanorelli/astro-domu/internal/server" - "github.com/jordanorelli/astro-domu/internal/server/sim" "github.com/jordanorelli/astro-domu/internal/wire" "github.com/jordanorelli/blammo" ) @@ -27,27 +25,28 @@ func (ui *UI) Run() { return } - res, err := ui.client.Send(server.Login{Name: ui.PlayerName}) + res, err := ui.client.Send(wire.Login{Name: ui.PlayerName}) if err != nil { ui.Error("login error: %v", err) return } - welcome, ok := res.Body.(*sim.Welcome) + welcome, ok := res.Body.(*wire.Welcome) if !ok { ui.Error("unexpected initial message of type %t", res.Body) return } ui.Info("spawned into room %s", welcome.Room) - entities := make(map[int]sim.Entity, len(welcome.Contents)) - for _, e := range welcome.Contents { - entities[e.ID] = e - } - ui.view = &roomDisplay{ - width: welcome.Size[0], - height: welcome.Size[1], - entities: entities, + // entities := make(map[int]sim.Entity, len(welcome.Contents)) + // for _, e := range welcome.Contents { + // entities[e.ID] = e + // } + ui.view = &gameView{ + Log: ui.Child("game-view"), + width: welcome.Room.Width, + height: welcome.Room.Height, + // entities: entities, } ui.Info("running ui") if ui.handleUserInput() { diff --git a/internal/app/view.go b/internal/app/view.go index bc39911..65ae7c0 100644 --- a/internal/app/view.go +++ b/internal/app/view.go @@ -2,7 +2,6 @@ package app import ( "github.com/gdamore/tcell/v2" - "github.com/jordanorelli/astro-domu/internal/server/sim" "github.com/jordanorelli/astro-domu/internal/wire" ) @@ -11,75 +10,3 @@ type view interface { notify(wire.Value) draw(*UI) } - -type roomDisplay struct { - width int - height int - entities map[int]sim.Entity -} - -func (m *roomDisplay) handleEvent(ui *UI, e tcell.Event) bool { - switch v := e.(type) { - case *tcell.EventKey: - key := v.Key() - if key == tcell.KeyRune { - switch v.Rune() { - case 'w': - m.move(ui, 0, -1) - case 'a': - m.move(ui, -1, 0) - case 's': - m.move(ui, 0, 1) - case 'd': - m.move(ui, 1, 0) - } - } - default: - // ui.Debug("screen saw unhandled event of type %T", e) - } - return true -} - -func (m *roomDisplay) notify(v wire.Value) { - if e, ok := v.(*sim.Entity); ok { - m.entities[e.ID] = *e - } -} - -func (m *roomDisplay) move(ui *UI, dx, dy int) { - reply, err := ui.client.Send(sim.Move{dx, dy}) - if err != nil { - return - } - e := reply.Body.(*sim.Entity) - m.entities[e.ID] = *e -} - -func (m *roomDisplay) draw(ui *UI) { - offset := point{1, 1} - - // fill in background dots first - for x := 0; x < m.width; x++ { - for y := 0; y < m.height; y++ { - ui.screen.SetContent(x+offset.x, y+offset.y, '·', nil, tcell.StyleDefault) - } - } - - // frame it - ui.screen.SetContent(offset.x-1, offset.y-1, '┌', nil, tcell.StyleDefault) - ui.screen.SetContent(offset.x+m.width, offset.y-1, '┐', nil, tcell.StyleDefault) - ui.screen.SetContent(offset.x-1, offset.y+m.height, '└', nil, tcell.StyleDefault) - ui.screen.SetContent(offset.x+m.width, offset.y+m.height, '┘', nil, tcell.StyleDefault) - for x := 0; x < m.width; x++ { - ui.screen.SetContent(x+offset.x, offset.y-1, '─', nil, tcell.StyleDefault) - ui.screen.SetContent(x+offset.x, offset.y+m.height, '─', nil, tcell.StyleDefault) - } - for y := 0; y < m.height; y++ { - ui.screen.SetContent(offset.x-1, y+offset.y, '│', nil, tcell.StyleDefault) - ui.screen.SetContent(offset.x+m.width, y+offset.y, '│', nil, tcell.StyleDefault) - } - - for _, e := range m.entities { - ui.screen.SetContent(e.Position[0]+offset.x, e.Position[1]+offset.y, e.Glyph, nil, tcell.StyleDefault) - } -} diff --git a/internal/math/vec.go b/internal/math/vec.go new file mode 100644 index 0000000..0b439ee --- /dev/null +++ b/internal/math/vec.go @@ -0,0 +1,26 @@ +package math + +import ( + "encoding/json" +) + +type Vec struct { + X int + Y int +} + +func (v Vec) MarshalJSON() ([]byte, error) { + return json.Marshal([2]int{v.X, v.Y}) +} + +func (v *Vec) UnmarshalJSON(b []byte) error { + var raw [2]int + if err := json.Unmarshal(b, &raw); err != nil { + return err + } + v.X = raw[0] + v.Y = raw[1] + return nil +} + +func (v Vec) Add(v2 Vec) Vec { return Vec{v.X + v2.X, v.Y + v2.Y} } diff --git a/internal/server/session.go b/internal/server/session.go index ac35dd0..bd619a5 100644 --- a/internal/server/session.go +++ b/internal/server/session.go @@ -92,7 +92,7 @@ func (sn *session) read() { sn.Info("received message of type %T", req.Body) switch v := req.Body.(type) { - case *Login: + case *wire.Login: sn.Name = v.Name sn.world.Inbox <- sim.Request{ From: sn.Name, @@ -141,13 +141,3 @@ func (sn *session) sendResponse(res wire.Response) error { sn.Child("sent-frame").Info(string(payload)) return nil } - -type Login struct { - Name string `json:"name"` -} - -func (Login) NetTag() string { return "login" } - -func init() { - wire.Register(func() wire.Value { return new(Login) }) -} diff --git a/internal/server/sim/entity.go b/internal/server/sim/entity.go index fd1fa70..71c5ab7 100644 --- a/internal/server/sim/entity.go +++ b/internal/server/sim/entity.go @@ -3,22 +3,16 @@ package sim import ( "time" - "github.com/jordanorelli/astro-domu/internal/wire" + "github.com/jordanorelli/astro-domu/internal/math" ) -type Entity struct { - ID int `json:"id"` - Position [2]int `json:"pos"` - Glyph rune `json:"glyph"` +type entity struct { + ID int `json:"id"` + Position math.Vec `json:"pos"` + Glyph rune `json:"glyph"` behavior } -func (Entity) NetTag() string { return "entity" } - -func init() { - wire.Register(func() wire.Value { return new(Entity) }) -} - type behavior interface { // update is the standard tick function update(time.Duration) diff --git a/internal/server/sim/player.go b/internal/server/sim/player.go index 7faddd4..c90f800 100644 --- a/internal/server/sim/player.go +++ b/internal/server/sim/player.go @@ -1,6 +1,7 @@ package sim import ( + "github.com/jordanorelli/astro-domu/internal/math" "github.com/jordanorelli/astro-domu/internal/wire" "github.com/jordanorelli/blammo" ) @@ -12,31 +13,35 @@ type player struct { name string outbox chan wire.Response pending []Request - entity *Entity + entity *entity } -type Move [2]int +type Move math.Vec func (Move) NetTag() string { return "move" } func (m *Move) exec(r *room, p *player, seq int) result { pos := p.entity.Position - target := [2]int{pos[0] + m[0], pos[1] + m[1]} + target := pos.Add(math.Vec(*m)) p.Info("running move for player %s from %v to %v", p.name, *m, target) - if target[0] >= r.width || target[0] < 0 { - return result{reply: wire.Errorf("target cell (%d, %d) is out of bounds", target[0], target[1])} + if target.X >= r.width || target.X < 0 { + return result{reply: wire.Errorf("target cell (%d, %d) is out of bounds", target.X, target.Y)} } - if target[1] >= r.height || target[1] < 0 { - return result{reply: wire.Errorf("target cell (%d, %d) is out of bounds", target[0], target[1])} + if target.Y >= r.height || target.Y < 0 { + return result{reply: wire.Errorf("target cell (%d, %d) is out of bounds", target.X, target.Y)} } - n := target[1]*r.width + target[0] + n := target.X*r.width + target.Y if r.tiles[n].here != nil { - return result{reply: wire.Errorf("target cell (%d, %d) is occupied", target[0], target[1])} + return result{reply: wire.Errorf("target cell (%d, %d) is occupied", target.X, target.Y)} } - r.tiles[p.entity.Position[1]*r.width+p.entity.Position[0]].here = nil + r.tiles[p.entity.Position.X*r.width+p.entity.Position.Y].here = nil p.entity.Position = target r.tiles[n].here = p.entity - return result{reply: p.entity, announce: p.entity} + e := wire.Entity{ + Position: p.entity.Position, + Glyph: '@', + } + return result{reply: e, announce: e} } // SpawnPlayer is a request to spawn a player @@ -64,9 +69,9 @@ func (s *SpawnPlayer) exec(r *room, _ *player, seq int) result { name: s.Name, outbox: s.Outbox, pending: make([]Request, 0, 32), - entity: &Entity{ + entity: &entity{ ID: lastEntityID, - Position: [2]int{0, 0}, + Position: math.Vec{0, 0}, Glyph: '@', behavior: doNothing{}, }, @@ -78,13 +83,11 @@ func (s *SpawnPlayer) exec(r *room, _ *player, seq int) result { return result{} } - return result{ - reply: Welcome{ - Room: r.name, - Size: [2]int{r.width, r.height}, - Contents: r.allEntities(), - }, - } + var welcome wire.Welcome + welcome.Room.Width = r.width + welcome.Room.Height = r.height + welcome.Room.Origin = math.Vec{0, 0} + return result{reply: welcome} } func (SpawnPlayer) NetTag() string { return "player/spawn" } @@ -97,33 +100,7 @@ type PlayerSpawned struct { func (PlayerSpawned) NetTag() string { return "player/spawned" } -type Welcome struct { - Room string `json:"room"` - Size [2]int `json:"size"` - Contents []Entity `json:"contents"` -} - -/* - -{ - "name": "foyer", - "width": 10, - "height": 10, - "contents": [ - [5, 3, 10], - ], - "entities": [ - [3, "pawn", {"name": "bones"}], - [10, "pawn", {"name": "steve"}] - ] -} - -*/ - -func (Welcome) NetTag() string { return "player/welcome" } - func init() { wire.Register(func() wire.Value { return new(Move) }) - wire.Register(func() wire.Value { return new(Welcome) }) // wire.Register(func() wire.Value { return new(pawn) }) } diff --git a/internal/server/sim/room.go b/internal/server/sim/room.go index 67199cd..bf403e4 100644 --- a/internal/server/sim/room.go +++ b/internal/server/sim/room.go @@ -39,8 +39,8 @@ func (r *room) update(dt time.Duration) { } } -func (r *room) allEntities() []Entity { - all := make([]Entity, 0, 4) +func (r *room) allEntities() []entity { + all := make([]entity, 0, 4) for _, t := range r.tiles { if t.here != nil { all = append(all, *t.here) diff --git a/internal/server/sim/tile.go b/internal/server/sim/tile.go index 6337cbe..1e5ad41 100644 --- a/internal/server/sim/tile.go +++ b/internal/server/sim/tile.go @@ -2,5 +2,5 @@ package sim type tile struct { floor floor - here *Entity + here *entity } diff --git a/internal/server/sim/world.go b/internal/server/sim/world.go index 1be7e86..99dfef3 100644 --- a/internal/server/sim/world.go +++ b/internal/server/sim/world.go @@ -3,6 +3,7 @@ package sim import ( "time" + "github.com/jordanorelli/astro-domu/internal/math" "github.com/jordanorelli/astro-domu/internal/wire" "github.com/jordanorelli/blammo" ) @@ -28,9 +29,9 @@ func NewWorld(log *blammo.Log) *World { tiles: make([]tile, 100), players: make(map[string]*player), } - foyer.tiles[55].here = &Entity{ + foyer.tiles[55].here = &entity{ ID: 777, - Position: [2]int{5, 5}, + Position: math.Vec{5, 5}, Glyph: 'd', behavior: doNothing{}, } diff --git a/internal/wire/entity.go b/internal/wire/entity.go index 4048932..bded82b 100644 --- a/internal/wire/entity.go +++ b/internal/wire/entity.go @@ -1 +1,16 @@ package wire + +import ( + "github.com/jordanorelli/astro-domu/internal/math" +) + +type Entity struct { + Position math.Vec `json:"position"` + Glyph rune `json:"glyph"` +} + +func (Entity) NetTag() string { return "entity" } + +func init() { + Register(func() Value { return new(Entity) }) +} diff --git a/internal/wire/login.go b/internal/wire/login.go new file mode 100644 index 0000000..c685b75 --- /dev/null +++ b/internal/wire/login.go @@ -0,0 +1,11 @@ +package wire + +type Login struct { + Name string `json:"name"` +} + +func (Login) NetTag() string { return "login" } + +func init() { + Register(func() Value { return new(Login) }) +} diff --git a/internal/wire/welcome.go b/internal/wire/welcome.go new file mode 100644 index 0000000..c936b49 --- /dev/null +++ b/internal/wire/welcome.go @@ -0,0 +1,20 @@ +package wire + +import ( + "github.com/jordanorelli/astro-domu/internal/math" +) + +type Welcome struct { + Room struct { + Origin math.Vec `json:"origin"` + Width int `json:"width"` + Height int `json:"height"` + } `json:"room"` + Entities map[int]Entity `json:"entities"` +} + +func (Welcome) NetTag() string { return "welcome" } + +func init() { + Register(func() Value { return new(Welcome) }) +}