mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-01-23 02:34:42 +00:00
Replace the hasMultitap option with a more general solution
The new hid option enables users to map a specific Libretro device (or multiple devices) to the input ports. For instance, this allows users to map a Multitap controller with the snes9x core.
This commit is contained in:
parent
cdbb5e98f5
commit
91ace06f8b
17 changed files with 47 additions and 88 deletions
|
|
@ -77,7 +77,6 @@ const (
|
|||
SaveGame PT = 106
|
||||
LoadGame PT = 107
|
||||
ChangePlayer PT = 108
|
||||
ToggleMultitap PT = 109
|
||||
RecordGame PT = 110
|
||||
GetWorkerList PT = 111
|
||||
ErrNoFreeSlots PT = 112
|
||||
|
|
@ -112,8 +111,6 @@ func (p PT) String() string {
|
|||
return "SaveGame"
|
||||
case LoadGame:
|
||||
return "LoadGame"
|
||||
case ToggleMultitap:
|
||||
return "ToggleMultitap"
|
||||
case RecordGame:
|
||||
return "RecordGame"
|
||||
case GetWorkerList:
|
||||
|
|
|
|||
|
|
@ -45,9 +45,6 @@ type (
|
|||
TerminateSessionRequest[T Id] struct {
|
||||
Stateful[T]
|
||||
}
|
||||
ToggleMultitapRequest[T Id] struct {
|
||||
StatefulRoom[T]
|
||||
}
|
||||
WebrtcAnswerRequest[T Id] struct {
|
||||
Stateful[T]
|
||||
Sdp string `json:"sdp"`
|
||||
|
|
|
|||
|
|
@ -180,8 +180,12 @@ emulator:
|
|||
# - ratio (float)
|
||||
# - isGlAllowed (bool)
|
||||
# - usesLibCo (bool)
|
||||
# - hasMultitap (bool)
|
||||
# - hasMultitap (bool) -- (removed)
|
||||
# - coreAspectRatio (bool) -- correct the aspect ratio on the client with the info from the core.
|
||||
# - hid (map[int][]int)
|
||||
# A list of device IDs to bind to the input ports.
|
||||
# Some cores allow binding multiple devices to a single port (DosBox), but typically,
|
||||
# you should bind just one device to one port.
|
||||
# - vfr (bool)
|
||||
# (experimental)
|
||||
# Enable variable frame rate only for cores that can't produce a constant frame rate.
|
||||
|
|
@ -226,7 +230,11 @@ emulator:
|
|||
snes:
|
||||
lib: snes9x_libretro
|
||||
roms: [ "smc", "sfc", "swc", "fig", "bs" ]
|
||||
hasMultitap: true
|
||||
hid:
|
||||
# set the 2nd port to RETRO_DEVICE_JOYPAD_MULTITAP ((1<<8) | 1) as SNES9x requires it
|
||||
# in order to support up to 5-player games
|
||||
# see: https://nintendo.fandom.com/wiki/Super_Multitap
|
||||
1: 257
|
||||
n64:
|
||||
lib: mupen64plus_next_libretro
|
||||
roms: [ "n64", "v64", "z64" ]
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ type LibretroCoreConfig struct {
|
|||
CoreAspectRatio bool
|
||||
Folder string
|
||||
Hacks []string
|
||||
HasMultitap bool
|
||||
Height int
|
||||
Hid map[int][]int
|
||||
IsGlAllowed bool
|
||||
Lib string
|
||||
Options map[string]string
|
||||
|
|
|
|||
|
|
@ -84,8 +84,6 @@ func (u *User) HandleRequests(info HasServerInfo, launcher games.Launcher, conf
|
|||
return api.ErrMalformed
|
||||
}
|
||||
u.HandleChangePlayer(*rq)
|
||||
case api.ToggleMultitap:
|
||||
u.HandleToggleMultitap()
|
||||
case api.RecordGame:
|
||||
if !conf.Recording.Enabled {
|
||||
return api.ErrForbidden
|
||||
|
|
|
|||
|
|
@ -98,8 +98,6 @@ func (u *User) HandleChangePlayer(rq api.ChangePlayerUserRequest) {
|
|||
u.Notify(api.ChangePlayer, rq)
|
||||
}
|
||||
|
||||
func (u *User) HandleToggleMultitap() { u.w.ToggleMultitap(u.Id()) }
|
||||
|
||||
func (u *User) HandleRecordGame(rq api.RecordGameRequest[com.Uid]) {
|
||||
if u.w == nil {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -49,10 +49,6 @@ func (w *Worker) ChangePlayer(id com.Uid, index int) (*api.ChangePlayerResponse,
|
|||
w.Send(api.ChangePlayer, api.ChangePlayerRequest[com.Uid]{StatefulRoom: StateRoom(id, w.RoomId), Index: index}))
|
||||
}
|
||||
|
||||
func (w *Worker) ToggleMultitap(id com.Uid) {
|
||||
_, _ = w.Send(api.ToggleMultitap, api.ToggleMultitapRequest[com.Uid]{StatefulRoom: StateRoom(id, w.RoomId)})
|
||||
}
|
||||
|
||||
func (w *Worker) RecordGame(id com.Uid, rec bool, recUser string) (*api.RecordGameResponse, error) {
|
||||
return api.UnwrapChecked[api.RecordGameResponse](
|
||||
w.Send(api.RecordGame, api.RecordGameRequest[com.Uid]{StatefulRoom: StateRoom(id, w.RoomId), Active: rec, User: recUser}))
|
||||
|
|
|
|||
|
|
@ -43,8 +43,6 @@ type Emulator interface {
|
|||
HasSave() bool
|
||||
// Close will be called when the game is done
|
||||
Close()
|
||||
// ToggleMultitap toggles multitap controller.
|
||||
ToggleMultitap()
|
||||
// Input passes input to the emulator
|
||||
Input(player int, data []byte)
|
||||
// Scale returns set video scale factor
|
||||
|
|
@ -159,8 +157,8 @@ func (f *Frontend) LoadCore(emu string) {
|
|||
meta := nanoarch.Metadata{
|
||||
AutoGlContext: conf.AutoGlContext,
|
||||
Hacks: conf.Hacks,
|
||||
HasMultitap: conf.HasMultitap,
|
||||
HasVFR: conf.VFR,
|
||||
Hid: conf.Hid,
|
||||
IsGlAllowed: conf.IsGlAllowed,
|
||||
LibPath: conf.Lib,
|
||||
Options: conf.Options,
|
||||
|
|
@ -309,7 +307,6 @@ func (f *Frontend) SetSessionId(name string) { f.storage.SetMainSaveName(na
|
|||
func (f *Frontend) SetDataCb(cb func([]byte)) { f.onData = cb }
|
||||
func (f *Frontend) SetVideoCb(ff func(app.Video)) { f.onVideo = ff }
|
||||
func (f *Frontend) Tick() { f.mu.Lock(); f.nano.Run(); f.mu.Unlock() }
|
||||
func (f *Frontend) ToggleMultitap() { f.nano.ToggleMultitap() }
|
||||
func (f *Frontend) ViewportRecalculate() { f.mu.Lock(); f.vw, f.vh = f.ViewportCalc(); f.mu.Unlock() }
|
||||
func (f *Frontend) ViewportSize() (int, int) { return f.vw, f.vh }
|
||||
|
||||
|
|
|
|||
|
|
@ -43,11 +43,7 @@ type Nanoarch struct {
|
|||
Handlers
|
||||
LastFrameTime int64
|
||||
LibCo bool
|
||||
multitap struct {
|
||||
supported bool
|
||||
enabled bool
|
||||
value C.unsigned
|
||||
}
|
||||
meta Metadata
|
||||
options *map[string]string
|
||||
reserved chan struct{} // limits concurrent use
|
||||
Rot uint
|
||||
|
|
@ -96,10 +92,10 @@ type Metadata struct {
|
|||
IsGlAllowed bool
|
||||
UsesLibCo bool
|
||||
AutoGlContext bool
|
||||
HasMultitap bool
|
||||
HasVFR bool
|
||||
Options map[string]string
|
||||
Hacks []string
|
||||
Hid map[int][]int
|
||||
CoreAspectRatio bool
|
||||
}
|
||||
|
||||
|
|
@ -159,6 +155,7 @@ func (n *Nanoarch) SetVideoDebounce(t time.Duration) { n.limiter = NewLimit(t) }
|
|||
|
||||
func (n *Nanoarch) CoreLoad(meta Metadata) {
|
||||
var err error
|
||||
n.meta = meta
|
||||
n.LibCo = meta.UsesLibCo
|
||||
n.vfr = meta.HasVFR
|
||||
n.Aspect = meta.CoreAspectRatio
|
||||
|
|
@ -170,10 +167,6 @@ func (n *Nanoarch) CoreLoad(meta Metadata) {
|
|||
|
||||
n.options = &meta.Options
|
||||
|
||||
n.multitap.supported = meta.HasMultitap
|
||||
n.multitap.enabled = false
|
||||
n.multitap.value = 0
|
||||
|
||||
filePath := meta.LibPath
|
||||
if ar, err := arch.Guess(); err == nil {
|
||||
filePath = filePath + ar.LibExt
|
||||
|
|
@ -298,34 +291,24 @@ func (n *Nanoarch) LoadGame(path string) error {
|
|||
}
|
||||
|
||||
// set default controller types on all ports
|
||||
// needed for nestopia
|
||||
for i := 0; i < MaxPort; i++ {
|
||||
C.bridge_retro_set_controller_port_device(retroSetControllerPortDevice, C.uint(i), C.RETRO_DEVICE_JOYPAD)
|
||||
}
|
||||
|
||||
// map custom devices to ports
|
||||
for k, v := range n.meta.Hid {
|
||||
for _, device := range v {
|
||||
C.bridge_retro_set_controller_port_device(retroSetControllerPortDevice, C.uint(k), C.unsigned(device))
|
||||
n.log.Debug().Msgf("set custom port-device: %v:%v", k, device)
|
||||
}
|
||||
}
|
||||
|
||||
n.LastFrameTime = time.Now().UnixNano()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToggleMultitap toggles multitap controller for cores.
|
||||
//
|
||||
// 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.
|
||||
func (n *Nanoarch) ToggleMultitap() {
|
||||
if !n.multitap.supported || n.multitap.value == 0 {
|
||||
return
|
||||
}
|
||||
mt := n.multitap.value
|
||||
if n.multitap.enabled {
|
||||
mt = C.RETRO_DEVICE_JOYPAD
|
||||
}
|
||||
C.bridge_retro_set_controller_port_device(retroSetControllerPortDevice, 1, mt)
|
||||
n.multitap.enabled = !n.multitap.enabled
|
||||
}
|
||||
|
||||
func (n *Nanoarch) Shutdown() {
|
||||
if n.LibCo {
|
||||
thread.Main(func() {
|
||||
|
|
@ -775,23 +758,30 @@ func coreEnvironment(cmd C.unsigned, data unsafe.Pointer) C.bool {
|
|||
}
|
||||
return false
|
||||
case C.RETRO_ENVIRONMENT_SET_CONTROLLER_INFO:
|
||||
// !to rewrite
|
||||
if !Nan0.multitap.supported {
|
||||
if Nan0.log.GetLevel() > logger.DebugLevel {
|
||||
return false
|
||||
}
|
||||
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" {
|
||||
Nan0.multitap.value = types[j].id
|
||||
return true
|
||||
}
|
||||
|
||||
info := (*[64]C.struct_retro_controller_info)(data)
|
||||
for c, controller := range info {
|
||||
tp := unsafe.Pointer(controller.types)
|
||||
if tp == nil {
|
||||
break
|
||||
}
|
||||
cInfo := strings.Builder{}
|
||||
cInfo.WriteString(fmt.Sprintf("Controller [%v] ", c))
|
||||
cd := (*[32]C.struct_retro_controller_description)(tp)
|
||||
delim := ", "
|
||||
n := int(controller.num_types)
|
||||
for i := 0; i < n; i++ {
|
||||
if i == n-1 {
|
||||
delim = ""
|
||||
}
|
||||
cInfo.WriteString(fmt.Sprintf("%v: %v%s", cd[i].id, C.GoString(cd[i].desc), delim))
|
||||
}
|
||||
Nan0.log.Debug().Msgf("%v", cInfo.String())
|
||||
}
|
||||
return false
|
||||
return true
|
||||
case C.RETRO_ENVIRONMENT_GET_CLEAR_ALL_THREAD_WAITS_CB:
|
||||
C.bridge_clear_all_thread_waits_cb(data)
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -125,12 +125,6 @@ func (c *coordinator) HandleRequests(w *Worker) chan struct{} {
|
|||
} else {
|
||||
out = c.HandleChangePlayer(*dat, w)
|
||||
}
|
||||
case api.ToggleMultitap:
|
||||
if dat := api.Unwrap[api.ToggleMultitapRequest[com.Uid]](x.Payload); dat == nil {
|
||||
err, out = api.ErrMalformed, api.EmptyPacket
|
||||
} else {
|
||||
c.HandleToggleMultitap(*dat, w)
|
||||
}
|
||||
case api.RecordGame:
|
||||
if dat := api.Unwrap[api.RecordGameRequest[com.Uid]](x.Payload); dat == nil {
|
||||
err, out = api.ErrMalformed, api.EmptyPacket
|
||||
|
|
|
|||
|
|
@ -234,15 +234,6 @@ func (c *coordinator) HandleChangePlayer(rq api.ChangePlayerRequest[com.Uid], w
|
|||
return api.Out{Payload: rq.Index}
|
||||
}
|
||||
|
||||
func (c *coordinator) HandleToggleMultitap(rq api.ToggleMultitapRequest[com.Uid], w *Worker) api.Out {
|
||||
r := w.router.FindRoom(rq.Rid)
|
||||
if r == nil {
|
||||
return api.ErrPacket
|
||||
}
|
||||
room.WithEmulator(r.App()).ToggleMultitap()
|
||||
return api.OkPacket
|
||||
}
|
||||
|
||||
func (c *coordinator) HandleRecordGame(rq api.RecordGameRequest[com.Uid], w *Worker) api.Out {
|
||||
if !w.conf.Recording.Enabled {
|
||||
return api.ErrPacket
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@
|
|||
<script src="js/event/event.js?v=5"></script>
|
||||
<script src="js/input/keys.js?v=3"></script>
|
||||
<script src="js/settings/opts.js?v=2"></script>
|
||||
<script src="js/settings/settings.js?v=4"></script>
|
||||
<script src="js/settings/settings.js?v=5"></script>
|
||||
<script src="js/env.js?v=5"></script>
|
||||
<script src="js/input/input.js?v=3"></script>
|
||||
<script src="js/gameList.js?v=3"></script>
|
||||
|
|
@ -136,7 +136,7 @@
|
|||
<script src="js/api/api.js?v=3"></script>
|
||||
<script src="js/workerManager.js?v=1"></script>
|
||||
<script src="js/stats/stats.js?v=1"></script>
|
||||
<script src="js/controller.js?v=9"></script>
|
||||
<script src="js/controller.js?v=10"></script>
|
||||
<script src="js/input/keyboard.js?v=6"></script>
|
||||
<script src="js/input/touch.js?v=3"></script>
|
||||
<script src="js/input/joystick.js?v=3"></script>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ const api = (() => {
|
|||
GAME_SAVE: 106,
|
||||
GAME_LOAD: 107,
|
||||
GAME_SET_PLAYER_INDEX: 108,
|
||||
GAME_TOGGLE_MULTITAP: 109,
|
||||
GAME_RECORDING: 110,
|
||||
GET_WORKER_LIST: 111,
|
||||
GAME_ERROR_NO_FREE_SLOTS: 112,
|
||||
|
|
@ -58,7 +57,6 @@ const api = (() => {
|
|||
record: record,
|
||||
record_user: recordUser,
|
||||
}),
|
||||
toggleMultitap: () => packet(endpoints.GAME_TOGGLE_MULTITAP),
|
||||
toggleRecording: (active = false, userName = '') =>
|
||||
packet(endpoints.GAME_RECORDING, {
|
||||
active: active,
|
||||
|
|
|
|||
|
|
@ -393,9 +393,6 @@
|
|||
case KEY.PAD4:
|
||||
updatePlayerIndex(3);
|
||||
break;
|
||||
case KEY.MULTITAP:
|
||||
api.game.toggleMultitap();
|
||||
break;
|
||||
case KEY.QUIT:
|
||||
input.poll.disable();
|
||||
api.game.quit(room.getId());
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ const keyboard = (() => {
|
|||
KeyH: KEY.HELP,
|
||||
Backslash: KEY.STATS,
|
||||
Digit9: KEY.SETTINGS,
|
||||
KeyM: KEY.MULTITAP,
|
||||
KeyT: KEY.DTOGGLE
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ const KEY = (() => {
|
|||
R2: 'r2',
|
||||
L3: 'l3',
|
||||
R3: 'r3',
|
||||
MULTITAP: 'multitap',
|
||||
REC: 'rec',
|
||||
}
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
const settings = (() => {
|
||||
// internal structure version
|
||||
const revision = 1.3;
|
||||
const revision = 1.4;
|
||||
|
||||
// default settings
|
||||
// keep them for revert to defaults option
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue