Skip to content

Format discovery responses like a real device in MIM requester - #67

Open
tovitch wants to merge 1 commit into
dhleong:mainfrom
tovitch:fix/second-screen-response-format
Open

Format discovery responses like a real device in MIM requester#67
tovitch wants to merge 1 commit into
dhleong:mainfrom
tovitch:fix/second-screen-response-format

Conversation

@tovitch

@tovitch tovitch commented Jul 12, 2026

Copy link
Copy Markdown

Problem

When running playactor login against 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):

HTTP/1.1 200 Ok
host-id:XXXXXXXXXXXX
host-type:PS4
host-name:PS4-028
host-request-port:997
device-discovery-protocol-version:00020020
system-version:13520001
...

Emulated device (current main):

HTTP/1.1 620 Server Standby * HTTP/1.1
host-id:1234567890AB
host-name:PlayActor
host-request-port:987
host-type:PS4
device-discovery-protocol-version:00020020

Three differences:

  1. The status line has a trailing * HTTP/1.1, as if it were a request.
  2. There is no system-version field.
  3. host-request-port advertises 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

  • Add formatDiscoveryResponse to protocol.ts: same outgoing-key filtering, but the first line is the bare status line, like a real device sends.
  • Make the MIM requester respond to SRCH with that formatter, advertising host-request-port:997 and borrowing the system-version of 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

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 dhleong left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for taking a look at this 🙏

Comment thread src/protocol.ts
formatted += `${key}:${data[key]}\n`;
}
}
return Buffer.from(`${type} * HTTP/1.1\n${formatOutgoingKeys(data)}device-discovery-protocol-version:${version}\n`);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

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,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

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); });

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Why two frames?

netFactory.onMessage!({
type: "SRCH",
sender: appSender,
} as unknown as IDiscoveryMessage);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Any chance we can just fill out the other couple of properties of IDiscoveryMessage rather than hard cast?

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.

Playactor device not appearing in Second Screen App on iOS No "PlayActor" device listed in PS4 Second Screen app

2 participants