mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 03:24:56 +08:00
200 lines
5.7 KiB
C
200 lines
5.7 KiB
C
/*
|
|
cl_steam.c - steam(tm) broker implementation
|
|
Copyright (C) 2026 Xash3D FWGS contributors
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <inttypes.h>
|
|
#include "common.h"
|
|
#include "client.h"
|
|
|
|
// What is a broker?
|
|
// From Wikipedia, the free encyclopedia:
|
|
// "The broker pattern is an architecture pattern that involves the use of an
|
|
// intermediary software entity, called a "broker", to facilitate communication
|
|
// between two or more software components. The broker acts as a "middleman"
|
|
// between the components, allowing them to communicate without being aware of
|
|
// each other's existence.
|
|
//
|
|
// Due to proprietary nature of Steamworks SDK, it cannot be run on same amount
|
|
// of platforms supported by Xash3D FWGS, neither we can link directly due to
|
|
// GNU GPLv3 license. However, here comes the broker, by running it (in trusted
|
|
// network, preferrably) on a machine that has Steam client installed, the
|
|
// engine can communicate with it, acquiring needed information to log-in into
|
|
// Steam protected multiplayer servers.
|
|
static CVAR_DEFINE_AUTO( cl_steam_broker_addr, "127.0.0.1:27420", FCVAR_ARCHIVE, "address of steam broker instance" );
|
|
|
|
static struct
|
|
{
|
|
netadr_t adr;
|
|
int challenge;
|
|
netadr_t serveradr;
|
|
qboolean announced;
|
|
qboolean addr_initialized;
|
|
} broker;
|
|
|
|
static qboolean SteamBroker_UpdateBrokerAddress( void )
|
|
{
|
|
if( NET_NetadrType( &broker.adr ) == NA_UNDEFINED )
|
|
{
|
|
if( !NET_StringToAdr( cl_steam_broker_addr.string, &broker.adr ))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
qboolean SteamBroker_InitiateGameConnection( netadr_t serveradr, int challenge )
|
|
{
|
|
// only ipv4 supported
|
|
if( NET_NetadrType( &serveradr ) != NA_IP )
|
|
return false;
|
|
|
|
broker.challenge = challenge;
|
|
broker.serveradr = serveradr;
|
|
|
|
// sb_connect <ip:port> <server's steam id> <secure> <challenge>
|
|
// the message calls
|
|
char buf[512];
|
|
int len = Q_snprintf( buf, sizeof( buf ), "sb_connect %s %"PRIu64" %s %d", NET_AdrToString( serveradr ), cls.server_steamid, cls.vac2_secure ? "true" : "false", broker.challenge );
|
|
|
|
NET_SendPacket( NS_CLIENT, len, buf, broker.adr );
|
|
|
|
return true;
|
|
}
|
|
|
|
void SteamBroker_TerminateGameConnection( void )
|
|
{
|
|
if( NET_NetadrType( &cls.serveradr ) != NA_IP )
|
|
return;
|
|
|
|
if( Q_stricmp( cl_ticket_generator.string, "steam" ) != 0 )
|
|
return;
|
|
|
|
// sb_disconnect <ip:port> <challenge>
|
|
char buf[512];
|
|
int len = Q_snprintf( buf, sizeof( buf ), "sb_disconnect %s %d", NET_AdrToString( cls.serveradr ), broker.challenge );
|
|
|
|
NET_SendPacket( NS_CLIENT, len, buf, broker.adr );
|
|
}
|
|
|
|
void SteamBroker_AnnounceGameStart( const char *gamedir )
|
|
{
|
|
if( Q_stricmp( cl_ticket_generator.string, "steam" ) != 0 )
|
|
return;
|
|
|
|
NET_Config( true, true ); // initialize sockets to be able to send packets to broker
|
|
|
|
char buf[512];
|
|
int len = Q_snprintf( buf, sizeof( buf ), "sb_gamedir %s", gamedir );
|
|
|
|
NET_SendPacket( NS_CLIENT, len, buf, broker.adr );
|
|
}
|
|
|
|
void SteamBroker_AnnounceGameShutdown( netadr_t broker_addr )
|
|
{
|
|
NET_SendPacket( NS_CLIENT, sizeof( "sb_terminate" ) - 1, "sb_terminate", broker_addr );
|
|
}
|
|
|
|
void SteamBroker_Frame( void )
|
|
{
|
|
if( Q_stricmp( cl_ticket_generator.string, "steam" ) != 0 )
|
|
return;
|
|
|
|
qboolean restart = FBitSet( cl_steam_broker_addr.flags|cl_ticket_generator.flags, FCVAR_CHANGED );
|
|
|
|
if( !broker.addr_initialized )
|
|
{
|
|
if( !SteamBroker_UpdateBrokerAddress( ))
|
|
{
|
|
Con_Printf( "%s: failed to resolve broker address \"%s\"\n", __func__, cl_steam_broker_addr.string );
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
broker.addr_initialized = true;
|
|
}
|
|
}
|
|
|
|
if( restart )
|
|
{
|
|
broker.announced = false;
|
|
ClearBits( cl_ticket_generator.flags, FCVAR_CHANGED );
|
|
ClearBits( cl_steam_broker_addr.flags, FCVAR_CHANGED );
|
|
}
|
|
|
|
if( !broker.announced )
|
|
{
|
|
netadr_t previous_addr = broker.adr;
|
|
if( !SteamBroker_UpdateBrokerAddress( ))
|
|
{
|
|
Con_Printf( "%s: failed to resolve broker address \"%s\"\n", __func__, cl_steam_broker_addr.string );
|
|
}
|
|
else
|
|
{
|
|
if( restart )
|
|
{
|
|
// terminate old steam broker if instance address has changed
|
|
SteamBroker_AnnounceGameShutdown( previous_addr );
|
|
}
|
|
SteamBroker_AnnounceGameStart( GI->gamefolder );
|
|
}
|
|
broker.announced = true;
|
|
}
|
|
}
|
|
|
|
void SteamBroker_HandlePacket( netadr_t from, sizebuf_t *msg )
|
|
{
|
|
// message format
|
|
// sb_connect\n<4 byte challenge><8 byte steamid><unsigned 4 byte len><len bytes ticket>
|
|
int challenge;
|
|
uint32_t len;
|
|
uint8_t ticket[2048]; // 2048 bytes according to SDK docs
|
|
|
|
if( !NET_CompareAdr( from, broker.adr ))
|
|
return;
|
|
|
|
challenge = MSG_ReadLong( msg );
|
|
|
|
if( broker.challenge != challenge ) // TODO: print error
|
|
return;
|
|
|
|
MSG_ReadBytes( msg, cls.steamid, sizeof( cls.steamid ));
|
|
|
|
len = MSG_ReadDword( msg );
|
|
if( len > sizeof( ticket )) // TODO: print error, proceed without ticket?
|
|
return;
|
|
|
|
MSG_ReadBytes( msg, ticket, len );
|
|
|
|
Con_Printf( "%s: SteamID: %"PRIu64", ticket: [%d, %d, %d, %d...]\n", __func__, *(uint64_t *)cls.steamid, ticket[0], ticket[1], ticket[2], ticket[3] );
|
|
|
|
CL_SendGoldSrcConnectPacket( broker.serveradr, challenge, ticket, len );
|
|
|
|
cls.broker_wait = false;
|
|
}
|
|
|
|
void SteamBroker_Init( void )
|
|
{
|
|
broker.announced = false;
|
|
broker.addr_initialized = false;
|
|
Cvar_RegisterVariable( &cl_steam_broker_addr );
|
|
NET_NetadrSetType( &broker.adr, NA_UNDEFINED );
|
|
}
|
|
|
|
void SteamBroker_Shutdown( void )
|
|
{
|
|
if( Q_stricmp( cl_ticket_generator.string, "steam" ) != 0 )
|
|
return;
|
|
|
|
SteamBroker_AnnounceGameShutdown( broker.adr );
|
|
}
|