Skip to content

Splitscreen multiplayer - #318

Open
SomeoneIsWorking wants to merge 11 commits into
libretro:masterfrom
SomeoneIsWorking:splitscreen-new
Open

Splitscreen multiplayer#318
SomeoneIsWorking wants to merge 11 commits into
libretro:masterfrom
SomeoneIsWorking:splitscreen-new

Conversation

@SomeoneIsWorking

@SomeoneIsWorking SomeoneIsWorking commented Feb 22, 2026

Copy link
Copy Markdown

I wanted to play Kirby & The Amazing Mirror split-screen co-op.
This works but I need a second opinion.
I have no idea if it's implemented correctly.

@hizzlekizzle

Copy link
Copy Markdown

I'm not sure it makes sense to have the core_controller, CoreController and multiplayer_controller all split up like that.

Looks like a lot of this would simpler if we took the core-level turbo out, which endrift would rather we did, as well.

@SomeoneIsWorking

SomeoneIsWorking commented Feb 22, 2026

Copy link
Copy Markdown
Author

I'm not sure it makes sense to have the core_controller, CoreController and multiplayer_controller all split up like that.

I don't really understand what's going on. I'm not an emulator guy.
I just ported CoreController and MultiplayerController from the Qt app.

I actually had a better working version for SDL client, not in this repo.
Maybe I should try that instead.

@SomeoneIsWorking
SomeoneIsWorking marked this pull request as draft February 22, 2026 22:31
@endrift

endrift commented Feb 23, 2026

Copy link
Copy Markdown
Collaborator

Judiciously avoiding mentioning that this is vibe coded this time I see.

@SomeoneIsWorking

SomeoneIsWorking commented Feb 23, 2026

Copy link
Copy Markdown
Author

I wasn't trying to avoid it. I'm just trying to figure out.
But you are right, I should've mentioned it.
I don't remember why I did that but I think I thought it wasn't worth mentioning.
I might have gotten a bit excited to see it was working.

@hizzlekizzle

Copy link
Copy Markdown

I assumed it was, based on (the code itself, obviously, but also) the remark about not knowing if it was implemented correctly. I had also assumed that was why it was split into 3 separate headers and files, but it sounds like it's that way because the Qt frontend does it that way, too. That makes it much more acceptable, IMO, if it makes it easier to track upstream changes to those Qt frontend files.

@endrift

endrift commented Feb 23, 2026

Copy link
Copy Markdown
Collaborator

Generally speaking, tracking reimplementations of those files is suboptimal. One of the things I'm planning to do in 0.12 with the netplay support code is lower-level implementations of core coordination for multiplayer purpose, for cases like this. It should be possible to do with even single-threaded code, akin to how TGB-Dual swaps between cores every so often. But it may be a year before I even get there, so who knows i it's worth waiting or just yanking it out and replacing it later.

@SomeoneIsWorking

Copy link
Copy Markdown
Author

It should be possible to do with even single-threaded code, akin to how TGB-Dual swaps between cores every so often.

I tried this a while ago because it makes perfect sense. Why do lockstep and mutex on the same thread, just run the cores one after another. But I could NOT get it working.

@SomeoneIsWorking

Copy link
Copy Markdown
Author

What the hell I'm going to try anyway.
I attempted so many different multiplayer implementations.
In my forks there is multiplayer via ffmpeg streams, Qt split screen, SDL split screen.
I also made a Python GUI and tried to add splitscreen which failed.

By "I" you get the point. 1% I, 99% Autocomplete bots.

@SomeoneIsWorking

SomeoneIsWorking commented Feb 23, 2026

Copy link
Copy Markdown
Author

Hey I got lucky. Working singlethreaded lockstep.
I started fresh and focused on single-thread without copying MultiplayerController.
I instead copied lockstep.c as libretro_lockstep.c to make it mutex-free.

I faced timing and desync issues and solved it with "pumping" (and cycle skips if pumping occurred)
Which is not super ideal but I tried playing for a while and nothing went wrong.

@SomeoneIsWorking
SomeoneIsWorking marked this pull request as ready for review February 23, 2026 18:27
@SomeoneIsWorking

Copy link
Copy Markdown
Author

Don't mind the last two force pushes, they are just cosmetics.

@hizzlekizzle

Copy link
Copy Markdown

Sounds good. I'll see if we can get some testers.

@SomeoneIsWorking

SomeoneIsWorking commented Feb 24, 2026

Copy link
Copy Markdown
Author

I tested it on macOS when developing.
I just tested it on Bazzite also (used distrobox to build it)

@SomeoneIsWorking

