Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

link cable emulation between multiple instances #18

Open
JulienNoble opened this issue Nov 8, 2017 · 98 comments
Open

link cable emulation between multiple instances #18

JulienNoble opened this issue Nov 8, 2017 · 98 comments

Comments

@JulienNoble
Copy link

Hi!

I realize the demand is pretty specific : I'd like to emulate link cables between virtual gameboys for sync stuff. The context is I use LSDJ to make music with gameboys, and you can connect multiple gameboys to get them to play together at the same rhythm, using a master/slave config.

On Windows, I usually use BGB, which supports it, but on Mac and Linux, you have to go through Wine. It works... okay let's say. It's just hella laggy and tends to desync pretty rapidly.

Just discovered your project today, so I don't know if this is possible at all. From what I can see, BGB creates a listening server that other instances can connect to. I'll check out if I can help about this

@LIJI32
Copy link
Owner

LIJI32 commented Nov 8, 2017

I plan to add netplay and multiple instances link cable emulation in version 0.11 (version 0.10 will be released this month).

@JulienNoble
Copy link
Author

nice! Thanks 👍

@andres-asm
Copy link
Contributor

That would be amazing, for the libretro core I could make a single libretro core run two instances if you can help me to link them :)

@LIJI32
Copy link
Owner

LIJI32 commented Nov 12, 2017

Actually, adding it to the libretro core might actually be already possible as SameBoy already emulates the Link Cable – the things missing in the Cocoa and SDL ports is the GUI, and networking and synchronization code, which are already provided by libretro itself as far as I know.

The Link Cable API is:

  • GB_set_serial_transfer_start_callback – called when the GB is in master mode and starts sending a byte of data.
  • GB_set_serial_transfer_end_callback – called when the GB is in master mode and finishes sending a byte of data. The callback should return the byte received by the other side.
  • GB_serial_get_data – call this function when the GB is in slave mode; it returns the data the GB wants to send over the cable. This should be called when the master side requests data from the slave side.
  • GB_serial_set_data – call this function when the GB is in slave mode to set the data the GB would read. This should be called when the master side provides data to the slave side.

@andres-asm
Copy link
Contributor

I wanted to start working on that, so I have a core that runs 2 instances (thanks to @bparker06 for helping me with the blitting issue)

image

But I have exactly half performance now, I suspect because of the vblank thing, not sure if there's a sleep somewhere that causes this.

Once we manage to iron these out I can start trying to hook up the proper API

@andres-asm
Copy link
Contributor

Errr... fixed the slowdown by commenting the audio_callback from GB2... guess that means I need to try to hookup serial now 📦

@LIJI32
Copy link
Owner

LIJI32 commented Jan 27, 2018

Oh looking cool! Slowdown makes sense because IIRC libretro times everything by the audio, and you were giving it 2 times the amount of audio. I hope frame synchronization would be good enough for serial though, it might be tricky.

@andres-asm
Copy link
Contributor

Well I have questions about the API, do I have to actually implement the callback for link cable?

Because as far as I can see in printer.c your actual implementation is there, so I guess I would need to know exactly what needs to be sent?

@LIJI32
Copy link
Owner

LIJI32 commented Jan 27, 2018

The printer is always running is slave mode (therefore the Game Boy runs in master mode), therefore the Game Boy side uses the callback API. Since you're emulating two Game Boys, both need to implement both master and slave logic (Because you can't know which will be master).

Basically it's very simple, you set configure the callbacks for both instances:

  • When GB_set_serial_transfer_start_callback is called for GB1, you call GB_serial_set_data on GB2 with byte_received, and vice versa
  • When GB_set_serial_transfer_start_callback is called for GB1, you return GB_serial_get_data on GB2, and vice versa.

Assuming both instances are synchronized well enough, this should just work :)

@andres-asm
Copy link
Contributor

Well, it does.. something

