buf.maxsize = 128;
buf.cursize = 0;
buf.data = data;
-
+
cl.cmd = *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]);
*/
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;
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 ();
}
/*
}
}
-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]);
}
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]);
CL_MoveParticles();
CL_UpdateTEnts();
- CL_LerpPlayerEye(frac);
- CL_LerpPlayerVelocity(frac);
+ CL_LerpPlayer(frac);
}
// 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)
{
"svc_spawnstatic",
"OBSOLETE svc_spawnbinary",
"svc_spawnbaseline",
-
+
"svc_temp_entity", // <variable>
"svc_setpause",
"svc_signonnum",
"", // 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
};
//=============================================================================
cl.idealpitch = MSG_ReadChar ();
else
cl.idealpitch = 0;
-
+
VectorCopy (cl.mvelocity[0], cl.mvelocity[1]);
for (i=0 ; i<3 ; i++)
{
cl.item_gettime[j] = cl.time;
cl.items = i;
}
-
+
cl.onground = (bits & SU_ONGROUND) != 0;
cl.inwater = (bits & SU_INWATER) != 0;
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;
+
}
/*
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;
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)
// 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
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;
/* 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))
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))
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++)
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.
#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]
myDriverLevel = net_driverlevel;
Cmd_AddCommand ("net_stats", NET_Stats_f);
+ Cvar_RegisterVariable (&cl_port);
if (COM_CheckParm("-nolan"))
return -1;
int activeNumber;
int clientNumber;
client_t *client;
-
+
playerNumber = MSG_ReadByte();
activeNumber = -1;
for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
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;
if (dfunc.GetAddrFromName(host, &sendaddr) == -1)
return NULL;
- newsock = dfunc.OpenSocket (0);
+ newsock = dfunc.OpenSocket (cl_port.integer);
if (newsock == -1)
return NULL;
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
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;
int eval_movement;
int eval_pmodel;
int eval_punchvector;
+int eval_viewzoom;
dfunction_t *SV_PlayerPhysicsQC;
dfunction_t *EndFrameQC;
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");
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)
#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)
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
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
}
int items;
eval_t *val;
vec3_t punchvector;
+ byte viewzoom;
//
// send a damage message
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])
{
MSG_WriteByte (msg, ent->v.weapon);
}
+
+ if (bits & SU_VIEWZOOM)
+ MSG_WriteByte (msg, viewzoom);
}
/*
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)
{
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);
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))
return;
}
- SV_AirMove ();
+ SV_AirMove ();
}
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++;
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 ();
val->vector[1] = move->sidemove;
val->vector[2] = move->upmove;
}
-
+
// read buttons
bits = MSG_ReadByte ();
host_client->edict->v.button0 = bits & 1;
Returns false if the client should be killed
===================
*/
+void SV_SendServerinfo (client_t *client);
qboolean SV_ReadClientMessage (void)
{
int ret;
{
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
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;