SomeoneIsWorking commented Feb 24, 2026

Copy link
Copy Markdown
Author

One thing eating my brain is Network possibility because technically let's say.

You have 2 people running the game.
Both players run both instances locally (only their own view is visible)
They send inputs to each other using rollback.
Theoretically I don't see why this can't work.

But I have no idea where someone would even begin with this.
I also do not know a single thing about how libretro network works.

Edit: I got netplay working but uhhh that discussion should be for another PR...

@SomeoneIsWorking

SomeoneIsWorking commented Feb 27, 2026

Copy link
Copy Markdown
Author

Okay I said I want to keep netplay related comments out of this PR but I had a problem regarding RA rollback netplay not exposing player index and I made a PR to RetroArch which got merged so now rollback netplay is playable but I still want to make it a separate PR because it has some parts that I want to talk about separately.

image

@SomeoneIsWorking

Copy link
Copy Markdown
Author

Replaced "pumping" with individual running based on state
Replaced player1 player2 with cores array
Added 4 players

@hizzlekizzle

Copy link
Copy Markdown

@SomeoneIsWorking is this ready to go, from your perspective? or are you still actively working on it?

@endrift do you have any thoughts on any of it? I/we don't want to step on your toes merging something you think would be a problem down the road. I haven't actually done any testing on it myself, yet, so it wouldn't be merged immediately anyway, just to be clear.

@SomeoneIsWorking

SomeoneIsWorking commented Mar 7, 2026

Copy link
Copy Markdown
Author

@hizzlekizzle it is ready to go IMO. I'm going to submit the netplay PR after this which contains some further refactoring.

@SomeoneIsWorking

Copy link
Copy Markdown
Author

I had a freezing issue around 10-30 minutes in the earlier versions but the latest should be working fine.

@SomeoneIsWorking

Copy link
Copy Markdown
Author

Oh wait. I discovered something. I was always building with DEBUG=1
Without it Advance Wars just crashes on me shortly after starting.
But it shouldn't be freezing in the latest version at least because there are hard asserts against getting stuck.
And it hits one for me (Cooperative scheduling watchdog expired)
Didn't run into trouble with Kirby.
I don't know how I can fix Advance Wars.

@SomeoneIsWorking

SomeoneIsWorking commented Mar 8, 2026

Copy link
Copy Markdown
Author

Sorry I was testing on netplay branch (I swear I had switched).
I backported the delay sanitation.
Now Kirby works but Advance Wars crashes without DEBUG=1, I'll try to resolve it.

@SomeoneIsWorking

Copy link
Copy Markdown
Author

I found a strange case with Advance Wars
It makes a problem if player 1 has named their character and player 2 is still naming theirs.
I'm trying to solve it.

@SomeoneIsWorking

SomeoneIsWorking commented Mar 8, 2026

Copy link
Copy Markdown
Author

Okay I added something that bypasses lockstep when transfer is not active.
Seems to fix Advance Wars without breaking Kirby.
And thank you so much for testing and informing me.

@hizzlekizzle

Copy link
Copy Markdown

my pleasure! Unfortunately, since the syncArmed commit, neither Advance Wars nor Kirby will actually link. They just sit at the linking screen waiting for the other player(s). On the bright side, they haven't frozen since, either.

@SomeoneIsWorking

Copy link
Copy Markdown
Author

That's strange. Try a clean build maybe.

make -f Makefile.libretro clean && make -f Makefile.libretro -j$(sysctl -n hw.ncpu)

This is how I always build it.

@hizzlekizzle

Copy link
Copy Markdown

The tree was clean, but what did help was reverting the syncArmed commit while leaving the sanitizeDelay one intact. That brought linking back and I didn't experience any freezes through entire games of both Kirby and Advance Wars.

I do like the concept behind the syncArmed commit, though, so if that could be brought back without breakage, it seems like a smart addition.

@SomeoneIsWorking

SomeoneIsWorking commented Mar 9, 2026

Copy link
Copy Markdown
Author

I tested on macOS first and I just tried syncArmed commit on Bazzite and it worked there too.

@kaysedwards

Copy link
Copy Markdown

I'm trying this for testing, but I'm having no luck.

I have Kirby and the Amazing Mirror on my SteamDeck; can you kind of walk me through setting up the game for two players using your commit?

@SomeoneIsWorking

SomeoneIsWorking commented Mar 10, 2026

Copy link
Copy Markdown
Author

If steps include compiling you need to setup a distrobox then install dependencies and build the core and copy/move it to RA cores directory
Then you just open RA, load the game, go into core options -> system -> find splitscreen option -> set two players side-by-side -> reset the game
Then go to Start Game -> Multiplayer with both players -> wait a second
image
Then press A on P1 then select a save file then the game should start

Or are you facing the same issue as hizzlekizzle?

@hizzlekizzle

Copy link
Copy Markdown

FWIW, I just compiled this for Windows and played Advance Wars with myself with no issues.

@kaysedwards what sort of platform/device/OS were you using?

@SomeoneIsWorking

Copy link
Copy Markdown
Author

@hizzlekizzle syncArmed or without it?

@hizzlekizzle

Copy link
Copy Markdown

Oh, I didn't even think to test that. I was the non-syncArmed commit.

@SomeoneIsWorking

SomeoneIsWorking commented Mar 17, 2026

Copy link
Copy Markdown
Author

Okay then I think @kaysedwards ran into the same problem you did

Edit: I just did a fresh build with latest commit on a Fedora machine and it worked fine.

By fresh build I mean. This is an entirely new setup with no RA installed.

So I downloaded RetroArch.AppImage.
Cloned the repo.
Ran make.
Copied the core.
Ran Kirbo > Enabled split-screen > reset game.
Went into multiplayer with both players and they connected fine.

@hizzlekizzle

Copy link
Copy Markdown

I just re-compiled with the syncArmed commit and it worked perfectly on Windows but failed in Linux, where it just spins on the connection screen with this output repeated indefinitely:

[libretro DEBUG] GBA Serial I/O: MULTI 1 write: SIOMLT_SEND <- EDCB
[libretro DEBUG] GBA Serial I/O: Lockstep: SIOCNT <- 601F
[libretro DEBUG] GBA Serial I/O: MULTI 0 write: SIOMLT_SEND <- EDCB
[libretro DEBUG] GBA Serial I/O: Lockstep: SIOCNT <- 608B
[libretro DEBUG] GBA Serial I/O: MULTI 1 write: SIOMLT_SEND <- EDCB
[libretro DEBUG] GBA Serial I/O: Lockstep: SIOCNT <- 601F
[libretro DEBUG] GBA DMA: Starting DMA 1 0x03005540 -> 0x040000A0 (B600:0004)
[libretro DEBUG] GBA DMA: Starting DMA 2 0x03005B70 -> 0x040000A4 (B600:0004)
[libretro DEBUG] GBA Serial I/O: MULTI 0 write: SIOMLT_SEND <- EDCB
[libretro DEBUG] GBA Serial I/O: Lockstep: SIOCNT <- 608B
[libretro DEBUG] GBA Serial I/O: MULTI 1 write: SIOMLT_SEND <- EDCB
[libretro DEBUG] GBA Serial I/O: Lockstep: SIOCNT <- 601F
[libretro DEBUG] GBA DMA: Starting DMA 1 0x03005540 -> 0x040000A0 (B600:0004)
[libretro DEBUG] GBA DMA: Starting DMA 2 0x03005B70 -> 0x040000A4 (B600:0004)
[libretro DEBUG] GBA Serial I/O: MULTI 0 write: SIOMLT_SEND <- EDCB
[libretro DEBUG] GBA Serial I/O: Lockstep: SIOCNT <- 608B
[libretro DEBUG] GBA Serial I/O: MULTI 1 write: SIOMLT_SEND <- EDCB
[libretro DEBUG] GBA Serial I/O: Lockstep: SIOCNT <- 601F
[libretro DEBUG] GBA Serial I/O: MULTI 0 write: SIOMLT_SEND <- EDCB
[libretro DEBUG] GBA Serial I/O: Lockstep: SIOCNT <- 608B
[libretro DEBUG] GBA Serial I/O: MULTI 1 write: SIOMLT_SEND <- EDCB
[libretro DEBUG] GBA Serial I/O: Lockstep: SIOCNT <- 601F
[libretro DEBUG] GBA Serial I/O: MULTI 1 write: SIOMLT_SEND <- EDCB
[libretro DEBUG] GBA Serial I/O: Lockstep: SIOCNT <- 601F
[libretro DEBUG] GBA Serial I/O: MULTI 0 write: SIOMLT_SEND <- EDCB
[libretro DEBUG] GBA Serial I/O: Lockstep: SIOCNT <- 608B
[libretro DEBUG] GBA Serial I/O: MULTI 0 write: SIOMLT_SEND <- EDCB
[libretro DEBUG] GBA Serial I/O: Lockstep: SIOCNT <- 608B
[libretro DEBUG] GBA Serial I/O: MULTI 1 write: SIOMLT_SEND <- EDCB
[libretro DEBUG] GBA Serial I/O: Lockstep: SIOCNT <- 601F

