Repository navigation
Conversation
Quick Play hands a match off to an ordinary traversal NetPlay room. This adds what that handoff needs, without changing the NetPlay wire format: - NetPlayServer takes an optional Quick Play attempt id. Such a room is not listed on the NetPlay index, accepts a single guest (later joins get "The server is full."), and reports its traversal state and room code to the dialog through NetPlayUI::OnHostTraversalStateChanged instead of the generic traversal error. - NetPlayDialog forwards that state and the guest's arrival as signals. - NetPlayIndex reads the listing timestamp and lets a session set its own version string (escaped, since it may contain '+'). - Common::HttpRequest gains DELETE, a per-request timeout, and a "sensitive" mode that keeps URLs carrying bearer tickets out of the log. Ordinary NetPlay sessions are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Quick Play pairs two Project+ players and hands them to an ordinary traversal NetPlay room. This adds the matchmaking, with no UI yet: - QuickPlayIndexMatchmaker (default): a serverless handshake (PPQP1) through Dolphin's NetPlay index. Each searcher keeps at most one listing whose name carries the handshake: wait, claim, accept, host ready. The room code is obfuscated in the host-ready listing, and listings use the version "<scm desc>+ppqp1" so ordinary browsers grey them out. - QuickPlayClient: an optional coordinator backend for any server that speaks the Quick Play coordinator protocol (version 3). No default URL. - QuickPlayMatchmaker: the shared interface, regions and host preference. - QuickPlayController: the attempt state machine on the GUI thread (search, match, validate, host or join, room setup timeout), with a fallback from an unreachable index to a configured coordinator. - QuickPlayLauncher: finds the Project+ Netplay Launcher in the game list; it is the room's game. - NetPlay settings for the backend, coordinator URL, regions and host preference. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A Quickplay toolbar button, next to Netplay, starts a search and shows its progress. A gear button opens Quick Play Settings: the backend (Dolphin lobby or a custom coordinator, with a connection test), regions and host preference. - QuickPlaySession owns Quick Play in the main window. On a match it hosts the Netplay Launcher in a traversal room, or joins the opponent's room with the code, then leaves the ordinary NetPlay lobby to the players. Cancel and closing the room end the attempt. - MainWindow implements the small QuickPlayWindow interface, refuses manual NetPlay host/join while Quick Play owns NetPlay, and forces traversal for a Quick Play room. - With a game running, the button asks the player to close it first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Yep, vibecoded slop. Closing. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Quick Play: one-click matchmaking into an ordinary NetPlay room
Quick Play finds another Project+ player and puts you both in a normal NetPlay room. There are
no NetPlay protocol changes and no emulation changes: once matched, it's the same traversal
room you'd get from Host/Join, with the Netplay Launcher selected.
Video & Screenshots
PP_QP_DEMO_1_1080p_small.2.mp4
What players see
Settings.
searched and the host preference, then Opponent found, Creating room... /
Waiting for host..., and finally the ordinary NetPlay lobby on both sides. Cancel stops
the search at any time.
default) with a Test button.
America, Caribbean, South America, Europe, Asia, Australia). Players match if they share at
least one region.
never a constraint, and never delays a match.
setup, manual NetPlay Host/Join is refused.
How it works
Dolphin's NetPlay index, which Dolphin already uses for the server browser. Each searcher
keeps at most one listing whose name carries a small handshake (
PPQP1, below).protocol (summarized below) can be set in Quick Play Settings. There is no
default URL. If the lobby is unreachable and a coordinator is configured, the search
continues there.
(
NetPlayServerwith a Quick Play attempt id) and publishes its room code through thebackend; the other player joins with that code. When the guest arrives, Quick Play steps
back and the players are in an ordinary NetPlay lobby. They pick settings and press Start
as usual.
A Quick Play room differs only locally on the host: it isn't listed on the index, it
accepts a single guest (others get The server is full.), and it reports its traversal
state to Quick Play instead of the generic traversal error dialog.
version
<scm describe>+ppqp1(e.g.v3.2.1+ppqp1), and the coordinator matches the exactbuild revision.
File map
DolphinQt/QuickPlay/QuickPlayIndexMatchmaker.{h,cpp}PPQP1handshake), runs on one worker threadDolphinQt/QuickPlay/QuickPlayClient.{h,cpp}DolphinQt/QuickPlay/QuickPlayMatchmaker.{h,cpp}DolphinQt/QuickPlay/QuickPlayController.{h,cpp}DolphinQt/QuickPlay/QuickPlayLauncher.{h,cpp}DolphinQt/QuickPlay/QuickPlayDialog.{h,cpp}DolphinQt/QuickPlay/QuickPlaySettingsDialog.{h,cpp}DolphinQt/QuickPlay/QuickPlaySession.{h,cpp}QuickPlayWindowis the 5-method interfaceMainWindowimplementsDolphinQt/MainWindow.{h,cpp}DolphinQt/ToolBar.{h,cpp}DolphinQt/NetPlay/NetPlayDialog.{h,cpp}Core/NetPlayServer.{h,cpp},Core/NetPlayClient.hNetPlayUI::OnHostTraversalStateChanged, default no-op)Core/Config/NetplaySettings.{h,cpp}QuickPlayMatchmaking,QuickPlayCoordinator,QuickPlayRegions,QuickPlayRegion(legacy),QuickPlayHostPreferenceUICommon/NetPlayIndex.{h,cpp}timestamp; a session may set its own (escaped)versionCommon/HttpRequest.{h,cpp}Delete,SetTimeout,SetSensitive(keeps ticket URLs out of the log)DolphinQt/CMakeLists.txt,DolphinQt.vcxprojCommits:
NetPlay: Add the hooks Quick Play needs from NetPlayDolphinQt: Add Quick Play matchmakingDolphinQt: Add the Quick Play button and dialogsThe lobby handshake (
PPQP1)The NetPlay index is a passive directory (
list,session/add,session/active,session/remove; a session times out 15 s after its last keepalive; at most 5 sessions perIP; no locks). Every agreement is built from listings each player reads and writes on its own.
Listings. Each is an index session with
name=P+ Quick Play · PPQP1 <tokens>,game=Project+,version=<scm describe>+ppqp1,password= 1,method=traversal,in_game= 0 forWand 1 otherwise (so "hide in-game" hides the handshake),region= the index continent of the first region (display only).WW <mask> <pref> <digest> <nonce>CC <mask> <pref> <digest> <waiter> <nonce>AA <host> <digest> <waiter> <claimer><host>isworcHH <digest> <waiter> <claimer>server_idcarries the obfuscated room code<mask>: 3 hex digits, one bit per built-in region.<pref>:h,norj.<digest>:always
00000000in this PR (a reserved field; listings with any other value are ignored).Nonces: 6 random hex digits per search.
Flow.
W(shared region bit, not blocked). If there isnone, list our own
W: at once when preferring to host, after a random 0–2 s with nopreference, after 15 s when preferring to join.
it), pick the host, replace
WwithA. If a waiter with a smaller nonce appears, claim itinstead, so two waiters never wait on each other.
C, then poll every 2 s. AnA/Hfor us means accepted. Anotherclaimer, the waiter missing twice, or 12 s without an answer means lost: block that waiter
for 60 s and search again.
The host opens the room and lists
H; the client polls forH, decrypts the code andjoins.
and the claimer doesn't, or the waiter has no preference and the claimer prefers to host.
Otherwise the waiter hosts.
NetPlaySession::EncryptIDwith keyPPQP1:none. This keeps ordinarybrowsers out; it isn't secret.
more than 15 s older than the newest one are ignored); per-IP cap handled by waiting for a
free slot.
The coordinator protocol
For anyone running their own server. All JSON over HTTP; the ticket is a bearer capability, so
the client keeps ticket URLs out of the log:
POST /v1/queuewithprotocol,build(GetScmRevGitStr()),session(
"ordinary"),pplus_version(GetScmDescStr()),regions(1–12 strings),host_preference(host/none/join),platformand a randomplayer_nonce. The responsecarries a
ticket.GET /v1/queue/{ticket}, polled once a second:stateissearching,matched(withmatch_id,role=host/client, and the agreedregion) orexpired.POST /v1/matches/{id}/host(ticket,host_code).The client polls
GET /v1/matches/{id}untilstateisreadywithhost_code(
waiting_for_hostuntil then).DELETE /v1/queue/{ticket}cancels; cancelling a matched ticket also ends the match, so thepeer's next poll gets
expired.protocol,buildandsession, and at least one shared region.First come, first served. The host is chosen by the same soft rule as above.
Testing
7757cba325via CMake-generated MSBuild; runtime reportsv3.2.1-3PatchAllowlist.VerifyHashesfails onmasteras wellv3.2.1-3+ppqp1master, both directions, one match eachTest setup on
v3.2.1: upstream master automatically assigns Wii ports alongside GC ports. ForGC-only play, clear the Wii assignments in the room’s Assign Controller Ports dialog
after both players join.
Generative AI disclosure
Most of this PR's code was generated with an AI coding assistant (Claude/Codex) under my direction.
I designed the feature, reviewed the changes, tested them on Linux and Windows, and take
responsibility for them. It contains no changes to emulated-console behavior.