https://www.youtube.com/watch?v=m62frQp3DPg&feature=youtu.be

@andres-asm
Copy link
Contributor

What should I return in the end callback?

@LIJI32
Copy link
Owner

LIJI32 commented Jan 27, 2018

This is.. interesting... The end callback should return the value received from the other side of the serial. I.e. GB_serial_get_data(&gb2) for gb1 and GB_serial_get_data(&gb1) for gb2

@andres-asm
Copy link
Contributor

Ok that makes more sense now, I think you made a typo in your explanation
Let me test.

@andres-asm
Copy link
Contributor

Ok both "linked now" very slowly and input no longer worked :P
But yay progress

@andres-asm
Copy link
Contributor

andres-asm commented Jan 27, 2018

Progress?

https://youtu.be/BrNBAqrzztc

static void serial_start1(GB_gameboy_t *gb, uint8_t byte_received)
{
   GB_serial_set_data(&gb2, byte_received);
}

static uint8_t serial_end1(GB_gameboy_t *gb)
{
   return GB_serial_get_data(&gb2);;
}

static void serial_start2(GB_gameboy_t *gb, uint8_t byte_received)
{
   GB_serial_set_data(&gb1, byte_received);
   
}

static uint8_t serial_end2(GB_gameboy_t *gb)
{
   return GB_serial_get_data(&gb1);;
}

This is my code

@LIJI32
Copy link
Owner

LIJI32 commented Jan 27, 2018

Looks correct other than the nop call to GB_serial_get_data(&gb2). Can you test a Pokémon link battle in GSC? I'm relatively familiar with serial oddities there.

@andres-asm
Copy link
Contributor

*GBC/GSC I guess?
I think I need a save for that? also I need to hookup saves for GB #2 :P

@LIJI32
Copy link
Owner

LIJI32 commented Jan 27, 2018

Yeah you'd need a save for that (otherwise it'd be very annoying) but it can be the same save for both instances. Any Gen 2 game would work.

@andres-asm
Copy link
Contributor

I just get this on pokemon

image

@andres-asm
Copy link
Contributor

I pushed it into a branch in case you want to check it out
https://github.com/libretro/SameBoy/tree/link

@LIJI32
Copy link
Owner

LIJI32 commented Jan 27, 2018

Exactly what I thought. Since you run the instances frame-by-frame, the master instance is always one frame (about 1/60 seconds) ahead of the slave one. The serial, however, runs in 8192Hz, meaning it sends about 17 bytes per frame. This means the master instance sends the slave instance 17 bytes before the slave instance gets a chance to process anything, and then it effectively processes the last byte 17 times in a row.

The way to solve it is by redoing how synchronizing the instances is done. An easy, naive, wrong, but still better way of doing it is by using GB_run instead of GB_run_frame, and using a vblank callback to end the run loop manually. A better method would require a slight API improvement on my side.

@andres-asm
Copy link
Contributor

What function would I use to end the loop in the vblank callback?

@LIJI32
Copy link
Owner

LIJI32 commented Jan 27, 2018

Set a VBlank callback for both instances that simply sets a bool to true. Then, instead of using GB_run_frame use:

while (!vblank1_occurred || !vblank2_occured) {
    GB_run(&gb1);
    GB_run(&gb2);
}
vblank1_occurred = vblank2_occured = false;

@andres-asm
Copy link
Contributor

Wasn't able to figure it out :p

@LIJI32
Copy link
Owner

LIJI32 commented Jan 27, 2018

I'll have a look at it tomorrow :)

@andres-asm
Copy link
Contributor

Cool ty!

@andres-asm
Copy link
Contributor

Hey, did you have a chance to take a look?

@LIJI32
Copy link
Owner

LIJI32 commented Jan 31, 2018

Yeah, I just finished adding proper synchronization between the two instances yet Tetris 2 behaves the same. I'll have to investigate further I guess.

@LIJI32
Copy link
Owner