@SomeoneIsWorking

SomeoneIsWorking commented Mar 19, 2026

Copy link
Copy Markdown
Author

@hizzlekizzle I hate to reiterate this but please try a clean build, run a make clean before otherwise this will happen 100%

Also. Which linux? Maybe I can test it in a VM

@hizzlekizzle

hizzlekizzle commented Mar 19, 2026

Copy link
Copy Markdown

Ubuntu linux. I clean between every build. I can try a fresh pull in case the clean routine isn't actually cleaning everything.

EDIT: yeah, same behavior with a fresh pull. Tested Kirby this time and it just sits there at the multiplayer connection screen with the connection data spinning forever.

@SomeoneIsWorking

Copy link
Copy Markdown
Author

I removed syncArmed checks from mode switches. I tested both games again and I tested the scenarios that crashed for me without syncArmed.

@hizzlekizzle

Copy link
Copy Markdown

Just tested the latest commit with linux and windows and both worked great.

@endrift

endrift commented Mar 20, 2026

Copy link
Copy Markdown
Collaborator

I've been waiting until you got something that worked before taking too close a look at it. I'll try to find some time to look at this in the next few days

@SomeoneIsWorking

Copy link
Copy Markdown
Author

Thank you @endrift (consider me a fan)

@hizzlekizzle

Copy link
Copy Markdown

hey @endrift did you ever get a chance to peek at this? it seemed solid/reliable in my testing, but I'm not that familiar with the mGBA codebase to know if it's implemented sensibly. OTOH, it does live all in the libretro directory, so any mess is confined there :)

@endrift

endrift commented May 3, 2026

Copy link
Copy Markdown
Collaborator

Sorry, I've been distracted and it fell off my radar. I'll try to look at this soon

@SuperDavid2

SuperDavid2 commented Aug 6, 2026

Copy link
Copy Markdown

I have tried this out a little, and I have observed a few things. First, from what I've tested, it works perfectly online through Retroarch Kaillera, although I have not tried the standard netplay version. I found that in Sonic Advance 3, at the boss of the Toy Kingdom world, there is a relatively high chance the emulator will crash. I have a replay file that consistently causes this crash if anyone wants it.

In Kirby Nightmare in Dream Land, it seems that most of the time you start up a game in multiplayer, as the star is crashing into the overworld area, the game feels laggy. If I stay in that area, I find that it takes a random amount of time before going back to full speed. Every time I go through a door, this lag returns before going away again. It seems to be running at about 45 fps when this happens (in game, since it's not my computer or Retroarch slowing down). I think observing how long it takes to jump and hit the ground is an especially easy way to check if it's lagging.

Finally, there are a few features that I personally consider missing. These are the ability to choose which screen(s) are seen in the main window, which GBAs the audio is coming from, and targeted cheats. I'm unsure if Retroarch itself is able to choose which GBA a cheat is used on, but I currently have it set for myself so that cheats affect either players 2 to 4 or all 4 players. I also was able to get those audio and videos features I mentioned working, but this was just low quality vibe coded stuff that I don't think would be helpful to share.

Edit: I also forgot to mention individual save files. Currently, it only loads a save file for the first GBA.

@SomeoneIsWorking

SomeoneIsWorking commented Aug 8, 2026

Copy link
Copy Markdown
Author

@SuperDavid2

Thank you.

It has been a very long time I tried this.
There is a separate netplay branch that I was planning to make a PR on if/when this gets merged.
Which solves the screens/audio issues you mentioned, no idea about cheats.
I didn't even know this branch had a working netplay.

And the game issues you mentioned, also not sure if netplay branch resolves them.
I'll try to test those when I get the chance.

@SuperDavid2

Copy link
Copy Markdown

@SomeoneIsWorking
The Kaillera netplay version of Retroarch, although it works well enough for many systems that I consider it underappreciated. I forgot to mention that it was only because I use Kaillera that I wanted some of those features. I record gameplay using Kaillera replay files, in which case, I want to be able to record all 4 screens, but not necessarily see all 4 screens while I'm playing online.
If you want to see the crash I'm referring to in Sonic Advance 3, it's here. The file just goes in the states folder and then you hit the play replay key, and then press the speed up button to speed through to where it crashes.
https://mega.nz/file/ltpSnRqR#vTsmFEcmtBYUEnhpBzzLhLXVBO692HTG4z75S2R1PO8

I appreciate the work done so far and am looking forward to future updates.

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

Successfully merging this pull request may close these issues.

5 participants