diff --git a/pkg/api/worker.go b/pkg/api/worker.go index c1b4f14d..b82d1914 100644 --- a/pkg/api/worker.go +++ b/pkg/api/worker.go @@ -25,10 +25,11 @@ type ( PlayerIndex int `json:"player_index"` } GameInfo struct { - Name string `json:"name"` - Base string `json:"base"` - Path string `json:"path"` - Type string `json:"type"` + Name string `json:"name"` + Base string `json:"base"` + Path string `json:"path"` + System string `json:"system"` + Type string `json:"type"` } StartGameResponse struct { Room diff --git a/pkg/coordinator/coordinator.go b/pkg/coordinator/coordinator.go index 7a8fb200..c3601666 100644 --- a/pkg/coordinator/coordinator.go +++ b/pkg/coordinator/coordinator.go @@ -14,7 +14,7 @@ import ( ) func New(conf config.CoordinatorConfig, log *logger.Logger) (services service.Group) { - lib := games.NewLibWhitelisted(conf.Coordinator.Library, conf.Emulator, log) + lib := games.NewLib(conf.Coordinator.Library, conf.Emulator, log) lib.Scan() hub := NewHub(conf, lib, log) h, err := NewHTTPServer(conf, log, func(mux *httpx.Mux) *httpx.Mux { diff --git a/pkg/coordinator/workerapi.go b/pkg/coordinator/workerapi.go index 0943d666..65b57589 100644 --- a/pkg/coordinator/workerapi.go +++ b/pkg/coordinator/workerapi.go @@ -23,7 +23,7 @@ func (w *Worker) StartGame(id com.Uid, app games.AppMeta, req api.GameStartUserR return api.UnwrapChecked[api.StartGameResponse]( w.Send(api.StartGame, api.StartGameRequest[com.Uid]{ StatefulRoom: StateRoom(id, req.RoomId), - Game: api.GameInfo{Name: app.Name, Base: app.Base, Path: app.Path, Type: app.Type}, + Game: api.GameInfo{Name: app.Name, Base: app.Base, Path: app.Path, System: app.System, Type: app.Type}, PlayerIndex: req.PlayerIndex, Record: req.Record, RecordUser: req.RecordUser, diff --git a/pkg/games/launcher.go b/pkg/games/launcher.go index b519b575..b9146594 100644 --- a/pkg/games/launcher.go +++ b/pkg/games/launcher.go @@ -14,10 +14,11 @@ type Launcher interface { } type AppMeta struct { - Name string - Type string - Base string - Path string + Name string + Type string + Base string + Path string + System string } type GameLauncher struct { @@ -31,7 +32,7 @@ func (gl GameLauncher) FindAppByName(name string) (AppMeta, error) { if game.Path == "" { return AppMeta{}, fmt.Errorf("couldn't find game info for the game %v", name) } - return AppMeta{Name: game.Name, Base: game.Base, Type: game.Type, Path: game.Path}, nil + return AppMeta{Name: game.Name, Base: game.Base, Type: game.Type, Path: game.Path, System: game.System}, nil } func (gl GameLauncher) ExtractAppNameFromUrl(name string) string { return ExtractGame(name) } diff --git a/pkg/games/library.go b/pkg/games/library.go index fec3cc47..d46b8093 100644 --- a/pkg/games/library.go +++ b/pkg/games/library.go @@ -1,6 +1,7 @@ package games import ( + "fmt" "io/fs" "path/filepath" "strings" @@ -34,6 +35,8 @@ type library struct { games map[string]GameMetadata log *logger.Logger + emuConf WithEmulatorInfo + // to restrict parallel execution or throttling // for file watch mode mu sync.Mutex @@ -47,8 +50,9 @@ type GameLibrary interface { Scan() } -type FileExtensionWhitelist interface { +type WithEmulatorInfo interface { GetSupportedExtensions() []string + GetEmulator(rom string, path string) string } type GameMetadata struct { @@ -58,7 +62,8 @@ type GameMetadata struct { Type string Base string // the game path relative to the library base path - Path string + Path string + System string } func (g GameMetadata) FullPath(base string) string { @@ -68,11 +73,7 @@ func (g GameMetadata) FullPath(base string) string { return filepath.Join(base, g.Path) } -func NewLib(conf config.Library, log *logger.Logger) GameLibrary { - return NewLibWhitelisted(conf, conf, log) -} - -func NewLibWhitelisted(conf config.Library, filter FileExtensionWhitelist, log *logger.Logger) GameLibrary { +func NewLib(conf config.Library, emu WithEmulatorInfo, log *logger.Logger) GameLibrary { hasSource := true dir, err := filepath.Abs(conf.BasePath) if err != nil { @@ -81,7 +82,7 @@ func NewLibWhitelisted(conf config.Library, filter FileExtensionWhitelist, log * } if len(conf.Supported) == 0 { - conf.Supported = filter.GetSupportedExtensions() + conf.Supported = emu.GetSupportedExtensions() } library := &library{ @@ -96,6 +97,7 @@ func NewLibWhitelisted(conf config.Library, filter FileExtensionWhitelist, log * games: map[string]GameMetadata{}, hasSource: hasSource, log: log, + emuConf: emu, } if conf.WatchMode && hasSource { @@ -152,6 +154,9 @@ func (lib *library) Scan() { if info != nil && !info.IsDir() && lib.isExtAllowed(path) { meta := getMetadata(path, dir) + + meta.System = lib.emuConf.GetEmulator(meta.Type, meta.Path) + if _, ok := lib.config.ignored[meta.Name]; !ok { games = append(games, meta) } @@ -258,7 +263,7 @@ func getMetadata(path string, basePath string) GameMetadata { func (lib *library) dumpLibrary() { var gameList strings.Builder for _, game := range lib.games { - gameList.WriteString(" " + game.Name + " (" + game.Path + ")" + "\n") + gameList.WriteString(fmt.Sprintf(" %5s %s (%s)\n", game.System, game.Name, game.Path)) } lib.log.Debug().Msgf("Lib dump\n"+ diff --git a/pkg/games/library_test.go b/pkg/games/library_test.go index f42890a8..74dedb27 100644 --- a/pkg/games/library_test.go +++ b/pkg/games/library_test.go @@ -10,32 +10,44 @@ import ( func TestLibraryScan(t *testing.T) { tests := []struct { directory string - expected []string + expected []struct { + name string + system string + } }{ { directory: "../../assets/games", - expected: []string{ - "Alwa's Awakening (Demo)", "Sushi The Cat", "anguna", + expected: []struct { + name string + system string + }{ + {name: "Alwa's Awakening (Demo)", system: "nes"}, + {name: "Sushi The Cat", system: "gba"}, + {name: "anguna", system: "gba"}, }, }, } + emuConf := config.Emulator{Libretro: config.LibretroConfig{}} + emuConf.Libretro.Cores.List = map[string]config.LibretroCoreConfig{ + "nes": {Roms: []string{"nes"}}, + "gba": {Roms: []string{"gba"}}, + } + l := logger.NewConsole(false, "w", false) for _, test := range tests { library := NewLib(config.Library{ BasePath: test.directory, Supported: []string{"gba", "zip", "nes"}, - }, l) + }, emuConf, l) library.Scan() games := library.GetAll() - list := _map(games, func(g GameMetadata) string { return g.Name }) - all := true for _, expect := range test.expected { found := false - for _, game := range list { - if game == expect { + for _, game := range games { + if game.Name == expect.name && (expect.system != "" && expect.system == game.System) { found = true break } @@ -43,7 +55,7 @@ func TestLibraryScan(t *testing.T) { all = all && found } if !all { - t.Errorf("Test fail for dir %v with %v != %v", test.directory, list, test.expected) + t.Errorf("Test fail for dir %v with %v != %v", test.directory, games, test.expected) } } } @@ -54,18 +66,10 @@ func Benchmark(b *testing.B) { library := NewLib(config.Library{ BasePath: "../../assets/games", Supported: []string{"gba", "zip", "nes"}, - }, log) + }, config.Emulator{}, log) for i := 0; i < b.N; i++ { library.Scan() _ = library.GetAll() } } - -func _map(vs []GameMetadata, f func(info GameMetadata) string) []string { - vsm := make([]string, len(vs)) - for i, v := range vs { - vsm[i] = f(v) - } - return vsm -} diff --git a/pkg/worker/coordinatorhandlers.go b/pkg/worker/coordinatorhandlers.go index 5dab2c9c..420aaaef 100644 --- a/pkg/worker/coordinatorhandlers.go +++ b/pkg/worker/coordinatorhandlers.go @@ -81,7 +81,9 @@ func (c *coordinator) HandleGameStart(rq api.StartGameRequest[com.Uid], w *Worke if room == nil { room = NewRoom( rq.Room.Rid, - games.GameMetadata{Name: rq.Game.Name, Base: rq.Game.Base, Type: rq.Game.Type, Path: rq.Game.Path}, + games.GameMetadata{ + Name: rq.Game.Name, Base: rq.Game.Base, Type: rq.Game.Type, Path: rq.Game.Path, System: rq.Game.System, + }, func(room *Room) { w.router.SetRoom(nil) c.CloseRoom(room.id) diff --git a/pkg/worker/room.go b/pkg/worker/room.go index 596a4cd6..2fff1cf3 100644 --- a/pkg/worker/room.go +++ b/pkg/worker/room.go @@ -55,7 +55,7 @@ func NewRoom(id string, game games.GameMetadata, onClose func(*Room), conf confi } room.emulator = nano room.emulator.SetMainSaveName(id) - room.emulator.LoadMetadata(conf.Emulator.GetEmulator(game.Type, game.Path)) + room.emulator.LoadMetadata(game.System) err = room.emulator.LoadGame(game.FullPath(conf.Worker.Library.BasePath)) if err != nil { log.Fatal().Err(err).Msgf("couldn't load the game %v", game) diff --git a/pkg/worker/room_test.go b/pkg/worker/room_test.go index 46ab2031..3d3543bf 100644 --- a/pkg/worker/room_test.go +++ b/pkg/worker/room_test.go @@ -43,7 +43,7 @@ type roomMock struct { func (rm roomMock) Close() { rm.Room.Close() // hack: wait room destruction - time.Sleep(3 * time.Second) + time.Sleep(2 * time.Second) } func (rm roomMock) CloseNowait() { rm.Room.Close() } @@ -64,9 +64,9 @@ var testTempDir = filepath.Join(os.TempDir(), "cloud-game-core-tests") // games var ( - alwas = games.GameMetadata{Name: "Alwa's Awakening (Demo)", Type: "nes", Path: "Alwa's Awakening (Demo).nes"} - sushi = games.GameMetadata{Name: "Sushi The Cat", Type: "gba", Path: "Sushi The Cat.gba"} - fd = games.GameMetadata{Name: "Florian Demo", Type: "n64", Path: "Sample Demo by Florian (PD).z64"} + alwas = games.GameMetadata{Name: "Alwa's Awakening (Demo)", Type: "nes", Path: "Alwa's Awakening (Demo).nes", System: "nes"} + sushi = games.GameMetadata{Name: "Sushi The Cat", Type: "gba", Path: "Sushi The Cat.gba", System: "gba"} + fd = games.GameMetadata{Name: "Florian Demo", Type: "n64", Path: "Sample Demo by Florian (PD).z64", System: "n64"} ) func init() {