2023-02-05 13:27:22 +01:00
|
|
|
/*
|
|
|
|
===========================================================================
|
2025-03-01 23:34:24 +01:00
|
|
|
Copyright (C) 2025 the OpenMoHAA team
|
2023-02-05 13:27:22 +01:00
|
|
|
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
|
|
|
|
OpenMoHAA source code 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 2 of the License,
|
|
|
|
or (at your option) any later version.
|
|
|
|
|
|
|
|
OpenMoHAA source code 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.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with OpenMoHAA source code; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
|
2023-06-17 01:24:20 +02:00
|
|
|
#include "../qcommon/q_shared.h"
|
|
|
|
#include "../server/server.h"
|
2025-04-20 21:41:38 +02:00
|
|
|
#include "../qcommon/q_version.h"
|
2023-02-05 01:40:14 +01:00
|
|
|
#include "sv_gqueryreporting.h"
|
2023-07-28 21:45:07 +02:00
|
|
|
#include "sv_gamespy.h"
|
2025-04-22 21:40:34 +02:00
|
|
|
#include "q_gamespy.h"
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-06-17 01:24:20 +02:00
|
|
|
#include "gcdkey/gcdkeys.h"
|
2023-08-24 23:19:21 +02:00
|
|
|
#include "common/gsCommon.h"
|
|
|
|
#include "common/gsAvailable.h"
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
static char gamemode[128];
|
|
|
|
static qboolean gcdInitialized = qfalse;
|
|
|
|
static qboolean gcdValid = qfalse;
|
2023-08-26 16:18:57 +02:00
|
|
|
extern GSIACResult __GSIACResult;
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-08-23 23:50:40 +02:00
|
|
|
static const char *SECRET_GS_KEYS[] =
|
|
|
|
{
|
|
|
|
"M5Fdwc",
|
|
|
|
"h2P1c9",
|
|
|
|
"y32FDc"
|
|
|
|
};
|
2023-07-02 20:07:15 +02:00
|
|
|
|
2023-08-23 23:50:40 +02:00
|
|
|
static const unsigned int GCD_GAME_IDS[] =
|
|
|
|
{
|
2023-07-02 20:07:15 +02:00
|
|
|
0,
|
2023-07-05 21:24:14 +02:00
|
|
|
641,
|
|
|
|
802,
|
2023-07-02 20:07:15 +02:00
|
|
|
};
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-08-23 23:50:40 +02:00
|
|
|
static const char *GS_GAME_NAME[] =
|
|
|
|
{
|
|
|
|
"mohaa",
|
|
|
|
"mohaas",
|
|
|
|
"mohaab"
|
|
|
|
};
|
|
|
|
|
2024-11-12 23:09:37 +01:00
|
|
|
static const char *GS_GAME_NAME_DEMO[] =
|
|
|
|
{
|
|
|
|
"mohaa",
|
|
|
|
"mohaas",
|
|
|
|
"mohaabd"
|
|
|
|
};
|
|
|
|
|
2023-08-23 23:50:40 +02:00
|
|
|
static const char *GS_GAME_VERSION[] =
|
|
|
|
{
|
2025-04-20 21:41:38 +02:00
|
|
|
TARGET_GAME_VERSION_MOH "+" PRODUCT_VERSION,
|
|
|
|
TARGET_GAME_VERSION_MOHTA "+" PRODUCT_VERSION,
|
|
|
|
TARGET_GAME_VERSION_MOHTT "+" PRODUCT_VERSION,
|
2023-08-23 23:50:40 +02:00
|
|
|
};
|
2023-07-02 20:10:38 +02:00
|
|
|
|
2024-11-13 00:06:45 +01:00
|
|
|
static const char* GS_GAME_VERSION_DEMO[] =
|
|
|
|
{
|
2025-04-20 21:41:38 +02:00
|
|
|
TARGET_GAME_VERSION_MOH "+" PRODUCT_VERSION,
|
|
|
|
"d" TARGET_GAME_VERSION_MOHTA "+" PRODUCT_VERSION,
|
|
|
|
"d" TARGET_GAME_VERSION_MOHTT_DEMO "+" PRODUCT_VERSION,
|
2024-11-13 00:06:45 +01:00
|
|
|
};
|
|
|
|
|
2023-02-05 01:55:58 +01:00
|
|
|
static const unsigned int GAMESPY_DEFAULT_PORT = 12300;
|
|
|
|
|
2023-02-05 01:40:14 +01:00
|
|
|
int qr_init(
|
2023-07-05 21:24:14 +02:00
|
|
|
qr_t *qrec,
|
2025-03-01 23:34:24 +01:00
|
|
|
const char *ip,
|
2023-07-05 21:24:14 +02:00
|
|
|
int baseport,
|
2025-03-01 23:34:24 +01:00
|
|
|
const char *gamename,
|
|
|
|
const char *secret_key,
|
2023-07-05 21:24:14 +02:00
|
|
|
qr_querycallback_t qr_basic_callback,
|
|
|
|
qr_querycallback_t qr_info_callback,
|
|
|
|
qr_querycallback_t qr_rules_callback,
|
|
|
|
qr_querycallback_t qr_players_callback,
|
|
|
|
void *userdata
|
2023-02-05 01:40:14 +01:00
|
|
|
);
|
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
const char *GS_GetGameKey(unsigned int index)
|
|
|
|
{
|
2023-07-28 21:45:07 +02:00
|
|
|
return SECRET_GS_KEYS[index];
|
|
|
|
}
|
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
const char *GS_GetCurrentGameKey()
|
|
|
|
{
|
2023-07-28 21:45:07 +02:00
|
|
|
return GS_GetGameKey(com_target_game->integer);
|
|
|
|
}
|
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
unsigned int GS_GetGameID(unsigned int index)
|
|
|
|
{
|
2023-07-28 21:45:07 +02:00
|
|
|
return GCD_GAME_IDS[index];
|
|
|
|
}
|
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
unsigned int GS_GetCurrentGameID()
|
|
|
|
{
|
2023-07-28 21:45:07 +02:00
|
|
|
return GS_GetGameID(com_target_game->integer);
|
|
|
|
}
|
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
const char *GS_GetGameName(unsigned int index)
|
|
|
|
{
|
2024-11-12 23:09:37 +01:00
|
|
|
if (!com_target_demo->integer) {
|
|
|
|
return GS_GAME_NAME[index];
|
|
|
|
} else {
|
|
|
|
return GS_GAME_NAME_DEMO[index];
|
|
|
|
}
|
2023-07-28 21:45:07 +02:00
|
|
|
}
|
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
const char *GS_GetCurrentGameName()
|
|
|
|
{
|
2023-07-28 21:45:07 +02:00
|
|
|
return GS_GetGameName(com_target_game->integer);
|
|
|
|
}
|
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
const char *GS_GetGameVersion(unsigned int index)
|
|
|
|
{
|
2024-11-13 00:06:45 +01:00
|
|
|
if (!com_target_demo->integer) {
|
|
|
|
return GS_GAME_VERSION[index];
|
|
|
|
} else {
|
|
|
|
return GS_GAME_VERSION_DEMO[index];
|
|
|
|
}
|
2023-08-23 23:50:40 +02:00
|
|
|
}
|
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
const char *GS_GetCurrentGameVersion()
|
|
|
|
{
|
2024-11-13 00:06:45 +01:00
|
|
|
return GS_GetGameVersion(com_target_game->integer);
|
2023-08-23 23:50:40 +02:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
static const char *ConvertMapFilename(const char *mapname)
|
2023-02-05 01:40:14 +01:00
|
|
|
{
|
2023-07-05 21:24:14 +02:00
|
|
|
static char converted[1024];
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
const char *name = strstr(mapname, "/");
|
|
|
|
if (!name) {
|
|
|
|
return mapname;
|
|
|
|
}
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
strcpy(converted, name + 1);
|
|
|
|
return converted;
|
2023-02-05 01:40:14 +01:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
static void basic_callback(char *outbuf, int maxlen, void *userdata)
|
2023-02-05 01:40:14 +01:00
|
|
|
{
|
2023-08-23 23:50:40 +02:00
|
|
|
Info_SetValueForKey(outbuf, "gamename", GS_GetCurrentGameName());
|
|
|
|
Info_SetValueForKey(outbuf, "gamever", GS_GetCurrentGameVersion());
|
2023-07-05 21:24:14 +02:00
|
|
|
Info_SetValueForKey(outbuf, "location", va("%i", sv_location->integer));
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
if (sv_debug_gamespy->integer) {
|
|
|
|
Com_DPrintf("Basic callback, sent: %s\n\n", outbuf);
|
|
|
|
}
|
2023-02-05 01:40:14 +01:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
static void info_callback(char *outbuf, int maxlen, void *userdata)
|
2023-02-05 01:40:14 +01:00
|
|
|
{
|
2023-07-05 21:24:14 +02:00
|
|
|
char infostring[1024];
|
|
|
|
qboolean allowlean = qfalse;
|
|
|
|
|
|
|
|
infostring[0] = 0;
|
|
|
|
Info_SetValueForKey(infostring, "hostname", sv_hostname->string);
|
2023-08-23 23:50:40 +02:00
|
|
|
Info_SetValueForKey(infostring, "hostport", Cvar_Get("net_port", "12203", CVAR_LATCH)->string);
|
2023-07-05 21:24:14 +02:00
|
|
|
Info_SetValueForKey(infostring, "mapname", ConvertMapFilename(svs.mapName));
|
|
|
|
Info_SetValueForKey(infostring, "gametype", g_gametypestring->string);
|
|
|
|
Info_SetValueForKey(infostring, "numplayers", va("%i", SV_NumClients()));
|
|
|
|
Info_SetValueForKey(infostring, "maxplayers", va("%i", svs.iNumClients - sv_privateClients->integer));
|
|
|
|
Info_SetValueForKey(infostring, "gamemode", gamemode);
|
|
|
|
Info_SetValueForKey(infostring, "gametype_i", va("%i", g_gametype->integer));
|
|
|
|
Info_SetValueForKey(infostring, "dedicated", Cvar_Get("ui_dedicated", "0", 0)->string);
|
|
|
|
Info_SetValueForKey(infostring, "sprinton", Cvar_Get("sv_sprinton", "1", 0)->string);
|
|
|
|
Info_SetValueForKey(infostring, "realism", Cvar_Get("g_realismmode", "0", 0)->string);
|
|
|
|
Info_SetValueForKey(infostring, "pure", va("%i", sv_pure->integer));
|
2023-08-23 23:50:40 +02:00
|
|
|
if ((Cvar_VariableIntegerValue("dmflags") & DF_ALLOW_LEAN_MOVEMENT) != 0) {
|
2023-07-05 21:24:14 +02:00
|
|
|
allowlean = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
Info_SetValueForKey(infostring, "allowlean", va("%i", allowlean));
|
|
|
|
if (strlen(infostring) < maxlen) {
|
|
|
|
strcpy(outbuf, infostring);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sv_debug_gamespy->integer) {
|
|
|
|
Com_DPrintf("Info callback, sent: %s\n\n", outbuf);
|
|
|
|
}
|
2023-02-05 01:40:14 +01:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
static void rules_callback(char *outbuf, int maxlen, void *userdata)
|
2023-02-05 01:40:14 +01:00
|
|
|
{
|
2023-07-05 21:24:14 +02:00
|
|
|
char infostring[1024];
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
infostring[0] = 0;
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
Info_SetValueForKey(infostring, "timelimit", Cvar_VariableString("timelimit"));
|
|
|
|
Info_SetValueForKey(infostring, "fraglimit", Cvar_VariableString("fraglimit"));
|
|
|
|
Info_SetValueForKey(infostring, "rankedserver", Cvar_VariableString("g_rankedserver"));
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
if (strlen(infostring) < maxlen) {
|
|
|
|
strcpy(outbuf, infostring);
|
|
|
|
}
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
if (sv_debug_gamespy->integer) {
|
|
|
|
Com_DPrintf("Rules callback, sent: %s\n\n", outbuf);
|
|
|
|
}
|
2023-02-05 01:40:14 +01:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
static void players_callback(char *outbuf, int maxlen, void *userdata)
|
2023-02-05 01:40:14 +01:00
|
|
|
{
|
2025-03-02 13:14:43 +01:00
|
|
|
client_t *cl;
|
|
|
|
playerState_t *ps;
|
|
|
|
size_t infolen;
|
|
|
|
size_t currlen = 0;
|
|
|
|
int i;
|
|
|
|
int index;
|
|
|
|
char infostring[128];
|
2025-04-22 00:59:22 +02:00
|
|
|
|
|
|
|
outbuf[0] = 0;
|
2023-07-05 21:24:14 +02:00
|
|
|
|
2023-08-26 18:16:42 +02:00
|
|
|
if (!svs.clients) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2025-03-02 13:14:43 +01:00
|
|
|
for (i = 0, index = 0; i < svs.iNumClients; i++) {
|
|
|
|
cl = &svs.clients[i];
|
|
|
|
|
|
|
|
if (cl->state == CS_FREE) {
|
2023-08-26 18:16:42 +02:00
|
|
|
// ignore inactive clients
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2025-03-02 13:14:43 +01:00
|
|
|
ps = SV_GameClientNum(i);
|
2023-07-05 21:24:14 +02:00
|
|
|
|
2025-03-02 13:26:47 +01:00
|
|
|
infolen = Com_sprintf(
|
2023-07-05 21:24:14 +02:00
|
|
|
infostring,
|
2025-03-02 13:14:43 +01:00
|
|
|
sizeof(infostring),
|
2023-07-05 21:24:14 +02:00
|
|
|
"\\player_%d\\%s\\frags_%d\\%d\\deaths_%d\\%d\\ping_%d\\%d",
|
2025-03-02 13:14:43 +01:00
|
|
|
index,
|
|
|
|
cl->name,
|
|
|
|
index,
|
2023-07-05 21:24:14 +02:00
|
|
|
ps->stats[STAT_KILLS],
|
2025-03-02 13:14:43 +01:00
|
|
|
index,
|
2023-07-05 21:24:14 +02:00
|
|
|
ps->stats[STAT_DEATHS],
|
2025-03-02 13:14:43 +01:00
|
|
|
index,
|
|
|
|
cl->ping
|
2023-07-05 21:24:14 +02:00
|
|
|
);
|
2025-03-02 13:26:47 +01:00
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
if (currlen + infolen < maxlen) {
|
|
|
|
strcat(outbuf, infostring);
|
|
|
|
currlen += infolen;
|
|
|
|
}
|
2025-03-02 13:14:43 +01:00
|
|
|
|
|
|
|
//
|
|
|
|
// Fixed in OPM
|
|
|
|
// Some programs enumerate by testing indexes, and stop iterating if the index doesn't exist
|
|
|
|
//
|
|
|
|
index++;
|
2023-07-05 21:24:14 +02:00
|
|
|
}
|
2023-02-05 01:40:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void SV_GamespyHeartbeat()
|
|
|
|
{
|
2023-08-23 23:50:40 +02:00
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER || !sv_gamespy->integer) {
|
2023-07-05 21:24:14 +02:00
|
|
|
return;
|
|
|
|
}
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
if (sv_debug_gamespy->integer) {
|
|
|
|
Com_DPrintf("GameSpy Heartbeat\n");
|
|
|
|
}
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
qr_send_statechanged(NULL);
|
2023-02-05 01:40:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void SV_ProcessGamespyQueries()
|
|
|
|
{
|
2023-08-23 23:50:40 +02:00
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER || !sv_gamespy->integer) {
|
2023-07-05 21:24:14 +02:00
|
|
|
return;
|
|
|
|
}
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
qr_process_queries(NULL);
|
2023-08-26 18:16:42 +02:00
|
|
|
|
|
|
|
if (gcdValid) {
|
|
|
|
// in case the game supports gcd
|
|
|
|
gcd_think();
|
|
|
|
}
|
2023-02-05 01:40:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void SV_ShutdownGamespy()
|
|
|
|
{
|
2023-08-23 23:50:40 +02:00
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER || !sv_gamespy->integer) {
|
2023-07-05 21:24:14 +02:00
|
|
|
return;
|
|
|
|
}
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
strcpy(gamemode, "exiting");
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
if (gcdInitialized) {
|
|
|
|
gcd_shutdown();
|
2023-08-24 23:19:21 +02:00
|
|
|
gcdInitialized = qfalse;
|
2023-07-05 21:24:14 +02:00
|
|
|
}
|
2023-02-05 01:40:14 +01:00
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
qr_send_statechanged(NULL);
|
|
|
|
qr_shutdown(NULL);
|
2023-02-05 01:40:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
qboolean SV_InitGamespy()
|
|
|
|
{
|
2023-07-05 21:24:14 +02:00
|
|
|
cvar_t *net_ip;
|
|
|
|
cvar_t *net_gamespy_port;
|
|
|
|
char secret_key[9];
|
|
|
|
const char *secret_gs_key;
|
|
|
|
const char *gs_game_name;
|
|
|
|
int gcd_game_id;
|
2023-07-02 20:07:15 +02:00
|
|
|
|
|
|
|
if (com_target_game->integer > ARRAY_LEN(SECRET_GS_KEYS)) {
|
2023-07-05 21:24:14 +02:00
|
|
|
Com_Error(ERR_DROP, "Invalid target game %d for GameSpy", com_target_game->integer);
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
2023-08-23 23:50:40 +02:00
|
|
|
secret_gs_key = GS_GetCurrentGameKey();
|
|
|
|
gcd_game_id = GS_GetCurrentGameID();
|
|
|
|
gs_game_name = GS_GetCurrentGameName();
|
2023-07-05 21:24:14 +02:00
|
|
|
|
|
|
|
sv_debug_gamespy = Cvar_Get("sv_debuggamespy", "0", 0);
|
|
|
|
sv_location = Cvar_Get("sv_location", "1", CVAR_ARCHIVE);
|
|
|
|
sv_gamespy = Cvar_Get("sv_gamespy", "1", CVAR_LATCH);
|
|
|
|
|
2023-08-23 23:50:40 +02:00
|
|
|
if (!sv_gamespy->integer || g_gametype->integer == GT_SINGLE_PLAYER) {
|
2023-07-05 21:24:14 +02:00
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(gamemode, "openplaying");
|
|
|
|
strcpy(secret_key, secret_gs_key);
|
|
|
|
|
|
|
|
net_ip = Cvar_Get("net_ip", "localhost", CVAR_LATCH);
|
|
|
|
net_gamespy_port = Cvar_Get("net_gamespy_port", va("%i", GAMESPY_DEFAULT_PORT), CVAR_LATCH);
|
|
|
|
|
|
|
|
if (qr_init(
|
|
|
|
NULL,
|
|
|
|
net_ip->string,
|
|
|
|
net_gamespy_port->integer,
|
|
|
|
gs_game_name,
|
|
|
|
secret_key,
|
|
|
|
basic_callback,
|
|
|
|
info_callback,
|
|
|
|
rules_callback,
|
|
|
|
players_callback,
|
|
|
|
NULL
|
|
|
|
)) {
|
|
|
|
Com_DPrintf("Error starting query sockets in SV_GamespyInit\n");
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!sv_gamespy->integer) {
|
|
|
|
strcpy(gamemode, "exiting");
|
|
|
|
qr_send_statechanged(NULL);
|
|
|
|
}
|
|
|
|
|
2023-08-24 23:19:21 +02:00
|
|
|
// this will set the GSI as available for cdkey authorization
|
2023-08-26 16:18:57 +02:00
|
|
|
__GSIACResult = GSIACAvailable;
|
2023-08-24 23:19:21 +02:00
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
if (!gcdInitialized) {
|
|
|
|
if (gcd_game_id) {
|
|
|
|
gcd_init(gcd_game_id);
|
2023-08-26 18:16:42 +02:00
|
|
|
gcdValid = qtrue;
|
2023-07-05 21:24:14 +02:00
|
|
|
}
|
|
|
|
|
2023-08-24 23:19:21 +02:00
|
|
|
gcdInitialized = qtrue;
|
2023-07-02 20:07:15 +02:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:24:14 +02:00
|
|
|
return qtrue;
|
2023-02-05 01:40:14 +01:00
|
|
|
}
|
2023-08-24 23:19:21 +02:00
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
void SV_CreateGamespyChallenge(char *challenge)
|
2023-08-24 23:19:21 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
|
|
challenge[i] = rand() % ('Z' - 'A' + 1) + 'A'; // random letters between A and Z
|
|
|
|
}
|
|
|
|
challenge[i] = 0;
|
|
|
|
}
|
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
challenge_t *FindChallengeById(int gameid)
|
2023-08-24 23:19:21 +02:00
|
|
|
{
|
2025-03-01 23:34:24 +01:00
|
|
|
challenge_t *challenge;
|
|
|
|
int i;
|
2023-08-24 23:19:21 +02:00
|
|
|
|
|
|
|
for (i = 0; i < MAX_CHALLENGES; i++) {
|
|
|
|
challenge = &svs.challenges[i];
|
|
|
|
if (challenge->connected == gameid) {
|
|
|
|
return challenge;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
void AuthenticateCallback(int gameid, int localid, int authenticated, char *errmsg, void *instance)
|
2023-08-24 23:19:21 +02:00
|
|
|
{
|
2025-03-01 23:34:24 +01:00
|
|
|
challenge_t *challenge;
|
|
|
|
qboolean valid = qfalse;
|
2023-08-24 23:19:21 +02:00
|
|
|
|
|
|
|
if (localid || !Q_stricmp(errmsg, "CD Key in use")) {
|
|
|
|
valid = qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
challenge = FindChallengeById(gameid);
|
2025-03-01 23:34:24 +01:00
|
|
|
if (valid) {
|
2023-08-24 23:19:21 +02:00
|
|
|
challenge->cdkeyState = 2;
|
2025-03-01 23:34:24 +01:00
|
|
|
challenge->pingTime = svs.time;
|
2023-08-24 23:19:21 +02:00
|
|
|
|
2024-11-09 21:15:49 +01:00
|
|
|
SV_NET_OutOfBandPrint(&svs.netprofile, challenge->adr, "challengeResponse %i", challenge->challenge);
|
2025-03-01 23:34:24 +01:00
|
|
|
} else {
|
2023-08-24 23:19:21 +02:00
|
|
|
char buf[32];
|
|
|
|
|
|
|
|
if (!challenge) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
Com_sprintf(
|
|
|
|
buf,
|
|
|
|
sizeof(buf),
|
|
|
|
"%d.%d.%d.%d",
|
|
|
|
challenge->adr.ip[0],
|
|
|
|
challenge->adr.ip[1],
|
|
|
|
challenge->adr.ip[2],
|
|
|
|
challenge->adr.ip[3]
|
|
|
|
);
|
2023-08-24 23:19:21 +02:00
|
|
|
Com_Printf("%s failed cdkey authorization\n", buf);
|
|
|
|
challenge->cdkeyState = 3;
|
|
|
|
// tell the client about the reason
|
2024-11-09 21:15:49 +01:00
|
|
|
SV_NET_OutOfBandPrint(&svs.netprofile, challenge->adr, "droperror\nServer rejected connection:\n%s", errmsg);
|
2023-08-24 23:19:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
void RefreshAuthCallback(int gameid, int localid, int hint, char *challenge, void *instance) {}
|
2023-08-24 23:19:21 +02:00
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
void SV_GamespyAuthorize(netadr_t from, const char *response)
|
2023-08-24 23:19:21 +02:00
|
|
|
{
|
2025-03-01 23:34:24 +01:00
|
|
|
char buf[64];
|
|
|
|
challenge_t *challenge = FindChallenge(from, qtrue);
|
2023-08-24 23:19:21 +02:00
|
|
|
if (!challenge) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2025-03-01 23:34:24 +01:00
|
|
|
switch (challenge->cdkeyState) {
|
2023-08-24 23:19:21 +02:00
|
|
|
case CDKS_NONE:
|
|
|
|
challenge->cdkeyState = CDKS_AUTHENTICATING;
|
2025-03-01 23:34:24 +01:00
|
|
|
challenge->firstTime = svs.time;
|
2023-08-24 23:19:21 +02:00
|
|
|
|
|
|
|
gcd_authenticate_user(
|
|
|
|
GS_GetCurrentGameID(),
|
|
|
|
challenge->gamespyId,
|
2025-03-01 23:34:24 +01:00
|
|
|
LittleLong(*(unsigned int *)from.ip),
|
2023-08-24 23:19:21 +02:00
|
|
|
challenge->gsChallenge,
|
|
|
|
response,
|
|
|
|
AuthenticateCallback,
|
|
|
|
RefreshAuthCallback,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case CDKS_AUTHENTICATING:
|
|
|
|
// the server can't reach the authentication server
|
|
|
|
// let the client connect
|
2025-03-01 23:34:24 +01:00
|
|
|
if (svs.time - challenge->firstTime > 5000) {
|
2023-08-24 23:19:21 +02:00
|
|
|
Com_DPrintf("authorize server timed out\n");
|
|
|
|
challenge->cdkeyState = CDKS_AUTHENTICATED;
|
2025-03-01 23:34:24 +01:00
|
|
|
challenge->pingTime = svs.time;
|
2024-11-09 21:15:49 +01:00
|
|
|
SV_NET_OutOfBandPrint(&svs.netprofile, from, "challengeResponse %i", challenge->challenge);
|
2023-08-24 23:19:21 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CDKS_AUTHENTICATED:
|
2024-11-09 21:15:49 +01:00
|
|
|
SV_NET_OutOfBandPrint(&svs.netprofile, from, "challengeResponse %i", challenge->challenge);
|
2023-08-24 23:19:21 +02:00
|
|
|
break;
|
|
|
|
case CDKS_FAILED:
|
|
|
|
// authentication server told the cdkey was invalid
|
2025-03-01 23:34:24 +01:00
|
|
|
Com_sprintf(
|
|
|
|
buf,
|
|
|
|
sizeof(buf),
|
|
|
|
"%d.%d.%d.%d",
|
|
|
|
challenge->adr.ip[0],
|
|
|
|
challenge->adr.ip[1],
|
|
|
|
challenge->adr.ip[2],
|
|
|
|
challenge->adr.ip[3]
|
|
|
|
);
|
2023-08-24 23:19:21 +02:00
|
|
|
Com_Printf("%s failed cdkey authorization\n", buf);
|
|
|
|
// reject the client
|
2024-11-09 21:15:49 +01:00
|
|
|
SV_NET_OutOfBandPrint(&svs.netprofile, from, "droperror\nServer rejected connection:\nInvalid CD Key");
|
2023-08-24 23:19:21 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2025-04-22 21:40:34 +02:00
|
|
|
|
2025-04-27 00:07:41 +02:00
|
|
|
unsigned int qr_get_num_masters()
|
2025-04-22 21:40:34 +02:00
|
|
|
{
|
2025-04-27 00:07:41 +02:00
|
|
|
return Com_GetNumMasterEntries();
|
2025-04-22 21:40:34 +02:00
|
|
|
}
|
|
|
|
|
2025-04-27 00:07:41 +02:00
|
|
|
const char *qr_get_master_host(int index)
|
2025-04-22 21:40:34 +02:00
|
|
|
{
|
2025-04-27 00:07:41 +02:00
|
|
|
return Com_GetMasterEntry(index)->host;
|
|
|
|
}
|
|
|
|
|
|
|
|
int qr_get_master_port(int index)
|
|
|
|
{
|
|
|
|
return Com_GetMasterEntry(index)->hbport;
|
2025-04-22 21:40:34 +02:00
|
|
|
}
|