added viewzoom extension to QC and client (smooth sniper zooming, with sensitivity...
authorlordhavoc <lordhavoc@d7cf8633-e32d-0410-b094-e92efae38249>
Tue, 19 Feb 2002 10:56:54 +0000 (10:56 +0000)
committerlordhavoc <lordhavoc@d7cf8633-e32d-0410-b094-e92efae38249>
Tue, 19 Feb 2002 10:56:54 +0000 (10:56 +0000)
protocol should be able to masq now (not a perfect fix like net_chan, but a fix for most people - I hope) - server waits to send serverinfo until first reply from client (any clc_ message will do), client spams clc_nop every 3 seconds until it gets serverinfo (FIXME: maybe only once would do?)
upgraded aiming angles sent from client to floats in DPPROTOCOL_VERSION2 mode
updated cl_shownet 2 svc name list in cl_parse.c (it was quite outdated)

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@1539 d7cf8633-e32d-0410-b094-e92efae38249

16 files changed:
cl_input.c
cl_main.c
cl_parse.c
cl_screen.c
client.h
in_svgalib.c
in_win.c
net_dgrm.c
net_loop.c
pr_edict.c
progs.h
protocol.h
server.h
sv_main.c
sv_user.c
vid_glx.c

index 11975ce..0943007 100644 (file)
@@ -372,7 +372,7 @@ void CL_SendMove (usercmd_t *cmd)
        buf.maxsize = 128;
        buf.cursize = 0;
        buf.data = data;
-       
+
        cl.cmd = *cmd;
 
 //
@@ -382,7 +382,12 @@ void CL_SendMove (usercmd_t *cmd)
 
        MSG_WriteFloat (&buf, cl.mtime[0]);     // so server can get ping times
 
-       if (dpprotocol)
+       if (dpprotocol == DPPROTOCOL_VERSION2)
+       {
+               for (i = 0;i < 3;i++)
+                       MSG_WriteFloat (&buf, cl.viewangles[i]);
+       }
+       else if (dpprotocol == DPPROTOCOL_VERSION1)
        {
                for (i=0 ; i<3 ; i++)
                        MSG_WritePreciseAngle (&buf, cl.viewangles[i]);
index 8eab435..122111e 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -226,6 +226,13 @@ Host should be either "local" or a net address to be passed on
 */
 void CL_EstablishConnection (char *host)
 {
+       sizebuf_t       buf;
+       byte    data[128];
+
+       buf.maxsize = 128;
+       buf.cursize = 0;
+       buf.data = data;
+
        if (cls.state == ca_dedicated)
                return;
 
@@ -242,6 +249,8 @@ void CL_EstablishConnection (char *host)
        cls.demonum = -1;                       // not in the demo loop now
        cls.state = ca_connected;
        cls.signon = 0;                         // need all the signon messages before playing
+
+       CL_ClearState ();
 }
 
 /*
@@ -626,8 +635,11 @@ static void CL_RelinkNetworkEntities()
        }
 }
 
-void CL_LerpPlayerEye(float frac)
+void CL_LerpPlayer(float frac)
 {
+       int i;
+       float d;
+
        if (cl.entitydatabase.numframes)
        {
                cl.viewentorigin[0] = cl.viewentoriginold[0] + frac * (cl.viewentoriginnew[0] - cl.viewentoriginold[0]);
@@ -636,12 +648,8 @@ void CL_LerpPlayerEye(float frac)
        }
        else
                VectorCopy (cl_entities[cl.viewentity].render.origin, cl.viewentorigin);
-}
 
-static void CL_LerpPlayerVelocity (float frac)
-{
-       int i;
-       float d;
+       cl.viewzoom = cl.viewzoomold + frac * (cl.viewzoomnew - cl.viewzoomold);
 
        for (i = 0;i < 3;i++)
                cl.velocity[i] = cl.mvelocity[1][i] + frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]);
@@ -751,8 +759,7 @@ void CL_RelinkEntities (void)
        CL_MoveParticles();
        CL_UpdateTEnts();
 
-       CL_LerpPlayerEye(frac);
-       CL_LerpPlayerVelocity(frac);
+       CL_LerpPlayer(frac);
 }
 
 
@@ -829,6 +836,19 @@ void CL_SendCmd (void)
        // send the unreliable message
                CL_SendMove (&cmd);
        }
+       else
+       {
+               // LordHavoc: fix for NAT routing of netquake:
+               // bounce back a clc_nop message to the newly allocated server port,
+               // to establish a routing connection for incoming frames,
+               // the server waits for this before sending anything
+               if (realtime > cl.sendnoptime)
+               {
+                       Con_DPrintf("sending clc_nop to get server's attention\n");
+                       cl.sendnoptime = realtime + 3;
+                       MSG_WriteByte(&cls.message, clc_nop);
+               }
+       }
 
        if (cls.demoplayback)
        {
index aac1a73..a76646a 100644 (file)
@@ -52,7 +52,7 @@ char *svc_strings[128] =
        "svc_spawnstatic",
        "OBSOLETE svc_spawnbinary",
        "svc_spawnbaseline",
-       
+
        "svc_temp_entity",              // <variable>
        "svc_setpause",
        "svc_signonnum",
@@ -80,10 +80,16 @@ char *svc_strings[128] =
        "", // 47
        "", // 48
        "", // 49
-       "", // 50
-       "svc_fog", // 51
-       "svc_effect", // [vector] org [byte] modelindex [byte] startframe [byte] framecount [byte] framerate
-       "svc_effect2", // [vector] org [short] modelindex [short] startframe [byte] framecount [byte] framerate
+       "svc_cgame", //                         50              // [short] length [bytes] data
+       "svc_fog", //                           51              // unfinished and obsolete
+       "svc_effect", //                        52              // [vector] org [byte] modelindex [byte] startframe [byte] framecount [byte] framerate
+       "svc_effect2", //                       53              // [vector] org [short] modelindex [short] startframe [byte] framecount [byte] framerate
+       "svc_sound2", //                        54              // short soundindex instead of byte
+       "svc_spawnbaseline2", //        55              // short modelindex instead of byte
+       "svc_spawnstatic2", //          56              // short modelindex instead of byte
+       "svc_entities", //                      57              // [int] deltaframe [int] thisframe [float vector] eye [variable length] entitydata
+       "svc_unusedlh3", //                     58
+       "svc_spawnstaticsound2", //     59              // [coord3] [short] samp [byte] vol [byte] aten
 };
 
 //=============================================================================
@@ -804,7 +810,7 @@ void CL_ParseClientdata (int bits)
                cl.idealpitch = MSG_ReadChar ();
        else
                cl.idealpitch = 0;
-       
+
        VectorCopy (cl.mvelocity[0], cl.mvelocity[1]);
        for (i=0 ; i<3 ; i++)
        {
@@ -835,7 +841,7 @@ void CL_ParseClientdata (int bits)
                                cl.item_gettime[j] = cl.time;
                cl.items = i;
        }
-               
+
        cl.onground = (bits & SU_ONGROUND) != 0;
        cl.inwater = (bits & SU_INWATER) != 0;
 
@@ -856,6 +862,18 @@ void CL_ParseClientdata (int bits)
                cl.stats[STAT_ACTIVEWEAPON] = (1<<i);
        else
                cl.stats[STAT_ACTIVEWEAPON] = i;
+
+       cl.viewzoomold = cl.viewzoomnew; // for interpolation
+       if (bits & SU_VIEWZOOM)
+       {
+               i = MSG_ReadByte();
+               if (i < 2)
+                       i = 2;
+               cl.viewzoomnew = (float) i * (1.0f / 255.0f);
+       }
+       else
+               cl.viewzoomnew = 1;
+
 }
 
 /*
@@ -1130,21 +1148,21 @@ void CL_ParseServerMessage (void)
                case svc_updatename:
                        i = MSG_ReadByte ();
                        if (i >= cl.maxclients)
-                               Host_Error ("CL_ParseServerMessage: svc_updatename >= MAX_SCOREBOARD");
+                               Host_Error ("CL_ParseServerMessage: svc_updatename >= cl.maxclients");
                        strcpy (cl.scores[i].name, MSG_ReadString ());
                        break;
 
                case svc_updatefrags:
                        i = MSG_ReadByte ();
                        if (i >= cl.maxclients)
-                               Host_Error ("CL_ParseServerMessage: svc_updatefrags >= MAX_SCOREBOARD");
+                               Host_Error ("CL_ParseServerMessage: svc_updatefrags >= cl.maxclients");
                        cl.scores[i].frags = MSG_ReadShort ();
                        break;
 
                case svc_updatecolors:
                        i = MSG_ReadByte ();
                        if (i >= cl.maxclients)
-                               Host_Error ("CL_ParseServerMessage: svc_updatecolors >= MAX_SCOREBOARD");
+                               Host_Error ("CL_ParseServerMessage: svc_updatecolors >= cl.maxclients");
                        cl.scores[i].colors = MSG_ReadByte ();
                        break;
 
index 0586a67..8f2f269 100644 (file)
@@ -736,7 +736,8 @@ static void SCR_CalcRefdef (void)
        r_refdef.x = bound(0, r_refdef.x, vid.realwidth - r_refdef.width) + vid.realx;
        r_refdef.y = bound(0, r_refdef.y, vid.realheight - r_refdef.height) + vid.realy;
 
-       r_refdef.fov_x = scr_fov.value;
+       // LordHavoc: viewzoom (zoom in for sniper rifles, etc)
+       r_refdef.fov_x = scr_fov.value * cl.viewzoom;
        r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.width, r_refdef.height);
 
        if (cl.worldmodel)
index afc4945..3513a3f 100644 (file)
--- a/client.h
+++ b/client.h
@@ -198,6 +198,7 @@ typedef struct
                                                                // throw out the first couple, so the player
                                                                // doesn't accidentally do something the
                                                                // first frame
+       float           sendnoptime;    // send a clc_nop periodically until connected
        usercmd_t       cmd;                    // last command sent to the server
 
 // information for local display
@@ -276,6 +277,8 @@ typedef struct
        scoreboard_t    *scores;                // [cl.maxclients]
 
        vec3_t          viewentorigin;
+       float           viewzoom;                       // LordHavoc: sniping zoom, QC controlled
+       float           viewzoomold, viewzoomnew; // for interpolation
 
        // entity database stuff
        vec3_t          viewentoriginold, viewentoriginnew;
index 971d497..c84c6d6 100644 (file)
@@ -353,8 +353,9 @@ void IN_Move(usercmd_t *cmd)
        /* Clear for next update */
        mx = my = 0;
 
-       mouse_x *= sensitivity.value;
-       mouse_y *= sensitivity.value;
+       // LordHavoc: viewzoom affects mouse sensitivity for sniping
+       mouse_x *= sensitivity.value * cl.viewzoom;
+       mouse_y *= sensitivity.value * cl.viewzoom;
 
        /* Add mouse X/Y movement to cmd */
        if ( (in_strafe.state & 1) || (lookstrafe.integer && mouselook))
index c6ce7aa..4d5ddbe 100644 (file)
--- a/in_win.c
+++ b/in_win.c
@@ -650,8 +650,9 @@ void IN_MouseMove (usercmd_t *cmd)
        old_mouse_x = mx;
        old_mouse_y = my;
 
-       mouse_x *= sensitivity.value;
-       mouse_y *= sensitivity.value;
+       // LordHavoc: viewzoom affects mouse sensitivity for sniping
+       mouse_x *= sensitivity.value * cl.viewzoom;
+       mouse_y *= sensitivity.value * cl.viewzoom;
 
 // add mouse X/Y movement to cmd
        if ( (in_strafe.state & 1) || (lookstrafe.integer && mouselook))
@@ -1050,7 +1051,8 @@ void IN_JoyMove (usercmd_t *cmd)
                speed = cl_movespeedkey.value;
        else
                speed = 1;
-       aspeed = speed * host_realframetime;
+       // LordHavoc: viewzoom affects sensitivity for sniping
+       aspeed = speed * host_realframetime * cl.viewzoom;
 
        // loop through the axes
        for (i = 0; i < JOY_MAX_AXES; i++)
index 513c256..dd4ed8a 100644 (file)
@@ -8,7 +8,7 @@ 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.  
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 See the GNU General Public License for more details.
 
@@ -55,6 +55,8 @@ unsigned long inet_addr(const char *cp);
 #include "quakedef.h"
 #include "net_dgrm.h"
 
+cvar_t cl_port = {CVAR_SAVE, "cl_port", "0"};
+
 // these two macros are to make the code more readable
 #define sfunc  net_landrivers[sock->landriver]
 #define dfunc  net_landrivers[net_landriverlevel]
@@ -767,6 +769,7 @@ int Datagram_Init (void)
 
        myDriverLevel = net_driverlevel;
        Cmd_AddCommand ("net_stats", NET_Stats_f);
+       Cvar_RegisterVariable (&cl_port);
 
        if (COM_CheckParm("-nolan"))
                return -1;
@@ -887,7 +890,7 @@ static qsocket_t *_Datagram_CheckNewConnections (void)
                int                     activeNumber;
                int                     clientNumber;
                client_t        *client;
-               
+
                playerNumber = MSG_ReadByte();
                activeNumber = -1;
                for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
@@ -1064,7 +1067,7 @@ static qsocket_t *_Datagram_CheckNewConnections (void)
                return NULL;
        }
 
-       // everything is allocated, just fill in the details    
+       // everything is allocated, just fill in the details
        sock->socket = newsock;
        sock->landriver = net_landriverlevel;
        sock->addr = clientaddr;
@@ -1224,7 +1227,7 @@ static qsocket_t *_Datagram_Connect (char *host)
        if (dfunc.GetAddrFromName(host, &sendaddr) == -1)
                return NULL;
 
-       newsock = dfunc.OpenSocket (0);
+       newsock = dfunc.OpenSocket (cl_port.integer);
        if (newsock == -1)
                return NULL;
 
@@ -1347,7 +1350,7 @@ static qsocket_t *_Datagram_Connect (char *host)
 
        dfunc.GetNameFromAddr (&sendaddr, sock->address);
 
-       Con_Printf ("Connection accepted\n");
+       Con_Printf ("Connection accepted to %s\n", sock->address);
        sock->lastMessageTime = SetNetTime();
 
        // switch the connection to the specified address
index 133766a..1ad3fee 100644 (file)
@@ -195,7 +195,8 @@ int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
 
        bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength;
 
-       if ((*bufferLength + data->cursize + sizeof(byte) + sizeof(short)) > NET_MAXMESSAGE)
+       // LordHavoc: added an extra sizeof(byte) to account for alignment
+       if ((*bufferLength + data->cursize + sizeof(byte) + sizeof(short) + sizeof(byte)) > NET_MAXMESSAGE)
                return 0;
 
        buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength;
index dc4166a..6d14b12 100644 (file)
@@ -116,6 +116,7 @@ int eval_ping;
 int eval_movement;
 int eval_pmodel;
 int eval_punchvector;
+int eval_viewzoom;
 
 dfunction_t *SV_PlayerPhysicsQC;
 dfunction_t *EndFrameQC;
@@ -164,6 +165,7 @@ void FindEdictFieldOffsets(void)
        eval_movement = FindFieldOffset("movement");
        eval_pmodel = FindFieldOffset("pmodel");
        eval_punchvector = FindFieldOffset("punchvector");
+       eval_viewzoom = FindFieldOffset("viewzoom");
 
        // LordHavoc: allowing QuakeC to override the player movement code
        SV_PlayerPhysicsQC = ED_FindFunction ("SV_PlayerPhysics");
diff --git a/progs.h b/progs.h
index bf96058..289f421 100644 (file)
--- a/progs.h
+++ b/progs.h
@@ -89,6 +89,7 @@ extern int eval_ping;
 extern int eval_movement;
 extern int eval_pmodel;
 extern int eval_punchvector;
+extern int eval_viewzoom;
 
 #define GETEDICTFIELDVALUE(ed, fieldoffset) (fieldoffset ? (eval_t*)((char*)&ed->v + fieldoffset) : NULL)
 
index f07830f..ad839c2 100644 (file)
@@ -111,7 +111,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define SU_PUNCHVEC1   (1<<16)
 #define SU_PUNCHVEC2   (1<<17)
 #define SU_PUNCHVEC3   (1<<18)
-#define SU_UNUSED19            (1<<19)
+#define SU_VIEWZOOM            (1<<19) // byte factor (0 = 0.0 (not valid), 255 = 1.0)
 #define SU_UNUSED20            (1<<20)
 #define SU_UNUSED21            (1<<21)
 #define SU_UNUSED22            (1<<22)
index 273b3de..175cd41 100644 (file)
--- a/server.h
+++ b/server.h
@@ -81,6 +81,10 @@ typedef struct client_s
        qboolean                dropasap;                       // has been told to go to another level
        qboolean                sendsignon;                     // only valid before spawned
 
+       // LordHavoc: to make netquake protocol get through NAT routers, have to wait for client to ack
+       qboolean                waitingforconnect;      // waiting for connect from client (stage 1)
+       qboolean                sendserverinfo;         // send server info in next datagram (stage 2)
+
        double                  last_message;           // reliable messages must be sent
                                                                                // periodically
 
index 1ea395f..ba3d483 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -333,7 +333,10 @@ void SV_ConnectClient (int clientnum)
                        client->spawn_parms[i] = (&pr_global_struct->parm1)[i];
        }
 
