diff --git a/engine/common/common.h b/engine/common/common.h index 32fe18f2..6d584cc8 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -709,6 +709,7 @@ int CL_Active( void ); void SV_Init( void ); void SV_Shutdown( const char *finalmsg ); +void SV_ShutdownFilter( void ); void Host_ServerFrame( void ); qboolean SV_Active( void ); @@ -831,6 +832,15 @@ void Key_EnableTextInput( qboolean enable, qboolean force ); #include "avi/avi.h" +// +// input.c +// + +#define INPUT_DEVICE_MOUSE (1<<0) +#define INPUT_DEVICE_TOUCH (1<<1) +#define INPUT_DEVICE_JOYSTICK (1<<2) +#define INPUT_DEVICE_VR (1<<3) + // shared calls struct physent_s; typedef struct sv_client_s sv_client_t; diff --git a/engine/common/host.c b/engine/common/host.c index cd725fb6..bb5c6488 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -1059,6 +1059,7 @@ void EXPORT Host_Shutdown( void ) #endif SV_Shutdown( "Server shutdown\n" ); + SV_ShutdownFilter(); CL_Shutdown(); Mod_Shutdown(); diff --git a/engine/server/server.h b/engine/server/server.h index db4dd50a..fdf488d1 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -253,6 +253,7 @@ typedef struct sv_client_s int challenge; // challenge of this user, randomly generated int userid; // identifying number on server int extensions; + char useragent[MAX_INFO_STRING]; } sv_client_t; /* @@ -457,6 +458,7 @@ void SV_ProcessFile( sv_client_t *cl, const char *filename ); void SV_SendResource( resource_t *pResource, sizebuf_t *msg ); void SV_SendResourceList( sv_client_t *cl ); void SV_AddToMaster( netadr_t from, sizebuf_t *msg ); +qboolean SV_ProcessUserAgent( netadr_t from, const char *useragent ); void Host_SetServerState( int state ); qboolean SV_IsSimulating( void ); qboolean SV_InitGame( void ); @@ -515,6 +517,8 @@ void SV_DirectConnect( netadr_t from ); void SV_TogglePause( const char *msg ); qboolean SV_ShouldUpdatePing( sv_client_t *cl ); const char *SV_GetClientIDString( sv_client_t *cl ); +sv_client_t *SV_ClientById( int id ); +sv_client_t *SV_ClientByName( const char *name ); void SV_FullClientUpdate( sv_client_t *cl, sizebuf_t *msg ); void SV_FullUpdateMovevars( sv_client_t *cl, sizebuf_t *msg ); void SV_GetPlayerStats( sv_client_t *cl, int *ping, int *packet_loss ); @@ -554,6 +558,14 @@ void SV_ClearResourceLists( sv_client_t *cl ); void SV_TransferConsistencyInfo( void ); void SV_RequestMissingResources( void ); +// +// sv_filter.c +// +void SV_InitFilter( void ); +void SV_ShutdownFilter( void ); +qboolean SV_CheckIP( netadr_t *adr ); +qboolean SV_CheckID( const char *id ); + // // sv_frame.c // diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 3a37d1de..e4606f4b 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -287,6 +287,12 @@ void SV_ConnectClient( netadr_t from ) return; } + if( !SV_ProcessUserAgent( from, Cmd_Argv( 6 ) ) ) + { + Netchan_OutOfBandPrint( NS_SERVER, from, "disconnect\n" ); + return; + } + challenge = Q_atoi( Cmd_Argv( 2 )); // get challenge // see if the challenge is valid (local clients don't need to challenge) @@ -372,6 +378,18 @@ void SV_ConnectClient( netadr_t from ) // build a new connection // accept the new client + if( Q_strncpy( newcl->useragent, Cmd_Argv( 6 ), MAX_INFO_STRING ) ) + { + const char *id = Info_ValueForKey( newcl->useragent, "i" ); + + if( *id ) + { + //sscanf( id, "%llx", &newcl->WonID ); + } + + // Q_strncpy( cl->auth_id, id, sizeof( cl->auth_id ) ); + } + sv.current_client = newcl; newcl->edict = EDICT_NUM( (newcl - svs.clients) + 1 ); newcl->challenge = challenge; // save challenge for checksumming @@ -662,6 +680,44 @@ const char *SV_GetClientIDString( sv_client_t *cl ) return result; } +sv_client_t *SV_ClientById( int id ) +{ + sv_client_t *cl; + int i; + + ASSERT( id >= 0 ); + + for( i = 0, cl = svs.clients; i < svgame.globals->maxClients; i++, cl++ ) + { + if( !cl->state ) + continue; + + if( cl->userid == id ) + return cl; + } + + return NULL; +} + +sv_client_t *SV_ClientByName( const char *name ) +{ + sv_client_t *cl; + int i; + + ASSERT( name && *name ); + + for( i = 0, cl = svs.clients; i < svgame.globals->maxClients; i++, cl++ ) + { + if( !cl->state ) + continue; + + if( !Q_strcmp( cl->name, name ) ) + return cl; + } + + return NULL; +} + /* ================ SV_TestBandWidth @@ -2146,6 +2202,10 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) char buf[MAX_SYSPATH]; int len = sizeof( buf ); + // prevent flooding from banned address + if( SV_CheckIP( &from ) ) + return; + MSG_Clear( msg ); MSG_ReadLong( msg );// skip the -1 marker diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index 53e1b6c2..8ee51f2b 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -541,15 +541,25 @@ Kick a user off of the server void SV_Kick_f( void ) { sv_client_t *cl; + const char *param, *clientId; if( Cmd_Argc() != 2 ) { - Con_Printf( S_USAGE "kick | \n" ); + Con_Printf( S_USAGE "kick <#id|name> [reason]\n" ); return; } - if(( cl = SV_SetPlayer( )) == NULL ) + param = Cmd_Argv( 1 ); + + if( *param == '#' && Q_isdigit( param + 1 ) ) + cl = SV_ClientById( Q_atoi( param + 1 ) ); + else cl = SV_ClientByName( param ); + + if( !cl ) + { + Con_Printf( "Client is not on the server\n" ); return; + } if( NET_IsLocalAddress( cl->netchan.remote_address )) { @@ -557,9 +567,31 @@ void SV_Kick_f( void ) return; } - Log_Printf( "Kick: \"%s<%i>\" was kicked\n", cl->name, cl->userid ); - SV_BroadcastPrintf( cl, "%s was kicked\n", cl->name ); - SV_ClientPrintf( cl, "You were kicked from the game\n" ); + param = Cmd_Argv( 2 ); + + clientId = SV_GetClientIDString( cl ); + + if( *param ) + { + Log_Printf( "Kick: \"%s<%i><%s><>\" was kicked by \"Console\" (message \"%s\")\n", cl->name, cl->userid, clientId, param ); + SV_BroadcastPrintf( cl, "%s was kicked with message: \"%s\"\n", cl->name, param ); + SV_ClientPrintf( cl, "You were kicked from the game with message: \"%s\"\n", param ); + } + else + { + Log_Printf( "Kick: \"%s<%i><%s><>\" was kicked by \"Console\"\n", cl->name, cl->userid, clientId ); + SV_BroadcastPrintf( cl, "%s was kicked\n", cl->name ); + SV_ClientPrintf( cl, "You were kicked from the game\n" ); + } + + if( cl->useragent[0] ) + { + if( *param ) + Netchan_OutOfBandPrint( NS_SERVER, cl->netchan.remote_address, "errormsg\nKicked with message:\n%s\n", param ); + else + Netchan_OutOfBandPrint( NS_SERVER, cl->netchan.remote_address, "errormsg\nYou were kicked from the game\n" ); + } + SV_DropClient( cl, false ); } @@ -781,6 +813,31 @@ void SV_ClientInfo_f( void ) } +/* +=========== +SV_ClientUserAgent_f + +Examine useragent strings +=========== +*/ +void SV_ClientUserAgent_f( void ) +{ + sv_client_t *cl; + + if( Cmd_Argc() != 2 ) + { + Con_Printf( S_USAGE "clientuseragent \n" ); + return; + } + + if(( cl = SV_SetPlayer( )) == NULL ) + return; + + Con_Printf( "useragent\n" ); + Con_Printf( "---------\n" ); + Info_Print( cl->useragent ); +} + /* =============== SV_KillServer_f @@ -912,6 +969,7 @@ void SV_InitOperatorCommands( void ) Cmd_AddCommand( "localinfo", SV_LocalInfo_f, "examine or change the localinfo string" ); Cmd_AddCommand( "serverinfo", SV_ServerInfo_f, "examine or change the serverinfo string" ); Cmd_AddCommand( "clientinfo", SV_ClientInfo_f, "print user infostring (player num required)" ); + Cmd_AddCommand( "clientuseragent", SV_ClientUserAgent_f, "print user agent (player num required)" ); Cmd_AddCommand( "playersonly", SV_PlayersOnly_f, "freezes time, except for players" ); Cmd_AddCommand( "restart", SV_Restart_f, "restarting current level" ); Cmd_AddCommand( "entpatch", SV_EntPatch_f, "write entity patch to allow external editing" ); diff --git a/engine/server/sv_filter.c b/engine/server/sv_filter.c new file mode 100644 index 00000000..ef8ccd1d --- /dev/null +++ b/engine/server/sv_filter.c @@ -0,0 +1,462 @@ +/* +sv_filter.c - server ID/IP filter +Copyright (C) 2017 a1batross + +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 "common.h" +#include "server.h" + + +typedef struct ipfilter_s +{ + float time; + float endTime; // -1 for permanent ban + struct ipfilter_s *next; + uint mask; + uint ip; +} ipfilter_t; + +static ipfilter_t *ipfilter = NULL; + + +// TODO: Is IP filter really needed? +// TODO: Make it IPv6 compatible, for future expansion + +typedef struct cidfilter_s +{ + float endTime; + struct cidfilter_s *next; + string id; +} cidfilter_t; + +static cidfilter_t *cidfilter = NULL; + +static void SV_RemoveID( const char *id ) +{ + cidfilter_t *filter, *prevfilter = NULL; + + for( filter = cidfilter; filter; filter = filter->next ) + { + if( Q_strcmp( filter->id, id ) ) + { + prevfilter = filter; + continue; + } + + if( filter == cidfilter ) + { + cidfilter = cidfilter->next; + Mem_Free( filter ); + return; + } + + if( prevfilter ) + prevfilter->next = filter->next; + Mem_Free( filter ); + return; + } +} + +static void SV_RemoveIP( uint ip, uint mask ) +{ + ipfilter_t *filter, *prevfilter = NULL; + + for( filter = ipfilter; filter; filter = filter->next ) + { + if( filter->ip != ip || mask != filter->mask ) + { + prevfilter = filter; + continue; + } + + if( filter == ipfilter ) + { + ipfilter = ipfilter->next; + Mem_Free( filter ); + return; + } + + if( prevfilter ) + prevfilter->next = filter->next; + Mem_Free( filter ); + return; + } +} + +qboolean SV_CheckID( const char *id ) +{ + qboolean ret = false; + cidfilter_t *filter; + + for( filter = cidfilter; filter; filter = filter->next ) + { + int len1 = Q_strlen( id ), len2 = Q_strlen( filter->id ); + int len = min( len1, len2 ); + + while( filter->endTime && host.realtime > filter->endTime ) + { + char *fid = filter->id; + filter = filter->next; + SV_RemoveID( fid ); + if( !filter ) + return false; + } + + if( !Q_strncmp( id, filter->id, len ) ) + { + ret = true; + break; + } + } + + return ret; +} + +qboolean SV_CheckIP( netadr_t *addr ) +{ + uint ip = addr->ip[0] << 24 | addr->ip[1] << 16 | addr->ip[2] << 8 | addr->ip[3]; + qboolean ret = false; + ipfilter_t *filter; + + for( filter = ipfilter; filter; filter = filter->next ) + { + while( filter->endTime && host.realtime > filter->endTime ) + { + uint rip = filter->ip; + uint rmask = filter->mask; + SV_RemoveIP( rip, rmask ); + filter = filter->next; + if( !filter ) + return false; + } + + if( (ip & filter->mask) == (filter->ip & filter->mask) ) + { + ret = true; + break; + } + } + + return ret; +} + +static void SV_BanID_f( void ) +{ + float time = Q_atof( Cmd_Argv( 1 ) ); + const char *id = Cmd_Argv( 2 ); + sv_client_t *cl = NULL; + cidfilter_t *filter; + + if( time ) + time = host.realtime + time * 60.0f; + + if( !id[0] ) + { + Con_Reportf( "Usage: banid <#userid or unique id>\n0 minutes for permanent ban\n" ); + return; + } + + if( !Q_strnicmp( id, "STEAM_", 6 ) || !Q_strnicmp( id, "VALVE_", 6 ) ) + id += 6; + if( !Q_strnicmp( id, "XASH_", 5 ) ) + id += 5; + + if( svs.clients ) + { + if( id[0] == '#' ) + cl = SV_ClientById( Q_atoi( id + 1 ) ); + + if( !cl ) + { + int i; + sv_client_t *cl1; + int len = Q_strlen( id ); + + for( i = 0, cl1 = svs.clients; i < sv_maxclients->value; i++, cl1++ ) + { + if( !Q_strncmp( id, Info_ValueForKey( cl1->useragent, "i" ), len ) ) + { + cl = cl1; + break; + } + } + } + + if( !cl ) + { + Con_DPrintf( S_WARN "banid: no such player\n" ); + } + else + id = Info_ValueForKey( cl->useragent, "i" ); + + if( !id[0] ) + { + Con_DPrintf( S_ERROR "Could not ban, not implemented yet\n" ); + return; + } + } + + if( !id[0] || id[0] == '#' ) + { + Con_DPrintf( S_ERROR "banid: bad id\n" ); + return; + } + + SV_RemoveID( id ); + + filter = Mem_Malloc( host.mempool, sizeof( cidfilter_t ) ); + filter->endTime = time; + filter->next = cidfilter; + Q_strncpy( filter->id, id, sizeof( filter->id ) ); + cidfilter = filter; + + if( cl && !Q_stricmp( Cmd_Argv( Cmd_Argc() - 1 ), "kick" ) ) + Cbuf_AddText( va( "kick #%d \"Kicked and banned\"\n", cl->userid ) ); +} + +static void SV_ListID_f( void ) +{ + cidfilter_t *filter; + + Con_Reportf( "id ban list\n" ); + Con_Reportf( "-----------\n" ); + + for( filter = cidfilter; filter; filter = filter->next ) + { + if( filter->endTime && host.realtime > filter->endTime ) + continue; // no negative time + + if( filter->endTime ) + Con_Reportf( "%s expries in %f minutes\n", filter->id, ( filter->endTime - host.realtime ) / 60.0f ); + else + Con_Reportf( "%s permanent\n", filter->id ); + } +} + +static void SV_RemoveID_f( void ) +{ + const char *id = Cmd_Argv( 1 ); + + if( id[0] == '#' && svs.clients ) + { + int num = Q_atoi( id + 1 ); + + if( num >= sv_maxclients->value || num < 0 ) + return; + + id = Info_ValueForKey( svs.clients[num].useragent, "i" ); + } + + if( !id[0] ) + { + Con_Reportf("Usage: removeid <#slotnumber or uniqueid>\n"); + return; + } + + SV_RemoveID( id ); +} + +static void SV_WriteID_f( void ) +{ + file_t *f = FS_Open( Cvar_VariableString( "bannedcfgfile" ), "w", false ); + cidfilter_t *filter; + + if( !f ) + { + Con_DPrintf( S_ERROR "Could not write %s\n", Cvar_VariableString( "bannedcfgfile" ) ); + return; + } + + FS_Printf( f, "//=======================================================================\n" ); + FS_Printf( f, "//\t\tCopyright Flying With Gauss Team %s ©\n", Q_timestamp( TIME_YEAR_ONLY )); + FS_Printf( f, "//\t\t %s - archive of id blacklist\n", Cvar_VariableString( "bannedcfgfile" ) ); + FS_Printf( f, "//=======================================================================\n" ); + + for( filter = cidfilter; filter; filter = filter->next ) + if( !filter->endTime ) // only permanent + FS_Printf( f, "banid 0 %s\n", filter->id ); + + FS_Close( f ); +} + +static qboolean StringToIP( const char *str, const char *maskstr, uint *outip, uint *outmask ) +{ + byte ip[4] = {0}; + byte mask[4] = {0}; + int i = 0; + + if( *str > '9' || *str < '0' ) + return false; + + do + { + while( *str <= '9' && *str >= '0' ) + { + ip[i] *=10; + ip[i] += *str - '0'; + str++; + } + mask[i] = 255; + i++; + if( *str != '.' ) break; + str++; + } while( i < 4 ); + + i = 0; + + if( !maskstr || *maskstr > '9' || *maskstr < '0' ) + goto end; + + do + { + byte mask1 = 0; + while( *maskstr <= '9' && *maskstr >= '0' ) + { + mask1 *=10; + mask1 += *maskstr - '0'; + maskstr++; + } + mask[i] &= mask1; + i++; + if( *maskstr != '.' ) break; + maskstr++; + } while( i < 4 ); + +end: + *outip = ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3]; + if( outmask ) + *outmask = mask[0] << 24 | mask[1] << 16 | mask[2] << 8 | mask[3]; + + return true; +} + +#define IPARGS(ip) (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF +static void SV_AddIP_f( void ) +{ + float time = Q_atof( Cmd_Argv( 1 ) ); + const char *ipstr = Cmd_Argv( 2 ); + const char *maskstr = Cmd_Argv( 3 ); + uint ip, mask; + ipfilter_t *filter; + + if( time ) + time = host.realtime + time * 60.0f; + + if( !StringToIP( ipstr, maskstr, &ip, &mask ) ) + { + Con_Reportf( "Usage: addip [mask]\n0 minutes for permanent ban\n"); + return; + } + + SV_RemoveIP( ip, mask ); + + filter = Mem_Malloc( host.mempool, sizeof( ipfilter_t ) ); + filter->endTime = time; + filter->ip = ip; + filter->mask = mask; + filter->next = ipfilter; + + ipfilter = filter; +} + +static void SV_ListIP_f( void ) +{ + ipfilter_t *filter; + + Con_Reportf( "ip ban list\n" ); + Con_Reportf( "-----------\n" ); + + for( filter = ipfilter; filter; filter = filter->next ) + { + if( filter->endTime && host.realtime > filter->endTime ) + continue; // no negative time + + if( filter->endTime ) + Con_Reportf( "%d.%d.%d.%d %d.%d.%d.%d expries in %f minutes\n", IPARGS( filter->ip ), IPARGS( filter->mask ), ( filter->endTime - host.realtime ) / 60.0f ); + else + Con_Reportf( "%d.%d.%d.%d %d.%d.%d.%d permanent\n", IPARGS( filter->ip ), IPARGS( filter->mask ) ); + } +} + +static void SV_RemoveIP_f( void ) +{ + uint ip, mask; + + if( !StringToIP( Cmd_Argv(1), Cmd_Argv(2), &ip, &mask ) ) + { + Con_Reportf( "Usage: removeip [mask]\n" ); + return; + } + + SV_RemoveIP( ip, mask ); +} + +static void SV_WriteIP_f( void ) +{ + file_t *f = FS_Open( Cvar_VariableString( "listipcfgfile" ), "w", false ); + ipfilter_t *filter; + + if( !f ) + { + Con_DPrintf( S_ERROR "Could not write %s\n", Cvar_VariableString( "listipcfgfile" ) ); + return; + } + + FS_Printf( f, "//=======================================================================\n" ); + FS_Printf( f, "//\t\tCopyright Flying With Gauss Team %s ©\n", Q_timestamp( TIME_YEAR_ONLY )); + FS_Printf( f, "//\t\t %s - archive of IP blacklist\n", Cvar_VariableString( "listipcfgfile" ) ); + FS_Printf( f, "//=======================================================================\n" ); + + for( filter = ipfilter; filter; filter = filter->next ) + if( !filter->endTime ) // only permanent + FS_Printf( f, "addip 0 %d.%d.%d.%d %d.%d.%d.%d\n", IPARGS(filter->ip), IPARGS(filter->mask) ); + + FS_Close( f ); +} + +void SV_InitFilter( void ) +{ + Cmd_AddCommand( "banid", SV_BanID_f, "ban player by ID" ); + Cmd_AddCommand( "listid", SV_ListID_f, "list banned players" ); + Cmd_AddCommand( "removeid", SV_RemoveID_f, "remove player from banned list" ); + Cmd_AddCommand( "writeid", SV_WriteID_f, "write banned.cfg" ); + Cmd_AddCommand( "addip", SV_AddIP_f, "add entry to IP filter" ); + Cmd_AddCommand( "listip", SV_ListIP_f, "list current IP filter" ); + Cmd_AddCommand( "removeip", SV_RemoveIP_f, "remove IP filter" ); + Cmd_AddCommand( "writeip", SV_WriteIP_f, "write listip.cfg" ); +} + +void SV_ShutdownFilter( void ) +{ + ipfilter_t *ipList, *ipNext; + cidfilter_t *cidList, *cidNext; + + // should be called manually because banned.cfg is not executed by engine + //SV_WriteIP_f(); + //SV_WriteID_f(); + + for( ipList = ipfilter; ipList; ipList = ipNext ) + { + ipNext = ipList->next; + Mem_Free( ipList ); + } + + for( cidList = cidfilter; cidList; cidList = cidNext ) + { + cidNext = cidList->next; + Mem_Free( cidList ); + } + + cidfilter = NULL; + ipfilter = NULL; +} diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 90baeeeb..69717b0d 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -117,6 +117,12 @@ convar_t *sv_validate_changelevel; convar_t *sv_sendvelocity; convar_t *sv_hostmap; +convar_t *sv_allow_noinputdevices; +convar_t *sv_allow_touch; +convar_t *sv_allow_mouse; +convar_t *sv_allow_joystick; +convar_t *sv_allow_vr; + void Master_Shutdown( void ); //============================================================================ @@ -765,6 +771,64 @@ void SV_AddToMaster( netadr_t from, sizebuf_t *msg ) NET_SendPacket( NS_SERVER, Q_strlen( s ), s, from ); } +/* +==================== +SV_ProcessUserAgent + +send error message and return false on wrong input devices +==================== +*/ +qboolean SV_ProcessUserAgent( netadr_t from, const char *useragent ) +{ + const char *input_devices_str = Info_ValueForKey( useragent, "d" ); + const char *id = Info_ValueForKey( useragent, "i" ); + + if( !sv_allow_noinputdevices->value && ( !input_devices_str || !input_devices_str[0] ) ) + { + Netchan_OutOfBandPrint( NS_SERVER, from, "print\nThis server does not allow\nconnect without input devices list.\nPlease update your engine.\n" ); + return false; + } + + if( input_devices_str ) + { + int input_devices = Q_atoi( input_devices_str ); + + if( !sv_allow_touch->value && ( input_devices & INPUT_DEVICE_TOUCH ) ) + { + Netchan_OutOfBandPrint( NS_SERVER, from, "errormsg\nThis server does not allow touch\nDisable it (touch_enable 0)\nto play on this server\n" ); + return false; + } + if( !sv_allow_mouse->value && ( input_devices & INPUT_DEVICE_MOUSE) ) + { + Netchan_OutOfBandPrint( NS_SERVER, from, "errormsg\nThis server does not allow mouse\nDisable it(m_ignore 1)\nto play on this server\n" ); + return false; + } + if( !sv_allow_joystick->value && ( input_devices & INPUT_DEVICE_JOYSTICK) ) + { + Netchan_OutOfBandPrint( NS_SERVER, from, "errormsg\nThis server does not allow joystick\nDisable it(joy_enable 0)\nto play on this server\n" ); + return false; + } + if( !sv_allow_vr->value && ( input_devices & INPUT_DEVICE_VR) ) + { + Netchan_OutOfBandPrint( NS_SERVER, from, "errormsg\nThis server does not allow VR\n" ); + return false; + } + } + + if( id ) + { + qboolean banned = SV_CheckID( id ); + + if( banned ) + { + Netchan_OutOfBandPrint( NS_SERVER, from, "errormsg\nYou are banned!\n" ); + return false; + } + } + + return true; +} + //============================================================================ /* @@ -868,6 +932,12 @@ void SV_Init( void ) Cvar_RegisterVariable (&mp_logfile); Cvar_RegisterVariable (&sv_background_freeze); + sv_allow_joystick = Cvar_Get( "sv_allow_joystick", "1", FCVAR_ARCHIVE, "allow connect with joystick enabled" ); + sv_allow_mouse = Cvar_Get( "sv_allow_mouse", "1", FCVAR_ARCHIVE, "allow connect with mouse" ); + sv_allow_touch = Cvar_Get( "sv_allow_touch", "1", FCVAR_ARCHIVE, "allow connect with touch controls" ); + sv_allow_vr = Cvar_Get( "sv_allow_vr", "1", FCVAR_ARCHIVE, "allow connect from vr version" ); + sv_allow_noinputdevices = Cvar_Get( "sv_allow_noinputdevices", "1", FCVAR_ARCHIVE, "allow connect from old versions without useragent" ); + // when we in developer-mode automatically turn cheats on if( host_developer.value ) Cvar_SetValue( "sv_cheats", 1.0f ); @@ -878,6 +948,7 @@ void SV_Init( void ) Cvar_FullSet( "sv_version", versionString, FCVAR_READ_ONLY ); + SV_InitFilter(); SV_ClearGameState (); // delete all temporary *.hl files }