Their first login to the server they start takes real time, a few hundred
ms on the Windows runner, and a group request gives up after 5 emulated
seconds: at 1600% every P2P one failed to create its group there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only there, the host's matching drops the departed client without a
TIMEOUT event. The instances stay in step (no sync warnings in the log),
and it passes on a Mac and on the other runners.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every hop through the relay waits a millisecond or so of real time, which
the speed multiplies: at 1600% on macOS CI a relay PTP receive in
pair/ptppoll timed out. Relay mode hardly gains above 800% anyway (26 s
against 22 s locally); P2P stays at 1600%.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Fixes#19248
The server loop polled every socket and slept 10 ms, so it woke 100
times a second with nothing to do, and every message could wait up to
10 ms of real time before it was handled, which at a sped-up emulation
speed is many times that in game time. It now waits in select() on the
listening socket and every user, and wakes at least every 50 ms to notice
being stopped. It also no longer stops serving everyone while the hosting
instance is paused in the debugger.
pspautotests: pair/discover and pair/ptpmsg.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PSP keeps what arrives in the socket's buffer, of the size given to
PdpCreate, and drops a datagram that doesn't fit: a buffer of 0 gets
nothing, one of 2048 bytes only some of ten 1000-byte datagrams
(pspautotests net/adhoc/pair/flow). Each transport now only hands over
the datagrams that have arrived (PdpTakeDatagram), into a queue per
socket that is filled every millisecond and whenever the game looks, and
PdpRecv, its blocking retry, GetPdpStat, PollSocket and game mode read
from that. This replaces each transport's own receive paths, so the
relay now reports the sender with NOT_ENOUGH_SPACE too, and P2P's
blocking receive no longer misreads a stale errno.
A datagram to ourselves goes straight into our own socket's queue, as on
a PSP, so it's there at once through the relay too (net/adhoc/term).
Also from comparing the transports:
- P2P: a blocking Accept of a connection from outside the group keeps
waiting instead of returning -1.
- Relay: an accepted socket gets an id of its own, not its listener's,
which blocking calls are told apart by.
- ALERT in PollSocket comes from the socket in both transports.
- No send/receive timeouts are set on the game's sockets: all host
sockets are non-blocking, and in relay mode the ids aren't host
sockets at all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It times the socket failures from when the peer drops off the peer list,
which the ad hoc server updates in wall time, so on macOS CI at 600% the
failure lands under a second after it instead of 1-2s. The relay run
still covers it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each instance runs on its own, so on a busy runner they drift apart in
emulated time, and a real second's lag is 8 emulated seconds at 800%:
long enough for pairSync (10 s) to give up on macOS CI. ADHOC_SPEED
still overrides it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Winsock never makes a socket whose connect failed writable; the failure
shows in select's exception set, with the reason in SO_ERROR. The poll
now checks both, and SO_ERROR on a writable socket too, which is where
other systems put a refusal.
Windows still retries a refused connect for about a second before
reporting it, far longer than a PSP at the tests' 800% speed, so
pair/basic is a known P2P failure on Windows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Like the two-console tests, each runs through the built-in server's relay
and in P2P, with adhoc_known_failures for the ones that fail in one mode.
pair/basic passes in P2P now and moves to tests_good, a known failure
through the relay.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PSP completes a connect to a listener without the game calling Accept,
so an opened PTP connection is up a few ms after PtpOpen (pspautotests
net/adhoc/pair/ptppoll). The relay only answers the connecting side once
the listening side accepts, so the relay transport now accepts pending
connections into a queue as long as the listener's backlog, from a new
per-millisecond AdhocTransport::Poll() and from polls and accepts, and
PtpAccept takes them from there. A poll reports ACCEPT while the queue
isn't empty.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Out of the group, a PSP's sends reach nobody and nothing reaches it, and
a PTP connection to a peer that has left fails once its retransmits would
have run out. Only the relay transport did this; the P2P one kept sending
over the host's sockets, so neither side ever noticed the other was gone
(pspautotests net/adhoc/pair/vanish).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--cpu-backend-only leaves out the tests that don't depend on the CPU
backend (net/ for now, the two-console ones taking about 40 s of a 60 s
run). The Linux CI jobs run everything with the JIT and pass it for
jit-ir, ir and the interpreter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pair tests that pass go in tests_good, and each runs twice: through
the built-in server's relay and P2P. pair_known_failures lists the ones
that fail in only one mode. A P2P test that needs a loopback address
the machine lacks (macOS has only 127.0.0.1) is skipped with a warning,
and the run fails at the end with the command that adds it. The macOS
CI runners add 127.0.0.2. Each run gets a random port offset.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Fixes#22857
PES 2009 calls sceNetApctlGetState first and only calls sceNetApctlInit
when it fails with 0x80410a0d. We returned success, so Init was never
called, apctl's state never advanced and the Netconf dialog waited on
"Connecting to the access point" forever. A PSP returns 0x80410a0d before
Init and after Term (pspautotests net/adhoc/apctlinit).
After that the game polls sceNetGetLocalEtherAddr until it succeeds, which
we only allowed in ad hoc mode. Also allow it once apctl has connected. It
still fails with apctl initialized but not connected, as the test shows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
As on a PSP (pspautotests net/adhoc/abandon): the Disconnect fails and
the join goes on to CONNECT. Before, it was accepted, and the join's late
answer from the server could still connect us afterwards.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With offsets of up to 30000, a port net/adhoc/limits listened on could
be one an outgoing connection was using, and the listen failed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--speed=PERCENT runs a --realtime headless at an alternate speed (speed
control while connected is allowed for it), and pacing follows it. The
ad hoc tests now run at 800%, which takes their part of test.py -g from
about 71 s to 12 s; only local network round trips stay real time.
test.py also gives each ad hoc test run its own --port-offset: a
connection the previous run made with the same ports is still in TCP
TIME_WAIT, and connecting again failed with EADDRINUSE once runs came
close together.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alerts, discover, gamemode, limits, loopback, self, socketids and term
pass, so they move to tests_good. They run in real time against the
built-in server, about 71 s together. The ad hoc tests left in
tests_next say why they don't pass yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PSP stays in its group through sceNetAdhocTerm and sceNetAdhocInit,
and matching keeps working; only the sockets go, and calls on them
return NOT_INITIALIZED until the next init (net/adhoc/term, recorded on
a PSP). We also terminated adhocctl, so a game that restarts the adhoc
library inside a group found itself disconnected. Every other caller
already terminates adhocctl itself first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PSP hands out socket IDs from 1 again after sceNetAdhocTerm and
sceNetAdhocInit (net/adhoc/socketids, recorded on a PSP). We kept
counting up. Pangya: Fantasy Golf tears down the network between its
room and the round, and then keeps using socket 1 without looking at
what PdpCreate returned, so every send and receive failed with
INVALID_SOCKET_ID and each player saw the other as "Off-line".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Fixes#22331
A child's GetMembers starts with itself, then switches to the parent
first one keepalive interval after it was accepted
(net/adhoc/pair/matchingorder, recorded on two PSPs). We kept it self
first. Bomberman's client checks that the first member is the host
before it marks itself ready, so its lobby stayed empty and the host's
start failed with "failed to connect to the game".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The u64 total length made both context structs 8-aligned, while the
game's context only has to be 4-aligned (hash/hash puts it in a char
array). On ARM32, GCC then copies buf with vld1.64 [:64], which faults
on such an address - on hardware too, not just under qemu as the
known-failures entry assumed. Store the length as two words; the layout
is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
All the enabled sub-interrupt handlers were queued when the interrupt fired,
so a handler disabled by an earlier one in the same VBLANK still ran, and one
an earlier handler enabled waited for the next VBLANK. The PSP checks each
handler as it gets to it (intr/disablepending): skip a disabled one, and
queue a newly enabled one if it comes after the handler that's running.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pair tests (net/adhoc/pair) run on two headless instances through
Tools/adhoc_multi.py and the host's built-in relay. They're all in
tests_next for now. The single-console ad hoc tests get the built-in
server and real-time speed, which they need to get past creating a
group, with a timeout to match.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every export of pspnet_inet.prx but the address conversions starts with
sceKernelCheckThreadStack(), and fails without doing anything if the
thread has less than 0x1000 bytes of stack free (0xFE0 for socket()):
the socket calls with -1, without setting errno, and Init, Term,
GetErrno, GetPspError and SocketAbort with 0x80410005. Each function's
limit plus its own stack frame comes to that. pspautotests
net/inet/errnothread shows it on a thread with a 4 KB stack.
GetPspError is 0x80010000 | errno, and 0 for no error, rather than a
table of guesses, so 0x80010001 on a thread with no errno record yet
(net/inet/errnothread).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Following pspautotests net/inet/errors, states and reuse:
- bind decides itself whether a port is in use, as 4.4BSD's in_pcbbind()
does with SO_REUSEADDR and SO_REUSEPORT. The host can't, since its
sockets have both on for running several instances, so any number of
sockets could bind the same port. A socket that's bound already is
EINVAL.
- Shutting down the sending side of an unconnected TCP socket, or a
failed connect, throws away its TCP state as in 4.4BSD's tcp_usrreq(),
after which bind, listen, connect, accept and shutdown fail with
EINVAL, and its port is free again. shutdown otherwise only fails for
a bad how, and still reaches the host, so a thread waiting in accept
wakes up.
- listen on an unbound TCP socket binds one, which Winsock didn't, and on
a connected one succeeds.
- connect on a listening socket is EOPNOTSUPP. Winsock's WSAEINVAL for
it looked like a connect in progress, and a blocking socket waited
forever. A second connect on a loopback connection the host hasn't
noticed finishing is EISCONN once it has.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Recvmsg was a stub that failed without setting errno. It now receives
like recvfrom, scattering into the buffers and waiting like the other
blocking calls. Sendmsg is rewritten around the same msghdr reading.
Following pspautotests net/inet/msg:
- Recvmsg truncates the address to namelen, gives TCP none and a namelen
of 0, sets MSG_TRUNC in msg_flags, and controllen to 0.
- Sendmsg with no buffers sends an empty datagram, which macOS failed.
- sendto without an address works on a connected socket. Linux failed
it, given an address of length 0.
On Windows, Sendmsg uses WSASendTo, since WSASendMsg only takes
datagram sockets, and Recvmsg WSARecvFrom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Following pspautotests net/inet/polledge:
- poll reports only what was asked for, and never POLLHUP or POLLERR,
even for a peer that closed or reset the connection.
- select with a closed descriptor in a set fails with EBADF and leaves
the sets alone. It used to skip it.
The host side now uses poll() outside Windows, where select() can't take
a descriptor numbered FD_SETSIZE or above and FD_SET wrote past the
fd_set. macOS reports a peer that closed as POLLHUP with POLLPRI, and
the POLLPRI is ignored, as its select() shows nothing for it. Winsock's
select() has no such limit, but takes only 64 sockets a set, so it's
called on groups of 64, and poll is no longer cut short at 64 entries
there. A failing poll sets the host's errno rather than EINTR.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PSP's getsockopt and setsockopt work like 4.4BSD's, which
pspautotests net/inet/options records in detail, and most of it didn't
match: flags read back as their bit (SO_REUSEADDR as 4, SO_NBIO as
0x80), buffer sizes have their own defaults, getsockopt copies as much
as fits and says how much, and unknown options and levels fail with
ENOPROTOOPT and EINVAL.
So the socket-level options are now kept in InetSocket, and only the
ones that change what the host socket does are passed on to it. That
also stops the timeouts reaching the host, where Winsock read the
timeval as a millisecond count. Accepted sockets copy their options from
the listener, like BSD's sonewconn(). IP_TTL is kept too, as Windows'
default is 128 and the PSP's 64.
Timeouts are set in microseconds, but kept in 10 ms clock ticks, and
one under a tick is no timeout at all (net/inet/limits). Buffers can be
up to 233016 bytes (net/inet/limits), 4.4BSD's limit with 256-byte
mbufs, where we allowed 8 MB. SO_ERROR reads as an SCE error code rather
than an errno (net/inet/tcp).
The host's TCP send buffer is capped at 64 KB, closer to the PSP's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- accept and recvfrom write only as much of the address as its length
allows, and set the length to that (pspautotests net/inet/udp and
tcp). They wrote all 16 bytes and set it to 16. They now also give the
host an address of their own, so one the game passes without a length
can't make the host fail the call with EFAULT.
- InetAton and InetAddr take the forms 4.4BSD's inet_aton() does
("1.2.3", "16909060", "0x7f.1", octal with a leading zero), which the
host's inet_pton() didn't. InetPton takes only a plain dotted quad,
and only AF_INET. All three stop at a space. InetNtop returns NULL with
ENOSPC when the string doesn't fit, leaving the buffer alone, and with
EAFNOSUPPORT for other families (net/inet/addr).
- getpeername after the peer closed is ENOTCONN rather than macOS's
EINVAL, as 4.4BSD checks for a connection first (net/inet/errors).
Coded Arms Contagion calls InetNtop with a 4-byte buffer, which now gets
NULL, as on hardware.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
errno:
- A thread that hasn't made a call yet reads 0x80410001 from GetErrno,
as the PSP has no errno record for it until then. errno is saved in
savestates now, in a new sceNet section version.
- socket(), close() and setsockopt() clear it when they succeed.
- Running out of descriptors is EMFILE, at 255 like the PSP, and an
unconnected UDP socket sending without an address EDESTADDRREQ, which
Winsock calls ENOTCONN.
- On Windows, ENOBUFS, EPROTOTYPE, ENOPROTOOPT, EMSGSIZE, ENETRESET and
EDESTADDRREQ now map to the PSP's errnos. They only did elsewhere.
All from pspautotests net/inet/errors, options and errnothread.
The socket table:
- Descriptor numbers are handed out round-robin up to 255, as on the PSP
(net/inet/states), rather than the lowest free one.
- Closing frees the slot even if the host's close fails, and Close and
CloseAll take the lock the rest of SocketManager does.
- accept no longer leaks the host socket when it runs out of numbers.
- bind no longer asks UPnP to forward port 0, and closing the socket
removes its forward, unless another socket still uses the port.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Host sockets are always non-blocking, so a game's blocking recv(), accept()
and so on came back with EAGAIN at once, and connect() and poll() blocked the
whole emulator in the host call instead. Now recv, recvfrom, send, sendto,
accept, connect, poll and select try once, and if the call would block on a
blocking socket (no SO_NBIO or MSG_DONTWAIT), the thread waits and a
CoreTiming event retries it every millisecond. It ends when the call completes
or fails, or when SO_RCVTIMEO/SO_SNDTIMEO runs out in emulated time.
Sockets accepted on Linux are made non-blocking too: unlike BSD and Windows,
Linux doesn't pass that on from the listening socket. select() rejects a
tv_usec of a second or more with EINVAL.
Hardware behaviour from pspautotests net/inet/blocking.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pspautotests audio/sceaudio/input and audio/sceaudio/routing.
- sceAudioInput fails until sceAudioInputInit, then checks that the
sample count is a nonzero multiple of 64 before checking the rate
(11025, 22050 or 44100 only). A rate of 0 now gets the PSP's error
instead of the USB mic's. Init takes about 3ms.
- A blocking mic input woke with the byte count rather than the sample
count that the non-blocking call returns.
- The routing functions are exported by both sceAudio and
sceAudioRouting, and the latter had its own copy that ignored the
stored modes. Both now share the one in sceAudio, with the ranges
checked. SetVolumeMode returns 0, not the previous mode, and both
modes start at 0.
sceAudio's savestate section goes to version 5 for the input init flag.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The apctl, NpAuth and NpMatching2 event loops kept references to the
front of their deque after popping it, so callbacks could get the
next event's arguments. NpMatching2 also dereferenced its optional
opt param when it was null.
- The resolver was left stopped after any savestate load or reboot,
which broke Outrun 2006 (it relies on it starting out running).
- Getpeername/Getsockname with a namelen under 2 copied a negative size.
They now write the PSP's 16-byte sockaddr truncated to namelen, with
sa_len 16, as pspautotests net/inet/poll shows on hardware.
- Poll handed an fd that isn't open (-1) to FD_SET. It now reports
POLLNVAL for it and counts it, as on hardware (net/inet/poll).
Setsockopt read past a local u32 for larger options, and passed a
negative optlen to the host. A zero cmsg_len looped forever.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pspautotests modules/idlist shows that sceKernelGetModuleIdList still
writes a partial entry at the end of the buffer (a size of 6 gives two
entries), which the previous commit had stopped. A NULL buffer or a
negative size returns ILLEGAL_ADDR without writing the count.
sysmem/kernel/heap shows sceKernelAllocHeapMemory returning null for
sizes near 4GB, rather than wrapping. The WithOption variant already
checked this. The plain one now does too.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>