2018-08-23 19:09:40 +00:00
# define NO_SEND_STATS
2018-03-16 13:47:23 +00:00
# ifdef NO_SEND_STATS
void D_DoAnonStats ( )
{
}
2018-03-18 11:26:04 +00:00
void D_ConfirmSendStats ( )
{
}
2018-03-16 13:47:23 +00:00
# else // !NO_SEND_STATS
2018-03-05 04:49:01 +00:00
# if defined(_WIN32)
2018-02-13 22:06:59 +00:00
# define _WIN32_WINNT 0x0501
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
2018-03-04 16:51:07 +00:00
# include <winsock2.h>
2018-02-13 22:06:59 +00:00
extern int sys_ostype ;
2018-03-04 16:51:07 +00:00
# else
2018-03-18 11:26:04 +00:00
# ifdef __APPLE__
# include <CoreFoundation/CoreFoundation.h>
# else // !__APPLE__
# include <SDL.h>
# endif // __APPLE__
2018-03-04 16:51:07 +00:00
# include <sys/socket.h>
# include <sys/types.h>
# include <netinet/in.h>
# include <netdb.h>
2018-03-04 18:24:50 +00:00
# include <unistd.h>
2018-02-13 22:06:59 +00:00
# endif
2018-03-05 04:49:01 +00:00
# include <thread>
2018-02-13 22:06:59 +00:00
# include "c_cvars.h"
# include "x86.h"
# include "version.h"
# include "v_video.h"
2018-07-21 17:14:11 +00:00
# include "gl_load/gl_interface.h"
2018-03-05 04:49:01 +00:00
2018-03-14 08:20:46 +00:00
CVAR ( Int , sys_statsenabled , - 1 , CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOSET )
2018-02-13 22:06:59 +00:00
CVAR ( String , sys_statshost , " gzstats.drdteam.org " , CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOSET )
CVAR ( Int , sys_statsport , 80 , CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOSET )
2018-03-05 04:49:01 +00:00
2018-02-13 22:06:59 +00:00
// Each machine will only send two reports, one when started with hardware rendering and one when started with software rendering.
2018-07-21 11:21:37 +00:00
# define CHECKVERSION 350
# define CHECKVERSIONSTR "350"
2018-03-04 16:51:07 +00:00
CVAR ( Int , sentstats_hwr_done , 0 , CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOSET )
2018-03-05 04:49:01 +00:00
2018-02-13 22:06:59 +00:00
std : : pair < double , bool > gl_getInfo ( ) ;
2018-07-21 17:14:11 +00:00
FString URLencode ( const char * s )
{
const char * unreserved = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~ " ;
FString out ;
for ( size_t i = 0 ; s [ i ] ; i + + )
{
if ( strchr ( unreserved , s [ i ] ) )
{
out + = s [ i ] ;
}
else
{
out . AppendFormat ( " %%%02X " , s [ i ] & 255 ) ;
}
}
return out ;
}
2018-03-04 16:51:07 +00:00
# ifdef _WIN32
bool I_HTTPRequest ( const char * request )
{
if ( sys_statshost . GetHumanString ( ) = = NULL )
return false ; // no host, disable
WSADATA wsaData ;
if ( WSAStartup ( MAKEWORD ( 2 , 2 ) , & wsaData ) ! = 0 )
{
DPrintf ( DMSG_ERROR , " WSAStartup failed. \n " ) ;
return false ;
}
SOCKET Socket = socket ( AF_INET , SOCK_STREAM , IPPROTO_TCP ) ;
struct hostent * host ;
host = gethostbyname ( sys_statshost . GetHumanString ( ) ) ;
2018-03-17 20:38:41 +00:00
if ( host = = nullptr )
{
DPrintf ( DMSG_ERROR , " Error looking up hostname. \n " ) ;
return false ;
}
2018-03-04 16:51:07 +00:00
SOCKADDR_IN SockAddr ;
SockAddr . sin_port = htons ( sys_statsport ) ;
SockAddr . sin_family = AF_INET ;
SockAddr . sin_addr . s_addr = * ( ( uint32_t * ) host - > h_addr ) ;
DPrintf ( DMSG_NOTIFY , " Connecting to host %s \n " , sys_statshost . GetHumanString ( ) ) ;
if ( connect ( Socket , ( SOCKADDR * ) ( & SockAddr ) , sizeof ( SockAddr ) ) ! = 0 )
{
DPrintf ( DMSG_ERROR , " Connection to host %s failed! \n " , sys_statshost . GetHumanString ( ) ) ;
return false ;
}
send ( Socket , request , ( int ) strlen ( request ) , 0 ) ;
char buffer [ 1024 ] ;
int nDataLength ;
while ( ( nDataLength = recv ( Socket , buffer , 1024 , 0 ) ) > 0 )
{
int i = 0 ;
while ( buffer [ i ] > = 32 | | buffer [ i ] = = ' \n ' | | buffer [ i ] = = ' \r ' )
{
i + + ;
}
}
closesocket ( Socket ) ;
WSACleanup ( ) ;
DPrintf ( DMSG_NOTIFY , " Stats send successful. \n " ) ;
return true ;
}
# else
bool I_HTTPRequest ( const char * request )
{
if ( sys_statshost . GetHumanString ( ) = = NULL | | sys_statshost . GetHumanString ( ) [ 0 ] = = 0 )
return false ; // no host, disable
int sockfd , portno , n ;
struct sockaddr_in serv_addr ;
struct hostent * server ;
portno = sys_statsport ;
sockfd = socket ( AF_INET , SOCK_STREAM , 0 ) ;
if ( sockfd < 0 )
{
DPrintf ( DMSG_ERROR , " Error opening TCP socket. \n " ) ;
return false ;
}
server = gethostbyname ( sys_statshost . GetHumanString ( ) ) ;
if ( server = = NULL )
{
DPrintf ( DMSG_ERROR , " Error looking up hostname. \n " ) ;
return false ;
}
bzero ( ( char * ) & serv_addr , sizeof ( serv_addr ) ) ;
serv_addr . sin_family = AF_INET ;
bcopy ( ( char * ) server - > h_addr ,
( char * ) & serv_addr . sin_addr . s_addr ,
server - > h_length ) ;
serv_addr . sin_port = htons ( portno ) ;
DPrintf ( DMSG_NOTIFY , " Connecting to host %s \n " , sys_statshost . GetHumanString ( ) ) ;
if ( connect ( sockfd , ( struct sockaddr * ) & serv_addr , sizeof ( serv_addr ) ) < 0 )
{
DPrintf ( DMSG_ERROR , " Connection to host %s failed! \n " , sys_statshost . GetHumanString ( ) ) ;
return false ;
}
2018-03-14 20:19:33 +00:00
n = write ( sockfd , request , strlen ( request ) ) ;
2018-03-04 16:51:07 +00:00
if ( n < 0 )
{
DPrintf ( DMSG_ERROR , " Error writing to socket. \n " ) ;
close ( sockfd ) ;
return false ;
}
2018-03-14 20:19:33 +00:00
char buffer [ 1024 ] = { } ;
2018-03-04 16:51:07 +00:00
n = read ( sockfd , buffer , 1023 ) ;
close ( sockfd ) ;
DPrintf ( DMSG_NOTIFY , " Stats send successful. \n " ) ;
return true ;
}
# endif
2018-02-13 22:06:59 +00:00
static int GetOSVersion ( )
2018-03-05 04:49:01 +00:00
{
2018-02-13 22:06:59 +00:00
# ifdef _WIN32
if ( sys_ostype = = 1 ) return 1 ;
if ( sizeof ( void * ) = = 4 ) // 32 bit
{
BOOL res ;
2018-03-13 13:34:15 +00:00
if ( IsWow64Process ( GetCurrentProcess ( ) , & res ) & & res )
2018-02-13 22:06:59 +00:00
{
return 6 ;
}
if ( sys_ostype = = 2 ) return 2 ;
else return 4 ;
}
else
{
if ( sys_ostype = = 2 ) return 3 ;
else return 5 ;
}
# elif defined __APPLE__
if ( sizeof ( void * ) = = 4 ) // 32 bit
{
return 7 ;
}
else
{
return 8 ;
}
2018-03-05 04:49:01 +00:00
# else
2018-02-13 22:06:59 +00:00
2018-03-05 04:51:26 +00:00
// fall-through linux stuff here
# ifdef __arm__
return 10 ;
# elif __ppc__
return 9 ;
# else
2018-02-13 22:06:59 +00:00
if ( sizeof ( void * ) = = 4 ) // 32 bit
{
return 11 ;
}
else
{
return 12 ;
2018-03-05 04:49:01 +00:00
}
2018-03-05 04:51:26 +00:00
# endif
2018-02-13 22:06:59 +00:00
2018-03-05 04:49:01 +00:00
# endif
}
2018-02-13 22:06:59 +00:00
2018-03-13 19:04:41 +00:00
# ifdef _WIN32
static int GetCoreInfo ( )
{
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL ;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL ;
DWORD returnLength = 0 ;
int cores = 0 ;
uint32_t byteOffset = 0 ;
auto rc = GetLogicalProcessorInformation ( buffer , & returnLength ) ;
if ( GetLastError ( ) = = ERROR_INSUFFICIENT_BUFFER )
{
buffer = ( PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ) malloc ( returnLength ) ;
if ( ! GetLogicalProcessorInformation ( buffer , & returnLength ) ) return 0 ;
}
else
{
return 0 ;
}
ptr = buffer ;
while ( byteOffset + sizeof ( SYSTEM_LOGICAL_PROCESSOR_INFORMATION ) < = returnLength )
{
if ( ptr - > Relationship = = RelationProcessorCore ) cores + + ;
byteOffset + = sizeof ( SYSTEM_LOGICAL_PROCESSOR_INFORMATION ) ;
ptr + + ;
}
free ( buffer ) ;
return cores < 2 ? 0 : cores < 4 ? 1 : cores < 6 ? 2 : cores < 8 ? 3 : 4 ;
}
# else
2018-02-13 22:06:59 +00:00
static int GetCoreInfo ( )
{
int cores = std : : thread : : hardware_concurrency ( ) ;
if ( CPU . HyperThreading ) cores / = 2 ;
return cores < 2 ? 0 : cores < 4 ? 1 : cores < 6 ? 2 : cores < 8 ? 3 : 4 ;
}
2018-03-13 19:04:41 +00:00
# endif
2018-02-13 22:06:59 +00:00
static int GetRenderInfo ( )
{
2018-04-03 18:14:26 +00:00
auto info = gl_getInfo ( ) ;
2018-07-27 18:46:48 +00:00
if ( info . first < 3.3 ) return 0 ;
if ( ! info . second )
{
2018-07-27 18:55:57 +00:00
if ( ( screen - > hwcaps & ( RFL_SHADER_STORAGE_BUFFER | RFL_BUFFER_STORAGE ) ) = = ( RFL_SHADER_STORAGE_BUFFER | RFL_BUFFER_STORAGE ) ) return 3 ;
2018-07-27 18:46:48 +00:00
return 4 ;
}
2018-04-03 18:14:26 +00:00
return 5 ;
2018-02-13 22:06:59 +00:00
}
2018-07-21 17:14:11 +00:00
static int GetGLVersion ( )
{
auto info = gl_getInfo ( ) ;
return int ( info . first * 10 ) ;
}
2018-02-13 22:06:59 +00:00
static void D_DoHTTPRequest ( const char * request )
{
if ( I_HTTPRequest ( request ) )
{
2018-04-07 21:30:28 +00:00
cvar_forceset ( " sentstats_hwr_done " , CHECKVERSIONSTR ) ;
2018-02-13 22:06:59 +00:00
}
}
void D_DoAnonStats ( )
{
2018-03-14 08:20:46 +00:00
if ( sys_statsenabled ! = 1 )
{
return ;
}
2018-02-13 22:06:59 +00:00
static bool done = false ; // do this only once per session.
if ( done ) return ;
done = true ;
// Do not repeat if already sent.
2018-04-07 21:30:28 +00:00
if ( sentstats_hwr_done > = CHECKVERSION ) return ;
2018-02-13 22:06:59 +00:00
2018-03-05 04:51:26 +00:00
static char requeststring [ 1024 ] ;
2018-07-21 17:14:11 +00:00
mysnprintf ( requeststring , sizeof requeststring , " GET /stats_35.py?render=%i&cores=%i&os=%i&glversion=%i&vendor=%s&model=%s HTTP/1.1 \n Host: %s \n Connection: close \n User-Agent: %s %s \n \n " ,
GetRenderInfo ( ) , GetCoreInfo ( ) , GetOSVersion ( ) , GetGLVersion ( ) , URLencode ( gl . vendorstring ) . GetChars ( ) , URLencode ( gl . modelstring ) . GetChars ( ) , sys_statshost . GetHumanString ( ) , GAMENAME , VERSIONSTR ) ;
2018-02-13 22:06:59 +00:00
DPrintf ( DMSG_NOTIFY , " Sending %s " , requeststring ) ;
std : : thread t1 ( D_DoHTTPRequest , requeststring ) ;
t1 . detach ( ) ;
}
2018-03-16 13:47:23 +00:00
2018-03-24 22:16:21 +00:00
2018-03-18 11:26:04 +00:00
void D_ConfirmSendStats ( )
{
if ( sys_statsenabled > = 0 )
{
return ;
}
// TODO: texts
2018-03-24 22:16:21 +00:00
static const char * const MESSAGE_TEXT = " In order to decide where to focus development, the GZDoom team would like to know a little bit about the hardware it is run on. \n " \
2018-03-25 04:35:57 +00:00
" For this we would like to ask you if we may send three bits of information to gzstats.drdteam.org. \n " \
2018-03-24 22:16:21 +00:00
" The three items we would like to know about are: \n " \
" - Operating system \n " \
" - Number of processor cores \n " \
2018-07-21 17:14:11 +00:00
" - OpenGL version and your graphics card's name \n \n " \
" All information sent will be anonymously. We will NOT be sending this information to any third party. \n " \
2018-03-25 04:35:57 +00:00
" It will merely be used for decision-making about GZDoom's future development. \n " \
2018-07-21 17:14:11 +00:00
" Data will only be sent once per system. \n " \
" If you are getting this notice more than once, please let us know on the forums. Thanks! \n \n " \
2018-03-24 22:16:21 +00:00
" May we send this data? If you click 'no', nothing will be sent and you will not be asked again. " ;
static const char * const TITLE_TEXT = " GZDoom needs your help! " ;
2018-03-18 11:26:04 +00:00
UCVarValue enabled = { 0 } ;
# ifdef _WIN32
extern HWND Window ;
enabled . Int = MessageBox ( Window , MESSAGE_TEXT , TITLE_TEXT , MB_ICONQUESTION | MB_YESNO ) = = IDYES ;
# elif defined __APPLE__
const CFStringRef messageString = CFStringCreateWithCStringNoCopy ( kCFAllocatorDefault , MESSAGE_TEXT , kCFStringEncodingASCII , kCFAllocatorNull ) ;
const CFStringRef titleString = CFStringCreateWithCStringNoCopy ( kCFAllocatorDefault , TITLE_TEXT , kCFStringEncodingASCII , kCFAllocatorNull ) ;
if ( messageString ! = nullptr & & titleString ! = nullptr )
{
CFOptionFlags response ;
const SInt32 result = CFUserNotificationDisplayAlert ( 0 , kCFUserNotificationNoteAlertLevel , nullptr , nullptr , nullptr ,
titleString , messageString , CFSTR ( " Yes " ) , CFSTR ( " No " ) , nullptr , & response ) ;
enabled . Int = result = = 0 & & ( response & 3 ) = = kCFUserNotificationDefaultResponse ;
CFRelease ( titleString ) ;
CFRelease ( messageString ) ;
}
# else // !__APPLE__
const SDL_MessageBoxButtonData buttons [ ] =
{
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT , 0 , " Yes " } ,
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT , 1 , " No " } ,
} ;
const SDL_MessageBoxData messageboxdata =
{
SDL_MESSAGEBOX_INFORMATION ,
nullptr ,
TITLE_TEXT ,
MESSAGE_TEXT ,
SDL_arraysize ( buttons ) ,
buttons ,
nullptr
} ;
int buttonid ;
enabled . Int = SDL_ShowMessageBox ( & messageboxdata , & buttonid ) = = 0 & & buttonid = = 0 ;
# endif // _WIN32
sys_statsenabled . ForceSet ( enabled , CVAR_Int ) ;
}
2018-03-16 13:47:23 +00:00
# endif // NO_SEND_STATS