SteamNetTransport: the Steam wire is implemented and live
Steamworks SDK 1.64 vendored at extern/steamworks_sdk_164 (headers + win32 redistributables only; .gitignore trims the rest). Both projects build with RP412_STEAM; activation stays behind the RP412STEAM=1 environment switch, so plain desktop runs never touch Steam. L4STEAMTRANSPORT.cpp implements NetTransport on ISteamNetworkingSockets with FakeIP: SteamNetTransport_Install brings up SteamAPI, relay network access, and a two-port FakeIP identity (fake port 0 = console channel, 1 = game mesh), then swaps the process wire; any failure logs the reason and the game carries on over TCP. Addressing keeps the engine untouched: all pods share the -net port convention, eggs carry fakeip:engineport, and the transport alone translates engine ports to Steam fake ports via the lobby-fed peer table (RegisterPeer). Connect mirrors the TCP retry-while-refused loop; Receive normalizes message lanes back into the stream semantics CheckBuffers expects. Runtime verified on this box: RP412STEAM=1 under AppID 480 came up as 169.254.59.52 (fake ports 32256/32257); without Steam credentials it falls back to TCP cleanly; default boot logs no Steam lines at all. steam_api.dll ships in the dist. Next: the lobby layer (ISteamMatchmaking member data -> RegisterPeer + egg build + RPL4CONSOLE marshal), which needs a second account to test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,651 @@
|
||||
#include "mungal4.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "l4steamtransport.h"
|
||||
|
||||
#ifdef RP412_STEAM
|
||||
|
||||
#include "l4nettransport.h"
|
||||
|
||||
// The engine builds with /Zp1; Valve's ABI expects default packing.
|
||||
// Callback structs carry their own pack pragmas, but everything else
|
||||
// must see the platform default.
|
||||
#pragma pack(push, 8)
|
||||
#include "steam/steam_api.h"
|
||||
#include "steam/isteamnetworkingsockets.h"
|
||||
#include "steam/isteamnetworkingutils.h"
|
||||
#include "steam/steamnetworkingfakeip.h"
|
||||
#pragma pack(pop)
|
||||
|
||||
//########################################################################
|
||||
// SteamNetTransport - see l4steamtransport.h for the design. The
|
||||
// engine is single threaded and so is this: every entry point pumps
|
||||
// SteamAPI_RunCallbacks, which is where the status-changed callback
|
||||
// fires (accepting incoming connections and marking drops).
|
||||
//########################################################################
|
||||
|
||||
namespace
|
||||
{
|
||||
enum
|
||||
{
|
||||
maxListeners = 4,
|
||||
maxConnections = 32,
|
||||
maxPeers = 16,
|
||||
maxPending = 8,
|
||||
leftoverSize = 4096
|
||||
};
|
||||
|
||||
struct ListenerRecord
|
||||
{
|
||||
HSteamListenSocket handle;
|
||||
int fakePortIndex;
|
||||
unsigned short enginePort; // host order
|
||||
HSteamNetConnection pending[maxPending];
|
||||
int pendingCount;
|
||||
};
|
||||
|
||||
struct ConnectionRecord
|
||||
{
|
||||
HSteamNetConnection handle;
|
||||
unsigned long remoteIP; // network order
|
||||
unsigned short remoteEnginePort; // network order
|
||||
Logical connected;
|
||||
Logical dead;
|
||||
char leftover[leftoverSize];
|
||||
int leftoverCount;
|
||||
};
|
||||
|
||||
struct PeerRecord
|
||||
{
|
||||
unsigned long fakeIP; // host order
|
||||
unsigned short fakeGamePort; // host order
|
||||
};
|
||||
|
||||
Logical gSteamReady = False;
|
||||
unsigned long gLocalFakeIP = 0; // host order
|
||||
unsigned short gLocalFakePorts[2] = { 0, 0 };
|
||||
char gLocalFakeAddressString[32] = "";
|
||||
|
||||
// engine ports in the order Listen sees them: [0] console, [1] game
|
||||
unsigned short gEnginePorts[2] = { 0, 0 };
|
||||
int gEnginePortCount = 0;
|
||||
|
||||
ListenerRecord gListeners[maxListeners];
|
||||
int gListenerCount = 0;
|
||||
ConnectionRecord gConnections[maxConnections];
|
||||
int gConnectionCount = 0;
|
||||
PeerRecord gPeers[maxPeers];
|
||||
int gPeerCount = 0;
|
||||
|
||||
ListenerRecord *
|
||||
FindListener(HSteamListenSocket handle)
|
||||
{
|
||||
for (int i = 0; i < gListenerCount; ++i)
|
||||
{
|
||||
if (gListeners[i].handle == handle)
|
||||
{
|
||||
return &gListeners[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ConnectionRecord *
|
||||
FindConnection(HSteamNetConnection handle)
|
||||
{
|
||||
for (int i = 0; i < gConnectionCount; ++i)
|
||||
{
|
||||
if (gConnections[i].handle == handle)
|
||||
{
|
||||
return &gConnections[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ConnectionRecord *
|
||||
AddConnection(
|
||||
HSteamNetConnection handle,
|
||||
unsigned long remote_ip_net,
|
||||
unsigned short remote_engine_port_net
|
||||
)
|
||||
{
|
||||
if (gConnectionCount >= maxConnections)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
ConnectionRecord *record = &gConnections[gConnectionCount++];
|
||||
memset(record, 0, sizeof(*record));
|
||||
record->handle = handle;
|
||||
record->remoteIP = remote_ip_net;
|
||||
record->remoteEnginePort = remote_engine_port_net;
|
||||
return record;
|
||||
}
|
||||
|
||||
void
|
||||
RemoveConnection(HSteamNetConnection handle)
|
||||
{
|
||||
for (int i = 0; i < gConnectionCount; ++i)
|
||||
{
|
||||
if (gConnections[i].handle == handle)
|
||||
{
|
||||
gConnections[i] = gConnections[gConnectionCount - 1];
|
||||
--gConnectionCount;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short
|
||||
LookupPeerFakeGamePort(unsigned long fake_ip_host)
|
||||
{
|
||||
for (int i = 0; i < gPeerCount; ++i)
|
||||
{
|
||||
if (gPeers[i].fakeIP == fake_ip_host)
|
||||
{
|
||||
return gPeers[i].fakeGamePort;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Status-changed callback (fires inside SteamAPI_RunCallbacks on
|
||||
// the game thread): accept incoming, queue connected, mark drops
|
||||
//---------------------------------------------------------------
|
||||
void __cdecl
|
||||
OnConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t *status)
|
||||
{
|
||||
switch (status->m_info.m_eState)
|
||||
{
|
||||
case k_ESteamNetworkingConnectionState_Connecting:
|
||||
if (status->m_info.m_hListenSocket != k_HSteamListenSocket_Invalid)
|
||||
{
|
||||
// incoming: accept immediately, queue it when Connected
|
||||
SteamNetworkingSockets()->AcceptConnection(status->m_hConn);
|
||||
}
|
||||
break;
|
||||
|
||||
case k_ESteamNetworkingConnectionState_Connected:
|
||||
if (status->m_info.m_hListenSocket != k_HSteamListenSocket_Invalid)
|
||||
{
|
||||
ListenerRecord *listener = FindListener(status->m_info.m_hListenSocket);
|
||||
if (listener != NULL && listener->pendingCount < maxPending)
|
||||
{
|
||||
listener->pending[listener->pendingCount++] = status->m_hConn;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ConnectionRecord *record = FindConnection(status->m_hConn);
|
||||
if (record != NULL)
|
||||
{
|
||||
record->connected = True;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case k_ESteamNetworkingConnectionState_ProblemDetectedLocally:
|
||||
case k_ESteamNetworkingConnectionState_ClosedByPeer:
|
||||
{
|
||||
ConnectionRecord *record = FindConnection(status->m_hConn);
|
||||
if (record != NULL)
|
||||
{
|
||||
record->dead = True;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// The transport
|
||||
//---------------------------------------------------------------
|
||||
class SteamNetTransport:
|
||||
public NetTransport
|
||||
{
|
||||
public:
|
||||
Logical
|
||||
Startup()
|
||||
{
|
||||
// Install() already brought Steam up; the network manager
|
||||
// just confirms the transport is live
|
||||
return gSteamReady;
|
||||
}
|
||||
|
||||
void
|
||||
Cleanup()
|
||||
{
|
||||
// mission teardown (single-binary race loop): drop the
|
||||
// wire but keep the Steam API up - the lobby lives on
|
||||
for (int i = 0; i < gConnectionCount; ++i)
|
||||
{
|
||||
SteamNetworkingSockets()->CloseConnection(
|
||||
gConnections[i].handle, 0, "mission teardown", true);
|
||||
}
|
||||
gConnectionCount = 0;
|
||||
for (int j = 0; j < gListenerCount; ++j)
|
||||
{
|
||||
SteamNetworkingSockets()->CloseListenSocket(gListeners[j].handle);
|
||||
}
|
||||
gListenerCount = 0;
|
||||
gEnginePortCount = 0;
|
||||
}
|
||||
|
||||
int
|
||||
GetLocalAddresses(
|
||||
unsigned long *addresses,
|
||||
int max_count
|
||||
)
|
||||
{
|
||||
if (!gSteamReady || max_count < 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
addresses[0] = htonl(gLocalFakeIP);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Logical
|
||||
Resolve(
|
||||
const char *host_name,
|
||||
SOCKADDR_IN *address
|
||||
)
|
||||
{
|
||||
memset(address, 0, sizeof(SOCKADDR_IN));
|
||||
address->sin_family = AF_INET;
|
||||
|
||||
SteamNetworkingIPAddr parsed;
|
||||
if (!parsed.ParseString(host_name))
|
||||
{
|
||||
return False;
|
||||
}
|
||||
address->sin_addr.S_un.S_addr = htonl(parsed.GetIPv4());
|
||||
address->sin_port = htons(parsed.m_port); // 0 when absent
|
||||
return True;
|
||||
}
|
||||
|
||||
Connection
|
||||
Connect(
|
||||
const SOCKADDR_IN *remote,
|
||||
int local_port
|
||||
)
|
||||
{
|
||||
unsigned long remote_fake_ip = ntohl(remote->sin_addr.S_un.S_addr);
|
||||
unsigned short fake_game_port = LookupPeerFakeGamePort(remote_fake_ip);
|
||||
if (fake_game_port == 0)
|
||||
{
|
||||
DEBUG_STREAM << "SteamNetTransport: no registered peer for "
|
||||
<< inet_ntoa(remote->sin_addr) << " - lobby did not feed it\n" << std::flush;
|
||||
return InvalidConnection;
|
||||
}
|
||||
|
||||
DEBUG_STREAM << "SteamNetTransport: connecting to "
|
||||
<< inet_ntoa(remote->sin_addr) << " (fake port "
|
||||
<< fake_game_port << ")...\n" << std::flush;
|
||||
|
||||
SteamNetworkingConfigValue_t option;
|
||||
option.SetPtr(
|
||||
k_ESteamNetworkingConfig_Callback_ConnectionStatusChanged,
|
||||
(void *) &OnConnectionStatusChanged);
|
||||
|
||||
SteamNetworkingIPAddr target;
|
||||
target.Clear();
|
||||
target.SetIPv4(remote_fake_ip, fake_game_port);
|
||||
|
||||
// mirror the TCP retry-while-refused loop, bounded: the
|
||||
// egg-ACK ordering means the peer may not be listening yet
|
||||
DWORD deadline = GetTickCount() + 120 * 1000;
|
||||
for (;;)
|
||||
{
|
||||
HSteamNetConnection handle =
|
||||
SteamNetworkingSockets()->ConnectByIPAddress(target, 1, &option);
|
||||
if (handle == k_HSteamNetConnection_Invalid)
|
||||
{
|
||||
return InvalidConnection;
|
||||
}
|
||||
ConnectionRecord *record =
|
||||
AddConnection(handle, remote->sin_addr.S_un.S_addr, remote->sin_port);
|
||||
|
||||
while (!record->connected && !record->dead &&
|
||||
(LONG)(GetTickCount() - deadline) < 0)
|
||||
{
|
||||
SteamAPI_RunCallbacks();
|
||||
Sleep(25);
|
||||
}
|
||||
if (record->connected)
|
||||
{
|
||||
return (Connection) handle;
|
||||
}
|
||||
|
||||
// attempt failed - drop it and retry until the deadline
|
||||
SteamNetworkingSockets()->CloseConnection(handle, 0, "retry", false);
|
||||
RemoveConnection(handle);
|
||||
if ((LONG)(GetTickCount() - deadline) >= 0)
|
||||
{
|
||||
DEBUG_STREAM << "SteamNetTransport: connect timed out\n" << std::flush;
|
||||
return InvalidConnection;
|
||||
}
|
||||
Sleep(250);
|
||||
}
|
||||
}
|
||||
|
||||
Connection
|
||||
Listen(
|
||||
int local_port,
|
||||
int backlog
|
||||
)
|
||||
{
|
||||
if (gListenerCount >= maxListeners)
|
||||
{
|
||||
return InvalidConnection;
|
||||
}
|
||||
|
||||
// engine port -> fake port index, in order of appearance:
|
||||
// the console channel always listens first, the mesh second
|
||||
int index = -1;
|
||||
for (int i = 0; i < gEnginePortCount; ++i)
|
||||
{
|
||||
if (gEnginePorts[i] == (unsigned short) local_port)
|
||||
{
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
if (index < 0)
|
||||
{
|
||||
if (gEnginePortCount >= 2)
|
||||
{
|
||||
DEBUG_STREAM << "SteamNetTransport: only two fake ports allocated!\n" << std::flush;
|
||||
return InvalidConnection;
|
||||
}
|
||||
index = gEnginePortCount;
|
||||
gEnginePorts[gEnginePortCount++] = (unsigned short) local_port;
|
||||
}
|
||||
|
||||
DEBUG_STREAM << "SteamNetTransport: listening on engine port "
|
||||
<< local_port << " (fake port " << gLocalFakePorts[index]
|
||||
<< ")...\n" << std::flush;
|
||||
|
||||
SteamNetworkingConfigValue_t option;
|
||||
option.SetPtr(
|
||||
k_ESteamNetworkingConfig_Callback_ConnectionStatusChanged,
|
||||
(void *) &OnConnectionStatusChanged);
|
||||
|
||||
HSteamListenSocket handle =
|
||||
SteamNetworkingSockets()->CreateListenSocketP2PFakeIP(index, 1, &option);
|
||||
if (handle == k_HSteamListenSocket_Invalid)
|
||||
{
|
||||
return InvalidConnection;
|
||||
}
|
||||
|
||||
ListenerRecord *listener = &gListeners[gListenerCount++];
|
||||
memset(listener, 0, sizeof(*listener));
|
||||
listener->handle = handle;
|
||||
listener->fakePortIndex = index;
|
||||
listener->enginePort = (unsigned short) local_port;
|
||||
return (Connection) handle;
|
||||
}
|
||||
|
||||
Connection
|
||||
Accept(Connection listener_handle)
|
||||
{
|
||||
SteamAPI_RunCallbacks();
|
||||
|
||||
ListenerRecord *listener = FindListener((HSteamListenSocket) listener_handle);
|
||||
if (listener == NULL || listener->pendingCount == 0)
|
||||
{
|
||||
return InvalidConnection;
|
||||
}
|
||||
|
||||
HSteamNetConnection handle = listener->pending[0];
|
||||
for (int i = 1; i < listener->pendingCount; ++i)
|
||||
{
|
||||
listener->pending[i - 1] = listener->pending[i];
|
||||
}
|
||||
--listener->pendingCount;
|
||||
|
||||
// the remote engine port is the same engine port we accept
|
||||
// on: all pods share the -net convention under Steam
|
||||
unsigned long remote_ip_net = 0;
|
||||
SteamNetworkingIPAddr remote_fake;
|
||||
if (SteamNetworkingSockets()->GetRemoteFakeIPForConnection(
|
||||
handle, &remote_fake) == k_EResultOK)
|
||||
{
|
||||
remote_ip_net = htonl(remote_fake.GetIPv4());
|
||||
}
|
||||
ConnectionRecord *record = AddConnection(
|
||||
handle, remote_ip_net, htons(listener->enginePort));
|
||||
if (record != NULL)
|
||||
{
|
||||
record->connected = True;
|
||||
}
|
||||
return (Connection) handle;
|
||||
}
|
||||
|
||||
void
|
||||
Close(Connection connection)
|
||||
{
|
||||
ListenerRecord *listener = FindListener((HSteamListenSocket) connection);
|
||||
if (listener != NULL)
|
||||
{
|
||||
SteamNetworkingSockets()->CloseListenSocket(listener->handle);
|
||||
*listener = gListeners[gListenerCount - 1];
|
||||
--gListenerCount;
|
||||
return;
|
||||
}
|
||||
SteamNetworkingSockets()->CloseConnection(
|
||||
(HSteamNetConnection) connection, 0, "closed", true);
|
||||
RemoveConnection((HSteamNetConnection) connection);
|
||||
}
|
||||
|
||||
int
|
||||
Send(
|
||||
Connection connection,
|
||||
const void *data,
|
||||
int size
|
||||
)
|
||||
{
|
||||
EResult result = SteamNetworkingSockets()->SendMessageToConnection(
|
||||
(HSteamNetConnection) connection, data, (uint32) size,
|
||||
k_nSteamNetworkingSend_Reliable, NULL);
|
||||
return (result == k_EResultOK) ? size : -1;
|
||||
}
|
||||
|
||||
int
|
||||
Receive(
|
||||
Connection connection,
|
||||
void *buffer,
|
||||
int size
|
||||
)
|
||||
{
|
||||
SteamAPI_RunCallbacks();
|
||||
|
||||
ConnectionRecord *record = FindConnection((HSteamNetConnection) connection);
|
||||
if (record == NULL)
|
||||
{
|
||||
return ReceiveNoData;
|
||||
}
|
||||
|
||||
char *out = (char *) buffer;
|
||||
int copied = 0;
|
||||
|
||||
// leftover bytes from a message that outsized the last call
|
||||
if (record->leftoverCount > 0)
|
||||
{
|
||||
int take = (record->leftoverCount < size) ? record->leftoverCount : size;
|
||||
memcpy(out, record->leftover, take);
|
||||
memmove(record->leftover, record->leftover + take,
|
||||
record->leftoverCount - take);
|
||||
record->leftoverCount -= take;
|
||||
out += take;
|
||||
copied += take;
|
||||
}
|
||||
|
||||
// drain messages while they fit; stash any partial tail
|
||||
while (copied < size && record->leftoverCount == 0)
|
||||
{
|
||||
SteamNetworkingMessage_t *message = NULL;
|
||||
int count = SteamNetworkingSockets()->ReceiveMessagesOnConnection(
|
||||
record->handle, &message, 1);
|
||||
if (count <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
int room = size - copied;
|
||||
int take = ((int) message->m_cbSize < room) ? (int) message->m_cbSize : room;
|
||||
memcpy(out, message->m_pData, take);
|
||||
out += take;
|
||||
copied += take;
|
||||
|
||||
int rest = (int) message->m_cbSize - take;
|
||||
if (rest > 0)
|
||||
{
|
||||
if (rest > leftoverSize)
|
||||
{
|
||||
rest = leftoverSize; // cannot happen: engine packets < 2K
|
||||
}
|
||||
memcpy(record->leftover, (const char *) message->m_pData + take, rest);
|
||||
record->leftoverCount = rest;
|
||||
}
|
||||
message->Release();
|
||||
}
|
||||
|
||||
if (copied > 0)
|
||||
{
|
||||
return copied;
|
||||
}
|
||||
if (record->dead)
|
||||
{
|
||||
return ReceiveDisconnected;
|
||||
}
|
||||
return ReceiveNoData;
|
||||
}
|
||||
|
||||
Logical
|
||||
GetRemoteAddress(
|
||||
Connection connection,
|
||||
SOCKADDR_IN *address
|
||||
)
|
||||
{
|
||||
memset(address, 0, sizeof(SOCKADDR_IN));
|
||||
ConnectionRecord *record = FindConnection((HSteamNetConnection) connection);
|
||||
if (record == NULL || !record->connected)
|
||||
{
|
||||
return False;
|
||||
}
|
||||
address->sin_family = AF_INET;
|
||||
address->sin_addr.S_un.S_addr = record->remoteIP;
|
||||
address->sin_port = record->remoteEnginePort;
|
||||
return True;
|
||||
}
|
||||
};
|
||||
|
||||
SteamNetTransport gSteamTransport;
|
||||
}
|
||||
|
||||
//########################################################################
|
||||
// Install: bring Steam up, get our FakeIP identity, take over the wire
|
||||
//########################################################################
|
||||
|
||||
Logical
|
||||
SteamNetTransport_Install()
|
||||
{
|
||||
if (gSteamReady)
|
||||
{
|
||||
return True;
|
||||
}
|
||||
|
||||
if (!SteamAPI_Init())
|
||||
{
|
||||
DEBUG_STREAM << "SteamNetTransport: SteamAPI_Init failed "
|
||||
<< "(Steam not running, or no steam_appid.txt) - staying on TCP\n" << std::flush;
|
||||
return False;
|
||||
}
|
||||
|
||||
SteamNetworkingUtils()->InitRelayNetworkAccess();
|
||||
|
||||
if (!SteamNetworkingSockets()->BeginAsyncRequestFakeIP(2))
|
||||
{
|
||||
DEBUG_STREAM << "SteamNetTransport: BeginAsyncRequestFakeIP failed - staying on TCP\n" << std::flush;
|
||||
return False;
|
||||
}
|
||||
|
||||
// FakeIP allocation is async: pump until it lands (or give up)
|
||||
SteamNetworkingFakeIPResult_t fake;
|
||||
memset(&fake, 0, sizeof(fake));
|
||||
DWORD deadline = GetTickCount() + 20 * 1000;
|
||||
for (;;)
|
||||
{
|
||||
SteamAPI_RunCallbacks();
|
||||
SteamNetworkingSockets()->GetFakeIP(0, &fake);
|
||||
if (fake.m_eResult == k_EResultOK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (fake.m_eResult != k_EResultBusy && fake.m_eResult != k_EResultNoMatch)
|
||||
{
|
||||
DEBUG_STREAM << "SteamNetTransport: FakeIP allocation failed (EResult "
|
||||
<< (int) fake.m_eResult << ") - staying on TCP\n" << std::flush;
|
||||
return False;
|
||||
}
|
||||
if ((LONG)(GetTickCount() - deadline) >= 0)
|
||||
{
|
||||
DEBUG_STREAM << "SteamNetTransport: FakeIP allocation timed out - staying on TCP\n" << std::flush;
|
||||
return False;
|
||||
}
|
||||
Sleep(50);
|
||||
}
|
||||
|
||||
gLocalFakeIP = fake.m_unIP;
|
||||
gLocalFakePorts[0] = fake.m_unPorts[0];
|
||||
gLocalFakePorts[1] = fake.m_unPorts[1];
|
||||
|
||||
SteamNetworkingIPAddr mine;
|
||||
mine.Clear();
|
||||
mine.SetIPv4(gLocalFakeIP, 0);
|
||||
mine.ToString(gLocalFakeAddressString, sizeof(gLocalFakeAddressString), false);
|
||||
|
||||
DEBUG_STREAM << "SteamNetTransport: up as " << gLocalFakeAddressString
|
||||
<< " (fake ports " << gLocalFakePorts[0] << " console, "
|
||||
<< gLocalFakePorts[1] << " game)\n" << std::flush;
|
||||
|
||||
gSteamReady = True;
|
||||
NetTransport_Set(&gSteamTransport);
|
||||
return True;
|
||||
}
|
||||
|
||||
const char *
|
||||
SteamNetTransport_GetFakeAddressString()
|
||||
{
|
||||
return gLocalFakeAddressString;
|
||||
}
|
||||
|
||||
int
|
||||
SteamNetTransport_GetFakeGamePort()
|
||||
{
|
||||
return gLocalFakePorts[1];
|
||||
}
|
||||
|
||||
void
|
||||
SteamNetTransport_RegisterPeer(
|
||||
const char *fake_ip,
|
||||
int fake_game_port
|
||||
)
|
||||
{
|
||||
if (gPeerCount >= maxPeers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SteamNetworkingIPAddr parsed;
|
||||
if (!parsed.ParseString(fake_ip))
|
||||
{
|
||||
return;
|
||||
}
|
||||
gPeers[gPeerCount].fakeIP = parsed.GetIPv4();
|
||||
gPeers[gPeerCount].fakeGamePort = (unsigned short) fake_game_port;
|
||||
++gPeerCount;
|
||||
DEBUG_STREAM << "SteamNetTransport: peer registered: " << fake_ip
|
||||
<< " game port " << fake_game_port << "\n" << std::flush;
|
||||
}
|
||||
|
||||
#endif // RP412_STEAM
|
||||
+61
-106
@@ -1,7 +1,7 @@
|
||||
//===========================================================================//
|
||||
// File: l4steamtransport.h //
|
||||
// Project: MUNGA Brick: Steam Network Transport (skeleton) //
|
||||
// Contents: NetTransport over ISteamNetworkingSockets - awaiting the SDK //
|
||||
// Project: MUNGA Brick: Steam Network Transport //
|
||||
// Contents: NetTransport over ISteamNetworkingSockets (FakeIP) //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc. //
|
||||
// PROPRIETARY AND CONFIDENTIAL //
|
||||
@@ -9,120 +9,75 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "..\munga\style.h"
|
||||
|
||||
//########################################################################
|
||||
// SteamNetTransport - the retail wire. NOT BUILT YET: everything below
|
||||
// is guarded by RP412_STEAM, which stays undefined until the Steamworks
|
||||
// SDK is dropped into the tree (needs a Steamworks partner login to
|
||||
// download; put it at extern\steamworks and add steam_api.lib +
|
||||
// steam_api.dll to the link/dist).
|
||||
// SteamNetTransport - the retail wire (l4steamtransport.cpp). Built
|
||||
// only under RP412_STEAM (Steamworks SDK vendored at
|
||||
// extern\steamworks_sdk_164; steam_api.dll ships beside the exe).
|
||||
//
|
||||
// Method mapping (see also docs/RP412-FRONTEND-DESIGN.md section 3):
|
||||
// Method mapping onto ISteamNetworkingSockets:
|
||||
//
|
||||
// Startup SteamAPI_Init + ISteamNetworkingUtils::InitRelay
|
||||
// NetworkAccessAuthorization; then
|
||||
// BeginAsyncRequestFakeIP so this pod has a fake
|
||||
// IPv4 identity to put in the [pilots] list.
|
||||
// Cleanup SteamAPI_Shutdown.
|
||||
// GetLocalAddresses our allocated FakeIP (the mesh identifies
|
||||
// "which pilots entry is me" against it, same as
|
||||
// the LAN build matches its interface list).
|
||||
// Resolve parse ip[:port] exactly like Winsock - FakeIPs
|
||||
// ARE IPv4 strings, so egg text stays unchanged.
|
||||
// Connect ConnectP2PByFakeIP (SDR picks the route, NAT
|
||||
// traversal and IP privacy come free); block on
|
||||
// the connection state callback until Connected,
|
||||
// mirroring the TCP retry-until-accepted loop.
|
||||
// Listen CreateListenSocketP2PFakeIP(port index); the
|
||||
// game port / console port pair becomes fake-port
|
||||
// index 0 / 1.
|
||||
// Accept poll the connection-request callback queue,
|
||||
// AcceptConnection, return the HSteamNetConnection
|
||||
// (it fits the NetTransport::Connection handle).
|
||||
// Close CloseConnection(reason 0, linger enabled).
|
||||
// Send SendMessageToConnection with
|
||||
// k_nSteamNetworkingSend_Reliable: the engine's
|
||||
// stream framing (NetworkPacketHeader + payload)
|
||||
// assumes ordered reliable delivery, exactly what
|
||||
// a reliable Steam message lane provides.
|
||||
// Receive ReceiveMessagesOnConnection; copy into the
|
||||
// host's pad buffer, normalize "no messages" to
|
||||
// ReceiveNoData and connection-closed callbacks
|
||||
// to ReceiveDisconnected.
|
||||
// GetRemoteAddress GetConnectionInfo -> m_addrRemote (the FakeIP),
|
||||
// so the mesh's identity checks keep working on
|
||||
// SOCKADDR_IN values.
|
||||
// Install SteamAPI_Init + InitRelayNetworkAccess, then
|
||||
// BeginAsyncRequestFakeIP(2): fake port 0 is the
|
||||
// console channel, fake port 1 the game mesh.
|
||||
// On success installs itself via NetTransport_Set;
|
||||
// on any failure the process stays on Winsock TCP.
|
||||
// GetLocalAddresses our FakeIP (the mesh identifies "which [pilots]
|
||||
// entry is me" against it).
|
||||
// Resolve SteamNetworkingIPAddr::ParseString - FakeIPs ARE
|
||||
// IPv4 strings, so egg text stays unchanged.
|
||||
// Connect ConnectByIPAddress to the peer's FakeIP + fake
|
||||
// game port (SDR routes it; NAT traversal and IP
|
||||
// privacy come free); blocks pumping callbacks
|
||||
// until Connected, retrying like the TCP
|
||||
// connect-while-refused loop.
|
||||
// Listen CreateListenSocketP2PFakeIP - first engine port
|
||||
// seen maps to fake port 0, second to fake port 1.
|
||||
// Accept connection-request callback AcceptConnections
|
||||
// queue up; Accept() pops fully-connected ones.
|
||||
// Send SendMessageToConnection, reliable lane (the
|
||||
// engine's stream framing assumes ordered
|
||||
// reliable delivery).
|
||||
// Receive ReceiveMessagesOnConnection into the caller's
|
||||
// buffer; normalized to ReceiveNoData /
|
||||
// ReceiveDisconnected like the Winsock transport.
|
||||
// GetRemoteAddress GetRemoteFakeIPForConnection, with the fake
|
||||
// port translated back to the ENGINE port so the
|
||||
// mesh's SOCKADDR_IN identity checks keep working.
|
||||
//
|
||||
// Lobby flow that drives this transport (front-end work, not here):
|
||||
// the lobby owner collects members' FakeIPs + loadouts via lobby data,
|
||||
// builds the canonical egg with the ordered [pilots] list of FakeIPs,
|
||||
// distributes it, and doubles as the console (RPL4CONSOLE marshals the
|
||||
// mission; StopMissionMessage goes out over the console channel).
|
||||
//
|
||||
// Install with NetTransport_Set(new SteamNetTransport) BEFORE the
|
||||
// network manager constructs (front of WinMain, when -steam / a Steam
|
||||
// launch is detected).
|
||||
// Addressing convention under Steam: every pod launches with the same
|
||||
// -net port (the lobby fixes it), so the egg's [pilots] entries are
|
||||
// "<fakeip>:<engine game port>" - the engine's self-match and peer
|
||||
// checks all work on engine ports, and only this transport knows the
|
||||
// Steam-assigned fake port values. The lobby layer exchanges each
|
||||
// member's FakeIP + fake game port and feeds RegisterPeer before the
|
||||
// egg is distributed.
|
||||
//########################################################################
|
||||
|
||||
#ifdef RP412_STEAM
|
||||
|
||||
#include "l4nettransport.h"
|
||||
// Bring Steam up and make this the process transport. False (with the
|
||||
// reason logged) leaves the Winsock transport in place - callers just
|
||||
// carry on over TCP.
|
||||
Logical
|
||||
SteamNetTransport_Install();
|
||||
|
||||
class SteamNetTransport:
|
||||
public NetTransport
|
||||
{
|
||||
public:
|
||||
SteamNetTransport();
|
||||
~SteamNetTransport();
|
||||
// Our FakeIP as a dotted string ("169.254.x.y"), for lobby member data
|
||||
// and the [pilots] list. Empty until Install succeeds.
|
||||
const char *
|
||||
SteamNetTransport_GetFakeAddressString();
|
||||
|
||||
Logical
|
||||
Startup();
|
||||
void
|
||||
Cleanup();
|
||||
// Our Steam-assigned fake game port (lobby member data).
|
||||
int
|
||||
SteamNetTransport_GetFakeGamePort();
|
||||
|
||||
int
|
||||
GetLocalAddresses(
|
||||
unsigned long *addresses,
|
||||
int max_count
|
||||
);
|
||||
Logical
|
||||
Resolve(
|
||||
const char *host_name,
|
||||
SOCKADDR_IN *address
|
||||
);
|
||||
|
||||
Connection
|
||||
Connect(
|
||||
const SOCKADDR_IN *remote,
|
||||
int local_port
|
||||
);
|
||||
Connection
|
||||
Listen(
|
||||
int local_port,
|
||||
int backlog
|
||||
);
|
||||
Connection
|
||||
Accept(Connection listener);
|
||||
void
|
||||
Close(Connection connection);
|
||||
|
||||
int
|
||||
Send(
|
||||
Connection connection,
|
||||
const void *data,
|
||||
int size
|
||||
);
|
||||
int
|
||||
Receive(
|
||||
Connection connection,
|
||||
void *buffer,
|
||||
int size
|
||||
);
|
||||
|
||||
Logical
|
||||
GetRemoteAddress(
|
||||
Connection connection,
|
||||
SOCKADDR_IN *address
|
||||
);
|
||||
};
|
||||
// The lobby feeds every member's FakeIP + fake game port here before
|
||||
// StartConnecting runs.
|
||||
void
|
||||
SteamNetTransport_RegisterPeer(
|
||||
const char *fake_ip,
|
||||
int fake_game_port
|
||||
);
|
||||
|
||||
#endif // RP412_STEAM
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<TargetName>Munga_l4</TargetName>
|
||||
<!-- Legacy DirectX SDK (June 2010) after the Windows SDK so modern headers/libs win;
|
||||
only d3dx9 and dxerr actually come from it. -->
|
||||
<IncludePath>$(IncludePath);$(DXSDK_DIR)Include</IncludePath>
|
||||
<IncludePath>$(IncludePath);$(DXSDK_DIR)Include;..\extern\steamworks_sdk_164\sdk\public</IncludePath>
|
||||
<LibraryPath>$(LibraryPath);$(DXSDK_DIR)Lib\x86</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
@@ -50,7 +50,9 @@
|
||||
build always compiled Windows headers at /Zp1, so this keeps those semantics.
|
||||
_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS: stdext::hash_map, pending
|
||||
an unordered_map port. -->
|
||||
<PreprocessorDefinitions>WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;WINDOWS_IGNORE_PACKING_MISMATCH;_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<!-- RP412_STEAM: SteamNetTransport compiles against the vendored Steamworks SDK;
|
||||
activation stays behind the RP412STEAM env switch at runtime. -->
|
||||
<PreprocessorDefinitions>WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;WINDOWS_IGNORE_PACKING_MISMATCH;_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS;RP412_STEAM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4996;4244;4267;4305;4018;4138;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
@@ -249,6 +251,7 @@
|
||||
<ClCompile Include=".\L4MPPR.cpp" />
|
||||
<ClCompile Include=".\L4NET.CPP" />
|
||||
<ClCompile Include=".\L4NETTRANSPORT.cpp" />
|
||||
<ClCompile Include=".\L4STEAMTRANSPORT.cpp" />
|
||||
<ClCompile Include=".\L4PARTICLES.cpp" />
|
||||
<ClCompile Include=".\L4MFDVIEW.cpp" />
|
||||
<ClCompile Include=".\L4PADRIO.cpp" />
|
||||
@@ -440,6 +443,7 @@
|
||||
<ClInclude Include=".\L4MPPR.h" />
|
||||
<ClInclude Include=".\L4NET.H" />
|
||||
<ClInclude Include=".\L4NETTRANSPORT.h" />
|
||||
<ClInclude Include=".\L4STEAMTRANSPORT.h" />
|
||||
<ClInclude Include=".\L4PARTICLES.h" />
|
||||
<ClInclude Include=".\L4MFDVIEW.h" />
|
||||
<ClInclude Include=".\L4PADRIO.h" />
|
||||
|
||||
Reference in New Issue
Block a user