master
Jordan Orelli 4 years ago
parent 55d638c7f0
commit b1cc63e471

@ -98,6 +98,7 @@ func (sn *session) read() {
From: sn.Name,
Seq: req.Seq,
Wants: &sim.SpawnPlayer{
Name: sn.Name,
Outbox: sn.outbox,
},
}

@ -4,7 +4,7 @@ import "github.com/jordanorelli/astro-domu/internal/wire"
type Effect interface {
wire.Value
exec(*room, string, int) result
exec(*room, *player, int) result
}
type result struct {

@ -1,11 +1,17 @@
package sim
import "time"
import (
"time"
)
// entity is any entity that can be simulated.
type entity interface {
id() int
type Entity struct {
ID int `json:"id"`
Position [2]int `json:"pos"`
Glyph rune `json:"glyph"`
behavior
}
type behavior interface {
// update is the standard tick function
update(time.Duration)
}

@ -1,65 +1,85 @@
package sim
import (
"time"
"github.com/jordanorelli/astro-domu/internal/wire"
"github.com/jordanorelli/blammo"
)
// player represents a player character in the simulation
// player is a player session in the simulation, eek
type player struct {
*blammo.Log
*room
name string
outbox chan wire.Response
sessionID int
entityID int
pending []Request
entity *Entity
}
func (p *player) update(dt time.Duration) {}
func (p *player) id() int { return p.entityID }
type Move [2]int
func (Move) NetTag() string { return "move" }
func (m *Move) exec(r *room, from *player, seq int) {
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]}
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[1] >= r.height || target[1] < 0 {
return result{reply: wire.Errorf("target cell (%d, %d) is out of bounds", target[0], target[1])}
}
n := target[1]*r.width + target[0]
if r.tiles[n].here != nil {
return result{reply: wire.Errorf("target cell (%d, %d) is occupied", target[0], target[1])}
}
p.entity.Position = target
return result{reply: wire.OK{}}
}
// type pawn struct {
// }
// SpawnPlayer is a request to spawn a player
type SpawnPlayer struct {
Outbox chan wire.Response
Name string
queued bool
}
func (s *SpawnPlayer) exec(r *room, from string, seq int) result {
func (s *SpawnPlayer) exec(r *room, _ *player, seq int) result {
if !s.queued {
r.Info("spawn player requested for: %s", from)
r.Info("spawn player requested for: %s", s.Name)
if _, ok := r.players[from]; ok {
s.Outbox <- wire.ErrorResponse(seq, "a player is already logged in as %q", from)
if _, ok := r.players[s.Name]; ok {
s.Outbox <- wire.ErrorResponse(seq, "a player is already logged in as %q", s.Name)
return result{}
}
p := &player{
Log: r.Log.Child("players").Child(from),
Log: r.Log.Child("players").Child(s.Name),
room: r,
name: from,
name: s.Name,
outbox: s.Outbox,
pending: make([]Request, 0, 32),
entity: &Entity{
ID: 999,
Position: [2]int{0, 0},
Glyph: '@',
},
}
p.pending = append(p.pending, Request{Seq: seq, From: from, Wants: s})
r.players[from] = p
p.pending = append(p.pending, Request{Seq: seq, From: s.Name, Wants: s})
r.players[s.Name] = p
s.queued = true
return result{}
}
return result{
reply: Welcome{Room: r.name},
reply: Welcome{
Room: r.name,
Size: [2]int{r.width, r.height},
Contents: r.allEntities(),
},
}
}
@ -67,16 +87,39 @@ func (SpawnPlayer) NetTag() string { return "player/spawn" }
// PlayerSpawned is an announcement that a player has spawned
type PlayerSpawned struct {
Name string
Name string `json:"name"`
Position [2]int `json:"position"`
}
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) })
}

@ -18,21 +18,29 @@ type room struct {
}
func (r *room) update(dt time.Duration) {
// announcements := make([]result, 0, 8)
for _, p := range r.players {
for _, req := range p.pending {
res := req.Wants.exec(r, p.name, req.Seq)
res := req.Wants.exec(r, p, req.Seq)
p.outbox <- wire.Response{Re: req.Seq, Body: res.reply}
}
p.pending = p.pending[0:0]
}
for _, t := range r.tiles {
for _, e := range t.contents {
e.update(dt)
if t.here != nil {
t.here.update(dt)
}
}
}
func (r *room) allEntities() []Entity {
all := make([]Entity, 0, 4)
for _, t := range r.tiles {
if t.here != nil {
all = append(all, *t.here)
}
}
return all
}
func (r *room) addPlayer(p *player) {

@ -1,30 +1,6 @@
package sim
// tile is an individual cell within the world simulation. Everything happens
// on a tile.
type tile struct {
// floor is the surface for the tile. All things sit atop the floor. The
// floor informs clients how to draw the tile in the event that the tile's
// contents are empty. The floor also determins whether or not the tile is
// traversable.
floor floor
// contents is all of the entities on this tile. A given tile may have many
// entities.
contents map[int]entity
}
func (t *tile) addEntity(e entity) {
if t.contents == nil {
t.contents = make(map[int]entity)
}
t.contents[e.id()] = e
}
func (t *tile) removeEntity(id int) entity {
if e, here := t.contents[id]; here {
delete(t.contents, id)
return e
}
return nil
here *Entity
}

@ -60,7 +60,9 @@ func (w *World) Run(hz int) {
spawn.Outbox <- wire.ErrorResponse(req.Seq, "a player is already logged in as %q", req.From)
break
}
spawn.exec(&w.rooms[0], req.From, req.Seq)
spawn.exec(&w.rooms[0], nil, req.Seq)
p := w.rooms[0].players[req.From]
w.players[req.From] = p
break
}
@ -68,7 +70,6 @@ func (w *World) Run(hz int) {
if !ok {
w.Error("received non login request of type %T from unknown player %q", req.Wants, req.From)
}
break
p.pending = append(p.pending, req)

@ -10,13 +10,13 @@ type Mode interface {
draw(*UI)
}
type boxWalker struct {
type roomDisplay struct {
width int
height int
position point
}
func (m *boxWalker) handleEvent(ui *UI, e tcell.Event) bool {
func (m *roomDisplay) handleEvent(ui *UI, e tcell.Event) bool {
switch v := e.(type) {
case *tcell.EventKey:
key := v.Key()
@ -24,16 +24,16 @@ func (m *boxWalker) handleEvent(ui *UI, e tcell.Event) bool {
switch v.Rune() {
case 'w':
ui.client.Send(sim.Move{0, -1})
m.move(0, -1)
// m.move(0, -1)
case 'a':
ui.client.Send(sim.Move{-1, 0})
m.move(-1, 0)
// m.move(-1, 0)
case 's':
ui.client.Send(sim.Move{0, 1})
m.move(0, 1)
// m.move(0, 1)
case 'd':
ui.client.Send(sim.Move{1, 0})
m.move(1, 0)
// m.move(1, 0)
}
}
default:
@ -42,12 +42,12 @@ func (m *boxWalker) handleEvent(ui *UI, e tcell.Event) bool {
return true
}
func (m *boxWalker) move(dx, dy int) {
func (m *roomDisplay) move(dx, dy int) {
m.position.x = clamp(m.position.x+dx, 0, m.width-1)
m.position.y = clamp(m.position.y+dy, 0, m.height-1)
}
func (m *boxWalker) draw(ui *UI) {
func (m *roomDisplay) draw(ui *UI) {
offset := point{1, 1}
// fill in background dots first
@ -72,5 +72,5 @@ func (m *boxWalker) draw(ui *UI) {
}
// add all characters
ui.screen.SetContent(m.position.x+offset.x, m.position.y+offset.y, '@', nil, tcell.StyleDefault)
// ui.screen.SetContent(m.position.x+offset.x, m.position.y+offset.y, '@', nil, tcell.StyleDefault)
}

@ -6,6 +6,7 @@ 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/sim"
"github.com/jordanorelli/astro-domu/internal/wire"
"github.com/jordanorelli/blammo"
)
@ -26,9 +27,23 @@ func (ui *UI) Run() {
return
}
ui.client.Send(server.Login{Name: ui.PlayerName})
res, err := ui.client.Send(server.Login{Name: ui.PlayerName})
if err != nil {
ui.Error("login error: %v", err)
return
}
ui.mode = &boxWalker{width: 10, height: 6}
welcome, ok := res.Body.(*sim.Welcome)
if !ok {
ui.Error("unexpected initial message of type %t", res.Body)
return
}
ui.Info("spawned into room %s", welcome.Room)
ui.mode = &roomDisplay{
width: welcome.Size[0],
height: welcome.Size[1],
}
ui.Info("running ui")
if ui.handleUserInput() {
ui.Info("user requested close")

@ -0,0 +1 @@
package wire
Loading…
Cancel
Save