diff -Nru tremulous/src/game/bg_pmove.c tremulous.temp/src/game/bg_pmove.c
--- tremulous/src/game/bg_pmove.c	2007-02-28 18:28:08.000000000 +0100
+++ tremulous.temp/src/game/bg_pmove.c	2007-02-28 18:32:32.000000000 +0100
@@ -3504,3 +3504,23 @@
       pmove->cmd.upmove = 20;
   }
 }
+
+/*
+================
+ PmovePredict
+
+ Used by the server to move a client along when they miss a server frame
+================
+*/
+
+void PmovePredict( pmove_t *pmove, float frametime )
+{
+  pm = pmove;
+  memset( &pml, 0, sizeof( pml ) );
+  pml.frametime = frametime;
+  PM_GroundTrace( );
+  if( pml.groundPlane )
+    PM_StepSlideMove( qfalse, qtrue );
+  else
+    PM_StepSlideMove( qtrue, qtrue );
+}
diff -Nru tremulous/src/game/bg_public.h tremulous.temp/src/game/bg_public.h
--- tremulous/src/game/bg_public.h	2007-02-28 18:32:09.000000000 +0100
+++ tremulous.temp/src/game/bg_public.h	2007-02-28 18:32:32.000000000 +0100
@@ -197,6 +197,7 @@
 // if a full pmove isn't done on the client, you can just update the angles
 void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd );
 void Pmove( pmove_t *pmove );
+void PmovePredict( pmove_t *pmove, float frametime );
 
 //===================================================================================
 
diff -Nru tremulous/src/game/g_active.c tremulous.temp/src/game/g_active.c
--- tremulous/src/game/g_active.c	2007-02-28 18:32:22.000000000 +0100
+++ tremulous.temp/src/game/g_active.c	2007-02-28 18:32:32.000000000 +0100
@@ -1281,6 +1281,23 @@
   // mark the time, so the connection sprite can be removed
   ucmd = &ent->client->pers.cmd;
 
