getting there omg this is a lot of work

master
Jordan Orelli 4 years ago
parent 6960c3a587
commit 5608cf4583

@ -88,14 +88,15 @@ func (s *Server) createSession(conn *websocket.Conn) *session {
conn: conn, conn: conn,
outbox: make(chan wire.Response), outbox: make(chan wire.Response),
done: make(chan bool, 1), done: make(chan bool, 1),
world: s.world,
} }
if s.sessions == nil { if s.sessions == nil {
s.sessions = make(map[int]*session) s.sessions = make(map[int]*session)
} }
s.waitOnSessions.Add(1) s.waitOnSessions.Add(1)
s.sessions[sn.id] = sn s.sessions[sn.id] = sn
sn.entityID = s.world.SpawnPlayer(sn.id) // sn.entityID = s.world.SpawnPlayer(sn.id)
s.Info("created session %d, %d sessions active", sn.id, len(s.sessions)) // s.Info("created session %d, %d sessions active", sn.id, len(s.sessions))
return sn return sn
} }

@ -6,13 +6,16 @@ import (
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/jordanorelli/astro-domu/internal/sim"
"github.com/jordanorelli/astro-domu/internal/wire" "github.com/jordanorelli/astro-domu/internal/wire"
"github.com/jordanorelli/blammo" "github.com/jordanorelli/blammo"
) )
type session struct { type session struct {
*blammo.Log *blammo.Log
Name string
id int id int
world *sim.World
entityID int entityID int
start time.Time start time.Time
conn *websocket.Conn conn *websocket.Conn
@ -79,13 +82,34 @@ func (sn *session) read() {
switch t { switch t {
case websocket.TextMessage: case websocket.TextMessage:
sn.Log.Child("received-frame").Info(string(b)) sn.Log.Child("received-frame").Info(string(b))
var req wire.Request var req wire.Request
if err := json.Unmarshal(b, &req); err != nil { if err := json.Unmarshal(b, &req); err != nil {
sn.Error("unable to parse request: %v", err) sn.Error("unable to parse request: %v", err)
sn.outbox <- wire.ErrorResponse(0, "unable to parse request: %v", err) sn.outbox <- wire.ErrorResponse(0, "unable to parse request: %v", err)
break break
} }
sn.Info("received message of type %T", req.Body)
switch v := req.Body.(type) {
case *Login:
sn.Name = v.Name
sn.world.Inbox <- sim.Request{
From: sn.Name,
Wants: sim.SpawnPlayer{
Outbox: sn.outbox,
},
}
sn.outbox <- wire.Response{req.Seq, wire.OK{}} sn.outbox <- wire.Response{req.Seq, wire.OK{}}
case sim.Effect:
sn.world.Inbox <- sim.Request{
From: sn.Name,
Wants: v,
}
sn.outbox <- wire.Response{req.Seq, wire.OK{}}
default:
sn.outbox <- wire.ErrorResponse(req.Seq, "not sure how to handle that")
}
case websocket.BinaryMessage: case websocket.BinaryMessage:
sn.outbox <- wire.ErrorResponse(0, "unable to parse binary frames") sn.outbox <- wire.ErrorResponse(0, "unable to parse binary frames")
} }
@ -116,3 +140,13 @@ func (sn *session) sendResponse(res wire.Response) error {
sn.Child("sent-frame").Info(string(payload)) sn.Child("sent-frame").Info(string(payload))
return nil 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) })
}

@ -0,0 +1,8 @@
package sim
import "github.com/jordanorelli/astro-domu/internal/wire"
type Effect interface {
wire.Value
exec(*World, string)
}

@ -3,16 +3,43 @@ package sim
import ( import (
"time" "time"
"github.com/jordanorelli/astro-domu/internal/wire"
"github.com/jordanorelli/blammo" "github.com/jordanorelli/blammo"
) )
// player represents a player character in the simulation // player represents a player character in the simulation
type player struct { type player struct {
*blammo.Log *blammo.Log
sessionID int
outbox chan wire.Response
entityID int entityID int
pending []Request
} }
func (p *player) update(dt time.Duration) { func (p *player) update(dt time.Duration) {}
}
func (p *player) id() int { return p.entityID } func (p *player) id() int { return p.entityID }
type Move [2]int
func (Move) NetTag() string { return "move" }
// SpawnPlayer is a request to spawn a player
type SpawnPlayer struct {
Outbox chan wire.Response
}
func (s SpawnPlayer) exec(w *World, from string) {
w.Info("spawn player requested for: %s", from)
}
func (SpawnPlayer) NetTag() string { return "player/spawn" }
// PlayerSpawned is an announcement that a player has spawned
type PlayerSpawned struct {
Name string
}
func init() {
wire.Register(func() wire.Value { return new(Move) })
}

@ -0,0 +1,6 @@
package sim
type Request struct {
From string
Wants Effect
}