LIJI32 commented Jan 31, 2018

Tetris 2 does seem to work on an old internal build of SameBoy 0.7 that has experimental Netplay support, so it's clearly not an emulation bug.

@andres-asm
Copy link
Contributor

ahh, ok, well let me know if I can do anything :)

@andres-asm
Copy link
Contributor

I just noticed super mario bros dx disconnects at the level select screen, pokemon seems to still work properly though

@andres-asm
Copy link
Contributor

andres-asm commented May 25, 2018

Tetris seems to still work too:

image

And Tetris DX

image

@andres-asm
Copy link
Contributor

Some guy did some thorough testing btw
https://www.youtube.com/watch?v=hTeZiN1S4i4&feature=youtu.be

A bit outdated gonna ask him if he can re-test

@ghost
Copy link

ghost commented Jun 16, 2018

Mario tennis works great.

@ghost
Copy link

ghost commented Jan 24, 2019

Anything new with this?

@LIJI32
Copy link
Owner

LIJI32 commented Jan 24, 2019

My estimate of v0.12 (First half of 2019) still applies :)

@lpla
Copy link
Contributor

lpla commented Jun 21, 2019

I guess this is delayed until v0.13, isn't it?

@LIJI32
Copy link
Owner

LIJI32 commented Jun 21, 2019

Sadly yes. While I do have a working POC, the performance penalty is too high and needs more work.

@whazzzzzup17
Copy link

Anyway to load (4) games with this feature instead of two for multiple players to play through RetroArch? Not so much interested in the trading aspect, but more interested in loading (4) separate Game Boy instances for each user to play their own game, such as Pokemon.

@LIJI32
Copy link
Owner

LIJI32 commented Jun 22, 2019

Isn't that just 4 instances of SameBoy open without any link cable emulation? What am I missing?

@whazzzzzup17
Copy link

Yes, but I don't believe there is anyway to run this through one RetroArch emulation. I've been using the link to run both side by side, but it would be awesome to run (4). Top-left, Top-right, Bottom-left, Bottom-right.

Running multiple SameBoy instances is a lot of manual work with a mouse and re-alignment, etc.

@andres-asm
Copy link
Contributor

I don't think there's a way to link 4 so I didn't think it would be worthwhile to implement that.

@whazzzzzup17
Copy link

Bummer. Looks like I'll just have to create a script to run multiple SameBoy outside of RetroArch and re-assign controllers to each exe.

@LIJI32
Copy link
Owner

LIJI32 commented Jun 22, 2019

There actually is a device that allows linking four Game Boys, but it's not emulated yet.

@whazzzzzup17
Copy link

I guess I'm more interested in not linking them through each game, although it would be great. It would be better having multiple instances run side by side, which would thus make a (1) player game into up to (4) players because each would have their own controller and it fills up the rest of the screen real-estate.

Its nice having them all lined up and not have to manually modify each .EXE settings during each initial setup. Whereas through RetroArch, the controller auto set. See example below where I cropped out the title bars, the bottom task bar, etc. But I would still need to change each exe setting before going forward.

image

@andres-asm
Copy link
Contributor

andres-asm commented Jun 22, 2019 via email

@whazzzzzup17
Copy link

whazzzzzup17 commented Jun 22, 2019

Not to mention if (1) user wants to play Pokemon, the other (3) users can play totally different games all through one monitor at the same time. Extremely useful if you have a big 65 inch TV or something like that.

Not sure if this is a different request or can be modified through the same request, as they both involve running multiple instances

@andres-asm
Copy link
Contributor

andres-asm commented Jun 22, 2019 via email

@ghost
Copy link

ghost commented Jul 21, 2019

Ah man I thought it will come soon...
I am still struggling with getting netplay to work with the libretro core.

@LIJI32
Copy link
Owner

LIJI32 commented Jul 21, 2019

