You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

134 lines
2.5 KiB
Go

2 years ago
package main
import (
"context"
"encoding/json"
"log"
"net"
"net/http"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
)
type Point2d struct {
X float64 `json:"x"`
Y float64 `json:"y"`
}
type Player struct {
Nickname string
Connection net.Conn
Position Point2d
}
type clientState Point2d
type serverState struct {
Entities []Point2d `json:"entities"`
}
func main() {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
players := map[string]*Player{}
r.Get("/api/ws", func(w http.ResponseWriter, r *http.Request) {
username := r.URL.Query().Get("username")
conn, _, _, err := ws.UpgradeHTTP(r, w)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
players[username] = &Player{
Nickname: username,
Connection: conn,
Position: Point2d{0, 0},
}
go func() {
log.Printf(`[%s] logged in`, username)
ctx, cancelComms := context.WithCancel(context.Background())
go func() {
defer cancelComms()
for {
serverMsg := &serverState{
Entities: []Point2d{},
}
// generate custom message for this client
for name, player := range players {
if name != username {
serverMsg.Entities = append(serverMsg.Entities, player.Position)
}
}
rawServerMsg, err := json.Marshal(serverMsg)
if err != nil {
log.Print(err)
return
}
if err := wsutil.WriteServerText(conn, rawServerMsg); err != nil {
log.Print(err)
return
}
select {
case <-ctx.Done():
return
case <-time.NewTimer(1000 / 30 * time.Millisecond).C:
}
}
}()
go func() {
defer cancelComms()
for {
rawClientMsg, err := wsutil.ReadClientText(conn)
if err != nil {
log.Print(err)
return
}
var clientMsg clientState
if err := json.Unmarshal(rawClientMsg, &clientMsg); err != nil {
log.Print(err)
return
}
// log.Printf(`[%v] Server received: %s`, username, repr.String(clientMsg))
players[username].Position = Point2d(clientMsg)
select {
case <-ctx.Done():
return
case <-time.NewTimer(1000 / 30 * time.Millisecond).C:
}
}
}()
<-ctx.Done()
log.Printf(`[%s] left the session`, username)
delete(players, username)
conn.Close()
}()
})
log.Printf(`Starting server on :4000...`)
log.Fatal(http.ListenAndServe(":4000", r))
}