Tweak network section
This commit is contained in:
parent
1abd45723d
commit
2513edc608
BIN
site/content/entry/citra-progress-report-2023-h2/frd.png
Normal file
BIN
site/content/entry/citra-progress-report-2023-h2/frd.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 699 KiB |
@ -84,23 +84,21 @@ Now, coming back to online multiplayer, PabloMK7 was facing a small issue with s
|
||||
|
||||
After several weeks of research, dumping network traffic, and getting assistance from developer m4xw, the issue was tracked down to a missing implementation detail in the SOC module. It turned out the game was setting the packet TTL (time to live) value to 0. This value tells routers in the network how many more “jumps” a packet can do before being dropped, and in most operating systems, setting the TTL to 0 simply disables it from jumping to more than one router (hence why the issue didn’t happen if both devices were on the same network) However, on the 3DS, the value of 0 has a special meaning: “set the TTL to the default value: 64”. After this detail was fixed, the game started working perfectly online.
|
||||
|
||||
At this point, a major roadblock was hit. It was observed that going online in even newer games, such as Super Smash Bros. or Pokemon Sun/Moon would just freeze the entire emulator, with the only option left being to forcefully close it.
|
||||
At the same time, PabloMK7 discovered a major inaccuracy in the `SOC` service. But to describe it, we have to talk a bit more about sockets. Sockets have two ways of operation: blocking and non-blocking. Those modes tell the operating system how the socket should behave when a read operation is performed, but with no data available. In blocking mode, the program execution is paused until data is received, at which point execution is resumed automatically. In non-blocking mode, the read function immediately returns with an error indicating that no data was received and that the operation would block (EWOULDBLOCK).
|
||||
|
||||
Before continuing, we have to talk a bit more about sockets. Sockets have two ways of operation: blocking and non-blocking. Those modes tell the operating system how the socket should behave when a read operation is performed, but with no data available. In blocking mode, the program execution is paused until data is received, at which point execution is resumed automatically. In non-blocking mode, the read function immediately returns with an error indicating that no data was received and that the operation would block (EWOULDBLOCK).
|
||||
|
||||
It was noticed that the way sockets were handled changed over time. In older games, the network code was constantly checking for incoming data using a combination of poll and non-blocking sockets, but in newer games, the network code spawned a new thread that would simply try to receive data on a blocking socket. If there was no data available, this would cause this new thread to block, but it didn’t matter as the rest of the game threads can move on and do other things in the meantime (such as sending data through the socket). Once the blocking socket receives some data, the network thread is unblocked and the data is processed according to the game’s needs.
|
||||
It was noticed that the way sockets were handled changed over time. In older games, the code was constantly checking for incoming data using a combination of poll and non-blocking sockets, but in newer games, it spawned a new thread that would simply try to receive data on a blocking socket. If there was no data available, this would cause this new thread to block, but it didn’t matter as the rest of the game threads can move on and do other things in the meantime (such as sending data through the socket). Once the blocking socket receives some data, the network thread is unblocked and the data is processed according to the game’s needs.
|
||||
|
||||
However, this pattern presents a problem in Citra: we don’t have real multithread support! Instead of having an emulation thread per game thread, a single emulation thread is used that periodically switches execution between the game threads, emulating the thread scheduler of the 3DS. This approach has many benefits. It ensures the emulator maintains high timing accuracy with minimal desync, while also making execution deterministic. That means that when launching any particular application, one can expect the emulator to behave the exact same way, timing-wise, on each run, instead of relying on scheduler details of the host operating system. However, this design choice has a weakness: if a game thread performs a blocking operation, it will also block the emulation thread, hanging the entire emulator.
|
||||
|
||||
Solving this issue in Citra requires tapping into making Citra multithreaded, to run each game thread in a dedicated host thread to mimic the way hardware works. However, the benefits of this approach aren't clear aside from this particular weakness,implementing it requires an almost complete kernel rewrite to make it multithread ready and would sacrifice a lot of emulation accuracy.
|
||||
|
||||
Faced with this conundrum, PabloMK7 needed to find a way to offload the socket operations from the main emulation thread without intrusive changes to the existing kernel and service code. His answer was adding a new function, RunAsync, that can execute any blocking code in a separate host thread while putting the requester game thread to sleep. This allows the emulated scheduler to select a new game thread to run so that the game can continue doing work. Eventually, when the blocking code finishes, the game thread is resumed and the received socket data is finally processed.
|
||||
|
||||
{{< figure src="smash.png"
|
||||
title="Freezing no more!" >}}
|
||||
Faced with this conundrum, PabloMK7 needed to find a way to offload the socket operations from the main emulation thread without intrusive changes to the existing kernel and service code. His answer was adding a new function, RunAsync, that can execute any blocking code in a separate host thread while putting the requester game thread to sleep. This allows the emulated scheduler to select a new game thread to run so that the game can continue doing work. Eventually, when the blocking code finishes, the game thread is resumed and the received socket data is finally processed, ensuring the service can work correctly when used in such a manner.
|
||||
|
||||
Another applet that received its well-deserved spa treatment is the Friend List applet. While it has been possible to boot it for quite some time by enabling the `LLE FRD` service module, the HLE implementation was simply lacking too much to boot it. To shrink the gap between implementations, FearlessTobi stubbed several `FRD` functions to prevent it from crashing. However we still recommend using the LLE service whenever `FRD` is required until further improvements to the HLE implementation are made.
|
||||
|
||||
{{< figure src="frd.png"
|
||||
title="Time to go out and make new friends!" >}}
|
||||
|
||||
PabloMK7 later amended FearlessTobi's previous `HTTPC` work by implementing another large chunk of the HLE service. This entails that many more online applications are able to function correctly. For example, the homebrew file manager [FBI](https://github.com/Steveice10/FBI) (not the one you may be thinking of), can now install remote applications, the Pokémon Sun and Moon demo can communicate with the legality check server, the Internet Browser can check its own version, and more!
|
||||
|
||||
# Amiibo
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 2.5 MiB |
Loading…
Reference in New Issue
Block a user