Format discovery responses like a real device in MIM requester - #67
Open
tovitch wants to merge 1 commit into
Open
Format discovery responses like a real device in MIM requester#67tovitch wants to merge 1 commit into
tovitch wants to merge 1 commit into
Conversation
The Second Screen app silently discards the emulated device's SRCH response, so "PlayActor" never shows up in the app and the PS4 credential flow cannot complete (dhleong#25, dhleong#59). Compared byte-for-byte with a real console's response, the emulated one had three differences: - the status line was formatted like a request ("HTTP/1.1 620 Server Standby * HTTP/1.1" instead of the bare status line) - no system-version field - host-request-port pointed to the UDP wake port (987) instead of the TCP second-screen port (997) that real consoles advertise Responding exactly like a real device makes the app list the emulated device again; verified end-to-end against the current iOS app and a PS4 on firmware 13.52. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dhleong
reviewed
Jul 20, 2026
dhleong
left a comment
Owner
There was a problem hiding this comment.
Thanks for taking a look at this 🙏
| formatted += `${key}:${data[key]}\n`; | ||
| } | ||
| } | ||
| return Buffer.from(`${type} * HTTP/1.1\n${formatOutgoingKeys(data)}device-discovery-protocol-version:${version}\n`); |
Owner
There was a problem hiding this comment.
I think we can simplify a lot in this PR by borrowing the heuristic I employed in ps4-waker and only append * HTTP/1.1 if type doesn't already include HTTP
| data: { | ||
| "host-id": this.emulatorOptions.hostId, | ||
| "host-name": this.emulatorOptions.hostName, | ||
| "host-request-port": secondScreenRequestPort, |
Owner
There was a problem hiding this comment.
Rather than a fixed constant, let's do a similar map lookup based on device.type that we did for the wakePortsByType, above. requestPortsByType perhaps? I would prefer to keep this less tightly coupled to PS4 specifics.
Comment on lines
+44
to
+45
| await new Promise(resolve => { setImmediate(resolve); }); | ||
| await new Promise(resolve => { setImmediate(resolve); }); |
| netFactory.onMessage!({ | ||
| type: "SRCH", | ||
| sender: appSender, | ||
| } as unknown as IDiscoveryMessage); |
Owner
There was a problem hiding this comment.
Any chance we can just fill out the other couple of properties of IDiscoveryMessage rather than hard cast?
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.
Problem
When running
playactor loginagainst a PS4, the emulated "PlayActor" device never shows up in the PS4 Second Screen app, so the credential flow can never complete. This is the issue reported in #25 and #59.Root cause
The MIM requester's SRCH response goes through
formatDiscoveryMessage, which formats requests. Comparing the emulated response byte-for-byte with a real console's response to the same SRCH probe:Real PS4 (firmware 13.52):
Emulated device (current main):
Three differences:
* HTTP/1.1, as if it were a request.system-versionfield.host-request-portadvertises the UDP wake port (987) instead of the TCP second-screen port (997) that real consoles advertise.The current Second Screen app (tested on iOS) silently discards this response, so the emulated device never appears in its device list.
Fix
formatDiscoveryResponsetoprotocol.ts: same outgoing-key filtering, but the first line is the bare status line, like a real device sends.host-request-port:997and borrowing thesystem-versionof the device we are requesting credentials for.Verified end-to-end: with this change the Second Screen app lists "PlayActor", taps through, and the credential exchange + device registration on the console complete successfully.
Fixes #25
Fixes #59
🤖 Generated with Claude Code