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()
|
removedIds := app.expireInactivePlayers()
|
||||||
for _, usrId := range removedIds {
|
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)
|
fmt.Printf("expired %s\n", usrId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
package application
|
package application
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"sirlab.de/go/knyt/user"
|
"sirlab.de/go/knyt/user"
|
||||||
"time"
|
"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()
|
usrId := usr.GetId()
|
||||||
|
|
||||||
app.mu.Lock()
|
app.mu.Lock()
|
||||||
prevTime := app.playerATime[usrId]
|
prevTime := app.playerATime[usrId]
|
||||||
if prevTime.IsZero() {
|
app.playerATime[usrId] = t
|
||||||
fmt.Printf("new Player %s\n", usr.GetName())
|
|
||||||
}
|
|
||||||
app.playerATime[usrId] = time.Now()
|
|
||||||
app.mu.Unlock()
|
app.mu.Unlock()
|
||||||
}
|
|
||||||
|
|
||||||
func (app *Application) getActivePlayerIds() []string {
|
if prevTime.IsZero() {
|
||||||
app.mu.Lock()
|
gm, err := app.GetGameById(usr.GetGameId())
|
||||||
defer app.mu.Unlock()
|
if err != nil {
|
||||||
|
return
|
||||||
playerIds := make([]string, 0)
|
}
|
||||||
for usrId, _ := range app.playerATime {
|
gm.EventPlayerJoins(usr)
|
||||||
playerIds = append(playerIds, usrId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return playerIds
|
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,8 @@ func (app *Application) SyncHandler(usr *user.User, w http.ResponseWriter, r *ht
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
app.updatePlayerATime(usr)
|
app.updatePlayerIsConnected(usr)
|
||||||
|
|
||||||
eng := gm.GetEngine()
|
eng := gm.GetEngine()
|
||||||
eng.SyncHandler(w, r)
|
eng.SyncHandler(w, r)
|
||||||
|
app.updatePlayerIsDisconnected(usr)
|
||||||
}
|
}
|
||||||
|
@ -3,41 +3,50 @@ package engine
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/imkira/go-observer"
|
"github.com/imkira/go-observer"
|
||||||
"math/rand"
|
|
||||||
"sirlab.de/go/knyt/syncdata"
|
"sirlab.de/go/knyt/syncdata"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewEngine(id string) *Engine {
|
func NewEngine() *Engine {
|
||||||
engine := Engine{
|
engine := Engine{
|
||||||
id: id,
|
|
||||||
versionRef: 0,
|
versionRef: 0,
|
||||||
obs: observer.NewProperty(syncdata.SyncData{}),
|
obs: observer.NewProperty(syncdata.SyncData{}),
|
||||||
|
notify: make(chan bool, 2),
|
||||||
}
|
}
|
||||||
|
|
||||||
return &engine
|
return &engine
|
||||||
}
|
}
|
||||||
|
|
||||||
func (eng *Engine) Run() {
|
func (eng *Engine) Run(populateSyncDataCb PopulateSyncDataCb) {
|
||||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
||||||
for {
|
for {
|
||||||
wait := int(1 + r.Float32()*5)
|
select {
|
||||||
for i := 0; i < wait; i++ {
|
case <-eng.notify:
|
||||||
time.Sleep(1 * time.Second)
|
// 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()
|
eng.mu.Lock()
|
||||||
defer eng.mu.Unlock()
|
defer eng.mu.Unlock()
|
||||||
|
|
||||||
eng.versionRef++
|
eng.versionRef++
|
||||||
fmt.Printf("game %s: %d\n", eng.id, eng.versionRef)
|
|
||||||
data := syncdata.SyncData{
|
data := syncdata.SyncData{
|
||||||
VersionRef: eng.versionRef,
|
VersionRef: eng.versionRef,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if populateSyncDataCb != nil {
|
||||||
|
populateSyncDataCb(&data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("engine versionRef %d\n", eng.versionRef)
|
||||||
eng.obs.Update(data)
|
eng.obs.Update(data)
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,16 @@ package engine
|
|||||||
import (
|
import (
|
||||||
"github.com/imkira/go-observer"
|
"github.com/imkira/go-observer"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sirlab.de/go/knyt/syncdata"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HandleFunc func(http.ResponseWriter, *http.Request)
|
type HandleFunc func(http.ResponseWriter, *http.Request)
|
||||||
|
type PopulateSyncDataCb func(*syncdata.SyncData)
|
||||||
|
|
||||||
type Engine struct {
|
type Engine struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
id string
|
|
||||||
versionRef int
|
versionRef int
|
||||||
obs observer.Property
|
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)
|
return nil, fmt.Errorf("%s: %v\n", fileName, err)
|
||||||
} else {
|
} else {
|
||||||
gm := Game{
|
gm := Game{
|
||||||
id: id,
|
id: id,
|
||||||
name: gmJson.Name,
|
name: gmJson.Name,
|
||||||
eng: engine.NewEngine(id),
|
eng: engine.NewEngine(),
|
||||||
|
players: make(map[string]playerInfo),
|
||||||
}
|
}
|
||||||
|
|
||||||
go gm.eng.Run()
|
go gm.eng.Run(gm.populateSyncDataCb)
|
||||||
|
|
||||||
return &gm, nil
|
return &gm, nil
|
||||||
}
|
}
|
||||||
@ -49,23 +50,3 @@ func (gm *Game) GetEngine() *engine.Engine {
|
|||||||
|
|
||||||
return gm.eng
|
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"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type playerInfo struct {
|
||||||
|
id string
|
||||||
|
name string
|
||||||
|
isPlaying bool
|
||||||
|
isIdle bool
|
||||||
|
}
|
||||||
|
|
||||||
type Game struct {
|
type Game struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
id string
|
id string
|
||||||
name string
|
name string
|
||||||
eng *engine.Engine
|
players map[string]playerInfo
|
||||||
|
eng *engine.Engine
|
||||||
}
|
}
|
||||||
|
|
||||||
type GameJson struct {
|
type GameJson struct {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type UserInfoJson struct {
|
type UserInfoJson struct {
|
||||||
|
Id string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
GameId string `json:"game"`
|
GameId string `json:"game"`
|
||||||
@ -15,6 +16,7 @@ type UserInfoJson struct {
|
|||||||
|
|
||||||
func (authMux *AuthMux) GetUserInfo(usr *user.User, w http.ResponseWriter, r *http.Request) {
|
func (authMux *AuthMux) GetUserInfo(usr *user.User, w http.ResponseWriter, r *http.Request) {
|
||||||
usrLight := UserInfoJson{
|
usrLight := UserInfoJson{
|
||||||
|
Id: usr.GetId(),
|
||||||
Name: usr.GetName(),
|
Name: usr.GetName(),
|
||||||
Role: usr.GetRole(),
|
Role: usr.GetRole(),
|
||||||
GameId: usr.GetGameId(),
|
GameId: usr.GetGameId(),
|
||||||
|
@ -3,14 +3,15 @@ package syncdata
|
|||||||
type PlayerInfo struct {
|
type PlayerInfo struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Name string `json:"name"`
|
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"`
|
Players []PlayerInfo `json:"players"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SyncData struct {
|
type SyncData struct {
|
||||||
VersionRef int `json:"version"`
|
VersionRef int `json:"version"`
|
||||||
Gameinfo *Gameinfo `json:"game"`
|
GameInfo GameInfo `json:"game"`
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user