@ -1,7 +1,6 @@
package sim package sim
import ( import (
"strconv"
"time" "time"
"github.com/jordanorelli/blammo" "github.com/jordanorelli/blammo"
@ -10,9 +9,12 @@ import (
// World is the entire simulated world. A world consists of many rooms. // World is the entire simulated world. A world consists of many rooms.
type World struct { type World struct {
*blammo.Log *blammo.Log
Inbox chan Request
rooms []room rooms []room
done chan bool done chan bool
lastEntityID int lastEntityID int
players map[string]*player
} }
func NewWorld(log *blammo.Log) *World { func NewWorld(log *blammo.Log) *World {
@ -28,6 +30,8 @@ func NewWorld(log *blammo.Log) *World {
Log: log, Log: log,
rooms: []room{foyer}, rooms: []room{foyer},
done: make(chan bool), done: make(chan bool),
Inbox: make(chan Request),
players: make(map[string]*player),
} }
} }
@ -38,11 +42,25 @@ func (w *World) Run(hz int) {
w.Info("starting world with a tick rate of %dhz, frame duration of %v", hz, period) w.Info("starting world with a tick rate of %dhz, frame duration of %v", hz, period)
ticker := time.NewTicker(period) ticker := time.NewTicker(period)
lastTick := time.Now() lastTick := time.Now()
for { for {
select { select {
case req := <-w.Inbox:
w.Info("read from inbox: %v", req)
if req.From == "" {
req.Wants.exec(w, "")
break
}
p, ok := w.players[req.From]
if !ok {
break
}
p.pending = append(p.pending, req)
case <-ticker.C: case <-ticker.C:
w.tick(time.Since(lastTick)) w.tick(time.Since(lastTick))
lastTick = time.Now() lastTick = time.Now()
case <-w.done: case <-w.done:
return return
} }
@ -55,18 +73,18 @@ func (w *World) Stop() error {
return nil return nil
} }
func (w *World) SpawnPlayer(id int) int { // func (w *World) SpawnPlayer(id int) int {
w.lastEntityID++ // w.lastEntityID++
r := w.rooms[0] // r := w.rooms[0]
w.Info("spawning player with id: %d into room %q", id, r.name) // w.Info("spawning player with id: %d into room %q", id, r.name)
t := &r.tiles[0] // t := &r.tiles[0]
p := player{ // p := player{
Log: w.Child("players").Child(strconv.Itoa(id)), // Log: w.Child("players").Child(strconv.Itoa(id)),
entityID: w.lastEntityID, // entityID: w.lastEntityID,
} // }
t.addEntity(&p) // t.addEntity(&p)
return p.entityID // return p.entityID
} // }
func (w *World) DespawnPlayer(id int) { func (w *World) DespawnPlayer(id int) {
w.Info("despawning player with id: %d", id) w.Info("despawning player with id: %d", id)

@ -2,7 +2,7 @@ package ui
import ( import (
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
"github.com/jordanorelli/astro-domu/internal/wire" "github.com/jordanorelli/astro-domu/internal/sim"
) )
type Mode interface { type Mode interface {
@ -23,16 +23,16 @@ func (m *boxWalker) handleEvent(ui *UI, e tcell.Event) bool {
if key == tcell.KeyRune { if key == tcell.KeyRune {
switch v.Rune() { switch v.Rune() {
case 'w': case 'w':
ui.client.Send(wire.Self_Move{Delta: true, X: 0, Y: -1}) ui.client.Send(sim.Move{0, -1})
m.move(0, -1) m.move(0, -1)
case 'a': case 'a':
ui.client.Send(wire.Self_Move{Delta: true, X: -1, Y: 0}) ui.client.Send(sim.Move{-1, 0})
m.move(-1, 0) m.move(-1, 0)
case 's': case 's':
ui.client.Send(wire.Self_Move{Delta: true, X: 0, Y: 1}) ui.client.Send(sim.Move{0, 1})
m.move(0, 1) m.move(0, 1)
case 'd': case 'd':
ui.client.Send(wire.Self_Move{Delta: true, X: 1, Y: 0}) ui.client.Send(sim.Move{1, 0})
m.move(1, 0) m.move(1, 0)
} }
} }

@ -5,12 +5,14 @@ import (
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
"github.com/jordanorelli/astro-domu/internal/exit" "github.com/jordanorelli/astro-domu/internal/exit"
"github.com/jordanorelli/astro-domu/internal/server"
"github.com/jordanorelli/astro-domu/internal/wire" "github.com/jordanorelli/astro-domu/internal/wire"
"github.com/jordanorelli/blammo" "github.com/jordanorelli/blammo"
) )
type UI struct { type UI struct {
*blammo.Log *blammo.Log
PlayerName string
screen tcell.Screen screen tcell.Screen
mode Mode mode Mode
client *wire.Client client *wire.Client
@ -24,6 +26,8 @@ func (ui *UI) Run() {
return return
} }
ui.client.Send(server.Login{Name: ui.PlayerName})
ui.mode = &boxWalker{width: 10, height: 6} ui.mode = &boxWalker{width: 10, height: 6}
ui.Info("running ui") ui.Info("running ui")
if ui.handleUserInput() { if ui.handleUserInput() {

@ -17,6 +17,15 @@ func (e Error) MarshalJSON() ([]byte, error) {
return json.Marshal(e.Error()) return json.Marshal(e.Error())
} }
func (e *Error) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
*e = Errorf(s)
return nil
}
func Errorf(t string, args ...interface{}) Error { func Errorf(t string, args ...interface{}) Error {
return Error{val: fmt.Errorf(t, args...)} return Error{val: fmt.Errorf(t, args...)}
} }

@ -1,13 +0,0 @@
package wire
type Self_Move struct {
Delta bool `json:"delta"`
X int `json:"x"`
Y int `json:"y"`
}
func (Self_Move) NetTag() string { return "self/move" }
func init() {
Register(func() Value { return new(Self_Move) })
}

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

Loading…
Cancel
Save