+ double packettime = 1.0 / bound(10, cl_netinputpacketspersecond.value, 100);
+ // don't send too often or else network connections can get clogged by a high renderer framerate
+ if (realtime < lastsendtime + packettime)
+ return;
+ // don't let it fall behind if CL_SendMove hasn't been called recently
+ // (such is the case when framerate is too low for instance)
+ lastsendtime = max(lastsendtime + packettime, realtime);
+ }
+#if MOVEAVERAGING
+ // average the accumulated changes
+ accumtotal = 1.0f / accumtotal;
+ forwardmove = accumforwardmove * accumtotal;
+ sidemove = accumsidemove * accumtotal;
+ upmove = accumupmove * accumtotal;
+ accumforwardmove = 0;
+ accumsidemove = 0;
+ accumupmove = 0;
+ accumtotal = 0;
+#else
+ // use the latest values
+ forwardmove = cl.cmd.forwardmove;
+ sidemove = cl.cmd.sidemove;
+ upmove = cl.cmd.upmove;
+#endif
+
+ if (cls.signon == SIGNONS)
+ CL_UpdatePrydonCursor();
+
+ buf.maxsize = 128;
+ buf.cursize = 0;
+ buf.data = data;
+
+ // set button bits
+ // LordHavoc: added 6 new buttons and use and chat buttons, and prydon cursor active button
+ bits = 0;
+ if (in_attack.state & 3) bits |= 1;in_attack.state &= ~2;
+ if (in_jump.state & 3) bits |= 2;in_jump.state &= ~2;
+ if (in_button3.state & 3) bits |= 4;in_button3.state &= ~2;
+ if (in_button4.state & 3) bits |= 8;in_button4.state &= ~2;
+ if (in_button5.state & 3) bits |= 16;in_button5.state &= ~2;
+ if (in_button6.state & 3) bits |= 32;in_button6.state &= ~2;
+ if (in_button7.state & 3) bits |= 64;in_button7.state &= ~2;
+ if (in_button8.state & 3) bits |= 128;in_button8.state &= ~2;
+ if (in_use.state & 3) bits |= 256;in_use.state &= ~2;
+ if (key_dest != key_game || key_consoleactive) bits |= 512;
+ if (cl_prydoncursor.integer) bits |= 1024;
+ if (in_button9.state & 3) bits |= 2048;in_button9.state &= ~2;
+ if (in_button10.state & 3) bits |= 4096;in_button10.state &= ~2;
+ if (in_button11.state & 3) bits |= 8192;in_button11.state &= ~2;
+ if (in_button12.state & 3) bits |= 16384;in_button12.state &= ~2;
+ if (in_button13.state & 3) bits |= 32768;in_button13.state &= ~2;
+ if (in_button14.state & 3) bits |= 65536;in_button14.state &= ~2;
+ if (in_button15.state & 3) bits |= 131072;in_button15.state &= ~2;
+ if (in_button16.state & 3) bits |= 262144;in_button16.state &= ~2;
+ // button bits 19-31 unused currently
+ // rotate/zoom view serverside if PRYDON_CLIENTCURSOR cursor is at edge of screen
+ if (cl.cmd.cursor_screen[0] <= -1) bits |= 8;
+ if (cl.cmd.cursor_screen[0] >= 1) bits |= 16;
+ if (cl.cmd.cursor_screen[1] <= -1) bits |= 32;
+ if (cl.cmd.cursor_screen[1] >= 1) bits |= 64;
+
+ impulse = in_impulse;
+ in_impulse = 0;
+
+ csqc_buttons = bits;
+
+ if (cls.signon == SIGNONS)
+ {
+ // always dump the first two messages, because they may contain leftover inputs from the last level
+ if (++cl.movemessages >= 2)
+ {
+ // send the movement message
+ // PROTOCOL_QUAKE clc_move = 16 bytes total
+ // PROTOCOL_QUAKEDP clc_move = 16 bytes total
+ // PROTOCOL_NEHAHRAMOVIE clc_move = 16 bytes total
+ // PROTOCOL_DARKPLACES1 clc_move = 19 bytes total
+ // PROTOCOL_DARKPLACES2 clc_move = 25 bytes total
+ // PROTOCOL_DARKPLACES3 clc_move = 25 bytes total
+ // PROTOCOL_DARKPLACES4 clc_move = 19 bytes total
+ // PROTOCOL_DARKPLACES5 clc_move = 19 bytes total
+ // PROTOCOL_DARKPLACES6 clc_move = 52 bytes total
+ // PROTOCOL_DARKPLACES7 clc_move = 56 bytes total
+ // PROTOCOL_QUAKEWORLD clc_move = 34 bytes total (typically, but can reach 43 bytes, or even 49 bytes with roll)
+ if (cls.protocol == PROTOCOL_QUAKEWORLD)
+ {
+ int checksumindex;
+ double msectime;
+ static double oldmsectime;
+ qw_usercmd_t *cmd, *oldcmd;
+ qw_usercmd_t nullcmd;
+
+ //Con_Printf("code qw_clc_move\n");
+
+ i = cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK;
+ cmd = &cl.qw_moves[i];
+ memset(&nullcmd, 0, sizeof(nullcmd));
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->buttons = bits;
+ cmd->impulse = impulse;
+ cmd->forwardmove = (short)bound(-32768, forwardmove, 32767);
+ cmd->sidemove = (short)bound(-32768, sidemove, 32767);
+ cmd->upmove = (short)bound(-32768, upmove, 32767);
+ VectorCopy(cl.viewangles, cmd->angles);
+ msectime = realtime * 1000;
+ cmd->msec = (unsigned char)bound(0, msectime - oldmsectime, 255);
+ // ridiculous value rejection (matches qw)
+ if (cmd->msec > 250)
+ cmd->msec = 100;
+ oldmsectime = msectime;
+
+ CL_ClientMovement_InputQW(cmd);
+
+ MSG_WriteByte(&buf, qw_clc_move);
+ // save the position for a checksum byte
+ checksumindex = buf.cursize;
+ MSG_WriteByte(&buf, 0);
+ // packet loss percentage
+ for (j = 0, packetloss = 0;j < 100;j++)
+ packetloss += cls.netcon->packetlost[j];
+ MSG_WriteByte(&buf, packetloss);
+ // write most recent 3 moves
+ i = (cls.netcon->qw.outgoing_sequence-2) & QW_UPDATE_MASK;
+ cmd = &cl.qw_moves[i];
+ QW_MSG_WriteDeltaUsercmd(&buf, &nullcmd, cmd);
+ oldcmd = cmd;
+ i = (cls.netcon->qw.outgoing_sequence-1) & QW_UPDATE_MASK;
+ cmd = &cl.qw_moves[i];
+ QW_MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd);
+ oldcmd = cmd;
+ i = cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK;
+ cmd = &cl.qw_moves[i];
+ QW_MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd);
+ // calculate the checksum
+ buf.data[checksumindex] = COM_BlockSequenceCRCByteQW(buf.data + checksumindex + 1, buf.cursize - checksumindex - 1, cls.netcon->qw.outgoing_sequence);
+ // if delta compression history overflows, request no delta
+ if (cls.netcon->qw.outgoing_sequence - cl.qw_validsequence >= QW_UPDATE_BACKUP-1)
+ cl.qw_validsequence = 0;
+ // request delta compression if appropriate
+ if (cl.qw_validsequence && !cl_nodelta.integer && cls.state == ca_connected && !cls.demorecording)
+ {
+ cl.qw_deltasequence[cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK] = cl.qw_validsequence;
+ MSG_WriteByte(&buf, qw_clc_delta);
+ MSG_WriteByte(&buf, cl.qw_validsequence & 255);
+ }
+ else
+ cl.qw_deltasequence[cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK] = -1;
+ }
+ else if (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE)
+ {
+ // 5 bytes
+ MSG_WriteByte (&buf, clc_move);
+ MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times
+ // 3 bytes
+ for (i = 0;i < 3;i++)
+ MSG_WriteAngle8i (&buf, cl.viewangles[i]);
+ // 6 bytes
+ MSG_WriteCoord16i (&buf, forwardmove);
+ MSG_WriteCoord16i (&buf, sidemove);
+ MSG_WriteCoord16i (&buf, upmove);
+ // 2 bytes
+ MSG_WriteByte (&buf, bits);
+ MSG_WriteByte (&buf, impulse);
+
+ CL_ClientMovement_Input((bits & 2) != 0, false);
+ }
+ else if (cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3)
+ {
+ // 5 bytes
+ MSG_WriteByte (&buf, clc_move);
+ MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times
+ // 12 bytes
+ for (i = 0;i < 3;i++)
+ MSG_WriteAngle32f (&buf, cl.viewangles[i]);
+ // 6 bytes
+ MSG_WriteCoord16i (&buf, forwardmove);
+ MSG_WriteCoord16i (&buf, sidemove);
+ MSG_WriteCoord16i (&buf, upmove);
+ // 2 bytes
+ MSG_WriteByte (&buf, bits);
+ MSG_WriteByte (&buf, impulse);
+
+ CL_ClientMovement_Input((bits & 2) != 0, false);
+ }
+ else if (cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES4 || cls.protocol == PROTOCOL_DARKPLACES5)
+ {
+ // 5 bytes
+ MSG_WriteByte (&buf, clc_move);
+ MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times
+ // 6 bytes
+ for (i = 0;i < 3;i++)
+ MSG_WriteAngle16i (&buf, cl.viewangles[i]);
+ // 6 bytes
+ MSG_WriteCoord16i (&buf, forwardmove);
+ MSG_WriteCoord16i (&buf, sidemove);
+ MSG_WriteCoord16i (&buf, upmove);
+ // 2 bytes
+ MSG_WriteByte (&buf, bits);
+ MSG_WriteByte (&buf, impulse);
+
+ CL_ClientMovement_Input((bits & 2) != 0, false);
+ }
+ else
+ {
+ // 5 bytes
+ MSG_WriteByte (&buf, clc_move);
+ if (cls.protocol != PROTOCOL_DARKPLACES6)
+ {
+ if (cl_movement.integer)
+ {
+ cl.movesequence++;
+ MSG_WriteLong (&buf, cl.movesequence);
+ }
+ else
+ MSG_WriteLong (&buf, 0);
+ }
+ MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times
+ // 6 bytes
+ for (i = 0;i < 3;i++)
+ MSG_WriteAngle16i (&buf, cl.viewangles[i]);
+ // 6 bytes
+ MSG_WriteCoord16i (&buf, forwardmove);
+ MSG_WriteCoord16i (&buf, sidemove);
+ MSG_WriteCoord16i (&buf, upmove);
+ // 5 bytes
+ MSG_WriteLong (&buf, bits);
+ MSG_WriteByte (&buf, impulse);
+ // PRYDON_CLIENTCURSOR
+ // 30 bytes
+ MSG_WriteShort (&buf, (short)(cl.cmd.cursor_screen[0] * 32767.0f));
+ MSG_WriteShort (&buf, (short)(cl.cmd.cursor_screen[1] * 32767.0f));
+ MSG_WriteFloat (&buf, cl.cmd.cursor_start[0]);
+ MSG_WriteFloat (&buf, cl.cmd.cursor_start[1]);
+ MSG_WriteFloat (&buf, cl.cmd.cursor_start[2]);
+ MSG_WriteFloat (&buf, cl.cmd.cursor_impact[0]);
+ MSG_WriteFloat (&buf, cl.cmd.cursor_impact[1]);
+ MSG_WriteFloat (&buf, cl.cmd.cursor_impact[2]);
+ MSG_WriteShort (&buf, cl.cmd.cursor_entitynumber);
+
+ // FIXME: bits & 16 is +button5, Nexuiz specific
+ CL_ClientMovement_Input((bits & 2) != 0, (bits & 16) != 0);
+ }
+ }
+
+ if (cls.protocol != PROTOCOL_QUAKEWORLD)
+ {
+ // ack the last few frame numbers
+ // (redundent to improve handling of client->server packet loss)
+ // for LATESTFRAMENUMS == 3 case this is 15 bytes
+ for (i = 0;i < LATESTFRAMENUMS;i++)
+ {
+ if (cl.latestframenums[i] > 0)
+ {
+ if (developer_networkentities.integer >= 1)
+ Con_Printf("send clc_ackframe %i\n", cl.latestframenums[i]);
+ MSG_WriteByte(&buf, clc_ackframe);
+ MSG_WriteLong(&buf, cl.latestframenums[i]);
+ }
+ }
+ }
+
+ // PROTOCOL_DARKPLACES6 = 67 bytes per packet
+ // PROTOCOL_DARKPLACES7 = 71 bytes per packet
+ }
+
+ if (cls.protocol != PROTOCOL_QUAKEWORLD)
+ {
+ // acknowledge any recently received data blocks
+ for (i = 0;i < CL_MAX_DOWNLOADACKS && (cls.dp_downloadack[i].start || cls.dp_downloadack[i].size);i++)
+ {
+ MSG_WriteByte(&buf, clc_ackdownloaddata);
+ MSG_WriteLong(&buf, cls.dp_downloadack[i].start);
+ MSG_WriteShort(&buf, cls.dp_downloadack[i].size);
+ cls.dp_downloadack[i].start = 0;
+ cls.dp_downloadack[i].size = 0;
+ }
+ }
+
+ // send the reliable message (forwarded commands) if there is one
+ NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol);
+
+ if (cls.netcon->message.overflowed)
+ {
+ Con_Print("CL_SendMove: lost server connection\n");
+ CL_Disconnect();
+ Host_ShutdownServer();