-       SV_SendServerinfo (client);
+       // send serverinfo on first nop
+       client->waitingforconnect = true;
+       client->sendsignon = true;
+       client->spawned = false;                // need prespawn, spawn, etc
 }
 
 
@@ -1144,6 +1147,7 @@ void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg)
        int             items;
        eval_t  *val;
        vec3_t  punchvector;
+       byte    viewzoom;
 
 //
 // send a damage message
@@ -1205,6 +1209,20 @@ void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg)
        if ((val = GETEDICTFIELDVALUE(ent, eval_punchvector)))
                VectorCopy(val->vector, punchvector);
 
+       i = 255;
+       if ((val = GETEDICTFIELDVALUE(ent, eval_viewzoom)))
+       {
+               i = val->_float * 255.0f;
+               if (i == 0)
+                       i = 255;
+               else
+                       i = bound(0, i, 255);
+       }
+       viewzoom = i;
+
+       if (viewzoom != 255)
+               bits |= SU_VIEWZOOM;
+
        for (i=0 ; i<3 ; i++)
        {
                if (ent->v.punchangle[i])
@@ -1287,6 +1305,9 @@ void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg)
        {
                MSG_WriteByte (msg, ent->v.weapon);
        }
+
+       if (bits & SU_VIEWZOOM)
+               MSG_WriteByte (msg, viewzoom);
 }
 
 /*
@@ -1306,14 +1327,17 @@ qboolean SV_SendClientDatagram (client_t *client)
        MSG_WriteByte (&msg, svc_time);
        MSG_WriteFloat (&msg, sv.time);
 
-// add the client specific data to the datagram
-       SV_WriteClientdataToMessage (client->edict, &msg);
+       if (!client->waitingforconnect)
+       {
+               // add the client specific data to the datagram
+               SV_WriteClientdataToMessage (client->edict, &msg);
 
-       SV_WriteEntitiesToClient (client, client->edict, &msg);
+               SV_WriteEntitiesToClient (client, client->edict, &msg);
 
-// copy the server datagram if there is space
-       if (msg.cursize + sv.datagram.cursize < msg.maxsize)
-               SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize);
+               // copy the server datagram if there is space
+               if (msg.cursize + sv.datagram.cursize < msg.maxsize)
+                       SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize);
+       }
 
 // send the datagram
        if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1)
@@ -1342,7 +1366,7 @@ void SV_UpdateToReliableMessages (void)
                {
                        for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
                        {
-                               if (!client->active)
+                               if (!client->active || !client->spawned)
                                        continue;
                                MSG_WriteByte (&client->message, svc_updatefrags);
                                MSG_WriteByte (&client->message, i);
@@ -1406,6 +1430,12 @@ void SV_SendClientMessages (void)
                if (!host_client->active)
                        continue;
 
+               if (host_client->sendserverinfo)
+               {
+                       host_client->sendserverinfo = false;
+                       SV_SendServerinfo (host_client);
+               }
+
                if (host_client->spawned)
                {
                        if (!SV_SendClientDatagram (host_client))
index 1fc0773..27c8ec9 100644 (file)
--- a/sv_user.c
+++ b/sv_user.c
@@ -447,7 +447,7 @@ void SV_ClientThink (void)
                return;
        }
 
-       SV_AirMove ();  
+       SV_AirMove ();
 }
 
 
@@ -463,7 +463,7 @@ void SV_ReadClientMove (usercmd_t *move)
        int             bits;
        eval_t  *val;
        float   total;
-       
+
 // read ping time
        host_client->ping_times[host_client->num_pings % NUM_PING_TIMES] = sv.time - MSG_ReadFloat ();
        host_client->num_pings++;
@@ -477,12 +477,12 @@ void SV_ReadClientMove (usercmd_t *move)
                val->_float = host_client->ping * 1000.0;
 
 // read current angles
-       // dpprotocol
-       for (i=0 ; i<3 ; i++)
-               angle[i] = MSG_ReadPreciseAngle ();
+       // dpprotocol version 2
+       for (i = 0;i < 3;i++)
+               angle[i] = MSG_ReadFloat ();
 
        VectorCopy (angle, host_client->edict->v.v_angle);
-               
+
 // read movement
        move->forwardmove = MSG_ReadShort ();
        move->sidemove = MSG_ReadShort ();
@@ -493,7 +493,7 @@ void SV_ReadClientMove (usercmd_t *move)
                val->vector[1] = move->sidemove;
                val->vector[2] = move->upmove;
        }
-       
+
 // read buttons
        bits = MSG_ReadByte ();
        host_client->edict->v.button0 = bits & 1;
@@ -518,6 +518,7 @@ SV_ReadClientMessage
 Returns false if the client should be killed
 ===================
 */