It was planned and mostly completed for v0.12, but sadly it had severe performance issues I've haven't managed to solve yet.

@VGkav
Copy link

VGkav commented Aug 19, 2020

So his isn't implemented, right? Because in Retroarch if I go to Subsystems and select "Load 2 Player Game Boy Link" it asks me for a rom, I select Pokemon Crystal and it seems to just load it normally.

(I just want to "trade")

@andres-asm
Copy link
Contributor

subsystems is definitely implemented correctly in the core.
No idea if they changed / broke something in the RA side

@ghost
Copy link

ghost commented Nov 25, 2020

Hi guys,

@fr500 and @LIJI32 , I can confirm that subsystems still works on RetroArch 1.9.0 (latest git code).
Tested on Lubuntu 20.04 64-bits.

For users, the steps are :
1°) Select Subsystems -> Load 2 Player Game Boy Link -> Your Game N°1 .
2°) Select Subsystems -> Load 2 Player Game Boy Link -> Your Game N°2 .
3°) Select Subsystems -> Start 2 Player Game Boy Link.

And Have a nice run ^^ . Congratulations for these huge works guys !

Note : Like me, you are in love with commands lines? Need this info for Retropie, EmulationStation and co? Here is an example of how to use it :

retroarch --libretro=/PATH/of/sameboy_libretro.so --subsystem gb_link_2p /PATH/of/Game/Player1 /PATH/of/Game/Player2
Ex: retroarch --libretro=$HOME/.config/retroarch/cores/sameboy_libretro.so --subsystem gb_link_2p $HOME/games/game1.gbc $HOME/games/game2.gbc 

@andres-asm
Copy link
Contributor

glad it works for you, you're from VBA right? does VBA have a similar API without relying on network?

@ghost
Copy link

ghost commented Nov 25, 2020

I'm not an official dev of VisualBoyAdvance-M, but I worked on a fork of VBALink that becames now a fork of VisualBoyAdvance-M with a working TCP/IP network for Linux and Windows (uses native socket code instead the SFML Library). In my knowledge, but i have never successfully used it, they have a support for local mode ( on the same computer ) that uses multiple instances.

For the tests that I done today with my code, I can use the equivalent method : use multiple instances on the same machine ( Player1 = server , Player 2 to 4 = client that connects to 127.0.0.1) with a working retroarch core . But I don't know if we can modify it to support the method you and @LIJI32 used in SameBoy, because of the lack of a system/API method in VBA-M and in my fork.

If you're interested to look on my project, I can create a repository of my fork if you want to look on the code ( personnally, GBA Link code is the main objective on my project (Multi-Pak and General Purpose Mode for FFTA works), the Joybus Protocol isn't necessary, and the GB/GBC Link works very better on SameBoy than VisualBoyAdvance-M + my fork ).

EDIT : Does mGBA libretro core already have this feature ?
EDIT2 : Yes apparently but it is not implemented on RetroArch core : mgba-emu/mgba#756 (comment)
EDIT3: After some research, can we really load different cores on a subsystem like said here ? libretro/RetroArch#7997 . I will create a repository that contains the work I done on GBA Link. I will begin to study that. I could be awesome to play GBA Link directly on a same computer on Retroarch like that

@ghost
Copy link

ghost commented Jan 22, 2021

The RetroArch core could also play online with some sort of rollback but i remember not getting it to work like I wanted lol.
I mean it was kinda playable when I played Mario Tennis with someone else but the rollback was messed up a bit.

@dev01111
Copy link

It was planned and mostly completed for v0.12, but sadly it had severe performance issues I've haven't managed to solve yet.

@LIJI32 Please put more attention into this issue. I mean, it's 0.15.x now, and we STILL haven't had network play arrive to SameBoy yet. I'd LOVE to have network play as a feature so I can battle Pokemon with my friends. Please work harder, I'd be willing to help!

@andres-asm
Copy link
Contributor

geezzz...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants