Skip to content

Commit

Permalink
support for steam dedicated server
Browse files Browse the repository at this point in the history
  • Loading branch information
malytomas committed Aug 7, 2024
1 parent f80b844 commit 58b5d67
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 76 deletions.
7 changes: 7 additions & 0 deletions sources/include/cage-core/networkSteam.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ namespace cage
};

CAGE_CORE_API Holder<SteamServer> newSteamServer(const SteamServerCreateConfig &config);

// requires steam sdk
// permanently switches implementation to the dedicated server api
// must be used before any other functions in this api
// the dedicated server must already be initialized
// cannot mix client api with dedicated server api
CAGE_CORE_API void useSteamDedicatedServerApi();
}

#endif // guard_networkSteam_h_vrdfk4q
176 changes: 100 additions & 76 deletions sources/libcore/network/steam.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,8 @@ namespace cage

constexpr uint32 LanesCount = 4;

CAGE_FORCE_INLINE ISteamNetworkingSockets *sockets()
{
return SteamNetworkingSockets();
//SteamGameServerNetworkingSockets();
}

CAGE_FORCE_INLINE ISteamNetworkingUtils *utils()
{
return SteamNetworkingUtils();
}
ISteamNetworkingSockets *sockets = nullptr;
ISteamNetworkingUtils *utils = nullptr;

#ifdef CAGE_DEBUG
const char *connectionStateToString(ESteamNetworkingConnectionState s)
Expand Down Expand Up @@ -122,61 +114,84 @@ namespace cage
}
#endif

void initializeCommon()
void initialize(bool useRelay)
{
static int dummy = []()
{
#if defined(CAGE_USE_STEAM_SOCKETS)
struct InitializerSockets
{
InitializerSockets()
{
SteamNetworkingSockets_SetServiceThreadInitCallback(+[]() { currentThreadName("steam sockets"); });
SteamNetworkingErrMsg msg;
if (!GameNetworkingSockets_Init(nullptr, msg))
{
CAGE_LOG_THROW(msg);
CAGE_THROW_ERROR(Exception, "failed to initialize steam sockets networking library");
SteamNetworkingSockets_SetServiceThreadInitCallback(+[]() { currentThreadName("steam sockets"); });
SteamNetworkingErrMsg msg;
if (!GameNetworkingSockets_Init(nullptr, msg))
{
CAGE_LOG_THROW(msg);
CAGE_THROW_ERROR(Exception, "failed to initialize steam sockets networking library");
}
}
}
~InitializerSockets() { GameNetworkingSockets_Kill(); }
};
static InitializerSockets initializerSockets;
#endif

utils()->SetDebugOutputFunction((ESteamNetworkingSocketsDebugOutputType)(sint32)confDebugLogLevel, &debugOutputHandler);
if (!sockets)
{
sockets = SteamNetworkingSockets();
utils = SteamNetworkingUtils();
CAGE_ASSERT(sockets);
CAGE_ASSERT(utils);
}

struct InitializerDebugOutput
{
InitializerDebugOutput() { utils->SetDebugOutputFunction((ESteamNetworkingSocketsDebugOutputType)(sint32)confDebugLogLevel, &debugOutputHandler); }
};
static InitializerDebugOutput initializerDebugOutput;

#if defined(CAGE_USE_STEAM_SDK)
struct InitializerSdk
{
InitializerSdk()
{
ESteamNetworkingAvailability a = sockets()->InitAuthentication();
while (!networkingAvailable(a))
{
threadSleep(5000);
SteamAPI_RunCallbacks();
a = sockets()->GetAuthenticationStatus(nullptr);
ESteamNetworkingAvailability a = sockets->InitAuthentication();
while (!networkingAvailable(a))
{
threadSleep(5000);
SteamAPI_RunCallbacks();
a = sockets->GetAuthenticationStatus(nullptr);
}
}
}
};
static InitializerSdk initializerSdk;
#endif

return 0;
}();
(void)dummy;
}

void initializeRelay()
{
#if defined(CAGE_USE_STEAM_SDK)
static int dummy = []()
if (useRelay)
{
utils()->InitRelayNetworkAccess();
while (true)
#if defined(CAGE_USE_STEAM_SDK)
struct InitializerRelay
{
ESteamNetworkingAvailability a = utils()->GetRelayNetworkStatus(nullptr);
if (networkingAvailable(a))
break;
threadSleep(5000);
SteamAPI_RunCallbacks();
}
return 0;
}();
(void)dummy;
InitializerRelay()
{
utils->InitRelayNetworkAccess();
while (true)
{
ESteamNetworkingAvailability a = utils->GetRelayNetworkStatus(nullptr);
if (networkingAvailable(a))
break;
threadSleep(5000);
SteamAPI_RunCallbacks();
}
}
};
static InitializerRelay initializerRelay;
#else
CAGE_THROW_CRITICAL(Exception, "steam relay network requires steam sdk");
CAGE_THROW_CRITICAL(Exception, "steam relay network requires steam sdk");
#endif
}
}

void statusChangedCallback(SteamNetConnectionStatusChangedCallback_t *info);
Expand Down Expand Up @@ -223,7 +238,6 @@ namespace cage

~Data()
{
CAGE_ASSERT(sockets());
if (msg)
msg->Release();
}
Expand All @@ -243,8 +257,8 @@ namespace cage
cfg[0].SetInt64(k_ESteamNetworkingConfig_ConnectionUserData, (sint64)this);
cfg[1].SetPtr(k_ESteamNetworkingConfig_Callback_ConnectionStatusChanged, (void *)&statusChangedCallback);
SteamNetworkingIPAddr sa = parseAddress(address, port);
sock = sockets()->ConnectByIPAddress(sa, array_size(cfg), cfg);
handleResult(sockets()->ConfigureConnectionLanes(sock, LanesCount, nullptr, nullptr));
sock = sockets->ConnectByIPAddress(sa, array_size(cfg), cfg);
handleResult(sockets->ConfigureConnectionLanes(sock, LanesCount, nullptr, nullptr));
}

SteamConnectionImpl(uint64 steamId)
Expand All @@ -255,28 +269,28 @@ namespace cage
cfg[1].SetPtr(k_ESteamNetworkingConfig_Callback_ConnectionStatusChanged, (void *)&statusChangedCallback);
SteamNetworkingIdentity id;
id.SetSteamID64(steamId);
sock = sockets()->ConnectP2P(id, 0, array_size(cfg), cfg);
handleResult(sockets()->ConfigureConnectionLanes(sock, LanesCount, nullptr, nullptr));
sock = sockets->ConnectP2P(id, 0, array_size(cfg), cfg);
handleResult(sockets->ConfigureConnectionLanes(sock, LanesCount, nullptr, nullptr));
}

SteamConnectionImpl(HSteamNetConnection s) : sock(s)
{
CAGE_ASSERT(((uint64)this % 2) == 0);
sockets()->SetConnectionUserData(sock, (sint64)this);
handleResult(sockets()->AcceptConnection(sock));
handleResult(sockets()->ConfigureConnectionLanes(sock, LanesCount, nullptr, nullptr));
sockets->SetConnectionUserData(sock, (sint64)this);
handleResult(sockets->AcceptConnection(sock));
handleResult(sockets->ConfigureConnectionLanes(sock, LanesCount, nullptr, nullptr));
}

~SteamConnectionImpl()
{
CAGE_ASSERT(sockets());
sockets()->CloseConnection(sock, 0, nullptr, false);
CAGE_ASSERT(sockets);
sockets->CloseConnection(sock, 0, nullptr, false);
}

