Skip to content
Snippets Groups Projects
mserv.c 9.35 KiB
Newer Older
Alam Ed Arias's avatar
Alam Ed Arias committed
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2018 by Sonic Team Junior.
// Copyright (C)      2020 by James R.
Alam Ed Arias's avatar
Alam Ed Arias committed
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file  mserv.c
James R.'s avatar
James R. committed
/// \brief Commands used to communicate with the master server
Alam Ed Arias's avatar
Alam Ed Arias committed

#if !defined (UNDER_CE)
#include <time.h>
#endif

#include "doomstat.h"
#include "doomdef.h"
#include "command.h"
#include "i_threads.h"
Alam Ed Arias's avatar
Alam Ed Arias committed
#include "mserv.h"
#include "m_menu.h"
#include "z_zone.h"
Alam Ed Arias's avatar
Alam Ed Arias committed

#ifdef HAVE_DISCORDRPC
#include "discord.h"
#endif

#ifdef MASTERSERVER

static int     MSId;
static int     MSRegisteredId = -1;
Alam Ed Arias's avatar
Alam Ed Arias committed

static boolean MSRegistered;
static boolean MSInProgress;
static boolean MSUpdateAgain;
Alam Ed Arias's avatar
Alam Ed Arias committed

static time_t  MSLastPing;
Alam Ed Arias's avatar
Alam Ed Arias committed

#ifdef HAVE_THREADS
static I_mutex MSMutex;
static I_cond  MSCond;

#  define Lock_state()   I_lock_mutex  (&MSMutex)
#  define Unlock_state() I_unlock_mutex (MSMutex)
#else/*HAVE_THREADS*/
#  define Lock_state()
#  define Unlock_state()
#endif/*HAVE_THREADS*/

Alam Ed Arias's avatar
Alam Ed Arias committed
#ifndef NONET
static void Command_Listserv_f(void);
#endif

#endif/*MASTERSERVER*/

static void Update_parameters (void);

Alam Ed Arias's avatar
Alam Ed Arias committed
static void MasterServer_OnChange(void);

static void Advertise_OnChange(void);

static CV_PossibleValue_t masterserver_update_rate_cons_t[] = {
	{2,  "MIN"},
	{60, "MAX"},
SteelT's avatar
SteelT committed
	{0, NULL}
Alam Ed Arias's avatar
Alam Ed Arias committed

consvar_t cv_masterserver = {"masterserver", "https://ms.kartkrew.org/ms/api", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_servername = {"servername", "SRB2Kart server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_server_contact = {"server_contact", "", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters, 0, NULL, NULL, 0, 0, NULL};
Alam Ed Arias's avatar
Alam Ed Arias committed

consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, MasterClient_Ticker, 0, NULL, NULL, 0, 0, NULL};
Alam Ed Arias's avatar
Alam Ed Arias committed

consvar_t cv_advertise = {"advertise", "No", CV_NETVAR|CV_CALL|CV_NOINIT, CV_YesNo, Advertise_OnChange, 0, NULL, NULL, 0, 0, NULL};
Alam Ed Arias's avatar
Alam Ed Arias committed

#if defined (MASTERSERVER) && defined (HAVE_THREADS)
int           ms_QueryId;
I_mutex       ms_QueryId_mutex;
Alam Ed Arias's avatar
Alam Ed Arias committed

msg_server_t *ms_ServerList;
I_mutex       ms_ServerList_mutex;
Alam Ed Arias's avatar
Alam Ed Arias committed
#endif

UINT16 current_port = 0;

/** Adds variables and commands relating to the master server.
  *
  * \sa cv_masterserver, cv_servername,
  *     Command_Listserv_f
  */
void AddMServCommands(void)
{
#ifndef NONET
	CV_RegisterVar(&cv_masterserver);
	CV_RegisterVar(&cv_masterserver_update_rate);
	CV_RegisterVar(&cv_masterserver_timeout);
	CV_RegisterVar(&cv_masterserver_debug);
James R.'s avatar
James R. committed
	CV_RegisterVar(&cv_masterserver_token);
	CV_RegisterVar(&cv_advertise);
Alam Ed Arias's avatar
Alam Ed Arias committed
	CV_RegisterVar(&cv_servername);
	CV_RegisterVar(&cv_server_contact);
#ifdef MASTERSERVER
Alam Ed Arias's avatar
Alam Ed Arias committed
	COM_AddCommand("listserv", Command_Listserv_f);
#endif
Alam Ed Arias's avatar
Alam Ed Arias committed
}

#ifdef MASTERSERVER

static void WarnGUI (void)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
#ifdef HAVE_THREADS
	I_lock_mutex(&m_menu_mutex);
Alam Ed Arias's avatar
Alam Ed Arias committed
#endif
	M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n\nCheck the console for details.\n"), NULL, MM_NOTHING);
#ifdef HAVE_THREADS
	I_unlock_mutex(m_menu_mutex);
Alam Ed Arias's avatar
Alam Ed Arias committed
#endif
}

#define NUM_LIST_SERVER MAXSERVERLIST
msg_server_t *GetShortServersList(int id)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
	msg_server_t *server_list;
Alam Ed Arias's avatar
Alam Ed Arias committed

	// +1 for easy test
	server_list = malloc(( NUM_LIST_SERVER + 1 ) * sizeof *server_list);
Alam Ed Arias's avatar
Alam Ed Arias committed

	if (HMS_fetch_servers(server_list, id))
		return server_list;
Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		WarnGUI();
Alam Ed Arias's avatar
Alam Ed Arias committed
		return NULL;
	}
}

#ifdef UPDATE_ALERT
char *GetMODVersion(int id)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
Alam Ed Arias's avatar
Alam Ed Arias committed

Alam Ed Arias's avatar
Alam Ed Arias committed

Alam Ed Arias's avatar
Alam Ed Arias committed

	c = HMS_compare_mod_version(buffer, 16);
Alam Ed Arias's avatar
Alam Ed Arias committed

#ifdef HAVE_THREADS
	I_lock_mutex(&ms_QueryId_mutex);
Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		if (id != ms_QueryId)
			c = -1;
	}
	I_unlock_mutex(ms_QueryId_mutex);
Alam Ed Arias's avatar
Alam Ed Arias committed
#endif

	if (c > 0)
		return buffer;
	else
Alam Ed Arias's avatar
Alam Ed Arias committed

		if (! c)
			WarnGUI();
Alam Ed Arias's avatar
Alam Ed Arias committed
		return NULL;
	}
}
#endif

#ifndef NONET
/** Gets a list of game servers. Called from console.
  */
static void Command_Listserv_f(void)
{
	CONS_Printf(M_GetText("Retrieving server list...\n"));

	{
		HMS_list_servers();
Alam Ed Arias's avatar
Alam Ed Arias committed
	}
}
#endif

static void
Finish_registration (void)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
Alam Ed Arias's avatar
Alam Ed Arias committed

	CONS_Printf("Registering this server on the master server...\n");
Alam Ed Arias's avatar
Alam Ed Arias committed

	registered = HMS_register();
Alam Ed Arias's avatar
Alam Ed Arias committed

Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		MSRegistered = registered;
		MSRegisteredId = MSId;

		time(&MSLastPing);
Alam Ed Arias's avatar
Alam Ed Arias committed
	}
	if (registered)
		CONS_Printf("Master server registration successful.\n");
Alam Ed Arias's avatar
Alam Ed Arias committed
}

static void
Finish_update (void)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
	int registered;
	int done;
Alam Ed Arias's avatar
Alam Ed Arias committed

Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		registered = MSRegistered;
		MSUpdateAgain = false;/* this will happen anyway */
Alam Ed Arias's avatar
Alam Ed Arias committed
	}
Alam Ed Arias's avatar
Alam Ed Arias committed

		if (HMS_update())
		{
			Lock_state();
			{
				time(&MSLastPing);
				MSRegistered = true;
			}
			Unlock_state();
			CONS_Printf("Updated master server listing.\n");
		}
		else
			Finish_registration();
		Finish_registration();
Alam Ed Arias's avatar
Alam Ed Arias committed

Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		done = ! MSUpdateAgain;

		if (done)
			MSInProgress = false;
Alam Ed Arias's avatar
Alam Ed Arias committed
	}
	Unlock_state();

	if (! done)
		Finish_update();
Alam Ed Arias's avatar
Alam Ed Arias committed
}

static void
Finish_unlist (void)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
Alam Ed Arias's avatar
Alam Ed Arias committed

Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		registered = MSRegistered;

		if (MSId == MSRegisteredId)
			MSId++;
Alam Ed Arias's avatar
Alam Ed Arias committed
	}
Alam Ed Arias's avatar
Alam Ed Arias committed

		CONS_Printf("Removing this server from the master server...\n");
		if (HMS_unlist())
			CONS_Printf("Server deregistration request successfully sent.\n");
Alam Ed Arias's avatar
Alam Ed Arias committed

		Lock_state();
		{
			MSRegistered = false;
		}
		Unlock_state();
Alam Ed Arias's avatar
Alam Ed Arias committed

#ifdef HAVE_THREADS
		I_wake_all_cond(&MSCond);
Alam Ed Arias's avatar
Alam Ed Arias committed
#endif
	}
}

#ifdef HAVE_THREADS
static int *
Server_id (void)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
	int *id;
	id = malloc(sizeof *id);
	Lock_state();
Alam Ed Arias's avatar
Alam Ed Arias committed
	{
Alam Ed Arias's avatar
Alam Ed Arias committed
	}
	Unlock_state();
	return id;
Alam Ed Arias's avatar
Alam Ed Arias committed
}

static int *
New_server_id (void)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
	int *id;
	id = malloc(sizeof *id);
	Lock_state();
	{
		*id = ++MSId;
		I_wake_all_cond(&MSCond);
	}
	Unlock_state();
	return id;
Alam Ed Arias's avatar
Alam Ed Arias committed
}

static void
Register_server_thread (int *id)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
Alam Ed Arias's avatar
Alam Ed Arias committed

Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		/* wait for previous unlist to finish */
		while (*id == MSId && MSRegistered)
			I_hold_cond(&MSCond, MSMutex);
Alam Ed Arias's avatar
Alam Ed Arias committed

		same = ( *id == MSId );/* it could have been a while */
Alam Ed Arias's avatar
Alam Ed Arias committed
	}
Alam Ed Arias's avatar
Alam Ed Arias committed

	if (same)/* it could have been a while */
		Finish_registration();
Alam Ed Arias's avatar
Alam Ed Arias committed

Alam Ed Arias's avatar
Alam Ed Arias committed
}

static void
Update_server_thread (int *id)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
Alam Ed Arias's avatar
Alam Ed Arias committed

	Lock_state();
	{
		same = ( *id == MSRegisteredId );
	}
	Unlock_state();
Alam Ed Arias's avatar
Alam Ed Arias committed

	if (same)
		Finish_update();
Alam Ed Arias's avatar
Alam Ed Arias committed

Alam Ed Arias's avatar
Alam Ed Arias committed
}

static void
Unlist_server_thread (int *id)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
Alam Ed Arias's avatar
Alam Ed Arias committed

Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		same = ( *id == MSRegisteredId );
Alam Ed Arias's avatar
Alam Ed Arias committed
	}
Alam Ed Arias's avatar
Alam Ed Arias committed

	if (same)
		Finish_unlist();
Alam Ed Arias's avatar
Alam Ed Arias committed

Alam Ed Arias's avatar
Alam Ed Arias committed
}

static void
Change_masterserver_thread (char *api)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
	Lock_state();
Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		while (MSRegistered)
			I_hold_cond(&MSCond, MSMutex);
Alam Ed Arias's avatar
Alam Ed Arias committed
	}
	Unlock_state();
Alam Ed Arias's avatar
Alam Ed Arias committed

	HMS_set_api(api);
Alam Ed Arias's avatar
Alam Ed Arias committed
}
#endif/*HAVE_THREADS*/
Alam Ed Arias's avatar
Alam Ed Arias committed

void RegisterServer(void)
{
#ifdef MASTERSERVER
#ifdef HAVE_THREADS
	I_spawn_thread(
			"register-server",
			(I_thread_fn)Register_server_thread,
			New_server_id()
	);
#else
	Finish_registration();
#endif
#endif/*MASTERSERVER*/
Alam Ed Arias's avatar
Alam Ed Arias committed

static void UpdateServer(void)
{
#ifdef HAVE_THREADS
	I_spawn_thread(
			"update-server",
			(I_thread_fn)Update_server_thread,
			Server_id()
	);
#else
	Finish_update();
#endif
}
Alam Ed Arias's avatar
Alam Ed Arias committed

void UnregisterServer(void)
{
#ifdef MASTERSERVER
#ifdef HAVE_THREADS
	I_spawn_thread(
			"unlist-server",
			(I_thread_fn)Unlist_server_thread,
			Server_id()
	);
#else
	Finish_unlist();
#endif
#endif/*MASTERSERVER*/
Alam Ed Arias's avatar
Alam Ed Arias committed
}

static boolean
Online (void)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
	return ( serverrunning && cv_advertise.value );
Alam Ed Arias's avatar
Alam Ed Arias committed

static inline void SendPingToMasterServer(void)
{
	int ready;
	time_t now;
Alam Ed Arias's avatar
Alam Ed Arias committed

	if (Online())
	{
		time(&now);
Alam Ed Arias's avatar
Alam Ed Arias committed

		Lock_state();
		{
			ready = (
					MSRegisteredId == MSId &&
					! MSInProgress &&
					now >= ( MSLastPing + 60 * cv_masterserver_update_rate.value )
			);

			if (ready)
				MSInProgress = true;
		}
		Unlock_state();
Alam Ed Arias's avatar
Alam Ed Arias committed

		if (ready)
			UpdateServer();
void MasterClient_Ticker(void)
{
#ifdef MASTERSERVER
	SendPingToMasterServer();
#endif
}

static void
Set_api (const char *api)
{
#ifdef HAVE_THREADS
	I_spawn_thread(
			"change-masterserver",
			(I_thread_fn)Change_masterserver_thread,
			strdup(api)
	);
#else
	HMS_set_api(strdup(api));
#endif
}

#endif/*MASTERSERVER*/

static void
Update_parameters (void)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
#ifdef MASTERSERVER
	int registered;
	int delayed;
Alam Ed Arias's avatar
Alam Ed Arias committed

Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		Lock_state();
		{
			delayed = MSInProgress;
Alam Ed Arias's avatar
Alam Ed Arias committed

			if (delayed)/* do another update after the current one */
				MSUpdateAgain = true;
			else
				registered = MSRegistered;
		}
		Unlock_state();
Alam Ed Arias's avatar
Alam Ed Arias committed

		if (! delayed && registered)
			UpdateServer();
Alam Ed Arias's avatar
Alam Ed Arias committed
	}
#endif/*MASTERSERVER*/
Alam Ed Arias's avatar
Alam Ed Arias committed
}

static void MasterServer_OnChange(void)
{
#ifdef MASTERSERVER
Alam Ed Arias's avatar
Alam Ed Arias committed
	UnregisterServer();

	/*
	TODO: remove this for v2, it's just a hack
	for those coming in with an old config.
	*/
	if (
			! cv_masterserver.changed &&
			strcmp(cv_masterserver.string, "ms.srb2.org:28900") == 0
	){
		CV_StealthSet(&cv_masterserver, cv_masterserver.defaultvalue);
Alam Ed Arias's avatar
Alam Ed Arias committed
	}

	Set_api(cv_masterserver.string);
	if (Online())
		RegisterServer();
#endif/*MASTERSERVER*/
Alam Ed Arias's avatar
Alam Ed Arias committed
}

static void
Advertise_OnChange(void)
{
	int different;

	if (cv_advertise.value)
	{
		if (serverrunning)
		{
			Lock_state();
			{
				different = ( MSId != MSRegisteredId );
			}
			Unlock_state();

			if (different)
			{
				RegisterServer();
			}
		}
	}
	else
	{
		UnregisterServer();
	}

#ifdef HAVE_DISCORDRPC
	DRPC_UpdatePresence();
#endif