Index: code/server/sv_client.c
===================================================================
--- code/server/sv_client.c	(revision 958)
+++ code/server/sv_client.c	(working copy)
@@ -442,6 +442,11 @@
 	newcl->lastPacketTime = svs.time;
 	newcl->lastConnectTime = svs.time;
 	
+	// set the default values for user command rate limiting.
+	newcl->ucmd_incoming = 125 * 4;
+	newcl->ucmd_currate = 1;
+	newcl->laststatframenum = sv.snapshotCounter;
+
 	// when we receive the first packet from the client, we will
 	// notice that it is from a different serverid and that the
 	// gamestate message was not just sent, forcing a retransmit
@@ -1360,6 +1365,7 @@
 Also called by bot code
 ==================
 */
+
 void SV_ClientThink (client_t *cl, usercmd_t *cmd) {
 	cl->lastUsercmd = *cmd;
 
@@ -1372,6 +1378,36 @@
 
 /*
 ==================
+SV_BufUserMove
+
+Execute maxcount commands from buffer.
+==================
+*/
+
+void SV_BufUserMove(client_t *cl, int maxcount)
+{
+	int index;
+
+	for(index = 0; index < maxcount && index < cl->ucmd_bufcount; index++)
+	{
+		// execute pending commands in the command buffer
+		SV_ClientThink(cl, &cl->ucmdbuf[index]);
+		cl->ucmd_processed++;
+	}
+	
+	if(index && index < cl->ucmd_bufcount)
+	{
+		int newcount = cl->ucmd_bufcount - index;
+		
+		memmove(cl->ucmdbuf, &cl->ucmdbuf[index], sizeof(*cl->ucmdbuf) * newcount);
+		cl->ucmd_bufcount = newcount;
+	}
+	else
+		cl->ucmd_bufcount = 0;
+}
+
+/*
+==================
 SV_UserMove
 
 The message usually contains all the movement commands 
@@ -1457,10 +1493,109 @@
 		return;
 	}
 
+	if(sv_antiWarp->integer)
+	{
+		// determine how many commands may be processed this snapshot
+		if(cl->lastframenum != sv.frameNumber)
+		{
+			float currate;
+
+			if(sv.frameNumber - cl->laststatframenum > USERCMD_RATECHECK
+			   || sv.frameNumber - cl->laststatframenum < 0)
+			{
+				// Get the average usercmd rate for last frame.
+				if(sv_antiWarpBias->integer)
+					cl->ucmd_lastinrate = cl->ucmd_inrate;
+				cl->ucmd_inrate = (float) cl->ucmd_incoming / (float) USERCMD_RATECHECK;
+
+				if(sv_antiWarpBias->integer && cl->ucmd_statcounter > USERCMD_BIASRATE)
+				{
+					// only use bias if inrate is stable.
+					if(Q_fabs(cl->ucmd_inrate - cl->ucmd_lastinrate) < 3)
+					{
+						int mydelta = cl->ucmd_delta;
+
+						// cap delta to current inrate.
+						if(mydelta < 0)
+						{
+							mydelta /= USERCMD_BIASRATE;
+
+							if(!mydelta)
+								mydelta = -1;
+							else if(mydelta < -(1 + cl->ucmd_inrate))
+								mydelta = -(1 + cl->ucmd_inrate);
+						}
+						else if(mydelta > 0)
+						{
+							mydelta /= USERCMD_BIASRATE / 2;
+							
+							if(!mydelta)
+								mydelta = 1;
+							else if(mydelta > 1 + cl->ucmd_inrate)
+								mydelta = 1 + cl->ucmd_inrate;
+						}
+						
+						cl->ucmd_bias += mydelta;
+
+						if(cl->ucmd_bias < 0 && cl->ucmd_bias < -USERCMD_BUFSIZE)
+							cl->ucmd_bias = -USERCMD_BUFSIZE;
+						else if(cl->ucmd_bias > 0 && cl->ucmd_bias > USERCMD_BUFSIZE)
+							cl->ucmd_bias = USERCMD_BUFSIZE;
+					}
+					else
+						cl->ucmd_bias = 0;
+				
+					cl->ucmd_statcounter = 0;
+					cl->ucmd_delta = USERCMD_BUFSIZE;
+				}
+			
+				cl->ucmd_statcounter++;
+				cl->ucmd_incoming = 0;
+				cl->laststatframenum = sv.frameNumber;
+			}
+		
+			// determine the current rate by taking the buffer status into account.
+			if(sv_antiWarpBias->integer)
+				currate = cl->ucmd_inrate + (float) (cl->ucmd_bufcount + cl->ucmd_bias) / (float) USERCMD_RATECHECK;
+			else
+				currate = cl->ucmd_inrate + (float) cl->ucmd_bufcount / (float) USERCMD_RATECHECK;
+
+			if((float) (sv.frameNumber - cl->laststatframenum) / USERCMD_RATECHECK >
+			   1.0f - (float) (currate - (int) currate))
+			{
+				// use a slightly higher rate for a few frames only.
+				currate++;
+			}
+
+			// ucmd_currate can never be null.
+			if(currate <= 0)
+				currate = 1;
+
+			// Set curucmdrate to the value of the fraction piece and consider the decimal places, too.
+			cl->ucmd_currate = currate;
+
+			cl->lastframenum = sv.frameNumber;
+			cl->ucmd_processed = 0;
+
+/*
+			// For debugging..
+			if(sv_antiWarpBias->integer)
+				Com_Printf("%f, %f, %d, %d, %d, %d\n", cl->ucmd_inrate, currate, cl->ucmd_currate, cl->ucmd_bias, cl->ucmd_bufcount, cl->ucmd_delta);
+			else
+				Com_Printf("%f, %f, %d, %d, %d\n", cl->ucmd_inrate, currate, cl->ucmd_currate, cl->ucmd_bias, cl->ucmd_bufcount);
+*/
+		}
+
+		if(cl->ucmd_processed < cl->ucmd_currate)
+			SV_BufUserMove(cl, cl->ucmd_currate - cl->ucmd_processed);
+
+	}
+
 	// usually, the first couple commands will be duplicates
 	// of ones we have previously received, but the servertimes
 	// in the commands will cause them to be immediately discarded
-	for ( i =  0 ; i < cmdCount ; i++ ) {
+	for(i = 0; i < cmdCount; i++)
+	{
 		// if this is a cmd from before a map_restart ignore it
 		if ( cmds[i].serverTime > cmds[cmdCount-1].serverTime ) {
 			continue;
@@ -1471,14 +1606,120 @@
 		//}
 		// don't execute if this is an old cmd which is already executed
 		// these old cmds are included when cl_packetdup > 0
-		if ( cmds[i].serverTime <= cl->lastUsercmd.serverTime ) {
+		if (cmds[i].serverTime <= cl->ucmd_newest) {
 			continue;
 		}
-		SV_ClientThink (cl, &cmds[ i ]);
+
+		cl->ucmd_newest = cmds[i].serverTime;
+		
+		if(sv_antiWarp->integer)
+		{
+			cl->ucmd_incoming++;
+		
+			if(cl->ucmd_processed >= cl->ucmd_currate)
+			{
+				// Don't process the next commands and add them to the buffer.
+
+				if(cl->ucmd_bufcount >= USERCMD_BUFSIZE)
+					SV_BufUserMove(cl, 1);
+			
+				memcpy(&cl->ucmdbuf[cl->ucmd_bufcount], &cmds[i], sizeof(*cl->ucmdbuf));
+				cl->ucmd_bufcount++;
+			}
+			else
+			{
+				SV_ClientThink(cl, &cmds[i]);
+				cl->ucmd_processed++;
+			}
+		}
+		else
+			SV_ClientThink(cl, &cmds[i]);
 	}
 }
 
+/*
+==================
+SV_CheckClientThink
 
+Check whether every client has been thought about already in this frame and let the game code think
+about the client accordingly to the last command rate in effect if that is not the case.
+==================
+*/
+
+void SV_CheckClientThink(void)
+{
+	client_t *cl;
+	int index, delta;
+	int incr;
+	
+	for(index = 0; index < sv_maxclients->integer; index++)
+	{
+		cl = &svs.clients[index];
+		
+		if(cl->state == CS_ACTIVE)
+		{
+			if(cl->lastframenum != sv.frameNumber)
+				cl->ucmd_processed = 0;
+
+			delta = cl->ucmd_currate - cl->ucmd_processed;
+
+			if(delta > 0)
+			{
+				// This client has not received enough attention yet. Think more about it.
+				SV_BufUserMove(cl, cl->ucmd_currate - cl->ucmd_processed);
+			}
+
+			delta = cl->ucmd_currate - cl->ucmd_processed;
+				
+			if(delta > 0)
+			{
+				if(sv.frameNumber - cl->lastframenum > USERCMD_RATECHECK)
+				{
+					// If we get here it means no user commands have come in
+					// in the last few frames. Let the client hang now.
+					continue;
+				}
+
+				if(sv_antiWarpBias->integer)
+				{
+					// Even the cmd buffer is empty. ucmd_delta will become negative.
+					if(cl->ucmd_delta > 0)
+						cl->ucmd_delta = 0;
+
+					cl->ucmd_delta -= delta;
+				}
+				
+				while(cl->ucmd_processed < cl->ucmd_currate)
+				{
+					incr = 1000.0f / (sv_fps->integer * cl->ucmd_currate);
+				
+					if(cl->ucmd_inrate < 1.0f)
+					{
+						// Framerate is lower than server framerate.
+						// Run the frame with last valid command.
+						cl->lastUsercmd.serverTime += incr;
+					}
+					else
+					{
+						// avoid setting serverTime to a moment after
+						// the next real user command.
+						cl->lastUsercmd.serverTime += incr / (delta + 1);
+						
+//						Com_Printf("Executing duplicate command "
+//							   "with serverTime += %d\n", incr/(delta + 1));
+					}
+						
+					VM_Call(gvm, GAME_CLIENT_THINK, cl - svs.clients);
+					cl->ucmd_processed++;
+				}
+			}
+			else if(sv_antiWarpBias->integer && cl->ucmd_bufcount < cl->ucmd_delta)
+				cl->ucmd_delta = cl->ucmd_bufcount;
+		}
+	}
+}
+
+
 /*
 ===========================================================================
 
Index: code/server/server.h
===================================================================
--- code/server/server.h	(revision 958)
+++ code/server/server.h	(working copy)
@@ -63,6 +63,7 @@
 	int				snapshotCounter;	// incremented for each snapshot built
 	int				timeResidual;		// <= 1000 / sv_frame->value
 	int				nextFrameTime;		// when time > nextFrameTime, process world
+	int				frameNumber;		// used for user command rate limiting.
 	struct cmodel_s	*models[MAX_MODELS];
 	char			*configstrings[MAX_CONFIGSTRINGS];
 	svEntity_t		svEntities[MAX_GENTITIES];
@@ -113,6 +114,18 @@
 	struct netchan_buffer_s *next;
 } netchan_buffer_t;
 
+// define the number of frames that have to pass between updating rate statistics.
+#define USERCMD_RATECHECK 20
+#define USERCMD_BUFSIZE USERCMD_RATECHECK
+/* If sv_antiWarpBias is enabled the rate will be chosen so that the buffer is filled up
+ * enough to ensure that there is a constant count of *unique* usercommands to be executed
+ * between every server frame.
+ * However, this will have a negative impact on the responsiveness of user input. If this is
+ * not defined, some user commands will be executed twice to ensure a nearly constant rate
+ * of usercommands every frame. This will have almost the same responsiveness compared to
+ * id's original but give you alot more prediction errors on the client. */
+#define USERCMD_BIASRATE 5
+
 typedef struct client_s {
 	clientState_t	state;
 	char			userinfo[MAX_INFO_STRING];		// name, etc
@@ -169,6 +182,27 @@
 
 	int				oldServerTime;
 	qboolean			csUpdated[MAX_CONFIGSTRINGS+1];	
+	
+	// We need a buffer for incoming commands from clients to ensure that we deliver a *constant* rate of move commands
+	// to the game code each server frame. If we don't do this, the movement of clients will look choppy.
+	usercmd_t		ucmdbuf[USERCMD_BUFSIZE];
+	int				ucmd_bufcount;
+	int				ucmd_currate;
+	// The average count of user command packages coming in per USERCMD_RATECHECK snapshots.
+	float				ucmd_inrate;
+	int				ucmd_incoming;
+	int				ucmd_newest;
+	// How many commands have been processed this snapshot
+	int				ucmd_processed;
+
+	// bias stuff.
+	int				ucmd_statcounter;
+	int				ucmd_delta;
+	int				ucmd_bias;
+	float				ucmd_lastinrate;
+
+	int				lastframenum;
+	int				laststatframenum;
 } client_t;
 
 //=============================================================================
@@ -248,6 +282,8 @@
 extern	cvar_t	*sv_floodProtect;
 extern	cvar_t	*sv_lanForceRate;
 extern	cvar_t	*sv_strictAuth;
+extern	cvar_t	*sv_antiWarp;
+extern	cvar_t	*sv_antiWarpBias;
 
 //===========================================================
 
@@ -300,6 +336,7 @@
 
 void SV_ExecuteClientCommand( client_t *cl, const char *s, qboolean clientOK );
 void SV_ClientThink (client_t *cl, usercmd_t *cmd);
+void SV_CheckClientThink(void);
 
 void SV_WriteDownloadToClient( client_t *cl , msg_t *msg );
 
Index: code/server/sv_init.c
===================================================================
--- code/server/sv_init.c	(revision 958)
+++ code/server/sv_init.c	(working copy)
@@ -478,6 +478,11 @@
 	sv.restartedServerId = sv.serverId; // I suppose the init here is just to be safe
 	sv.checksumFeedServerId = sv.serverId;
 	Cvar_Set( "sv_serverid", va("%i", sv.serverId ) );
+	
+	sv.frameNumber = 0;
+	// Reset last usercmd time on all clients.
+	for(i = 0; i < sv_maxclients->integer; i++)
+		svs.clients[i].ucmd_newest = 0;
 
 	// clear physics interaction links
 	SV_ClearWorld ();
@@ -668,6 +673,8 @@
 	sv_mapChecksum = Cvar_Get ("sv_mapChecksum", "", CVAR_ROM);
 	sv_lanForceRate = Cvar_Get ("sv_lanForceRate", "1", CVAR_ARCHIVE );
 	sv_strictAuth = Cvar_Get ("sv_strictAuth", "1", CVAR_ARCHIVE );
+	sv_antiWarp = Cvar_Get ("sv_antiWarp", "0", CVAR_ARCHIVE);
+	sv_antiWarpBias = Cvar_Get ("sv_antiWarpBias", "0", CVAR_ARCHIVE);
 
 	// initialize bot cvars so they are listed and can be set before loading the botlib
 	SV_BotInitCvars();
Index: code/server/sv_main.c
===================================================================
--- code/server/sv_main.c	(revision 958)
+++ code/server/sv_main.c	(working copy)
@@ -53,6 +53,8 @@
 cvar_t	*sv_floodProtect;
 cvar_t	*sv_lanForceRate; // dedicated 1 (LAN) server forces local client rates to 99999 (bug #491)
 cvar_t	*sv_strictAuth;
+cvar_t  *sv_antiWarp;	// restrict usercmd rates to ensure smooth movement of players with bad settings.
+cvar_t  *sv_antiWarpBias;
 
 /*
 =============================================================================
@@ -862,6 +864,10 @@
 
 	if (com_dedicated->integer) SV_BotFrame (sv.time);
 
+	// check whether all clients have been thought about enough this frame.
+	if(sv_antiWarp->integer)
+		SV_CheckClientThink();
+
 	// run the game simulation in chunks
 	while ( sv.timeResidual >= frameMsec ) {
 		sv.timeResidual -= frameMsec;
@@ -884,6 +890,9 @@
 
 	// send a heartbeat to the master if needed
 	SV_MasterHeartbeat();
+	
+	// finally increase frame number
+	sv.frameNumber++;
 }
 
 //============================================================================
Index: README
===================================================================
--- README	(revision 958)
+++ README	(working copy)
@@ -149,7 +149,11 @@
   r_ext_texture_filter_anisotropic  - anisotropic texture filtering
   cl_cURLLib                        - filename of cURL library to load
   sv_dlURL                          - the base of the HTTP or FTP site that
-                                      holds custom pk3 files for your server 
+                                      holds custom pk3 files for your server
+  sv_antiWarp                       - Try to make playermovement look as smooth
+                                      as possible.
+  sv_antiWarpBias                   - If enabled, bad effect on latency but no
+                                      additional prediction errors.
 
 New commands
   video [filename]        - start video capture (use with demo command)
