mtsatellite/cmd/mtwebmapper/players.go

180 lines
3.5 KiB
Go
Raw Normal View History

// Copyright 2014, 2015 by Sascha L. Teichmann
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.
2015-03-09 13:09:13 +01:00
package main
2015-03-09 14:01:30 +01:00
import (
2015-03-09 14:31:20 +01:00
"bufio"
2015-03-09 14:01:30 +01:00
"bytes"
2022-02-28 13:21:28 +01:00
"context"
"database/sql"
"encoding/json"
2015-03-09 14:01:30 +01:00
"html/template"
"log"
"math"
2015-03-09 14:01:30 +01:00
"net/http"
2015-03-09 14:31:20 +01:00
"os"
"sort"
"sync"
2015-03-09 14:31:20 +01:00
"time"
2022-02-28 13:21:28 +01:00
"bitbucket.org/s_l_teichmann/mtsatellite/common"
2015-03-09 14:01:30 +01:00
)
2022-02-28 13:37:00 +01:00
const (
sleepInterval = time.Second * 5
sleepPG = time.Second
)
2015-03-09 14:31:20 +01:00
2015-03-09 14:01:30 +01:00
var geoJSONTmpl = template.Must(template.New("geojson").Parse(
`{ "type": "Feature",
"geometry": {
"type": "Point",
2015-03-09 17:50:58 +01:00
"coordinates": [{{.Z}}, {{.X}}]
2015-03-09 14:01:30 +01:00
},
"properties": {
2015-03-10 12:57:00 +01:00
"name": "{{.Name | html }}"
2015-03-09 14:01:30 +01:00
}
}`))
type player struct {
2015-03-09 14:31:20 +01:00
X float64 `json:"x"`
Y float64 `json:"y"`
Z float64 `json:"z"`
Name string `json:"name"`
2015-03-09 14:01:30 +01:00
}
2015-03-09 13:09:13 +01:00
type players struct {
fifo string
wsf *websocketForwarder
2015-03-09 14:01:30 +01:00
pls []*player
mu sync.RWMutex
2015-03-09 13:09:13 +01:00
}
func newPlayers(fifo string, wsf *websocketForwarder) *players {
2015-03-09 14:32:14 +01:00
return &players{fifo: fifo, wsf: wsf, pls: []*player{}}
2015-03-09 13:09:13 +01:00
}
2015-03-09 14:01:30 +01:00
func (p *player) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
if err := geoJSONTmpl.Execute(&buf, p); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func (p *player) same(o *player) bool {
return p.Name == o.Name &&
math.Abs(p.X-o.X) < 0.000001 &&
math.Abs(p.Y-o.Y) < 0.000001 &&
math.Abs(p.Z-o.Z) < 0.000001
}
2022-02-28 13:21:28 +01:00
const selectPlayersSQL = `
2022-02-28 13:37:00 +01:00
SELECT posx/10.0, posy/10.0, posz/10.0, name
2022-02-28 13:21:28 +01:00
FROM player
WHERE modification_date > now() - '2m'::interval`
func playersFromPostgreSQL(connS string) ([]*player, error) {
2022-02-28 13:37:00 +01:00
time.Sleep(sleepPG)
2022-02-28 13:21:28 +01:00
db, err := sql.Open("pgx", connS)
if err != nil {
return nil, err
}
defer db.Close()
rows, err := db.QueryContext(context.Background(), selectPlayersSQL)
if err != nil {
return nil, err
}
defer rows.Close()
var pls []*player
for rows.Next() {
var p player
2022-02-28 13:37:00 +01:00
if err := rows.Scan(&p.X, &p.Y, &p.Z, &p.Name); err != nil {
2022-02-28 13:21:28 +01:00
return nil, err
}
pls = append(pls, &p)
}
return pls, rows.Err()
}
2015-03-09 14:31:20 +01:00
func (ps *players) readFromFIFO() ([]*player, error) {
2022-02-28 13:21:28 +01:00
if host, ok := common.IsPostgreSQL(ps.fifo); ok {
return playersFromPostgreSQL(host)
}
2015-03-09 14:31:20 +01:00
file, err := os.Open(ps.fifo)
if err != nil {
return nil, err
}
defer file.Close()
reader := bufio.NewReader(file)
decoder := json.NewDecoder(reader)
var pls []*player
if err = decoder.Decode(&pls); err != nil {
return nil, err
}
return pls, nil
}
2015-03-10 12:17:28 +01:00
func samePlayers(a, b []*player) bool {
if len(a) != len(b) {
return false
}
for i, p := range a {
if !p.same(b[i]) {
2015-03-10 12:17:28 +01:00
return false
}
}
2015-03-10 12:17:28 +01:00
return true
}
2015-03-09 13:09:13 +01:00
func (ps *players) run() {
for {
ps.mu.Lock()
empty := len(ps.pls) == 0
ps.mu.Unlock()
2015-03-09 14:31:20 +01:00
pls, err := ps.readFromFIFO()
if err != nil {
//log.Printf("err: %s\n", err)
2015-03-09 14:31:20 +01:00
time.Sleep(sleepInterval)
continue
}
if empty && pls == nil {
//log.Println("no players")
continue
}
//log.Printf("%+q\n", pls)
sort.Slice(pls, func(i, j int) bool {
return pls[i].Name < pls[j].Name
})
var change bool
2015-03-09 14:31:20 +01:00
ps.mu.Lock()
//log.Printf("%+q\n", pls)
//log.Printf("%+q\n", ps.pls)
2015-03-10 12:17:28 +01:00
if change = !samePlayers(pls, ps.pls); change {
ps.pls = pls
}
2015-03-09 14:31:20 +01:00
ps.mu.Unlock()
if change && ps.wsf != nil {
// TODO: Throttle this!
ps.wsf.BroadcastPlayers(pls)
}
2015-03-09 13:09:13 +01:00
}
}
func (ps *players) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("Content-Type", "application/json")
var pls []*player
ps.mu.RLock()
pls = ps.pls
ps.mu.RUnlock()
encoder := json.NewEncoder(rw)
if err := encoder.Encode(pls); err != nil {
log.Printf("error: sending JSON failed: %s\n", err)
}
2015-03-09 13:09:13 +01:00
}