mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-07-19 17:44:50 +00:00
Merge branch '88hcsif-multitap'
This commit is contained in:
commit
10fa3c5170
17 changed files with 156 additions and 23 deletions
BIN
assets/emulator/libretro/cores/snes9x_libretro.armv7-neon-hf.so
vendored
Executable file
BIN
assets/emulator/libretro/cores/snes9x_libretro.armv7-neon-hf.so
vendored
Executable file
Binary file not shown.
BIN
assets/emulator/libretro/cores/snes9x_libretro.dll
vendored
Executable file
BIN
assets/emulator/libretro/cores/snes9x_libretro.dll
vendored
Executable file
Binary file not shown.
BIN
assets/emulator/libretro/cores/snes9x_libretro.dylib
vendored
Normal file
BIN
assets/emulator/libretro/cores/snes9x_libretro.dylib
vendored
Normal file
Binary file not shown.
BIN
assets/emulator/libretro/cores/snes9x_libretro.so
vendored
BIN
assets/emulator/libretro/cores/snes9x_libretro.so
vendored
Binary file not shown.
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -19,4 +19,6 @@ type CloudEmulator interface {
|
|||
GetHashPath() string
|
||||
// Close will be called when the game is done
|
||||
Close()
|
||||
|
||||
ToggleMultitap() error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
6
web/js/controller.js
vendored
6
web/js/controller.js
vendored
|
|
@ -389,6 +389,12 @@
|
|||
updatePlayerIndex(3);
|
||||
break;
|
||||
|
||||
// toggle multitap
|
||||
case KEY.MULTITAP:
|
||||
socket.toggleMultitap();
|
||||
break;
|
||||
|
||||
// quit
|
||||
case KEY.QUIT:
|
||||
input.poll().disable();
|
||||
|
||||
|
|
|
|||
47
web/js/input/joystick.js
vendored
47
web/js/input/joystick.js
vendored
|
|
@ -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
|
||||
|
|
|
|||
3
web/js/input/keyboard.js
vendored
3
web/js/input/keyboard.js
vendored
|
|
@ -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 = {};
|
||||
|
|
|
|||
1
web/js/input/keys.js
vendored
1
web/js/input/keys.js
vendored
|
|
@ -29,5 +29,6 @@ const KEY = (() => {
|
|||
R2: 'r2',
|
||||
L3: 'l3',
|
||||
R3: 'r3',
|
||||
MULTITAP: 'multitap',
|
||||
}
|
||||
})();
|
||||
|
|
|
|||
4
web/js/network/socket.js
vendored
4
web/js/network/socket.js
vendored
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue