Merge branch '88hcsif-multitap'

This commit is contained in:
giongto35 2020-09-13 03:42:34 +08:00
commit 10fa3c5170
17 changed files with 156 additions and 23 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -61,6 +61,7 @@ type EmulatorMeta struct {
Rotation image.Rotate
IsGlAllowed bool
UsesLibCo bool
HasMultitap bool
}
var EmulatorConfig = map[string]EmulatorMeta{
@ -80,9 +81,10 @@ var EmulatorConfig = map[string]EmulatorMeta{
Height: 240,
},
"snes": {
Path: "assets/emulator/libretro/cores/mednafen_snes_libretro",
Path: "assets/emulator/libretro/cores/snes9x_libretro",
Width: 256,
Height: 224,
HasMultitap: true,
},
"mame": {
Path: "assets/emulator/libretro/cores/fbneo_libretro",

View file

@ -171,4 +171,19 @@ func (o *Server) RouteBrowser(client *BrowserClient) {
return resp
})
client.Receive("multitap", func(resp cws.WSPacket) (req cws.WSPacket) {
client.Println("Received multitap request from a browser -> relay to worker")
// TODO: Async
resp.SessionID = client.SessionID
resp.RoomID = client.RoomID
wc, ok := o.workerClients[client.WorkerID]
if !ok {
return cws.EmptyPacket
}
resp = wc.SyncSend(resp)
return resp
})
}

View file

