Network races: the local console marshals remote pods over the wire
The lobby-owner-as-console architecture, in-engine. RPL4CONSOLE gains RPL4LocalConsole_InstallNetworkRace: the owner pod switches to network mode and meshes like any pod (egg fed locally via FeedLocalEgg, which now opens the ConsoleOnly state gate), while the console tick also marshals REMOTE pods over NetTransport speaking the exact arcade protocol - egg chunks with 5s-retry-until-ACK, 1Hz state polling, RunMission once every pod stages at WaitingForLaunch, StopMission at expiry (remotes first, local pod holds until their EndMission scores land), score intake labeled with [pilots]-order names on the results screen. The front end builds the multi-pilot egg: RP412HOSTPODS lists member console channels (lobby stand-in; the Steam lobby feeds the same path), RP412HOSTPORT/RP412HOSTADDR set the owner side. Winsock Connect now redials with a fresh socket per attempt (a refused TCP socket is dead; the old loop reused it) bounded at 120s - needed whenever a peer boots after the caller, which is the normal Steam lobby launch order. Verified on loopback: member pod in -net, owner hosting from its menu; mesh completed both sides, 30s race, remote score collected over the wire (host 3), local stop after the drain, results screen shows both pilots by name in one process that returns to the menu. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+59
-53
@@ -112,66 +112,72 @@ namespace
|
||||
<< inet_ntoa(remote->sin_addr) << ":" << ntohs(remote->sin_port)
|
||||
<< "...\n" << std::flush;
|
||||
|
||||
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (sock == INVALID_SOCKET)
|
||||
//
|
||||
// Retry-while-refused, bounded: the egg-ACK ordering means
|
||||
// the peer may not be listening yet. A refused TCP socket
|
||||
// is dead - every attempt needs a fresh one.
|
||||
//
|
||||
DWORD deadline = GetTickCount() + 120 * 1000;
|
||||
for (;;)
|
||||
{
|
||||
DEBUG_STREAM << "ERROR: socket() failed with "
|
||||
<< WSAGetLastError() << "!\n";
|
||||
return InvalidConnection;
|
||||
}
|
||||
|
||||
bool reuse_address = true;
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
|
||||
(char *) &reuse_address, sizeof(bool)))
|
||||
{
|
||||
DEBUG_STREAM << "ERROR: Could not set SO_REUSEADDR on socket; setsockopt() failed with "
|
||||
<< WSAGetLastError() << "!\n" << std::flush;
|
||||
closesocket(sock);
|
||||
return InvalidConnection;
|
||||
}
|
||||
|
||||
// bind the game port: the mesh identifies peers by
|
||||
// address AND port, so the source port must be ours
|
||||
sockaddr_in local_endpoint;
|
||||
memset(&local_endpoint, 0, sizeof(local_endpoint));
|
||||
local_endpoint.sin_family = AF_INET;
|
||||
local_endpoint.sin_port = htons((unsigned short) local_port);
|
||||
local_endpoint.sin_addr.S_un.S_addr = INADDR_ANY;
|
||||
if (bind(sock, (sockaddr *) &local_endpoint, sizeof(local_endpoint)))
|
||||
{
|
||||
DEBUG_STREAM << "ERROR: Could not bind local socket; bind() failed with "
|
||||
<< WSAGetLastError() << "!\n" << std::flush;
|
||||
closesocket(sock);
|
||||
return InvalidConnection;
|
||||
}
|
||||
|
||||
int wsa_error = 0, result;
|
||||
do
|
||||
{
|
||||
result = connect(sock, (sockaddr *) remote, sizeof(SOCKADDR_IN));
|
||||
if (result != 0)
|
||||
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (sock == INVALID_SOCKET)
|
||||
{
|
||||
wsa_error = WSAGetLastError();
|
||||
DEBUG_STREAM << "ERROR: socket() failed with "
|
||||
<< WSAGetLastError() << "!\n";
|
||||
return InvalidConnection;
|
||||
}
|
||||
} while (result != 0 && wsa_error == WSAECONNREFUSED);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
DEBUG_STREAM << "ERROR: connect() failed with "
|
||||
<< wsa_error << "!\n" << std::flush;
|
||||
closesocket(sock);
|
||||
return InvalidConnection;
|
||||
}
|
||||
bool reuse_address = true;
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
|
||||
(char *) &reuse_address, sizeof(bool)))
|
||||
{
|
||||
DEBUG_STREAM << "ERROR: Could not set SO_REUSEADDR on socket; setsockopt() failed with "
|
||||
<< WSAGetLastError() << "!\n" << std::flush;
|
||||
closesocket(sock);
|
||||
return InvalidConnection;
|
||||
}
|
||||
|
||||
unsigned long enable = 1;
|
||||
if (ioctlsocket(sock, FIONBIO, &enable))
|
||||
{
|
||||
DEBUG_STREAM << "ERROR: Could not set actively opened socket to nonblocking; ioctlsocket() failed with "
|
||||
<< WSAGetLastError() << "!\n" << std::flush;
|
||||
// bind the game port: the mesh identifies peers by
|
||||
// address AND port, so the source port must be ours
|
||||
// (port 0 = ephemeral, for console channels)
|
||||
sockaddr_in local_endpoint;
|
||||
memset(&local_endpoint, 0, sizeof(local_endpoint));
|
||||
local_endpoint.sin_family = AF_INET;
|
||||
local_endpoint.sin_port = htons((unsigned short) local_port);
|
||||
local_endpoint.sin_addr.S_un.S_addr = INADDR_ANY;
|
||||
if (bind(sock, (sockaddr *) &local_endpoint, sizeof(local_endpoint)))
|
||||
{
|
||||
DEBUG_STREAM << "ERROR: Could not bind local socket; bind() failed with "
|
||||
<< WSAGetLastError() << "!\n" << std::flush;
|
||||
closesocket(sock);
|
||||
return InvalidConnection;
|
||||
}
|
||||
|
||||
if (connect(sock, (sockaddr *) remote, sizeof(SOCKADDR_IN)) == 0)
|
||||
{
|
||||
unsigned long enable = 1;
|
||||
if (ioctlsocket(sock, FIONBIO, &enable))
|
||||
{
|
||||
DEBUG_STREAM << "ERROR: Could not set actively opened socket to nonblocking; ioctlsocket() failed with "
|
||||
<< WSAGetLastError() << "!\n" << std::flush;
|
||||
closesocket(sock);
|
||||
return InvalidConnection;
|
||||
}
|
||||
return (Connection) sock;
|
||||
}
|
||||
|
||||
int wsa_error = WSAGetLastError();
|
||||
closesocket(sock);
|
||||
return InvalidConnection;
|
||||
if (wsa_error != WSAECONNREFUSED ||
|
||||
(LONG)(GetTickCount() - deadline) >= 0)
|
||||
{
|
||||
DEBUG_STREAM << "ERROR: connect() failed with "
|
||||
<< wsa_error << "!\n" << std::flush;
|
||||
return InvalidConnection;
|
||||
}
|
||||
Sleep(250); // peer not up yet - redial
|
||||
}
|
||||
return (Connection) sock;
|
||||
}
|
||||
|
||||
Connection
|
||||
|
||||
Reference in New Issue
Block a user