+  if( g_maxWarp.integer > 0 && client->warping )
+  {
+    int frames = ( level.framenum - client->lastUpdateFrame );
+    int maxtime = 0;
+
+    if( frames > g_maxWarp.integer )
+      frames = g_maxWarp.integer;
+
+    maxtime = ( frames  * ( level.time - level.previousTime ) );
+    //ucmd->serverTime = client->ps.commandTime + maxtime;
+    ucmd->serverTime = level.previousTime;
+    client->ps.commandTime = level.previousTime - maxtime;
+    client->warped = qtrue;
+  }
+  client->warping = qfalse;
+  client->lastUpdateFrame = level.framenum;
+
   // sanity check the command time to prevent speedup cheating
   if( ucmd->serverTime > level.time + 200 )
   {
@@ -1781,6 +1798,58 @@
 
 /*
 ==============
+ G_PredictPmove
+
+ Make use of the 'Pmove' functions to figure out where a player
+ would have moved on their present course in frametime seconds.
+ Link them at this predicted location and send it out in the next
+ snapshot, BUT don't alter their location so subsequent cmds from
+ this client process as though nothing has happened.
+==============
+*/
+static void G_PredictPmove( gentity_t *ent, float frametime )
+{
+  pmove_t pm;
+  pmoveExt_t pmext;
+  playerState_t ps;
+  vec3_t currentOrigin;
+
+  if( !ent || !ent->inuse || !ent->r.linked || !ent->client ||
+      ent->health < 1 || ent->client->ps.pm_type != PM_NORMAL ||
+      ent->waterlevel > 1 )
+  {
+    return;
+  }
+
+  memset( &pm, 0, sizeof( pm ) );
+  memcpy( &ps, &ent->client->ps, sizeof( ps ) );
+  memcpy( &pmext, &ent->client->pmext, sizeof( pmext ) );
+  pm.ps = &ps;
+  pm.pmext = &pmext;
+  pm.trace = trap_Trace;
+  pm.pointcontents = trap_PointContents;
+  pm.tracemask = MASK_PLAYERSOLID;
+  VectorCopy( ent->r.mins, pm.mins );
+  VectorCopy( ent->r.maxs, pm.maxs );
+
+  PmovePredict( &pm, frametime );
+  SnapVector( ps.origin );
+  SnapVector( ps.velocity );
+
+  // link the entity at this new location so they can be hit there but don't
+  // really move them since it will interfere with movement
+  VectorCopy( ent->r.currentOrigin , currentOrigin );
+  VectorCopy( ps.origin, ent->r.currentOrigin );
+  trap_LinkEntity( ent );
+  VectorCopy( currentOrigin, ent->r.currentOrigin );
+
+  // send this new origin/velocity in the next snapshot
+  VectorCopy( ps.origin, ent->s.pos.trBase );
+  VectorCopy( ps.velocity, ent->s.pos.trDelta );
+}
+
+/*
+==============
 ClientEndFrame
 
 Called at the end of each server frame for each connected client
@@ -1791,6 +1860,7 @@
 void ClientEndFrame( gentity_t *ent )
 {
   clientPersistant_t  *pers;
+  int frames;
 
   if( ent->client->sess.sessionTeam == TEAM_SPECTATOR )
   {
@@ -1830,6 +1900,28 @@
     BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
 
   SendPendingPredictableEvents( &ent->client->ps );
+
+  // see how many frames the client has missed
+  frames = level.framenum - ent->client->lastUpdateFrame - 1;
+
+  if( g_maxWarp.integer > 0 )
+  {
+    if( frames > g_maxWarp.integer )
+    {
+      ent->client->warping = qtrue;
+      frames = g_maxWarp.integer;
+    } 
+  }
+  else if( frames > 3 )
+    frames = 3;
+
+  if( g_skipCorrection.integer && !ent->client->warped && frames > 0 )
+  {
+    float pmtime = 0.001f * ( frames * ( level.time - level.previousTime ) );
+
+    G_PredictPmove( ent, pmtime );
+  }
+  ent->client->warped = qfalse;
 }
 
 void do_health( gentity_t *ent, int quantity )
diff -Nru tremulous/src/game/g_local.h tremulous.temp/src/game/g_local.h
--- tremulous/src/game/g_local.h	2007-02-28 18:32:28.000000000 +0100
+++ tremulous.temp/src/game/g_local.h	2007-02-28 18:32:32.000000000 +0100
@@ -429,6 +429,10 @@
 
   gentity_t           *hovel;
 
+  int                 lastUpdateFrame;
+  qboolean            warping;
+  qboolean            warped;
+
   int                 lastPoisonTime;
   int                 poisonImmunityTime;
   gentity_t           *lastPoisonClient;
@@ -1156,6 +1160,9 @@
 
 extern  vmCvar_t  g_shove;
 
+extern  vmCvar_t  g_maxWarp;
+extern  vmCvar_t  g_skipCorrection;
+
 extern  vmCvar_t  g_mapConfigs;
 
 extern  vmCvar_t  g_layouts;
diff -Nru tremulous/src/game/g_main.c tremulous.temp/src/game/g_main.c
--- tremulous/src/game/g_main.c	2007-02-28 18:32:28.000000000 +0100
+++ tremulous.temp/src/game/g_main.c	2007-02-28 18:32:32.000000000 +0100
@@ -119,6 +119,9 @@
 
 vmCvar_t  g_shove;
 
+vmCvar_t  g_maxWarp;
+vmCvar_t  g_skipCorrection;
+
 vmCvar_t  g_mapConfigs;
 vmCvar_t  g_chatTeamPrefix;
 vmCvar_t  g_specsSeeTeamchat;
@@ -244,6 +247,10 @@
   { &g_currentMap, "g_currentMap", "0", 0, 0, qfalse  },
   { &g_initialMapRotation, "g_initialMapRotation", "", CVAR_ARCHIVE, 0, qfalse  },
   { &g_shove, "g_shove", "0.0", CVAR_ARCHIVE, 0, qfalse  },
+
+  { &g_maxWarp, "g_maxWarp", "3", CVAR_ARCHIVE, 0, qfalse  },
+  { &g_skipCorrection, "g_skipCorrection", "1", CVAR_ARCHIVE, 0, qfalse  },
+
   { &g_mapConfigs, "g_mapConfigs", "", CVAR_ARCHIVE, 0, qfalse  },
   { NULL, "g_mapConfigsLoaded", "0", CVAR_ROM, 0, qfalse  },
 
