mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-01-23 10:35:44 +00:00
Add the ability to set emulator ROM folder (#348)
Emulators now can load games with the same file extensions if you either place ROMs into the folder named as the emulator in the config file (libretro.core.list value) or any folder as you specify in the `libretro.core.list.{emulator}.folder` config param.
This commit is contained in:
parent
e7a2b6a33e
commit
47a9d70b74
9 changed files with 109 additions and 26 deletions
14
configs/config.yaml
vendored
14
configs/config.yaml
vendored
|
|
@ -125,10 +125,19 @@ emulator:
|
|||
compression: zip
|
||||
# Libretro core configuration
|
||||
#
|
||||
# The emulator selection will happen in this order:
|
||||
# - based on the folder name in the folder param
|
||||
# - based on the folder name (core name) in the list (i.e. nes, snes)
|
||||
# - based on the rom names in the roms param
|
||||
#
|
||||
# Available config params:
|
||||
# - lib (string)
|
||||
# - config (string)
|
||||
# - roms ([]string)
|
||||
# - folder (string)
|
||||
# By default emulator selection is based on the folder named as cores
|
||||
# in the list (i.e. nes, snes) but if you specify folder param,
|
||||
# then it will try to load the ROM file from that folder first.
|
||||
# - width (int) -- broken
|
||||
# - height (int) -- broken
|
||||
# - ratio (float)
|
||||
|
|
@ -142,11 +151,16 @@ emulator:
|
|||
pcsx:
|
||||
lib: pcsx_rearmed_libretro
|
||||
roms: [ "cue" ]
|
||||
# example of folder override
|
||||
folder: psx
|
||||
# MAME core requires additional manual setup, please read:
|
||||
# https://docs.libretro.com/library/fbneo/
|
||||
mame:
|
||||
lib: fbneo_libretro
|
||||
roms: [ "zip" ]
|
||||
mame2003:
|
||||
lib: mame2003_libretro
|
||||
roms: [ "zip" ]
|
||||
nes:
|
||||
lib: nestopia_libretro
|
||||
roms: [ "nes" ]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package emulator
|
|||
import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Emulator struct {
|
||||
|
|
@ -42,6 +43,7 @@ type LibretroCoreConfig struct {
|
|||
Lib string
|
||||
Config string
|
||||
Roms []string
|
||||
Folder string
|
||||
Width int
|
||||
Height int
|
||||
Ratio float64
|
||||
|
|
@ -64,17 +66,24 @@ func (e Emulator) GetLibretroCoreConfig(emulator string) LibretroCoreConfig {
|
|||
return conf
|
||||
}
|
||||
|
||||
// GetEmulatorByRom returns emulator name by its supported ROM name.
|
||||
// !to cache into an optimized data structure
|
||||
func (e Emulator) GetEmulatorByRom(rom string) string {
|
||||
// GetEmulator tries to find a suitable emulator.
|
||||
// !to remove quadratic complexity
|
||||
func (e Emulator) GetEmulator(rom string, path string) string {
|
||||
found := ""
|
||||
for emu, core := range e.Libretro.Cores.List {
|
||||
for _, romName := range core.Roms {
|
||||
if rom == romName {
|
||||
return emu
|
||||
found = emu
|
||||
if p := strings.SplitN(filepath.ToSlash(path), "/", 2); len(p) > 1 {
|
||||
folder := p[0]
|
||||
if (folder != "" && folder == core.Folder) || folder == emu {
|
||||
return emu
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return found
|
||||
}
|
||||
|
||||
func (e Emulator) GetSupportedExtensions() []string {
|
||||
|
|
|
|||
52
pkg/config/emulator/config_test.go
Normal file
52
pkg/config/emulator/config_test.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package emulator
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGetEmulator(t *testing.T) {
|
||||
tests := []struct {
|
||||
rom string
|
||||
path string
|
||||
config map[string]LibretroCoreConfig
|
||||
emulator string
|
||||
}{
|
||||
{
|
||||
rom: "nes",
|
||||
path: "test/game.nes",
|
||||
config: map[string]LibretroCoreConfig{
|
||||
"snes": {Roms: []string{"nes"}},
|
||||
"nes": {Folder: "test", Roms: []string{"nes"}},
|
||||
},
|
||||
emulator: "nes",
|
||||
},
|
||||
{
|
||||
rom: "nes",
|
||||
path: "nes/game.nes",
|
||||
config: map[string]LibretroCoreConfig{
|
||||
"snes": {Roms: []string{"nes"}},
|
||||
"nes": {Roms: []string{"nes"}},
|
||||
},
|
||||
emulator: "nes",
|
||||
},
|
||||
{
|
||||
rom: "nes",
|
||||
path: "test/game.nes",
|
||||
config: map[string]LibretroCoreConfig{
|
||||
"snes": {Roms: []string{"nes"}},
|
||||
"nes": {Roms: []string{"nes"}},
|
||||
},
|
||||
emulator: "nes",
|
||||
},
|
||||
}
|
||||
|
||||
emu := Emulator{
|
||||
Libretro: LibretroConfig{},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
emu.Libretro.Cores.List = test.config
|
||||
em := emu.GetEmulator(test.rom, test.path)
|
||||
if test.emulator != em {
|
||||
t.Errorf("expected result: %v, but was %v with: %v, %v", test.emulator, em, test.rom, test.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -205,6 +205,7 @@ func newGameStartCall(roomId string, data string, library games.GameLibrary) (ap
|
|||
|
||||
return api.GameStartCall{
|
||||
Name: gameInfo.Name,
|
||||
Base: gameInfo.Base,
|
||||
Path: gameInfo.Path,
|
||||
Type: gameInfo.Type,
|
||||
}, nil
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ func (packet *GameStartRequest) From(data string) error { return from(packet, da
|
|||
|
||||
type GameStartCall struct {
|
||||
Name string `json:"name"`
|
||||
Base string `json:"base"`
|
||||
Path string `json:"path"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ type GameMetadata struct {
|
|||
Name string
|
||||
// the game file extension (e.g. nes, n64)
|
||||
Type string
|
||||
Base string
|
||||
// the game path relative to the library base path
|
||||
Path string
|
||||
}
|
||||
|
|
@ -126,8 +127,8 @@ func (lib *library) GetAll() []GameMetadata {
|
|||
func (lib *library) FindGameByName(name string) GameMetadata {
|
||||
var game GameMetadata
|
||||
if val, ok := lib.games[name]; ok {
|
||||
val.Path = filepath.Join(lib.config.path, val.Path)
|
||||
game = val
|
||||
val.Base = lib.config.path
|
||||
return val
|
||||
}
|
||||
return game
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package games
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
|
@ -31,7 +30,19 @@ func TestLibraryScan(t *testing.T) {
|
|||
return meta.Name
|
||||
})
|
||||
|
||||
if !reflect.DeepEqual(test.expected, list) {
|
||||
// ^2 complexity (;
|
||||
all := true
|
||||
for _, expect := range test.expected {
|
||||
found := false
|
||||
for _, game := range list {
|
||||
if game == expect {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
all = all && found
|
||||
}
|
||||
if !all {
|
||||
t.Errorf("Test fail for dir %v with %v != %v", test.directory, list, test.expected)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,28 +110,21 @@ func (h *Handler) handleGameStart() cws.PacketHandler {
|
|||
log.Println("Received a start request from coordinator")
|
||||
session := h.getSession(resp.SessionID)
|
||||
if session == nil {
|
||||
log.Printf("Error: No session for ID: %s\n", resp.SessionID)
|
||||
log.Printf("error: no session with id: %s", resp.SessionID)
|
||||
return cws.EmptyPacket
|
||||
}
|
||||
|
||||
peerconnection := session.peerconnection
|
||||
// TODO: Standardize for all types of packet. Make WSPacket generic
|
||||
startPacket := api.GameStartCall{}
|
||||
if err := startPacket.From(resp.Data); err != nil {
|
||||
rom := api.GameStartCall{}
|
||||
if err := rom.From(resp.Data); err != nil {
|
||||
return cws.EmptyPacket
|
||||
}
|
||||
gameMeta := games.GameMetadata{
|
||||
Name: startPacket.Name,
|
||||
Type: startPacket.Type,
|
||||
Path: startPacket.Path,
|
||||
}
|
||||
|
||||
room := h.startGameHandler(gameMeta, resp.RoomID, resp.PlayerIndex, peerconnection)
|
||||
game := games.GameMetadata{Name: rom.Name, Type: rom.Type, Base: rom.Base, Path: rom.Path}
|
||||
room := h.startGameHandler(game, resp.RoomID, resp.PlayerIndex, session.peerconnection)
|
||||
session.RoomID = room.ID
|
||||
// TODO: can data race
|
||||
// TODO: can data race (and it does)
|
||||
h.rooms[room.ID] = room
|
||||
|
||||
return cws.WSPacket{ID: "start", RoomID: room.ID}
|
||||
return cws.WSPacket{ID: api.GameStart, RoomID: room.ID}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"math"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/giongto35/cloud-game/v2/pkg/config/worker"
|
||||
|
|
@ -157,8 +158,8 @@ func NewRoom(roomID string, game games.GameMetadata, onlineStorage *storage.Clie
|
|||
// If not then load room or create room from local.
|
||||
log.Printf("Room %s started. GameName: %s, WithGame: %t", roomID, game.Name, cfg.Encoder.WithoutGame)
|
||||
|
||||
// Spawn new emulator based on gameName and plug-in all channels
|
||||
emuName := cfg.Emulator.GetEmulatorByRom(game.Type)
|
||||
// Spawn new emulator and plug-in all channels
|
||||
emuName := cfg.Emulator.GetEmulator(game.Type, game.Path)
|
||||
libretroConfig := cfg.Emulator.GetLibretroCoreConfig(emuName)
|
||||
|
||||
if cfg.Encoder.WithoutGame {
|
||||
|
|
@ -176,7 +177,7 @@ func NewRoom(roomID string, game games.GameMetadata, onlineStorage *storage.Clie
|
|||
room.audioChannel = audioChannel
|
||||
}
|
||||
|
||||
gameMeta := room.director.LoadMeta(game.Path)
|
||||
gameMeta := room.director.LoadMeta(filepath.Join(game.Base, game.Path))
|
||||
|
||||
// nwidth, nheight are the WebRTC output size
|
||||
var nwidth, nheight int
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue