Browse Source

engine: server: move master announce logic to masterlist, keep unique heartbeat challenge and heartbeat timer for each master

pull/2/head
Alibek Omarov 2 years ago
parent
commit
01e0542223
  1. 4
      engine/common/common.h
  2. 196
      engine/common/masterlist.c
  3. 3
      engine/server/server.h
  4. 8
      engine/server/sv_client.c
  5. 4
      engine/server/sv_cmds.c
  6. 2
      engine/server/sv_init.c
  7. 79
      engine/server/sv_main.c

4
engine/common/common.h

@ -876,6 +876,10 @@ void NET_InitMasters( void );
void NET_SaveMasters( void ); void NET_SaveMasters( void );
qboolean NET_SendToMasters( netsrc_t sock, size_t len, const void *data ); qboolean NET_SendToMasters( netsrc_t sock, size_t len, const void *data );
qboolean NET_IsMasterAdr( netadr_t adr ); qboolean NET_IsMasterAdr( netadr_t adr );
void NET_MasterHeartbeat( void );
void NET_MasterClear( void );
void NET_MasterShutdown( void );
qboolean NET_GetMaster( netadr_t from, uint *challenge, double *last_heartbeat );
#ifdef REF_DLL #ifdef REF_DLL
#error "common.h in ref_dll" #error "common.h in ref_dll"

196
engine/common/masterlist.c

