package main import ( "database/sql" "encoding/json" "errors" "fmt" "time" "github.com/jordanorelli/kloam/db" ) type request struct { Cmd string `json:"cmd"` } func (r request) parse(msg string) command { var c command switch r.Cmd { case "login": c = new(login) case "death": c = new(death) case "collect-soul": c = new(collectSoul) default: return nil } json.Unmarshal([]byte(msg), &c) return c } type vector3 struct { X float64 `json:"x"` Y float64 `json:"y"` Z float64 `json:"z"` } type command interface { exec(*server, *player) } type collectSoul struct { PlayerName string `json:"playerName"` Position vector3 `json:"position"` } func (c *collectSoul) exec(s *server, from *player) { soul, ok := s.souls[c.PlayerName] if !ok { return } delete(s.souls, c.PlayerName) b, err := json.Marshal(soul) if err != nil { s.Error("unable to serialize soul: %v", err) return } msg := fmt.Sprintf("soul-collected %s", string(b)) for _, player := range s.players { select { case player.outbox <- msg: default: s.Error("can't write to player %s's outbox", player.username) } } } type login struct { Username string `json:"username"` Password string `json:"password"` } type loginResult struct { Passed bool `json:"passed"` Dead bool `json:"dead"` DiedAt time.Time `json:"died_at"` Error string `json:"error,omitempty"` } func (l *login) exec(s *server, from *player) { sendResult := func(res loginResult) { b, _ := json.Marshal(res) msg := fmt.Sprintf("login-result %s", string(b)) from.outbox <- msg } row := db.Player{Name: l.Username} if err := s.db.ReadPlayer(&row); err != nil { sendResult(loginResult{ Error: fmt.Sprintf("failed to read player from database: %v", err), }) return } fmt.Printf("login read row from database: %v\n", row) if row.HasPassword(l.Password) { from.username = l.Username from.id = row.ID } else { sendResult(loginResult{Error: "bad password"}) return } body := db.Body{PlayerID: row.ID} if err := s.db.ReadBody(&body); err != nil { if errors.Is(err, sql.ErrNoRows) { sendResult(loginResult{Passed: true}) } else { sendResult(loginResult{Error: err.Error()}) return } } else { sendResult(loginResult{ Passed: true, Dead: true, DiedAt: body.DiedAt, }) } messages := make([]string, 0, len(s.souls)) for _, soul := range s.souls { b, _ := json.Marshal(soul) msg := fmt.Sprintf("spawn-soul %s", string(b)) messages = append(messages, msg) } go func() { time.Sleep(1 * time.Second) for _, msg := range messages { select { case from.outbox <- msg: default: } } }() } type death struct { Position vector3 `json:"position"` } func (d *death) exec(s *server, from *player) { s.Info("executing a death: %#v", d) body := &db.Body{ PlayerID: from.id, X: d.Position.X, Y: d.Position.Y, Z: d.Position.Z, } s.Info("adding body: %#v", body) if err := s.db.AddBody(body); err != nil { s.Error("db error: %v", err) } _soul := soul{ PlayerName: from.username, Position: d.Position, } s.souls[from.username] = _soul b, err := json.Marshal(_soul) if err != nil { s.Error("unable to serialize soul: %v", err) return } msg := fmt.Sprintf("spawn-soul %s", string(b)) for _, player := range s.players { select { case player.outbox <- msg: default: s.Error("can't write to player %s's outbox", player.username) } } }