refined idle detection, send notification to clients
This commit is contained in:
parent
342483d62d
commit
0014a81ce3
@ -11,6 +11,17 @@ func (app *Application) expireInactivePlayersThread() {
|
||||
|
||||
removedIds := app.expireInactivePlayers()
|
||||
for _, usrId := range removedIds {
|
||||
usr, err := app.GetUserById(usrId)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
gm, err2 := app.GetGameById(usr.GetGameId())
|
||||
if err2 != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
gm.EventPlayerLeaves(usr)
|
||||
fmt.Printf("expired %s\n", usrId)
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,31 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sirlab.de/go/knyt/user"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (app *Application) updatePlayerATime(usr *user.User) {
|
||||
func (app *Application) updatePlayerIsConnected(usr *user.User) {
|
||||
app.updatePlayerATime(usr, time.Now().Add(30*time.Second))
|
||||
}
|
||||
|
||||
func (app *Application) updatePlayerIsDisconnected(usr *user.User) {
|
||||
app.updatePlayerATime(usr, time.Now())
|
||||
}
|
||||
|
||||
func (app *Application) updatePlayerATime(usr *user.User, t time.Time) {
|
||||
usrId := usr.GetId()
|
||||
|
||||
app.mu.Lock()
|
||||
prevTime := app.playerATime[usrId]
|
||||
if prevTime.IsZero() {
|
||||
fmt.Printf("new Player %s\n", usr.GetName())
|
||||
}
|
||||
app.playerATime[usrId] = time.Now()
|
||||
app.playerATime[usrId] = t
|
||||
app.mu.Unlock()
|
||||
}
|
||||
|
||||
func (app *Application) getActivePlayerIds() []string {
|
||||
app.mu.Lock()
|
||||
defer app.mu.Unlock()
|
||||
|
||||
playerIds := make([]string, 0)
|
||||
for usrId, _ := range app.playerATime {
|
||||
playerIds = append(playerIds, usrId)
|
||||
if prevTime.IsZero() {
|
||||
gm, err := app.GetGameById(usr.GetGameId())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
gm.EventPlayerJoins(usr)
|
||||
}
|
||||
|
||||
return playerIds
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ func (app *Application) SyncHandler(usr *user.User, w http.ResponseWriter, r *ht
|
||||
return
|
||||
}
|
||||
|
||||
app.updatePlayerATime(usr)
|
||||
|
||||
app.updatePlayerIsConnected(usr)
|
||||
eng := gm.GetEngine()
|
||||
eng.SyncHandler(w, r)
|
||||
app.updatePlayerIsDisconnected(usr)
|
||||
}
|
||||
|
@ -3,41 +3,50 @@ package engine
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/imkira/go-observer"
|
||||
"math/rand"
|
||||
"sirlab.de/go/knyt/syncdata"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewEngine(id string) *Engine {
|
||||
func NewEngine() *Engine {
|
||||
engine := Engine{
|
||||
id: id,
|
||||
versionRef: 0,
|
||||
obs: observer.NewProperty(syncdata.SyncData{}),
|
||||
notify: make(chan bool, 2),
|
||||
}
|
||||
|
||||
return &engine
|
||||
}
|
||||
|
||||
func (eng *Engine) Run() {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
func (eng *Engine) Run(populateSyncDataCb PopulateSyncDataCb) {
|
||||
for {
|
||||
wait := int(1 + r.Float32()*5)
|
||||
for i := 0; i < wait; i++ {
|
||||
time.Sleep(1 * time.Second)
|
||||
select {
|
||||
case <-eng.notify:
|
||||
// nop
|
||||
case <-time.After(15 * time.Second):
|
||||
// nop
|
||||
}
|
||||
|
||||
eng.doSomething()
|
||||
eng.publish(populateSyncDataCb)
|
||||
}
|
||||
}
|
||||
|
||||
func (eng *Engine) doSomething() {
|
||||
func (eng *Engine) Update() {
|
||||
eng.notify <- true
|
||||
}
|
||||
|
||||
func (eng *Engine) publish(populateSyncDataCb PopulateSyncDataCb) {
|
||||
eng.mu.Lock()
|
||||
defer eng.mu.Unlock()
|
||||
|
||||
eng.versionRef++
|
||||
fmt.Printf("game %s: %d\n", eng.id, eng.versionRef)
|
||||
data := syncdata.SyncData{
|
||||
VersionRef: eng.versionRef,
|
||||
}
|
||||
|
||||
if populateSyncDataCb != nil {
|
||||
populateSyncDataCb(&data)
|
||||
}
|
||||
|
||||
fmt.Printf("engine versionRef %d\n", eng.versionRef)
|
||||
eng.obs.Update(data)
|
||||
}
|
||||
|
@ -3,14 +3,16 @@ package engine
|
||||
import (
|
||||
"github.com/imkira/go-observer"
|
||||
"net/http"
|
||||
"sirlab.de/go/knyt/syncdata"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type HandleFunc func(http.ResponseWriter, *http.Request)
|
||||
type PopulateSyncDataCb func(*syncdata.SyncData)
|
||||
|
||||
type Engine struct {
|
||||
mu sync.Mutex
|
||||
id string
|
||||
versionRef int
|
||||
obs observer.Property
|
||||
notify chan bool
|
||||
}
|
||||
|
@ -18,12 +18,13 @@ func NewGameFromFile(id, fileName string) (*Game, error) {
|
||||
return nil, fmt.Errorf("%s: %v\n", fileName, err)
|
||||
} else {
|
||||
gm := Game{
|
||||
id: id,
|
||||
name: gmJson.Name,
|
||||
eng: engine.NewEngine(id),
|
||||
id: id,
|
||||
name: gmJson.Name,
|
||||
eng: engine.NewEngine(),
|
||||
players: make(map[string]playerInfo),
|
||||
}
|
||||
|
||||
go gm.eng.Run()
|
||||
go gm.eng.Run(gm.populateSyncDataCb)
|
||||
|
||||
return &gm, nil
|
||||
}
|
||||
@ -49,23 +50,3 @@ func (gm *Game) GetEngine() *engine.Engine {
|
||||
|
||||
return gm.eng
|
||||
}
|
||||
|
||||
// func (gm *Game) GetActivePlayers() []string {
|
||||
// gm.mu.Lock()
|
||||
// defer gm.mu.Unlock()
|
||||
//
|
||||
// players := make([]string, 0)
|
||||
//
|
||||
// now := time.Now()
|
||||
// for usrId, timestamp := range gm.playerTimestamp {
|
||||
// elapsed := now.Sub(timestamp)
|
||||
//
|
||||
// fmt.Printf("%s: %.0f\n", usrId, elapsed.Seconds())
|
||||
//
|
||||
// if elapsed.Seconds() < 30.0 {
|
||||
// players = append(players, usrId)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return players
|
||||
// }
|
||||
|
49
server/src/game/player.go
Normal file
49
server/src/game/player.go
Normal file
@ -0,0 +1,49 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sirlab.de/go/knyt/user"
|
||||
)
|
||||
|
||||
func (gm *Game) EventPlayerJoins(usr *user.User) {
|
||||
usrId := usr.GetId()
|
||||
usrName := usr.GetName()
|
||||
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
|
||||
prevPlayer := gm.players[usrId]
|
||||
player := playerInfo{
|
||||
id: usrId,
|
||||
name: usrName,
|
||||
isPlaying: true,
|
||||
isIdle: false,
|
||||
}
|
||||
gm.players[usrId] = player
|
||||
|
||||
if prevPlayer.id != "" {
|
||||
fmt.Printf("player \"%s\" re-joined\n", gm.players[usrId].name)
|
||||
} else {
|
||||
fmt.Printf("player \"%s\" joined\n", usrName)
|
||||
}
|
||||
|
||||
gm.eng.Update()
|
||||
}
|
||||
|
||||
func (gm *Game) EventPlayerLeaves(usr *user.User) {
|
||||
usrId := usr.GetId()
|
||||
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
|
||||
player := gm.players[usrId]
|
||||
if player.id != usrId {
|
||||
return
|
||||
}
|
||||
player.isIdle = true
|
||||
gm.players[usrId] = player
|
||||
|
||||
fmt.Printf("player \"%s\" is idle\n", usr.GetName())
|
||||
|
||||
gm.eng.Update()
|
||||
}
|
27
server/src/game/populateSyncDataCb.go
Normal file
27
server/src/game/populateSyncDataCb.go
Normal file
@ -0,0 +1,27 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"sirlab.de/go/knyt/syncdata"
|
||||
)
|
||||
|
||||
func (gm *Game) populateSyncDataCb(syncData *syncdata.SyncData) {
|
||||
gm.populatePlayers(syncData)
|
||||
}
|
||||
|
||||
func (gm *Game) populatePlayers(syncData *syncdata.SyncData) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
|
||||
syncData.GameInfo.GameId = gm.id
|
||||
players := make([]syncdata.PlayerInfo, 0)
|
||||
for _, p := range gm.players {
|
||||
if p.isPlaying {
|
||||
players = append(players, syncdata.PlayerInfo{
|
||||
Id: p.id,
|
||||
Name: p.name,
|
||||
IsIdle: p.isIdle,
|
||||
})
|
||||
}
|
||||
}
|
||||
syncData.GameInfo.Players = players
|
||||
}
|
@ -5,11 +5,19 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type playerInfo struct {
|
||||
id string
|
||||
name string
|
||||
isPlaying bool
|
||||
isIdle bool
|
||||
}
|
||||
|
||||
type Game struct {
|
||||
mu sync.Mutex
|
||||
id string
|
||||
name string
|
||||
eng *engine.Engine
|
||||
mu sync.Mutex
|
||||
id string
|
||||
name string
|
||||
players map[string]playerInfo
|
||||
eng *engine.Engine
|
||||
}
|
||||
|
||||
type GameJson struct {
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
)
|
||||
|
||||
type UserInfoJson struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Role string `json:"role"`
|
||||
GameId string `json:"game"`
|
||||
@ -15,6 +16,7 @@ type UserInfoJson struct {
|
||||
|
||||
func (authMux *AuthMux) GetUserInfo(usr *user.User, w http.ResponseWriter, r *http.Request) {
|
||||
usrLight := UserInfoJson{
|
||||
Id: usr.GetId(),
|
||||
Name: usr.GetName(),
|
||||
Role: usr.GetRole(),
|
||||
GameId: usr.GetGameId(),
|
||||
|
@ -3,14 +3,15 @@ package syncdata
|
||||
type PlayerInfo struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Active bool `json:"active"`
|
||||
IsIdle bool `json:"isIdle"`
|
||||
}
|
||||
|
||||
type Gameinfo struct {
|
||||
type GameInfo struct {
|
||||
GameId string `json:"id"`
|
||||
Players []PlayerInfo `json:"players"`
|
||||
}
|
||||
|
||||
type SyncData struct {
|
||||
VersionRef int `json:"version"`
|
||||
Gameinfo *Gameinfo `json:"game"`
|
||||
VersionRef int `json:"version"`
|
||||
GameInfo GameInfo `json:"game"`
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user