+void SV_SendServerinfo (client_t *client);
 qboolean SV_ReadClientMessage (void)
 {
        int             ret;
@@ -547,24 +548,30 @@ nextmsg:
                        {
                                Sys_Printf ("SV_ReadClientMessage: badread\n");
                                return false;
-                       }       
-       
+                       }
+
                        cmd = MSG_ReadChar ();
-                       
+
+                       if (cmd != -1 && host_client->waitingforconnect)
+                       {
+                               host_client->waitingforconnect = false;
+                               host_client->sendserverinfo = true;
+                       }
+
                        switch (cmd)
                        {
                        case -1:
                                goto nextmsg;           // end of message
-                               
+
                        default:
                                Sys_Printf ("SV_ReadClientMessage: unknown command char %i\n", cmd);
                                return false;
-                                                       
+
                        case clc_nop:
 //                             Sys_Printf ("clc_nop\n");
                                break;
-                               
-                       case clc_stringcmd:     
+
+                       case clc_stringcmd:
                                s = MSG_ReadString ();
                                ret = 0;
                                if (Q_strncasecmp(s, "status", 6) == 0
index 3fc5efb..8124ed7 100644 (file)
--- a/vid_glx.c
+++ b/vid_glx.c
@@ -849,8 +849,9 @@ void IN_MouseMove (usercmd_t *cmd)
                old_mouse_y = mouse_y;
        }
 
-       mouse_x *= sensitivity.value;
-       mouse_y *= sensitivity.value;
+       // LordHavoc: viewzoom affects mouse sensitivity for sniping
+       mouse_x *= sensitivity.value * cl.viewzoom;
+       mouse_y *= sensitivity.value * cl.viewzoom;
 
        if (in_strafe.state & 1)
                cmd->sidemove += m_side.value * mouse_x;