@ -14,22 +14,49 @@ GNU General Public License for more details.
*/ */
#include "common.h" #include "common.h"
#include "netchan.h" #include "netchan.h"
#include "server.h"
typedef struct master_s typedef struct master_s
{ {
struct master_s *next; struct master_s *next;
qboolean sent; qboolean sent; // TODO: get rid of this internal state
qboolean save; qboolean save;
string address; string address;
netadr_t adr; // temporary, rewritten after each send netadr_t adr; // temporary, rewritten after each send
uint heartbeat_challenge;
double last_heartbeat;
} master_t; } master_t;
struct masterlist_s static struct masterlist_s
{ {
master_t *list; master_t *list;
qboolean modified; qboolean modified;
} ml; } ml;
static CVAR_DEFINE_AUTO( sv_verbose_heartbeats, "0", 0, "print every heartbeat to console" );
#define HEARTBEAT_SECONDS ((sv_nat.value > 0.0f) ? 60.0f : 300.0f) // 1 or 5 minutes
/*
========================
NET_GetMasterHostByName
========================
*/
static net_gai_state_t NET_GetMasterHostByName( master_t *m )
{
net_gai_state_t res = NET_StringToAdrNB( m->address, &m->adr );
if( res == NET_EAI_OK )
return res;
m->adr.type = NA_UNUSED;
if( res == NET_EAI_NONAME )
Con_Reportf( "Can't resolve adr: %s\n", m->address );
return res;
}
/* /*
======================== ========================
NET_SendToMasters NET_SendToMasters
@ -45,65 +72,176 @@ qboolean NET_SendToMasters( netsrc_t sock, size_t len, const void *data )
for( list = ml.list; list; list = list->next ) for( list = ml.list; list; list = list->next )
{ {
int res;
if( list->sent ) if( list->sent )
continue; continue;
res = NET_StringToAdrNB( list->address, &list->adr ); switch( NET_GetMasterHostByName( list ))
if( !res )
{ {
Con_Reportf( "Can't resolve adr: %s\n", list->address ); case NET_EAI_AGAIN:
list->sent = false;
wait = true;
break;
case NET_EAI_NONAME:
list->sent = true; list->sent = true;
list->adr.type = NA_UNUSED; break;
continue; case NET_EAI_OK:
list->sent = true;
NET_SendPacket( sock, len, data, list->adr );
break;
} }
}
if( res == 2 ) if( !wait )
{ {
// reset sent state
for( list = ml.list; list; list = list->next )
list->sent = false; list->sent = false;
list->adr.type = NA_UNUSED; }
wait = true;
continue; return wait;
} }
/*
========================
NET_AnnounceToMaster
========================
*/
static void NET_AnnounceToMaster( master_t *m )
{
sizebuf_t msg;
char buf[16];
m->heartbeat_challenge = COM_RandomLong( 0, INT_MAX );
list->sent = true; MSG_Init( &msg, "Master Join", buf, sizeof( buf ));
MSG_WriteBytes( &msg, "q\xFF", 2 );
MSG_WriteDword( &msg, m->heartbeat_challenge );
NET_SendPacket( sock, len, data, list->adr ); NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &msg ), MSG_GetBuf( &msg ), m->adr );
if( sv_verbose_heartbeats.value )
{
Con_Printf( S_NOTE "sent heartbeat to %s (%s, 0x%x)\n",
m->address, NET_AdrToString( m->adr ), m->heartbeat_challenge );
} }
}
if( !wait ) /*
========================
NET_AnnounceToMaster
========================
*/
void NET_MasterClear( void )
{
master_t *m;
for( m = ml.list; m; m = m->next )
m->last_heartbeat = MAX_HEARTBEAT;
}
/*
========================
NET_MasterHeartbeat
========================
*/
void NET_MasterHeartbeat( void )
{
master_t *m;
if(( !public_server.value && !sv_nat.value ) || svs.maxclients == 1 )
return; // only public servers send heartbeats
for( m = ml.list; m; m = m->next )
{ {
list = ml.list; if( host.realtime - m->last_heartbeat < HEARTBEAT_SECONDS )
continue;
while( list ) switch( NET_GetMasterHostByName( m ))
{ {
list->sent = false; case NET_EAI_AGAIN:
list = list->next; m->last_heartbeat = MAX_HEARTBEAT; // retry on next frame
if( sv_verbose_heartbeats.value )
Con_Printf( S_NOTE "delay heartbeat to next frame until %s resolves\n", m->address );
break;
case NET_EAI_NONAME:
m->last_heartbeat = host.realtime; // try to resolve again on next heartbeat
break;
case NET_EAI_OK:
m->last_heartbeat = host.realtime;
NET_AnnounceToMaster( m );
break;
} }
} }
}
return wait; /*
=================
NET_MasterShutdown
Informs all masters that this server is going down
(ignored by master servers in current implementation)
=================
*/
void NET_MasterShutdown( void )
{
NET_Config( true, false ); // allow remote
while( NET_SendToMasters( NS_SERVER, 2, "\x62\x0A" ));
} }
/* /*
======================== ========================
NET_IsMasterAdr NET_GetMasterFromAdr
======================== ========================
*/ */
qboolean NET_IsMasterAdr( netadr_t adr ) static master_t *NET_GetMasterFromAdr( netadr_t adr )
{ {
master_t *master; master_t *master;
for( master = ml.list; master; master = master->next ) for( master = ml.list; master; master = master->next )
{ {
if( NET_CompareAdr( adr, master->adr )) if( NET_CompareAdr( adr, master->adr ))
return true; return master;
} }
return false; return NULL;
}
/*
========================
NET_GetMaster
========================
*/
qboolean NET_GetMaster( netadr_t from, uint *challenge, double *last_heartbeat )
{
master_t *m;
m = NET_GetMasterFromAdr( from );
if( m )
{
*challenge = m->heartbeat_challenge;
*last_heartbeat = m->last_heartbeat;
}
return m != NULL;
}
/*
========================
NET_IsMasterAdr
========================
*/
qboolean NET_IsMasterAdr( netadr_t adr )
{
return NET_GetMasterFromAdr( adr ) != NULL;
} }
/* /*
@ -277,6 +415,8 @@ void NET_InitMasters( void )
Cmd_AddRestrictedCommand( "clearmasters", NET_ClearMasters_f, "clear masterserver list" ); Cmd_AddRestrictedCommand( "clearmasters", NET_ClearMasters_f, "clear masterserver list" );
Cmd_AddCommand( "listmasters", NET_ListMasters_f, "list masterservers" ); Cmd_AddCommand( "listmasters", NET_ListMasters_f, "list masterservers" );
Cvar_RegisterVariable( &sv_verbose_heartbeats );
// keep main master always there // keep main master always there
NET_AddMaster( MASTERSERVER_ADR, false ); NET_AddMaster( MASTERSERVER_ADR, false );
NET_LoadMasters( ); NET_LoadMasters( );

3
engine/server/server.h

@ -374,9 +374,7 @@ typedef struct
entity_state_t *baselines; // [GI->max_edicts] entity_state_t *baselines; // [GI->max_edicts]
entity_state_t *static_entities; // [MAX_STATIC_ENTITIES]; entity_state_t *static_entities; // [MAX_STATIC_ENTITIES];
double last_heartbeat;
challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting
uint heartbeat_challenge;
} server_static_t; } server_static_t;
//============================================================================= //=============================================================================
@ -444,6 +442,7 @@ extern convar_t skill;
extern convar_t coop; extern convar_t coop;
extern convar_t sv_cheats; extern convar_t sv_cheats;
extern convar_t public_server; extern convar_t public_server;
extern convar_t sv_nat;
extern convar_t *sv_pausable; // allows pause in multiplayer extern convar_t *sv_pausable; // allows pause in multiplayer
extern convar_t *sv_check_errors; extern convar_t *sv_check_errors;

8
engine/server/sv_client.c

@ -475,7 +475,7 @@ void SV_ConnectClient( netadr_t from )
Log_Printf( "\"%s<%i><%i><>\" connected, address \"%s\"\n", newcl->name, newcl->userid, i, NET_AdrToString( newcl->netchan.remote_address )); Log_Printf( "\"%s<%i><%i><>\" connected, address \"%s\"\n", newcl->name, newcl->userid, i, NET_AdrToString( newcl->netchan.remote_address ));
if( count == 1 || count == svs.maxclients ) if( count == 1 || count == svs.maxclients )
svs.last_heartbeat = MAX_HEARTBEAT; NET_MasterClear();
} }
/* /*
@ -542,7 +542,7 @@ edict_t *SV_FakeConnect( const char *netname )
cl->state = cs_spawned; cl->state = cs_spawned;
if( count == 1 || count == svs.maxclients ) if( count == 1 || count == svs.maxclients )
svs.last_heartbeat = MAX_HEARTBEAT; NET_MasterClear();
return cl->edict; return cl->edict;
} }
@ -619,7 +619,7 @@ void SV_DropClient( sv_client_t *cl, qboolean crash )
} }
if( i == svs.maxclients ) if( i == svs.maxclients )
svs.last_heartbeat = MAX_HEARTBEAT; NET_MasterClear();
} }
/* /*
@ -3116,7 +3116,7 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
else if( !Q_strcmp( pcmd, "s" )) SV_AddToMaster( from, msg ); else if( !Q_strcmp( pcmd, "s" )) SV_AddToMaster( from, msg );
else if( !Q_strcmp( pcmd, "T" "Source" )) SV_TSourceEngineQuery( from ); else if( !Q_strcmp( pcmd, "T" "Source" )) SV_TSourceEngineQuery( from );
else if( !Q_strcmp( pcmd, "i" )) NET_SendPacket( NS_SERVER, 5, "\xFF\xFF\xFF\xFFj", from ); // A2A_PING else if( !Q_strcmp( pcmd, "i" )) NET_SendPacket( NS_SERVER, 5, "\xFF\xFF\xFF\xFFj", from ); // A2A_PING
else if( !Q_strcmp( pcmd, "c" ) && Cvar_VariableInteger( "sv_nat" ) && NET_IsMasterAdr( from )) else if( !Q_strcmp( pcmd, "c" ) && sv_nat.value && NET_IsMasterAdr( from ))
{ {
netadr_t to; netadr_t to;
if( NET_StringToAdr( Cmd_Argv( 1 ), &to ) && !NET_IsReservedAdr( to )) if( NET_StringToAdr( Cmd_Argv( 1 ), &to ) && !NET_IsReservedAdr( to ))

4
engine/server/sv_cmds.c

@ -749,9 +749,9 @@ void SV_ConSay_f( void )
SV_Heartbeat_f SV_Heartbeat_f
================== ==================
*/ */
void SV_Heartbeat_f( void ) static void SV_Heartbeat_f( void )
{ {
svs.last_heartbeat = MAX_HEARTBEAT; NET_MasterClear();
} }
/* /*

2
engine/server/sv_init.c

@ -1012,7 +1012,7 @@ qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean ba
} }
// heartbeats will always be sent to the id master // heartbeats will always be sent to the id master
svs.last_heartbeat = MAX_HEARTBEAT; // send immediately NET_MasterClear();
// get actual movevars // get actual movevars
SV_UpdateMovevars( true ); SV_UpdateMovevars( true );

79
engine/server/sv_main.c

@ -18,8 +18,6 @@ GNU General Public License for more details.
#include "net_encode.h" #include "net_encode.h"
#include "platform/platform.h" #include "platform/platform.h"
#define HEARTBEAT_SECONDS ((sv_nat.value > 0.0f) ? 60.0f : 300.0f) // 1 or 5 minutes
// server cvars // server cvars
CVAR_DEFINE_AUTO( sv_lan, "0", 0, "server is a lan server ( no heartbeat, no authentication, no non-class C addresses, 9999.0 rate, etc." ); CVAR_DEFINE_AUTO( sv_lan, "0", 0, "server is a lan server ( no heartbeat, no authentication, no non-class C addresses, 9999.0 rate, etc." );
CVAR_DEFINE_AUTO( sv_lan_rate, "20000.0", 0, "rate for lan server" ); CVAR_DEFINE_AUTO( sv_lan_rate, "20000.0", 0, "rate for lan server" );
@ -142,8 +140,6 @@ convar_t *sv_allow_mouse;
convar_t *sv_allow_joystick; convar_t *sv_allow_joystick;
convar_t *sv_allow_vr; convar_t *sv_allow_vr;
static void Master_Heartbeat( void );
//============================================================================ //============================================================================
/* /*
================ ================
@ -668,7 +664,7 @@ void Host_ServerFrame( void )
Platform_UpdateStatusLine (); Platform_UpdateStatusLine ();
// send a heartbeat to the master if needed // send a heartbeat to the master if needed
Master_Heartbeat (); NET_MasterHeartbeat ();
} }
/* /*
@ -683,68 +679,6 @@ void Host_SetServerState( int state )
} }
//============================================================================ //============================================================================
/*
=================
Master_Add
=================
*/
static void Master_Add( void )
{
sizebuf_t msg;
char buf[16];
uint challenge;
NET_Config( true, false ); // allow remote
svs.heartbeat_challenge = challenge = COM_RandomLong( 0, INT_MAX );
MSG_Init( &msg, "Master Join", buf, sizeof( buf ));
MSG_WriteBytes( &msg, "q\xFF", 2 );
MSG_WriteDword( &msg, challenge );
if( NET_SendToMasters( NS_SERVER, MSG_GetNumBytesWritten( &msg ), MSG_GetBuf( &msg )))
svs.last_heartbeat = MAX_HEARTBEAT;
}
/*
================
Master_Heartbeat
Send a message to the master every few minutes to
let it know we are alive, and log information
================
*/
static void Master_Heartbeat( void )
{
if(( !public_server.value && !sv_nat.value ) || svs.maxclients == 1 )
return; // only public servers send heartbeats
// check for time wraparound
if( svs.last_heartbeat > host.realtime )
svs.last_heartbeat = host.realtime;
if(( host.realtime - svs.last_heartbeat ) < HEARTBEAT_SECONDS )
return; // not time to send yet
svs.last_heartbeat = host.realtime;
Master_Add();
}
/*
=================
Master_Shutdown
Informs all masters that this server is going down
=================
*/
static void Master_Shutdown( void )
{
NET_Config( true, false ); // allow remote
while( NET_SendToMasters( NS_SERVER, 2, "\x62\x0A" ));
}
/* /*
================= =================
SV_AddToMaster SV_AddToMaster
@ -755,18 +689,19 @@ Master will validate challenge and this server to public list
*/ */
void SV_AddToMaster( netadr_t from, sizebuf_t *msg ) void SV_AddToMaster( netadr_t from, sizebuf_t *msg )
{ {
uint challenge, challenge2; uint challenge, challenge2, heartbeat_challenge;
char s[MAX_INFO_STRING] = "0\n"; // skip 2 bytes of header char s[MAX_INFO_STRING] = "0\n"; // skip 2 bytes of header
int clients, bots; int clients, bots;
double last_heartbeat;
const int len = sizeof( s ); const int len = sizeof( s );
if( !NET_IsMasterAdr( from )) if( !NET_GetMaster( from, &heartbeat_challenge, &last_heartbeat ))
{ {
Con_Printf( S_WARN "unexpected master server info query packet from %s\n", NET_AdrToString( from )); Con_Printf( S_WARN "unexpected master server info query packet from %s\n", NET_AdrToString( from ));
return; return;
} }
if( svs.last_heartbeat + sv_master_response_timeout.value < host.realtime ) if( last_heartbeat + sv_master_response_timeout.value < host.realtime )
{ {
Con_Printf( S_WARN "unexpected master server info query packet (too late? try increasing sv_master_response_timeout value)\n"); Con_Printf( S_WARN "unexpected master server info query packet (too late? try increasing sv_master_response_timeout value)\n");
return; return;
@ -775,7 +710,7 @@ void SV_AddToMaster( netadr_t from, sizebuf_t *msg )
challenge = MSG_ReadDword( msg ); challenge = MSG_ReadDword( msg );
challenge2 = MSG_ReadDword( msg ); challenge2 = MSG_ReadDword( msg );
if( challenge2 != svs.heartbeat_challenge ) if( challenge2 != heartbeat_challenge )
{ {
Con_Printf( S_WARN "unexpected master server info query packet (wrong challenge!)\n" ); Con_Printf( S_WARN "unexpected master server info query packet (wrong challenge!)\n" );
return; return;
@ -1114,7 +1049,7 @@ void SV_Shutdown( const char *finalmsg )
SV_FinalMessage( finalmsg, false ); SV_FinalMessage( finalmsg, false );
if( public_server.value && svs.maxclients != 1 ) if( public_server.value && svs.maxclients != 1 )
Master_Shutdown(); NET_MasterShutdown();
NET_Config( false, false ); NET_Config( false, false );
SV_UnloadProgs (); SV_UnloadProgs ();

Loading…
Cancel
Save