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

Emulated local Wi-Fi multiplayer support #242

Open
wants to merge 21 commits into
base: dev
Choose a base branch
from

Conversation

BernardoGomesNegri
Copy link

@BernardoGomesNegri BernardoGomesNegri commented Dec 1, 2024

This patch adds support for emulated local (Wi-Fi) multiplayer using libretro's netpacket API, which allows a core to send and receive arbitrary data packets across the network. In RetroArch, this can be done by using the same interface used for netplay.
Unfortunately, due to the DS's tight latency requirements for local networks, it only works for good LAN connections on a few games. I managed to play Pokémon HeartGold between my phone (connected to a WiFi AP very close by) and my PC (wired), for example. On an emulated connection, the same game worked with 8ms ping and 3% packet loss, but it could get rather laggy at times.
Thanks to @JesseTG for helping me with this.

@BernardoGomesNegri BernardoGomesNegri changed the base branch from main to dev December 1, 2024 16:51
@JesseTG
Copy link
Owner

JesseTG commented Dec 1, 2024

Thanks for contributing! I'll review and test this and get back to you as soon as I can.

@JesseTG JesseTG self-requested a review December 1, 2024 16:55
@JesseTG JesseTG added the enhancement New feature or request label Dec 1, 2024
@BernardoGomesNegri
Copy link
Author

I seem to have made a bit of a mess when trying to rebase this to the dev branch. I Will try to fix it.

@BernardoGomesNegri
Copy link
Author

Would close issue #225

Copy link
Owner

@JesseTG JesseTG left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution, and for your patience. Broadly speaking this looks okay, but there are some changes I'd like you to make. Some of the proposed changes will reduce the number of copies made of packets, which may help performance.

I also suggest adding Tracy markers at the start of important functions, then running a RelWithDebInfo build through Tracy.

Once you make these changes I'll take another review pass and test it. Keep up the good work!

src/libretro/core/core.hpp Outdated Show resolved Hide resolved
src/libretro/net/mp.hpp Outdated Show resolved Hide resolved
src/libretro/core/core.hpp Outdated Show resolved Hide resolved
src/libretro/net/mp.hpp Outdated Show resolved Hide resolved
src/libretro/environment.cpp Outdated Show resolved Hide resolved
src/libretro/libretro.cpp Outdated Show resolved Hide resolved
src/libretro/libretro.cpp Show resolved Hide resolved
src/libretro/libretro.cpp Outdated Show resolved Hide resolved
src/libretro/net/mp.hpp Outdated Show resolved Hide resolved
src/libretro/libretro.hpp Outdated Show resolved Hide resolved
@BernardoGomesNegri
Copy link
Author

Added the tracepoints, will run with Tracy and analyze the result later.

@BernardoGomesNegri
Copy link
Author

Ran it with Tracy, it appears that most of the time spent in the multiplayer code is spent waiting blocking for a packet. Sending a packet and receiving one are super quick by comparison.
The biggest improvements I can see are if waiting for a packet did not require busy-looping (which would improve battery usage on mobile devices) and if waiting for a packet could be done asynchronously from actually rendering and processing and everything else that has to be done in a frame.
The only room for improvement in melonDS DS that I see is tweaking the timeout. But even that has little effect: melonDS (or the game) will often call recv again after receiving a timeout.
Maybe improvements could also be made on RetroArch's implementation of netpacket? But I think this is very close to the best that can be done while only touching melonDS DS and with my rather limited knowledge of emulation and networking.

@BernardoGomesNegri
Copy link
Author

BernardoGomesNegri commented Dec 3, 2024

I've been trying to get Tracy to connect to my Android phone, but no luck so far. Any ideas?
Edit: fixed it.

@BernardoGomesNegri
Copy link
Author

After testing more, it seems timeout has little impact on performance. However, too low of a timeout will cause frequent crashes. The value of 25 ms is good.

@JesseTG
Copy link
Owner

JesseTG commented Dec 5, 2024

I'll test this today and get back to you with thoughts. I'll also compare this to the upstream implementation, see if there's something we can do about the blocking.

// Necessary because arithmetic on void* is forbidden
const char *indexableBuf = (const char *)buf;
const char *data = indexableBuf + HeaderSize;
retro_assert(len > HeaderSize);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this assertion doesn't hold up; I just triggered it in Mario Kart DS. I created the in-game lobby from one of my devices, but as soon as the second device connected this retro_assert went off.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try changing it to len >= HeaderSize. If it works, then zero sized packets are possible.
Interestingly, I was not able to reproduce this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Managed to reproduce it, but switching to len >= HeaderSize fixes it.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What game did you test it with? I used Mario Kart DS.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me too. .nds file format, MD5 hash: 8cbf438d5a7c0d9acbe56a9b627ca6c5
The game indeed crashes if the condition is len > HeaderSize but switching to len >= HeaderSize fixed it for me.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially tested it with Pokémon HeartGold as well.

@JesseTG
Copy link
Owner

JesseTG commented Dec 5, 2024

I had a chance to reproduce the lag in the core you described. Notably, standalone melonDS does not have this issue.

Before we can merge this PR, we need to diagnose the lag and see how much of it actually comes from the core. I'll take some traces and get back to you.

@JesseTG
Copy link
Owner

JesseTG commented Dec 5, 2024

When you took your trace, what build settings did you use? I built the core (but not RetroArch itself) with optimizations and the lag wasn't as pronounced. I'm still looking over the trace for specifics.

@BernardoGomesNegri
Copy link
Author

The traces I took were taken in a RelWithDebInfo build.
The lag might be because right now this PR does not drop stale packets. I did not think it was needed because games often try to get incoming packets more than once per frame.

@JesseTG
Copy link
Owner

JesseTG commented Dec 6, 2024

I wonder if upstream melonDS does the same thing? I'll look into it.

@JesseTG
Copy link
Owner

JesseTG commented Dec 11, 2024

The lag is far less pronounced when building the core in RelWithDebInfo mode, and RetroArch in Release mode. (For both devices I'm testing with, too.)

Let me see what happens when using a wireless connection.

@JesseTG
Copy link
Owner

JesseTG commented Dec 11, 2024

Not too bad over wireless. It looks like on frames where retro_netpacket_poll_receive_t is most responsible for slowdown, it's because it's called more often. Or, rather, Platform::MP_RecvReplies is called more often -- and that leads to retro_netpacket_poll_receive_t being called more.

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

Successfully merging this pull request may close these issues.

2 participants