Holder<PointerRange<char>> read(uint32 &channel, bool &reliable)
{
SteamNetworkingMessage_t *msg = nullptr;
if (sockets()->ReceiveMessagesOnConnection(sock, &msg, 1) != 1)
if (sockets->ReceiveMessagesOnConnection(sock, &msg, 1) != 1)
return {};
channel = msg->m_idxLane;
reliable = msg->m_nFlags & k_nSteamNetworkingSend_Reliable;
Expand All @@ -287,14 +301,14 @@ namespace cage
{
CAGE_ASSERT(!buffer.empty());
CAGE_ASSERT(channel < LanesCount);
SteamNetworkingMessage_t *msg = utils()->AllocateMessage(buffer.size());
SteamNetworkingMessage_t *msg = utils->AllocateMessage(buffer.size());
if (!msg)
CAGE_THROW_ERROR(Exception, "failed to allocate new message for steam sockets");
detail::memcpy(msg->m_pData, buffer.data(), buffer.size());
msg->m_conn = sock;
msg->m_idxLane = channel;
msg->m_nFlags = reliable ? k_nSteamNetworkingSend_Reliable : 0;
sockets()->SendMessages(1, &msg, nullptr);
sockets->SendMessages(1, &msg, nullptr);
}

sint64 capacity() const
Expand All @@ -305,7 +319,7 @@ namespace cage

void update()
{
sockets()->RunCallbacks();
sockets->RunCallbacks();
if (disconnected)
{
detail::OverrideBreakpoint ob;
Expand All @@ -316,7 +330,7 @@ namespace cage
SteamStatistics statistics() const
{
SteamNetConnectionRealTimeStatus_t r = {};
handleResult(sockets()->GetConnectionRealTimeStatus(sock, &r, 0, nullptr));
handleResult(sockets->GetConnectionRealTimeStatus(sock, &r, 0, nullptr));
SteamStatistics s;
s.sendingBytesPerSecond = r.m_flOutBytesPerSec;
s.receivingBytesPerSecond = r.m_flInBytesPerSec;
Expand Down Expand Up @@ -344,28 +358,28 @@ namespace cage
CAGE_ASSERT(config.port > 1024);
SteamNetworkingIPAddr sa = {};
sa.m_port = config.port;
socks[0] = sockets()->CreateListenSocketIP(sa, array_size(cfg), cfg);
socks[0] = sockets->CreateListenSocketIP(sa, array_size(cfg), cfg);
}
if (config.listenSteamRelay)
socks[1] = sockets()->CreateListenSocketP2P(0, array_size(cfg), cfg);
socks[1] = sockets->CreateListenSocketP2P(0, array_size(cfg), cfg);
}

~SteamServerImpl()
{
CAGE_ASSERT(sockets());
CAGE_ASSERT(sockets);
waiting.terminate();
{
Holder<SteamConnection> c;
while (waiting.tryPop(c, true))
c.clear();
}
for (auto &it : socks)
sockets()->CloseListenSocket(it);
sockets->CloseListenSocket(it);
}

Holder<SteamConnection> accept()
{
sockets()->RunCallbacks();
sockets->RunCallbacks();
Holder<SteamConnection> c;
waiting.tryPop(c);
return c;
Expand All @@ -374,7 +388,7 @@ namespace cage

SteamConnectionImpl *getConnection(HSteamNetConnection sock)
{
sint64 v = sockets()->GetConnectionUserData(sock);
sint64 v = sockets->GetConnectionUserData(sock);
CAGE_ASSERT(v >= 0);
if ((v % 2) == 0)
return (SteamConnectionImpl *)v;
Expand All @@ -383,7 +397,7 @@ namespace cage

SteamServerImpl *getServer(HSteamNetConnection sock)
{
sint64 v = sockets()->GetConnectionUserData(sock);
sint64 v = sockets->GetConnectionUserData(sock);
CAGE_ASSERT(v >= 0);
if ((v % 2) == 1)
return (SteamServerImpl *)(v - 1);
Expand All @@ -410,7 +424,7 @@ namespace cage
else
{
CAGE_LOG(SeverityEnum::Warning, "steamsocks", "ignoring prematurely attempted connection");
sockets()->CloseConnection(info->m_hConn, 0, nullptr, false);
sockets->CloseConnection(info->m_hConn, 0, nullptr, false);
}
}
switch (info->m_info.m_eState)
Expand Down Expand Up @@ -482,7 +496,7 @@ namespace cage
{
const SteamConnectionImpl *impl = (const SteamConnectionImpl *)this;
SteamNetConnectionInfo_t info = {};
if (!sockets()->GetConnectionInfo(impl->sock, &info))
if (!sockets->GetConnectionInfo(impl->sock, &info))
CAGE_THROW_ERROR(Exception, "failed to read remote connection info");
SteamRemoteInfo res;
info.m_addrRemote.ToString(res.address.rawData(), res.address.MaxLength, false);
Expand All @@ -496,7 +510,7 @@ namespace cage
{
const SteamServerImpl *impl = (const SteamServerImpl *)this;
SteamNetworkingIPAddr addr = {};
if (!sockets()->GetListenSocketAddress(impl->socks[0], &addr))
if (!sockets->GetListenSocketAddress(impl->socks[0], &addr))
CAGE_THROW_ERROR(Exception, "failed to read local server info");
return addr.m_port;
}
Expand All @@ -509,24 +523,34 @@ namespace cage

Holder<SteamConnection> newSteamConnection(const String &address, uint16 port)
{
initializeCommon();
initialize(false);
return systemMemory().createImpl<SteamConnection, SteamConnectionImpl>(address, port);
}

Holder<SteamConnection> newSteamConnection(uint64 steamId)
{
initializeCommon();
initializeRelay();
initialize(true);
return systemMemory().createImpl<SteamConnection, SteamConnectionImpl>(steamId);
}

Holder<SteamServer> newSteamServer(const SteamServerCreateConfig &config)
{
initializeCommon();
if (config.listenSteamRelay)
initializeRelay();
initialize(config.listenSteamRelay);
return systemMemory().createImpl<SteamServer, SteamServerImpl>(config);
}

#ifdef CAGE_USE_STEAM_SDK
void useSteamDedicatedServerApi()
{
CAGE_ASSERT(!sockets);
CAGE_ASSERT(!utils);
sockets = SteamGameServerNetworkingSockets();
utils = SteamNetworkingUtils();
CAGE_ASSERT(sockets);
CAGE_ASSERT(utils);
initialize(true);
}
#endif // CAGE_USE_STEAM_SDK
}

#endif

0 comments on commit 58b5d67

Please sign in to comment.