@ -83,6 +83,10 @@ bool bridge_retro_unserialize(void *f, void *data, size_t size) {
return ((bool (*)(void*, size_t))f)(data, size);
}
void bridge_retro_set_controller_port_device(void *f, unsigned port, unsigned device) {
return ((void (*)(unsigned, unsigned))f)(port, device);
}
bool coreEnvironment_cgo(unsigned cmd, void *data) {
bool coreEnvironment(unsigned, void*);
return coreEnvironment(cmd, data);

View file

@ -189,7 +189,7 @@ func (na *naEmulator) listenInput() {
}
func (na *naEmulator) LoadMeta(path string) config.EmulatorMeta {
coreLoad(na.meta.Path, na.meta.IsGlAllowed, na.meta.UsesLibCo, na.meta.Config)
coreLoad(na.meta)
coreLoadGame(path)
na.gamePath = path
@ -255,6 +255,14 @@ func (na *naEmulator) LoadGame() error {
return nil
}
func (na *naEmulator) ToggleMultitap() error {
if na.roomID != "" {
toggleMultitap()
}
return nil
}
func (na *naEmulator) GetHashPath() string {
return util.GetSavePath(na.roomID)
}

View file

@ -49,6 +49,7 @@ void* bridge_retro_get_memory_data(void *f, unsigned id);
bool bridge_retro_serialize(void *f, void *data, size_t size);
bool bridge_retro_unserialize(void *f, void *data, size_t size);
size_t bridge_retro_serialize_size(void *f);
void bridge_retro_set_controller_port_device(void *f, unsigned port, unsigned device);
bool coreEnvironment_cgo(unsigned cmd, void *data);
void coreVideoRefresh_cgo(void *data, unsigned width, unsigned height, size_t pitch);
@ -100,6 +101,12 @@ var isGlAllowed bool
var usesLibCo bool
var coreConfig ConfigProperties
var multitap struct {
supported bool
enabled bool
value C.unsigned
}
var systemDirectory = C.CString("./pkg/emulator/libretro/system")
var saveDirectory = C.CString(".")
var currentUser *C.char
@ -131,6 +138,7 @@ type CloudEmulator interface {
LoadGame() error
GetHashPath() string
Close()
ToggleMultitap() error
}
//export coreVideoRefresh
@ -328,6 +336,22 @@ func coreEnvironment(cmd C.unsigned, data unsafe.Pointer) C.bool {
return true
}
return false
case C.RETRO_ENVIRONMENT_SET_CONTROLLER_INFO:
if (multitap.supported) {
info := (*[100]C.struct_retro_controller_info)(data)
var i C.unsigned
for i = 0; unsafe.Pointer(info[i].types) != nil; i++ {
var j C.unsigned
types := (*[100]C.struct_retro_controller_description)(unsafe.Pointer(info[i].types))
for j = 0; j < info[i].num_types; j++ {
if (C.GoString(types[j].desc) == "Multitap") {
multitap.value = types[j].id
return true
}
}
}
}
return false
default:
//fmt.Println("[Env]: command not implemented", cmd)
return false
@ -478,6 +502,7 @@ var retroGetMemoryData unsafe.Pointer
var retroSerializeSize unsafe.Pointer
var retroSerialize unsafe.Pointer
var retroUnserialize unsafe.Pointer
var retroSetControllerPortDevice unsafe.Pointer
func loadFunction(handle unsafe.Pointer, name string) unsafe.Pointer {
cs := C.CString(name)
@ -486,15 +511,19 @@ func loadFunction(handle unsafe.Pointer, name string) unsafe.Pointer {
return pointer
}
func coreLoad(pathNoExt string, isGlAllowedParam bool, usesLibCoParam bool, pathToCfg string) {
isGlAllowed = isGlAllowedParam
usesLibCo = usesLibCoParam
coreConfig = ScanConfigFile(pathToCfg)
func coreLoad(meta config.EmulatorMeta) {
isGlAllowed = meta.IsGlAllowed
usesLibCo = meta.UsesLibCo
coreConfig = ScanConfigFile(meta.Config)
multitap.supported = meta.HasMultitap
multitap.enabled = false
multitap.value = 0
mu.Lock()
// Different OS requires different library, bruteforce till it finish
for _, ext := range config.EmulatorExtension {
pathWithExt := pathNoExt + ext
pathWithExt := meta.Path + ext
cs := C.CString(pathWithExt)
retroHandle = C.dlopen(cs, C.RTLD_LAZY)
C.free(unsafe.Pointer(cs))
@ -505,7 +534,7 @@ func coreLoad(pathNoExt string, isGlAllowedParam bool, usesLibCoParam bool, path
if retroHandle == nil {
err := C.dlerror()
log.Fatalf("error loading %s, err %+v", pathNoExt, *err)
log.Fatalf("error loading %s, err %+v", meta.Path, *err)
}
retroInit = loadFunction(retroHandle, "retro_init")
@ -525,6 +554,7 @@ func coreLoad(pathNoExt string, isGlAllowedParam bool, usesLibCoParam bool, path
retroSerializeSize = loadFunction(retroHandle, "retro_serialize_size")
retroSerialize = loadFunction(retroHandle, "retro_serialize")
retroUnserialize = loadFunction(retroHandle, "retro_unserialize")
retroSetControllerPortDevice = loadFunction(retroHandle, "retro_set_controller_port_device")
mu.Unlock()
@ -650,6 +680,21 @@ func coreLoadGame(filename string) {
}
}
func toggleMultitap() {
if (multitap.supported && multitap.value != 0) {
// Official SNES games only support a single multitap device
// Most require it to be plugged in player 2 port
// And Snes9X requires it to be "plugged" after the game is loaded
// Control this from the browser since player 2 will stop working in some games if multitap is "plugged" in
if (multitap.enabled) {
C.bridge_retro_set_controller_port_device(retroSetControllerPortDevice, 1, C.RETRO_DEVICE_JOYPAD)
} else {
C.bridge_retro_set_controller_port_device(retroSetControllerPortDevice, 1, multitap.value)
}
multitap.enabled = !multitap.enabled
}
}
// serializeSize returns the amount of data the implementation requires to serialize
// internal state (save states).
// Between calls to retro_load_game() and retro_unload_game(), the

View file

@ -19,4 +19,6 @@ type CloudEmulator interface {
GetHashPath() string
// Close will be called when the game is done
Close()
ToggleMultitap() error
}

View file

@ -265,6 +265,26 @@ func (h *Handler) RouteCoordinator() {
return req
})
oClient.Receive(
"multitap",
func(resp cws.WSPacket) (req cws.WSPacket) {
log.Println("Received a multitap toggle from coordinator")
req.ID = "multitap"
req.Data = "ok"
if resp.RoomID != "" {
room := h.getRoom(resp.RoomID)
err := room.ToggleMultitap()
if err != nil {
log.Println("[!] Could not toggle multitap state: ", err)
req.Data = "error"
}
} else {
req.Data = "error"
}
return req
})
oClient.Receive(
"terminateSession",
func(resp cws.WSPacket) (req cws.WSPacket) {

View file

@ -451,6 +451,12 @@ func (r *Room) LoadGame() error {
return err
}
func (r *Room) ToggleMultitap() error {
err := r.director.ToggleMultitap()
return err
}
func (r *Room) EmptySessions() bool {
return len(r.rtcSessions) == 0
}

View file

@ -389,6 +389,12 @@
updatePlayerIndex(3);
break;
// toggle multitap
case KEY.MULTITAP:
socket.toggleMultitap();
break;
// quit
case KEY.QUIT:
input.poll().disable();

View file

@ -205,19 +205,40 @@ const joystick = (() => {
}
// https://bugs.chromium.org/p/chromium/issues/detail?id=1076272
if (browser === 'chrome' && gamepad.id.includes('PLAYSTATION(R)3')) {
joystickMap = {
0: KEY.A,
1: KEY.B,
2: KEY.Y,
3: KEY.X,
4: KEY.L,
5: KEY.R,
8: KEY.SELECT,
9: KEY.START,
10: KEY.DTOGGLE,
11: KEY.R3,
};
if (gamepad.id.includes('PLAYSTATION(R)3')) {
if (browser === 'chrome') {
joystickMap = {
1: KEY.A,
0: KEY.B,
2: KEY.Y,
3: KEY.X,
4: KEY.L,
5: KEY.R,
8: KEY.SELECT,
9: KEY.START,
10: KEY.DTOGGLE,
11: KEY.R3,
};
} else {
joystickMap = {
13: KEY.A,
14: KEY.B,
12: KEY.X,
15: KEY.Y,
3: KEY.START,
0: KEY.SELECT,
4: KEY.UP,
6: KEY.DOWN,
7: KEY.LEFT,
5: KEY.RIGHT,
10: KEY.L,
11: KEY.R,
8: KEY.L2,
9: KEY.R2,
1: KEY.DTOGGLE,
2: KEY.R3,
};
}
}
// reset state

View file

@ -30,7 +30,8 @@ const keyboard = (() => {
KeyF: KEY.FULL,
KeyH: KEY.HELP,
Backslash: KEY.STATS,
Digit9: KEY.SETTINGS
Digit9: KEY.SETTINGS,
KeyM: KEY.MULTITAP
});
let keyMap = {};

View file

@ -29,5 +29,6 @@ const KEY = (() => {
R2: 'r2',
L3: 'l3',
R3: 'r3',
MULTITAP: 'multitap',
}
})();

View file

@ -114,6 +114,7 @@ const socket = (() => {
"player_index": playerIndex
});
const quitGame = (roomId) => send({"id": "quit", "data": "", "room_id": roomId});
const toggleMultitap = () => send({"id": "multitap", "data": ""});
return {
init: init,
@ -123,6 +124,7 @@ const socket = (() => {
loadGame: loadGame,
updatePlayerIndex: updatePlayerIndex,
startGame: startGame,
quitGame: quitGame
quitGame: quitGame,
toggleMultitap: toggleMultitap,
}
})($, event, log);