diff --git a/README b/README index d776efcc..65b86670 100644 --- a/README +++ b/README @@ -144,7 +144,8 @@ New cvars com_standalone - Run in standalone mode com_maxfpsUnfocused - Maximum frames per second when unfocused com_maxfpsMinimized - Maximum frames per second when minimized - + com_busyWait - Will use a busy loop to wait for rendering + next frame when set to non-zero value. in_joystickNo - select which joystick to use in_keyboardDebug - print keyboard debug info @@ -235,6 +236,16 @@ Using Demo Data Files data, nor is it something that we like to spend much time maintaining or supporting. +Help! Ioquake3 won't give me an fps of X anymore when setting com_maxfps! + Ioquake3 now uses the select() system call to wait for the rendering of the + next frame when com_maxfps was hit. This will improve your CPU load + considerably in these cases. However, not all systems may support a + granularity for its timing functions that is required to perform this waiting + correctly. For instance, ioquake3 tells select() to wait 2 milliseconds, but + really it can only wait for a multiple of 5ms, i.e. 5, 10, 15, 20... ms. + In this case you can always revert back to the old behaviour by setting the + cvar com_busyWait to 1. + QuakeLive mouse acceleration (patch and this text written by TTimo from id) I've been using an experimental mouse acceleration code for a while, and decided to make it available to everyone. Don't be too worried if you don't diff --git a/code/null/mac_net.c b/code/null/mac_net.c index 8f9c22f5..d83d6d20 100644 --- a/code/null/mac_net.c +++ b/code/null/mac_net.c @@ -52,14 +52,3 @@ Sys_SendPacket */ void Sys_SendPacket( int length, void *data, netadr_t to ) { } - -/* -================== -Sys_GetPacket - -Never called by the game logic, just the system event queing -================== -*/ -qboolean Sys_GetPacket ( netadr_t *net_from, msg_t *net_message ) { - return false; -} diff --git a/code/null/null_net.c b/code/null/null_net.c index 04d5e7a3..081f36ca 100644 --- a/code/null/null_net.c +++ b/code/null/null_net.c @@ -51,14 +51,3 @@ Sys_SendPacket */ void Sys_SendPacket( int length, void *data, netadr_t to ) { } - -/* -================== -Sys_GetPacket - -Never called by the game logic, just the system event queing -================== -*/ -qboolean Sys_GetPacket ( netadr_t *net_from, msg_t *net_message ) { - return false; -} diff --git a/code/qcommon/common.c b/code/qcommon/common.c index 8ab7baff..dff23f99 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -59,7 +59,6 @@ cvar_t *com_developer; cvar_t *com_dedicated; cvar_t *com_timescale; cvar_t *com_fixedtime; -cvar_t *com_dropsim; // 0.0 to 1.0, simulated packet drops cvar_t *com_journal; cvar_t *com_maxfps; cvar_t *com_altivec; @@ -84,6 +83,7 @@ cvar_t *com_minimized; cvar_t *com_maxfpsMinimized; cvar_t *com_abnormalExit; cvar_t *com_standalone; +cvar_t *com_busyWait; // com_speeds times int time_game; @@ -91,7 +91,6 @@ int time_frontend; // renderer frontend time int time_backend; // renderer backend time int com_frameTime; -int com_frameMsec; int com_frameNumber; qboolean com_errorEntered = qfalse; @@ -1945,7 +1944,6 @@ EVENT LOOP static sysEvent_t eventQueue[ MAX_QUEUED_EVENTS ]; static int eventHead = 0; static int eventTail = 0; -static byte sys_packetReceived[ MAX_MSGLEN ]; /* ================ @@ -1998,8 +1996,6 @@ sysEvent_t Com_GetSystemEvent( void ) { sysEvent_t ev; char *s; - msg_t netmsg; - netadr_t adr; // return if we have data if ( eventHead > eventTail ) @@ -2021,21 +2017,6 @@ sysEvent_t Com_GetSystemEvent( void ) Com_QueueEvent( 0, SE_CONSOLE, 0, 0, len, b ); } - // check for network packets - MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) ); - if ( Sys_GetPacket ( &adr, &netmsg ) ) - { - netadr_t *buf; - int len; - - // copy out to a seperate buffer for qeueing - len = sizeof( netadr_t ) + netmsg.cursize; - buf = Z_Malloc( len ); - *buf = adr; - memcpy( buf+1, netmsg.data, netmsg.cursize ); - Com_QueueEvent( 0, SE_PACKET, 0, 0, len, buf ); - } - // return if we have data if ( eventHead > eventTail ) { @@ -2195,7 +2176,6 @@ int Com_EventLoop( void ) { MSG_Init( &buf, bufData, sizeof( bufData ) ); while ( 1 ) { - NET_FlushPacketQueue(); ev = Com_GetEvent(); // if no more events are available @@ -2216,57 +2196,26 @@ int Com_EventLoop( void ) { } - switch ( ev.evType ) { - default: - Com_Error( ERR_FATAL, "Com_EventLoop: bad event type %i", ev.evType ); + switch(ev.evType) + { + case SE_KEY: + CL_KeyEvent( ev.evValue, ev.evValue2, ev.evTime ); break; - case SE_NONE: - break; - case SE_KEY: - CL_KeyEvent( ev.evValue, ev.evValue2, ev.evTime ); + case SE_CHAR: + CL_CharEvent( ev.evValue ); break; - case SE_CHAR: - CL_CharEvent( ev.evValue ); + case SE_MOUSE: + CL_MouseEvent( ev.evValue, ev.evValue2, ev.evTime ); break; - case SE_MOUSE: - CL_MouseEvent( ev.evValue, ev.evValue2, ev.evTime ); + case SE_JOYSTICK_AXIS: + CL_JoystickEvent( ev.evValue, ev.evValue2, ev.evTime ); break; - case SE_JOYSTICK_AXIS: - CL_JoystickEvent( ev.evValue, ev.evValue2, ev.evTime ); + case SE_CONSOLE: + Cbuf_AddText( (char *)ev.evPtr ); + Cbuf_AddText( "\n" ); break; - case SE_CONSOLE: - Cbuf_AddText( (char *)ev.evPtr ); - Cbuf_AddText( "\n" ); - break; - case SE_PACKET: - // this cvar allows simulation of connections that - // drop a lot of packets. Note that loopback connections - // don't go through here at all. - if ( com_dropsim->value > 0 ) { - static int seed; - - if ( Q_random( &seed ) < com_dropsim->value ) { - break; // drop this packet - } - } - - evFrom = *(netadr_t *)ev.evPtr; - buf.cursize = ev.evPtrLength - sizeof( evFrom ); - - // we must copy the contents of the message out, because - // the event buffers are only large enough to hold the - // exact payload, but channel messages need to be large - // enough to hold fragment reassembly - if ( (unsigned)buf.cursize > buf.maxsize ) { - Com_Printf("Com_EventLoop: oversize packet\n"); - continue; - } - Com_Memcpy( buf.data, (byte *)((netadr_t *)ev.evPtr + 1), buf.cursize ); - if ( com_sv_running->integer ) { - Com_RunAndTimeServerPacket( &evFrom, &buf ); - } else { - CL_PacketEvent( evFrom, &buf ); - } + default: + Com_Error( ERR_FATAL, "Com_EventLoop: bad event type %i", ev.evType ); break; } @@ -2639,7 +2588,6 @@ void Com_Init( char *commandLine ) { // Clear queues Com_Memset( &eventQueue[ 0 ], 0, MAX_QUEUED_EVENTS * sizeof( sysEvent_t ) ); - Com_Memset( &sys_packetReceived[ 0 ], 0, MAX_MSGLEN * sizeof( byte ) ); // initialize the weak pseudo-random number generator for use later. Com_InitRand(); @@ -2720,7 +2668,6 @@ void Com_Init( char *commandLine ) { com_timescale = Cvar_Get ("timescale", "1", CVAR_CHEAT | CVAR_SYSTEMINFO ); com_fixedtime = Cvar_Get ("fixedtime", "0", CVAR_CHEAT); com_showtrace = Cvar_Get ("com_showtrace", "0", CVAR_CHEAT); - com_dropsim = Cvar_Get ("com_dropsim", "0", CVAR_CHEAT); com_speeds = Cvar_Get ("com_speeds", "0", 0); com_timedemo = Cvar_Get ("timedemo", "0", CVAR_CHEAT); com_cameraMode = Cvar_Get ("com_cameraMode", "0", CVAR_CHEAT); @@ -2740,6 +2687,7 @@ void Com_Init( char *commandLine ) { com_maxfpsMinimized = Cvar_Get( "com_maxfpsMinimized", "0", CVAR_ARCHIVE ); com_abnormalExit = Cvar_Get( "com_abnormalExit", "0", CVAR_ROM ); com_standalone = Cvar_Get( "com_standalone", "0", CVAR_INIT ); + com_busyWait = Cvar_Get("com_busyWait", "0", CVAR_ARCHIVE); com_introPlayed = Cvar_Get( "com_introplayed", "0", CVAR_ARCHIVE); @@ -2947,19 +2895,16 @@ Com_Frame void Com_Frame( void ) { int msec, minMsec; - static int lastTime; - int key; + int timeVal; + static int lastTime = 0; int timeBeforeFirstEvents; - int timeBeforeServer; - int timeBeforeEvents; - int timeBeforeClient; - int timeAfter; + int timeBeforeServer; + int timeBeforeEvents; + int timeBeforeClient; + int timeAfter; - - - if ( setjmp (abortframe) ) { return; // an ERR_DROP was thrown } @@ -2970,10 +2915,6 @@ void Com_Frame( void ) { timeBeforeClient = 0; timeAfter = 0; - - // old net chan encryption key - key = 0x87243987; - // write config file if anything changed Com_WriteConfiguration(); @@ -2984,37 +2925,62 @@ void Com_Frame( void ) { timeBeforeFirstEvents = Sys_Milliseconds (); } - // we may want to spin here if things are going too fast - if ( !com_dedicated->integer && !com_timedemo->integer ) { - if( com_minimized->integer && com_maxfpsMinimized->integer > 0 ) { - minMsec = 1000 / com_maxfpsMinimized->integer; - } else if( com_unfocused->integer && com_maxfpsUnfocused->integer > 0 ) { - minMsec = 1000 / com_maxfpsUnfocused->integer; - } else if( com_maxfps->integer > 0 ) { - minMsec = 1000 / com_maxfps->integer; - } else { - minMsec = 1; + // Figure out how much time we have + if(!com_timedemo->integer) + { + if(com_dedicated->integer) + minMsec = SV_FrameMsec(); + else + { + if(com_minimized->integer && com_maxfpsMinimized->integer > 0) + minMsec = 1000 / com_maxfpsMinimized->integer; + else if(com_unfocused->integer && com_maxfpsUnfocused->integer > 0) + minMsec = 1000 / com_maxfpsUnfocused->integer; + else if(com_maxfps->integer > 0) + minMsec = 1000 / com_maxfps->integer; + else + minMsec = 1; + + timeVal = com_frameTime - lastTime; + if(timeVal > minMsec) + { + // Adjust minMsec if previous frame took too long to render so + // that framerate is stable at the requested value. + timeVal -= minMsec; + + if(timeVal > minMsec) + minMsec = 0; + else + minMsec -= timeVal; + } + } - } else { - minMsec = 1; } + else + minMsec = 1; - msec = minMsec; - do { - int timeRemaining = minMsec - msec; + timeVal = 0; + do + { + if(com_busyWait->integer) + NET_Sleep(0); + else + NET_Sleep(timeVal); - // The existing Sys_Sleep implementations aren't really - // precise enough to be of use beyond 100fps - // FIXME: implement a more precise sleep (RDTSC or something) - if( timeRemaining >= 10 ) - Sys_Sleep( timeRemaining ); + msec = Sys_Milliseconds() - com_frameTime; + + if(msec >= minMsec) + timeVal = 0; + else + timeVal = minMsec - msec; + + } while(timeVal > 0); + + lastTime = com_frameTime; + com_frameTime = Com_EventLoop(); + + msec = com_frameTime - lastTime; - com_frameTime = Com_EventLoop(); - if ( lastTime > com_frameTime ) { - lastTime = com_frameTime; // possible on first frame - } - msec = com_frameTime - lastTime; - } while ( msec < minMsec ); Cbuf_Execute (); if (com_altivec->modified) @@ -3023,11 +2989,8 @@ void Com_Frame( void ) { com_altivec->modified = qfalse; } - lastTime = com_frameTime; - // mess with msec if needed - com_frameMsec = msec; - msec = Com_ModifyMsec( msec ); + msec = Com_ModifyMsec(msec); // // server side @@ -3087,6 +3050,9 @@ void Com_Frame( void ) { } #endif + + NET_FlushPacketQueue(); + // // report timing information // @@ -3120,9 +3086,6 @@ void Com_Frame( void ) { c_pointcontents = 0; } - // old net chan encryption key - key = lastTime * 0x87243987; - com_frameNumber++; } diff --git a/code/qcommon/net_ip.c b/code/qcommon/net_ip.c index 6874d2bf..ba40f75b 100644 --- a/code/qcommon/net_ip.c +++ b/code/qcommon/net_ip.c @@ -107,6 +107,8 @@ static cvar_t *net_port6; static cvar_t *net_mcast6addr; static cvar_t *net_mcast6iface; +static cvar_t *net_dropsim; + static struct sockaddr socksRelayAddr; static SOCKET ip_socket = INVALID_SOCKET; @@ -201,7 +203,7 @@ char *NET_ErrorString( void ) { default: return "NO ERROR"; } #else - return strerror (errno); + return strerror(socketError); #endif } @@ -524,16 +526,17 @@ qboolean NET_IsLocalAddress( netadr_t adr ) { /* ================== -Sys_GetPacket +NET_GetPacket -Never called by the game logic, just the system event queing +Receive one packet ================== */ #ifdef _DEBUG int recvfromCount; #endif -qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ) { +qboolean NET_GetPacket(netadr_t *net_from, msg_t *net_message, fd_set *fdr) +{ int ret; struct sockaddr_storage from; socklen_t fromlen; @@ -543,7 +546,7 @@ qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ) { recvfromCount++; // performance check #endif - if(ip_socket != INVALID_SOCKET) + if(ip_socket != INVALID_SOCKET && FD_ISSET(ip_socket, fdr)) { fromlen = sizeof(from); ret = recvfrom( ip_socket, (void *)net_message->data, net_message->maxsize, 0, (struct sockaddr *) &from, &fromlen ); @@ -577,7 +580,7 @@ qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ) { net_message->readcount = 0; } - if( ret == net_message->maxsize ) { + if( ret >= net_message->maxsize ) { Com_Printf( "Oversize packet from %s\n", NET_AdrToString (*net_from) ); return qfalse; } @@ -587,7 +590,7 @@ qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ) { } } - if(ip6_socket != INVALID_SOCKET) + if(ip6_socket != INVALID_SOCKET && FD_ISSET(ip6_socket, fdr)) { fromlen = sizeof(from); ret = recvfrom(ip6_socket, (void *)net_message->data, net_message->maxsize, 0, (struct sockaddr *) &from, &fromlen); @@ -604,7 +607,7 @@ qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ) { SockadrToNetadr((struct sockaddr *) &from, net_from); net_message->readcount = 0; - if(ret == net_message->maxsize) + if(ret >= net_message->maxsize) { Com_Printf( "Oversize packet from %s\n", NET_AdrToString (*net_from) ); return qfalse; @@ -615,7 +618,7 @@ qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ) { } } - if(multicast6_socket != INVALID_SOCKET && multicast6_socket != ip6_socket) + if(multicast6_socket != INVALID_SOCKET && multicast6_socket != ip6_socket && FD_ISSET(multicast6_socket, fdr)) { fromlen = sizeof(from); ret = recvfrom(multicast6_socket, (void *)net_message->data, net_message->maxsize, 0, (struct sockaddr *) &from, &fromlen); @@ -632,7 +635,7 @@ qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ) { SockadrToNetadr((struct sockaddr *) &from, net_from); net_message->readcount = 0; - if(ret == net_message->maxsize) + if(ret >= net_message->maxsize) { Com_Printf( "Oversize packet from %s\n", NET_AdrToString (*net_from) ); return qfalse; @@ -1499,6 +1502,8 @@ static qboolean NET_GetCvars( void ) { modified += net_socksPassword->modified; net_socksPassword->modified = qfalse; + net_dropsim = Cvar_Get("net_dropsim", "", CVAR_TEMP); + return modified ? qtrue : qfalse; } @@ -1627,6 +1632,42 @@ void NET_Shutdown( void ) { #endif } +/* +==================== +NET_Event + +Called from NET_Sleep which uses select() to determine which sockets have seen action. +==================== +*/ + +void NET_Event(fd_set *fdr) +{ + byte bufData[MAX_MSGLEN + 1]; + netadr_t from; + msg_t netmsg; + + while(1) + { + MSG_Init(&netmsg, bufData, sizeof(bufData)); + + if(NET_GetPacket(&from, &netmsg, fdr)) + { + if(net_dropsim->value > 0.0f && net_dropsim->value <= 100.0f) + { + // com_dropsim->value percent of incoming packets get dropped. + if(rand() < (int) (((double) RAND_MAX) / 100.0 * (double) net_dropsim->value)) + continue; // drop this packet + } + + if(com_sv_running->integer) + Com_RunAndTimeServerPacket(&from, &netmsg); + else + CL_PacketEvent(from, &netmsg); + } + else + break; + } +} /* ==================== @@ -1635,31 +1676,23 @@ NET_Sleep Sleeps msec or until something happens on the network ==================== */ -void NET_Sleep( int msec ) { +void NET_Sleep(int msec) +{ struct timeval timeout; - fd_set fdset; - int highestfd = -1; + fd_set fdr; + int highestfd = -1, retval; - if (!com_dedicated->integer) - return; // we're not a server, just run full speed - - if (ip_socket == INVALID_SOCKET && ip6_socket == INVALID_SOCKET) - return; - - if (msec < 0 ) - return; - - FD_ZERO(&fdset); + FD_ZERO(&fdr); if(ip_socket != INVALID_SOCKET) { - FD_SET(ip_socket, &fdset); + FD_SET(ip_socket, &fdr); highestfd = ip_socket; } if(ip6_socket != INVALID_SOCKET) { - FD_SET(ip6_socket, &fdset); + FD_SET(ip6_socket, &fdr); if(ip6_socket > highestfd) highestfd = ip6_socket; @@ -1667,9 +1700,32 @@ void NET_Sleep( int msec ) { timeout.tv_sec = msec/1000; timeout.tv_usec = (msec%1000)*1000; - select(highestfd + 1, &fdset, NULL, NULL, &timeout); -} + +#ifdef _WIN32 + if(highestfd < 0) + { + // windows ain't happy when select is called without valid FDs + + if(msec > 0) + msec--; + + SleepEx(msec, 0); + return; + } + #define TVW32_BIAS 999 + // windows adds a whole millisecond of latency, otherwise granularity seems to be fine. + if(timeout.tv_usec > TVW32_BIAS) + timeout.tv_usec -= TVW32_BIAS; +#endif + + retval = select(highestfd + 1, &fdr, NULL, NULL, &timeout); + + if(retval < 0) + Com_Printf("Warning: select() syscall failed: %s\n", NET_ErrorString()); + else if(retval > 0) + NET_Event(&fdr); +} /* ==================== diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index c6a61e29..f293b265 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -768,13 +768,12 @@ typedef enum typedef enum { // SE_NONE must be zero - SE_NONE = 0, // evTime is still valid - SE_KEY, // evValue is a key code, evValue2 is the down flag - SE_CHAR, // evValue is an ascii char - SE_MOUSE, // evValue and evValue2 are reletive signed x / y moves + SE_NONE = 0, // evTime is still valid + SE_KEY, // evValue is a key code, evValue2 is the down flag + SE_CHAR, // evValue is an ascii char + SE_MOUSE, // evValue and evValue2 are reletive signed x / y moves SE_JOYSTICK_AXIS, // evValue is an axis number and evValue2 is the current state (-127 to 127) - SE_CONSOLE, // evPtr is a char* - SE_PACKET // evPtr is a netadr_t followed by data bytes to evPtrLength + SE_CONSOLE // evPtr is a char* } sysEventType_t; typedef struct { @@ -807,6 +806,7 @@ int Com_Filter(char *filter, char *name, int casesensitive); int Com_FilterPath(char *filter, char *name, int casesensitive); int Com_RealTime(qtime_t *qtime); qboolean Com_SafeMode( void ); +void Com_RunAndTimeServerPacket(netadr_t *evFrom, msg_t *buf); void Com_StartupVariable( const char *match ); // checks for and removes command line "+set var arg" constructs @@ -845,7 +845,6 @@ extern int time_frontend; extern int time_backend; // renderer backend time extern int com_frameTime; -extern int com_frameMsec; extern qboolean com_errorEntered; @@ -997,6 +996,7 @@ void SV_Init( void ); void SV_Shutdown( char *finalmsg ); void SV_Frame( int msec ); void SV_PacketEvent( netadr_t from, msg_t *msg ); +int SV_FrameMsec(void); qboolean SV_GameCommand( void ); @@ -1068,7 +1068,6 @@ cpuFeatures_t Sys_GetProcessorFeatures( void ); void Sys_SetErrorText( const char *text ); void Sys_SendPacket( int length, const void *data, netadr_t to ); -qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ); qboolean Sys_StringToAdr( const char *s, netadr_t *a, netadrtype_t family ); //Does NOT parse port numbers, only base addresses. diff --git a/code/server/sv_main.c b/code/server/sv_main.c index c354bf9f..42ae6b4b 100644 --- a/code/server/sv_main.c +++ b/code/server/sv_main.c @@ -30,17 +30,17 @@ serverStatic_t svs; // persistant server info server_t sv; // local server vm_t *gvm = NULL; // game virtual machine -cvar_t *sv_fps; // time rate for running non-clients +cvar_t *sv_fps = NULL; // time rate for running non-clients cvar_t *sv_timeout; // seconds without any message cvar_t *sv_zombietime; // seconds to sink messages after disconnect cvar_t *sv_rconPassword; // password for remote server commands -cvar_t *sv_privatePassword; // password for the privateClient slots +cvar_t *sv_privatePassword; // password for the privateClient slots cvar_t *sv_allowDownload; cvar_t *sv_maxclients; cvar_t *sv_privateClients; // number of clients reserved for password cvar_t *sv_hostname; -cvar_t *sv_master[MAX_MASTER_SERVERS]; // master server ip address +cvar_t *sv_master[MAX_MASTER_SERVERS]; // master server ip address cvar_t *sv_reconnectlimit; // minimum seconds between connect messages cvar_t *sv_showloss; // report when usercmds are lost cvar_t *sv_padPackets; // add nop bytes to messages @@ -1000,6 +1000,29 @@ static qboolean SV_CheckPaused( void ) { return qtrue; } +/* +================== +SV_FrameMsec +Return time in millseconds until processing of the next server frame. +================== +*/ +int SV_FrameMsec() +{ + if(sv_fps) + { + int frameMsec; + + frameMsec = 1000.0f / sv_fps->value; + + if(frameMsec < sv.timeResidual) + return 0; + else + return frameMsec - sv.timeResidual; + } + else + return 1; +} + /* ================== SV_Frame @@ -1052,13 +1075,6 @@ void SV_Frame( int msec ) { if (!com_dedicated->integer) SV_BotFrame (sv.time + sv.timeResidual); - if ( com_dedicated->integer && sv.timeResidual < frameMsec ) { - // NET_Sleep will give the OS time slices until either get a packet - // or time enough for a server frame has gone by - NET_Sleep(frameMsec - sv.timeResidual); - return; - } - // if time is about to hit the 32nd bit, kick all clients // and clear sv.time, rather // than checking for negative time wraparound everywhere.