From 2a705ef6b69a9fa576a6deb271f1309663aed4c9 Mon Sep 17 00:00:00 2001 From: Sergey Stepanov Date: Fri, 14 Jul 2023 20:32:17 +0300 Subject: [PATCH] Add app state interface --- pkg/worker/caged/caged.go | 7 +++--- pkg/worker/coordinatorhandlers.go | 37 +++++++++++++------------------ pkg/worker/room.go | 15 +++++++++---- pkg/worker/router.go | 14 ++++++------ pkg/worker/storage.go | 14 ++++++++++-- 5 files changed, 48 insertions(+), 39 deletions(-) diff --git a/pkg/worker/caged/caged.go b/pkg/worker/caged/caged.go index a6642d40d..0a5455243 100644 --- a/pkg/worker/caged/caged.go +++ b/pkg/worker/caged/caged.go @@ -5,8 +5,7 @@ type App interface { } type State interface { - EnableAutosave(periodS int) - HasSave() bool - LoadGame() error - SaveGame() error + Autosave(periodS int) + LoadState() error + SaveState() error } diff --git a/pkg/worker/coordinatorhandlers.go b/pkg/worker/coordinatorhandlers.go index 494cd39e1..ea0d8484f 100644 --- a/pkg/worker/coordinatorhandlers.go +++ b/pkg/worker/coordinatorhandlers.go @@ -77,8 +77,10 @@ func (c *coordinator) HandleGameStart(rq api.StartGameRequest[com.Uid], w *Worke } w.log.Info().Msgf("Starting game: %v", rq.Game.Name) - room := w.router.FindRoom(rq.Rid) - if room == nil { + var room GameRoom + r := w.router.FindRoom(rq.Rid) + + if r == nil { room = NewRoom( rq.Room.Rid, games.GameMetadata(rq.Game), @@ -93,30 +95,21 @@ func (c *coordinator) HandleGameStart(rq api.StartGameRequest[com.Uid], w *Worke user.SetPlayerIndex(rq.PlayerIndex) if w.storage != nil { - cs := &StorageRoom{ - GameRoom: room, - stateLocalPath: room.Emulator().GetHashPath(), - stateName: room.Id(), - storage: w.storage, - } - if err := cs.Download(); err != nil { - room.Log().Warn().Err(err).Msgf("[%v] is not in the cloud", cs.stateName) - } else { - room.Log().Debug().Msgf("Downloaded [%v] from the cloud", cs.stateName) - } - room = cs + room = WithCloudStorage(room, room.Emulator().GetHashPath(), room.Id(), w.storage) } if w.conf.Recording.Enabled { - room = WithRecording(room.(*Room), rq.Record, rq.RecordUser, rq.Game.Name, w.conf) + room = WithRecording(room, rq.Record, rq.RecordUser, rq.Game.Name, w.conf) } w.router.SetRoom(room) room.StartEmulator() if w.conf.Emulator.AutosaveSec > 0 { - // !to can crash if emulator starts earlier - go room.EnableAutosave(w.conf.Emulator.AutosaveSec) + // !to can crash if the emulator starts later + go room.Autosave(w.conf.Emulator.AutosaveSec) } + } else { + room = r.(GameRoom) } if room == nil { @@ -142,7 +135,7 @@ func (c *coordinator) HandleTerminateSession(rq api.TerminateSessionRequest[com. user.Disconnect() if room := user.Room(); room != nil { user.SetRoom(nil) - room.Expel(user) + room.(GameRoom).Expel(user) } } } @@ -154,7 +147,7 @@ func (c *coordinator) HandleQuitGame(rq api.GameQuitRequest[com.Uid], w *Worker) // since users hold their room reference // !to remove rid, maybe if room := w.router.FindRoom(rq.Rid); room != nil { - room.Expel(user) + room.(GameRoom).Expel(user) } } } @@ -164,7 +157,7 @@ func (c *coordinator) HandleSaveGame(rq api.SaveGameRequest[com.Uid], w *Worker) if room == nil { return api.ErrPacket } - if err := room.SaveGame(); err != nil { + if err := room.SaveState(); err != nil { c.log.Error().Err(err).Msg("cannot save game state") return api.ErrPacket } @@ -176,7 +169,7 @@ func (c *coordinator) HandleLoadGame(rq api.LoadGameRequest[com.Uid], w *Worker) if room == nil { return api.ErrPacket } - if err := room.LoadGame(); err != nil { + if err := room.LoadState(); err != nil { c.log.Error().Err(err).Msg("cannot load game state") return api.ErrPacket } @@ -198,7 +191,7 @@ func (c *coordinator) HandleToggleMultitap(rq api.ToggleMultitapRequest[com.Uid] if room == nil { return api.ErrPacket } - room.ToggleMultitap() + room.(GameRoom).ToggleMultitap() return api.OkPacket } diff --git a/pkg/worker/room.go b/pkg/worker/room.go index e30ab532c..f2b0eecd0 100644 --- a/pkg/worker/room.go +++ b/pkg/worker/room.go @@ -21,6 +21,7 @@ type GameRoom interface { Emulator() emulator.Emulator Expel(*Session) HasUser(*Session) bool + HasSave() bool Id() string Log() *logger.Logger PollUserInput(*Session) @@ -28,6 +29,12 @@ type GameRoom interface { ToggleMultitap() } +type AppRoom interface { + caged.State + Close() + Id() string +} + type Room struct { closed bool done chan struct{} @@ -79,13 +86,13 @@ func (r *Room) HasSave() bool { return os.Exists(r.emulator.GetHas func (r *Room) HasUser(u *Session) bool { return r != nil && r.users.Has(u.id) } func (r *Room) Id() string { return r.id } func (r *Room) IsEmpty() bool { return r.users.Len() == 0 } -func (r *Room) LoadGame() error { return r.emulator.LoadGameState() } +func (r *Room) LoadState() error { return r.emulator.LoadGameState() } func (r *Room) Log() *logger.Logger { return r.log } -func (r *Room) SaveGame() error { return r.emulator.SaveGameState() } +func (r *Room) SaveState() error { return r.emulator.SaveGameState() } func (r *Room) StartEmulator() { go r.emulator.Start() } func (r *Room) ToggleMultitap() { r.emulator.ToggleMultitap() } -func (r *Room) EnableAutosave(periodSec int) { +func (r *Room) Autosave(periodSec int) { r.log.Info().Msgf("Autosave every [%vs]", periodSec) ticker := time.NewTicker(time.Duration(periodSec) * time.Second) defer ticker.Stop() @@ -168,7 +175,7 @@ func (r *Room) Close() { // Save game on quit if it was saved before (shared or click-saved). if r.HasSave() { r.log.Debug().Msg("Save on quit") - if err := r.SaveGame(); err != nil { + if err := r.SaveState(); err != nil { r.log.Error().Err(err).Msg("save on quit failed") } } diff --git a/pkg/worker/router.go b/pkg/worker/router.go index ad9c7133f..f8f43fd2c 100644 --- a/pkg/worker/router.go +++ b/pkg/worker/router.go @@ -6,12 +6,12 @@ import ( "github.com/pion/webrtc/v3/pkg/media" ) -// Router tracks and routes freshly connected users to a game room. +// Router tracks and routes freshly connected users to an app room. // Basically, it holds user connection data until some user makes (connects to) -// a new room (game), then it manages all the cross-references between room and users. +// a new room (app), then it manages all the cross-references between room and users. // Rooms and users has 1-to-n relationship. type Router struct { - room GameRoom + room AppRoom users com.NetMap[*Session] } @@ -19,8 +19,8 @@ type Router struct { type Session struct { conn *webrtc.Peer id com.Uid - pi int // player index - room GameRoom // back reference + pi int // player index + room AppRoom // back reference } func NewRouter() Router { return Router{users: com.NewNetMap[*Session]()} } @@ -31,7 +31,7 @@ func (r *Router) Close() { r.room.Close() } } -func (r *Router) FindRoom(id string) GameRoom { +func (r *Router) FindRoom(id string) AppRoom { if r.room != nil && r.room.Id() == id { return r.room } @@ -48,7 +48,7 @@ func (s *Session) Id() com.Uid { return s.id } func (s *Session) IsConnected() bool { return s.conn.IsConnected() } func (s *Session) PeerConn() *webrtc.Peer { return s.conn } func (s *Session) PlayerIndex() int { return s.pi } -func (s *Session) Room() GameRoom { return s.room } +func (s *Session) Room() AppRoom { return s.room } func (s *Session) SendAudio(sample *media.Sample) error { return s.conn.WriteAudio(sample) } func (s *Session) SendVideo(sample *media.Sample) error { return s.conn.WriteVideo(sample) } func (s *Session) SetPlayerIndex(index int) { s.pi = index } diff --git a/pkg/worker/storage.go b/pkg/worker/storage.go index 06cfc6912..480c05e55 100644 --- a/pkg/worker/storage.go +++ b/pkg/worker/storage.go @@ -29,6 +29,16 @@ type StorageRoom struct { storage Storage // a cloud storage to store room state online } +func WithCloudStorage(parent GameRoom, stateLocal string, stateName string, storage Storage) *StorageRoom { + r := &StorageRoom{GameRoom: parent, stateLocalPath: stateLocal, stateName: stateName, storage: storage} + if err := r.Download(); err != nil { + parent.Log().Warn().Err(err).Msgf("[%v] is not in the cloud", stateName) + } else { + parent.Log().Debug().Msgf("Downloaded [%v] from the cloud", stateName) + } + return r +} + func (c *StorageRoom) Download() error { // saveOnlineRoomToLocal save online room to local. // !Supports only one file of main save state. @@ -54,8 +64,8 @@ func (c *StorageRoom) HasSave() bool { return c.GameRoom.HasSave() } -func (c *StorageRoom) SaveGame() error { - if err := c.GameRoom.SaveGame(); err != nil { +func (c *StorageRoom) SaveState() error { + if err := c.GameRoom.SaveState(); err != nil { return err } if err := c.storage.Save(c.stateName, c.stateLocalPath); err != nil {