2
0
mirror of https://github.com/rehlds/rehlds.git synced 2025-01-07 12:35:33 +03:00
rehlds/rehlds/engine/net_chan.cpp
s1lentq 2a24337eee Cut of nicknames '+' if next there is something digit or alphanumeric character.
Added prefix Q_* for functions strlen/strcmp/strpcy/memset/memcpy/memmove etc.
Added for solution configuration (Debug Swds Play/Release Swds Play) the path to libacof32.lib in linker.
2015-10-27 21:14:08 +06:00

1794 lines
45 KiB
C++

/*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 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. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
#include "precompiled.h"
int net_drop;
char gDownloadFile[256];
/*
* Globals initialization
*/
#ifndef HOOK_ENGINE
cvar_t net_log = { "net_log", "0", 0, 0.0f, NULL };
cvar_t net_showpackets = { "net_showpackets", "0", 0, 0.0f, NULL };
cvar_t net_showdrop = { "net_showdrop", "0", 0, 0.0f, NULL };
cvar_t net_drawslider = { "net_drawslider", "0", 0, 0.0f, NULL };
cvar_t net_chokeloopback = { "net_chokeloop", "0", 0, 0.0f, NULL };
cvar_t sv_filetransfercompression = { "sv_filetransfercompression", "1", 0, 0.0f, NULL };
cvar_t sv_filetransfermaxsize = { "sv_filetransfermaxsize", "10485760", 0, 0.0f, NULL };
#else //HOOK_ENGINE
cvar_t net_log;
cvar_t net_showpackets;
cvar_t net_showdrop;
cvar_t net_drawslider;
cvar_t net_chokeloopback;
cvar_t sv_filetransfercompression;
cvar_t sv_filetransfermaxsize;
#endif //HOOK_ENGINE
/* <6565b> ../engine/net_chan.c:103 */
void Netchan_UnlinkFragment(fragbuf_t *buf, fragbuf_t **list)
{
fragbuf_t *search;
if (list == NULL)
{
Con_Printf(__FUNCTION__ ": Asked to unlink fragment from empty list, ignored\n");
return;
}
if (*list == buf)
{
*list = buf->next;
Mem_Free(buf);
return;
}
search = *list;
while (search->next)
{
if (search->next == buf)
{
search->next = buf->next;
Mem_Free(buf);
return;
}
search = search->next;
}
Con_Printf(__FUNCTION__ ": Couldn't find fragment\n");
}
/* <656b9> ../engine/net_chan.c:148 */
void Netchan_OutOfBand(netsrc_t sock, netadr_t adr, int length, byte *data)
{
sizebuf_t send;
byte send_buf[NET_MAX_PAYLOAD];
send.buffername = "Netchan_OutOfBand";
send.data = send_buf;
send.maxsize = sizeof(send_buf);
send.cursize = 0;
send.flags = SIZEBUF_ALLOW_OVERFLOW;
MSG_WriteLong(&send, -1);
SZ_Write(&send, data, length);
if (!g_pcls.demoplayback)
{
NET_SendPacket(sock, send.cursize, send.data, adr);
}
}
/* <65700> ../engine/net_chan.c:177 */
void Netchan_OutOfBandPrint(netsrc_t sock, netadr_t adr, char *format, ...)
{
va_list argptr;
char string[8192];
size_t len;
va_start(argptr, format);
len = Q_vsnprintf(string, sizeof(string), format, argptr);
va_end(argptr);
Netchan_OutOfBand(sock, adr, len + 1, (byte *)string);
}
/* <65776> ../engine/net_chan.c:196 */
void Netchan_ClearFragbufs(fragbuf_t **ppbuf)
{
fragbuf_t *buf, *n;
if (!ppbuf)
{
return;
}
// Throw away any that are sitting around
buf = *ppbuf;
while (buf)
{
n = buf->next;
Mem_Free(buf);
buf = n;
}
*ppbuf = NULL;
}
/* <65810> ../engine/net_chan.c:220 */
void Netchan_ClearFragments(netchan_t *chan)
{
fragbufwaiting_t *wait, *next;
for (int i = 0; i < MAX_STREAMS; i++)
{
wait = chan->waitlist[i];
while (wait)
{
next = wait->next;
Netchan_ClearFragbufs(&wait->fragbufs);
Mem_Free(wait);
wait = next;
}
chan->waitlist[i] = NULL;
Netchan_ClearFragbufs(&chan->fragbufs[i]);
Netchan_FlushIncoming(chan, i);
}
}
/* <6590d> ../engine/net_chan.c:250 */
void Netchan_Clear(netchan_t *chan)
{
Netchan_ClearFragments(chan);
if (chan->reliable_length)
{
Con_DPrintf(__FUNCTION__ ": reliable length not 0, reliable_sequence: %d, incoming_reliable_acknowledged: %d\n", chan->reliable_length, chan->incoming_reliable_acknowledged);
chan->reliable_sequence ^= 1;
chan->reliable_length = 0;
}
chan->cleartime = 0.0;
for (int i = 0; i < MAX_STREAMS; i++)
{
chan->reliable_fragid[i] = 0;
chan->reliable_fragment[i] = 0;
chan->fragbufcount[i] = 0;
chan->frag_startpos[i] = 0;
chan->frag_length[i] = 0;
chan->incomingready[i] = FALSE;
}
if (chan->tempbuffer)
{
Mem_Free(chan->tempbuffer);
chan->tempbuffer = NULL;
}
chan->tempbuffersize = 0;
}
/* <65957> ../engine/net_chan.c:295 */
void Netchan_Setup(netsrc_t socketnumber, netchan_t *chan, netadr_t adr, int player_slot, void *connection_status, qboolean(*pfnNetchan_Blocksize)(void *))
{
Netchan_Clear(chan);
Q_memset(chan, 0, sizeof(netchan_t));
chan->player_slot = player_slot + 1;
chan->sock = socketnumber;
chan->remote_address = adr;
chan->last_received = (float)realtime;
chan->connect_time = (float)realtime;
chan->message.buffername = "netchan->message";
chan->message.data = chan->message_buf;
chan->message.maxsize = sizeof(chan->message_buf);
#ifdef REHLDS_FIXES
chan->message.cursize = 0;
#endif // REHLDS_FIXES
chan->message.flags = SIZEBUF_ALLOW_OVERFLOW;
chan->rate = DEFAULT_RATE;
// Prevent the first message from getting dropped after connection is set up.
chan->outgoing_sequence = 1;
chan->connection_status = connection_status;
chan->pfnNetchan_Blocksize = pfnNetchan_Blocksize;
}
/* <65a0a> ../engine/net_chan.c:327 */
qboolean Netchan_CanPacket(netchan_t *chan)
{
// Never choke loopback packets.
if (net_chokeloopback.value == 0.0f && chan->remote_address.type == NA_LOOPBACK)
{
chan->cleartime = realtime;
return TRUE;
}
return chan->cleartime < realtime ? TRUE : FALSE;
}
/* <65a37> ../engine/net_chan.c:350 */
void Netchan_UpdateFlow(netchan_t *chan)
{
if (!chan)
return;
int bytes = 0;
float faccumulatedtime = 0.0f;
for (int flow = 0; flow < MAX_FLOWS; flow++)
{
flow_t *pflow = &chan->flow[flow];
if (realtime - pflow->nextcompute < 0.1)
continue;
pflow->nextcompute = realtime + 0.1;
int start = pflow->current - 1;
for (int i = 0; i < MAX_LATENT - 1; i++)
{
flowstats_t *pprev = &pflow->stats[(start - i) & 0x1F];
flowstats_t *pstat = &pflow->stats[(start - i - 1) & 0x1F];
faccumulatedtime += pprev->time - pstat->time;
bytes += pstat->size;
}
pflow->kbytespersec = (faccumulatedtime == 0.0f) ? 0.0f : bytes / faccumulatedtime / 1024.0f;
pflow->avgkbytespersec = pflow->avgkbytespersec * (2.0 / 3) + pflow->kbytespersec * (1.0 / 3);
}
}
/* <65b51> ../engine/net_chan.c:412 */
void Netchan_Transmit(netchan_t *chan, int length, byte *data)
{
byte send_buf[NET_MAX_MESSAGE];
qboolean send_reliable;
qboolean send_reliable_fragment;
qboolean send_resending = false;
unsigned w1, w2;
int i, j;
float fRate;
sizebuf_t sb_send;
sb_send.data = send_buf;
sb_send.buffername = "Netchan_Transmit";
sb_send.maxsize = sizeof(send_buf);
sb_send.flags = 0;
sb_send.cursize = 0;
// check for message overflow
if (chan->message.flags & 2) {
Con_Printf("%s:Outgoing message overflow\n", NET_AdrToString(chan->remote_address));
return;
}
// if the remote side dropped the last reliable message, resend it
send_reliable = false;
if (chan->incoming_acknowledged > chan->last_reliable_sequence &&
chan->incoming_reliable_acknowledged != chan->reliable_sequence)
{
send_reliable = true;
send_resending = true;
}
//
// A packet can have "reliable payload + frag payload + unreliable payload
// frag payload can be a file chunk, if so, it needs to be parsed on the receiving end and reliable payload + unreliable payload need
// to be passed on to the message queue. The processing routine needs to be able to handle the case where a message comes in and a file
// transfer completes
//
//
// if the reliable transmit buffer is empty, copy the current message out
if (!chan->reliable_length)
{
qboolean send_frag = false;
fragbuf_t *pbuf;
// Will be true if we are active and should let chan->message get some bandwidth
int send_from_frag[MAX_STREAMS] = { 0, 0 };
int send_from_regular = 0;
// If we have data in the waiting list(s) and we have cleared the current queue(s), then
// push the waitlist(s) into the current queue(s)
Netchan_FragSend(chan);
// Sending regular payload
send_from_regular = (chan->message.cursize) ? 1 : 0;
// Check to see if we are sending a frag payload
//
for (i = 0; i < MAX_STREAMS; i++)
{
if (chan->fragbufs[i])
{
send_from_frag[i] = 1;
}
}
// Stall reliable payloads if sending from frag buffer
if (send_from_regular && (send_from_frag[FRAG_NORMAL_STREAM]))
{
send_from_regular = false;
// If the reliable buffer has gotten too big, queue it at the end of everything and clear out buffer
//
if (chan->message.cursize > MAX_RELIABLE_PAYLOAD)
{
Netchan_CreateFragments_(chan == &g_pcls.netchan ? 1 : 0, chan, &chan->message);
SZ_Clear(&chan->message);
}
}
// Startpos will be zero if there is no regular payload
for (i = 0; i < MAX_STREAMS; i++)
{
chan->frag_startpos[i] = 0;
// Assume no fragment is being sent
chan->reliable_fragment[i] = 0;
chan->reliable_fragid[i] = 0;
chan->frag_length[i] = 0;
if (send_from_frag[i])
{
send_frag = true;
}
}
if (send_from_regular || send_frag)
{
chan->reliable_sequence ^= 1;
send_reliable = true;
}
if (send_from_regular) {
Q_memcpy(chan->reliable_buf, chan->message_buf, chan->message.cursize);
chan->reliable_length = chan->message.cursize;
SZ_Clear(&chan->message);
// If we send fragments, this is where they'll start
for (i = 0; i < MAX_STREAMS; i++) {
chan->frag_startpos[i] = chan->reliable_length;
}
}
for (i = 0; i < MAX_STREAMS; i++) {
int fragment_size;
// Is there something in the fragbuf?
pbuf = chan->fragbufs[i];
fragment_size = 0; // Compiler warning.
if (pbuf) {
fragment_size = pbuf->frag_message.cursize;
// Files set size a bit differently.
if (pbuf->isfile && !pbuf->isbuffer)
{
fragment_size = pbuf->size;
}
}
// Make sure we have enought space left
if (send_from_frag[i] && pbuf && ((chan->reliable_length + fragment_size) < MAX_RELIABLE_PAYLOAD)) {
chan->reliable_fragid[i] = MAKE_FRAGID(pbuf->bufferid, chan->fragbufcount[i]); // Which buffer are we sending?
// If it's not in-memory, then we'll need to copy it in frame the file handle.
if (pbuf->isfile && !pbuf->isbuffer) {
char compressedfilename[MAX_PATH];
FileHandle_t hfile;
if (pbuf->iscompressed)
{
Q_snprintf(compressedfilename, sizeof(compressedfilename), "%s.ztmp", pbuf->filename);
hfile = FS_Open(compressedfilename, "rb");
}
else
{
hfile = FS_Open(pbuf->filename, "rb");
}
FS_Seek(hfile, pbuf->foffset, FILESYSTEM_SEEK_HEAD);
FS_Read(&pbuf->frag_message.data[pbuf->frag_message.cursize], pbuf->size, 1, hfile);
pbuf->frag_message.cursize += pbuf->size;
FS_Close(hfile);
}
Q_memcpy(chan->reliable_buf + chan->reliable_length, pbuf->frag_message.data, pbuf->frag_message.cursize);
chan->reliable_length += pbuf->frag_message.cursize;
chan->frag_length[i] = pbuf->frag_message.cursize;
// Unlink pbuf
Netchan_UnlinkFragment(pbuf, &chan->fragbufs[i]);
chan->reliable_fragment[i] = 1;
// Offset the rest of the starting positions
for (j = i + 1; j < MAX_STREAMS; j++)
{
chan->frag_startpos[j] += chan->frag_length[i];
}
}
}
}
// Prepare the packet header
w1 = chan->outgoing_sequence | (send_reliable << 31);
w2 = chan->incoming_sequence | (chan->incoming_reliable_sequence << 31);
send_reliable_fragment = false;
for (i = 0; i < MAX_STREAMS; i++)
{
if (chan->reliable_fragment[i])
{
send_reliable_fragment = true;
break;
}
}
if (send_reliable && send_reliable_fragment)
{
w1 |= (1 << 30);
}
chan->outgoing_sequence++;
MSG_WriteLong(&sb_send, w1);
MSG_WriteLong(&sb_send, w2);
if (send_reliable && send_reliable_fragment)
{
for (i = 0; i < MAX_STREAMS; i++)
{
if (chan->reliable_fragment[i])
{
MSG_WriteByte(&sb_send, 1);
MSG_WriteLong(&sb_send, chan->reliable_fragid[i]);
MSG_WriteShort(&sb_send, chan->frag_startpos[i]);
MSG_WriteShort(&sb_send, chan->frag_length[i]);
}
else
{
MSG_WriteByte(&sb_send, 0);
}
}
}
// Copy the reliable message to the packet first
if (send_reliable) {
SZ_Write(&sb_send, chan->reliable_buf, chan->reliable_length);
chan->last_reliable_sequence = chan->outgoing_sequence - 1;
}
// Is there room for the unreliable payload?
int max_send_size = MAX_ROUTEABLE_PACKET;
if (!send_resending)
max_send_size = sb_send.maxsize;
if ((max_send_size - sb_send.cursize) >= length) {
SZ_Write(&sb_send, data, length);
}
else {
Con_DPrintf("Netchan_Transmit: Unreliable would overfow, ignoring\n");
}
// Deal with packets that are too small for some networks
if (sb_send.cursize < 16) // Packet too small for some networks
{
int i;
// Go ahead and pad a full 16 extra bytes -- this only happens during authentication / signon
for (i = sb_send.cursize; i < 16; i++)
{
// Note that the server can parse svc_nop, too.
MSG_WriteByte(&sb_send, svc_nop);
}
}
int statId = chan->flow[FLOW_OUTGOING].current & 0x1F;
chan->flow[FLOW_OUTGOING].stats[statId].size = sb_send.cursize + UDP_HEADER_SIZE;
chan->flow[FLOW_OUTGOING].stats[statId].time = realtime;
chan->flow[FLOW_OUTGOING].current++;
Netchan_UpdateFlow(chan);
if (!g_pcls.demoplayback)
{
COM_Munge2(sb_send.data + 8, sb_send.cursize - 8, (unsigned char)(chan->outgoing_sequence - 1));
if (g_modfuncs.m_pfnProcessOutgoingNet)
g_modfuncs.m_pfnProcessOutgoingNet(chan, &sb_send);
NET_SendPacket(chan->sock, sb_send.cursize, sb_send.data, chan->remote_address);
}
if (g_psv.active && sv_lan.value != 0.0f && sv_lan_rate.value > MIN_RATE)
fRate = 1.0 / sv_lan_rate.value;
else
fRate = 1.0 / chan->rate;
if (chan->cleartime < realtime) {
chan->cleartime = realtime;
}
chan->cleartime += (sb_send.cursize + UDP_HEADER_SIZE) * fRate;
if (net_showpackets.value != 0.0f && net_showpackets.value != 2.0f) {
char c = (chan == &g_pcls.netchan) ? 'c' : 's';
Con_Printf(" %c --> sz=%i seq=%i ack=%i rel=%i tm=%f\n"
, c
, sb_send.cursize
, chan->outgoing_sequence - 1
, chan->incoming_sequence
, send_reliable ? 1 : 0
, (float)(chan == &g_pcls.netchan ? g_pcl.time : g_psv.time));
}
}
/* <65449> ../engine/net_chan.c:730 */
fragbuf_t *Netchan_FindBufferById(fragbuf_t **pplist, int id, qboolean allocate)
{
fragbuf_t *list = *pplist;
fragbuf_t *pnewbuf;
while (list)
{
if (list->bufferid == id)
return list;
list = list->next;
}
if (!allocate)
return NULL;
// Create new entry
pnewbuf = Netchan_AllocFragbuf();
pnewbuf->bufferid = id;
Netchan_AddBufferToList(pplist, pnewbuf);
return pnewbuf;
}
/* <65e8c> ../engine/net_chan.c:767 */
void Netchan_CheckForCompletion(netchan_t *chan, int stream, int intotalbuffers)
{
int c;
int size;
int id;
fragbuf_t *p;
size = 0;
c = 0;
p = chan->incomingbufs[stream];
if (!p)
return;
while (p)
{
size += p->frag_message.cursize;
c++;
id = FRAG_GETID(p->bufferid);
if (id != c && chan == &g_pcls.netchan)
{
if (chan->sock == NS_MULTICAST)
{
char szCommand[32];
Q_snprintf(szCommand, sizeof(szCommand), "listen %s\n", NET_AdrToString(chan->remote_address));
Cbuf_AddText(szCommand);
return;
}
Con_Printf("Netchan_CheckForCompletion: Lost/dropped fragment would cause stall, retrying connection\n");
Cbuf_AddText("retry\n");
}
p = p->next;
}
// Received final message
if (c == intotalbuffers)
{
chan->incomingready[stream] = true;
}
}
/* <65f1e> ../engine/net_chan.c:826 */
qboolean Netchan_Validate(netchan_t *chan, qboolean *frag_message, unsigned int *fragid, int *frag_offset, int *frag_length)
{
for (int i = 0; i < MAX_STREAMS; i++)
{
if (!frag_message[i])
continue;
#ifndef REHLDS_FIXES
if (FRAG_GETID(fragid[i]) > MAX_FRAGMENTS || FRAG_GETCOUNT(fragid[i]) > MAX_FRAGMENTS)
{
return FALSE;
}
if ((unsigned int)frag_length[i] > 0x800 || (unsigned int)frag_offset[i] > 0x4000)
{
return FALSE;
}
#else // REHLDS_FIXES
// total fragments should be <= MAX_FRAGMENTS and current fragment can't be > total fragments
if (FRAG_GETCOUNT(fragid[i]) > MAX_FRAGMENTS || FRAG_GETID(fragid[i]) > FRAG_GETCOUNT(fragid[i]))
return FALSE;
if ((size_t)frag_length[i] > FRAGMENT_SIZE || (size_t)frag_offset[i] > NET_MAX_PAYLOAD - 1)
return FALSE;
int frag_end = frag_offset[i] + frag_length[i];
// end of fragment is out of the packet
if (frag_end + msg_readcount > net_message.cursize)
return FALSE;
// fragment overlaps next stream's fragment or placed after it
for (int j = i + 1; j < MAX_STREAMS; j++)
{
if (frag_end > frag_offset[j] && frag_message[j]) // don't add msg_readcount for comparison
return FALSE;
}
#endif // REHLDS_FIXES
}
return TRUE;
}
/* <65fe9> ../engine/net_chan.c:872 */
qboolean Netchan_Process(netchan_t *chan)
{
// int i; // 874
// unsigned int sequence; // 875
// unsigned int sequence_ack; // 875
// unsigned int reliable_ack; // 876
// unsigned int reliable_message; // 876
// unsigned int fragid; // 877
// qboolean frag_message; // 878
// int frag_offset; // 879
// int frag_length; // 880
// qboolean message_contains_fragments; // 881
// Netchan_Validate(netchan_t *chan,
// qboolean *frag_message,
// unsigned int *fragid,
// int *frag_offset,
// int *frag_length); /* size=0, low_pc=0 */ // 933
// {
// char c; // 946
// int mask; // 947
// }
// {
// int j; // 1038
// unsigned char *src; // 1039
// unsigned char *dst; // 1039
// int len; // 1040
// fragbuf_t *pbuf; // 1041
// int inbufferid; // 1042
// int intotalbuffers; // 1043
// Netchan_FindBufferById(fragbuf_t **pplist,
// int id,
// qboolean allocate); /* size=0, low_pc=0 */ // 1053
// {
// int nbytes; // 1056
// }
// }
int i;
unsigned int sequence, sequence_ack;
unsigned int reliable_ack, reliable_message;
unsigned int fragid[MAX_STREAMS] = { 0, 0 };
qboolean frag_message[MAX_STREAMS] = { false, false };
int frag_offset[MAX_STREAMS] = { 0, 0 };
int frag_length[MAX_STREAMS] = { 0, 0 };
qboolean message_contains_fragments;
if (!g_pcls.demoplayback && !g_pcls.passive)
{
if (!NET_CompareAdr(net_from, chan->remote_address))
return FALSE;
}
chan->last_received = realtime;
// get sequence numbers
MSG_BeginReading();
sequence = MSG_ReadLong();
sequence_ack = MSG_ReadLong();
if (sequence_ack & 0x40000000)
{
if (!g_modfuncs.m_pfnProcessIncomingNet)
return FALSE;
}
if (g_modfuncs.m_pfnProcessIncomingNet)
{
if (!g_modfuncs.m_pfnProcessIncomingNet(chan, &net_message))
return FALSE;
}
reliable_message = sequence >> 31;
reliable_ack = sequence_ack >> 31;
message_contains_fragments = sequence & (1 << 30) ? true : false;
COM_UnMunge2(&net_message.data[8], net_message.cursize - 8, sequence & 0xFF);
if (message_contains_fragments)
{
for (i = 0; i < MAX_STREAMS; i++)
{
if (MSG_ReadByte())
{
frag_message[i] = true;
fragid[i] = MSG_ReadLong();
frag_offset[i] = MSG_ReadShort();
frag_length[i] = MSG_ReadShort();
}
}
if (!Netchan_Validate(chan, frag_message, fragid, frag_offset, frag_length))
return FALSE;
}
sequence &= ~(1 << 31);
sequence &= ~(1 << 30);
sequence_ack &= ~(1 << 31);
sequence_ack &= ~(1 << 30);
if (net_showpackets.value != 0.0 && net_showpackets.value != 3.0)
{
char c = (chan == &g_pcls.netchan) ? 'c' : 's';
Con_Printf(
" %c <-- sz=%i seq=%i ack=%i rel=%i tm=%f\n",
c,
net_message.cursize,
sequence,
sequence_ack,
reliable_message,
(chan == &g_pcls.netchan) ? g_pcl.time : g_psv.time);
}
if (sequence <= (unsigned)chan->incoming_sequence)
{
if (net_showdrop.value != 0.0) {
if (sequence == (unsigned)chan->incoming_sequence)
Con_Printf("%s:duplicate packet %i at %i\n", NET_AdrToString(chan->remote_address), sequence, chan->incoming_sequence);
else
Con_Printf("%s:out of order packet %i at %i\n", NET_AdrToString(chan->remote_address), sequence, chan->incoming_sequence);
}
return FALSE;
}
//
// dropped packets don't keep the message from being used
//
net_drop = sequence - (chan->incoming_sequence + 1);
if (net_drop > 0 && net_showdrop.value != 0.0)
{
Con_Printf("%s:Dropped %i packets at %i\n", NET_AdrToString(chan->remote_address), net_drop, sequence);
}
//
// if the current outgoing reliable message has been acknowledged
// clear the buffer to make way for the next
//
if (reliable_ack == (unsigned)chan->reliable_sequence)
{
// Make sure we actually could have ack'd this message
if (chan->incoming_acknowledged + 1 >= chan->last_reliable_sequence)
{
chan->reliable_length = 0; // it has been received
}
}
//
// if this message contains a reliable message, bump incoming_reliable_sequence
//
chan->incoming_sequence = sequence;
chan->incoming_acknowledged = sequence_ack;
chan->incoming_reliable_acknowledged = reliable_ack;
if (reliable_message)
{
chan->incoming_reliable_sequence ^= 1;
}
int statId = chan->flow[FLOW_INCOMING].current & 0x1F;
chan->flow[FLOW_INCOMING].stats[statId].size = net_message.cursize + UDP_HEADER_SIZE;
chan->flow[FLOW_INCOMING].stats[statId].time = realtime;
chan->flow[FLOW_INCOMING].current++;
Netchan_UpdateFlow(chan);
if (message_contains_fragments)
{
for (i = 0; i < MAX_STREAMS; i++)
{
int j;
fragbuf_t *pbuf;
int inbufferid;
int intotalbuffers;
if (!frag_message[i])
continue;
inbufferid = FRAG_GETID(fragid[i]);
intotalbuffers = FRAG_GETCOUNT(fragid[i]);
if (fragid[i] != 0)
{
pbuf = Netchan_FindBufferById(&chan->incomingbufs[i], fragid[i], true);
if (pbuf) {
int len = frag_length[i];
SZ_Clear(&pbuf->frag_message);
SZ_Write(&pbuf->frag_message, &net_message.data[msg_readcount + frag_offset[i]], len);
}
else {
Con_Printf("Netchan_Process: Couldn't allocate or find buffer %i\n", inbufferid);
}
// Count # of incoming bufs we've queued? are we done?
Netchan_CheckForCompletion(chan, i, intotalbuffers);
}
// Rearrange incoming data to not have the frag stuff in the middle of it
int wpos = msg_readcount + frag_offset[i];
int rpos = wpos + frag_length[i];
Q_memmove(net_message.data + wpos, net_message.data + rpos, net_message.cursize - rpos);
net_message.cursize -= frag_length[i];
for (j = i + 1; j < MAX_STREAMS; j++)
{
frag_offset[j] -= frag_length[i]; // fragments order already validated
}
}
// Is there anything left to process?
if (net_message.cursize <= 16)
return FALSE;
}
return TRUE;
}
/* <65aeb> ../engine/net_chan.c:1123 */
void Netchan_FragSend(netchan_t *chan)
{
fragbufwaiting_t *wait;
int i;
if (!chan)
return;
for (i = 0; i < MAX_STREAMS; i++)
{
// Already something queued up, just leave in waitlist
if (chan->fragbufs[i])
{
continue;
}
wait = chan->waitlist[i];
// Nothing to queue?
if (!wait)
{
continue;
}
chan->waitlist[i] = wait->next;
wait->next = NULL;
// Copy in to fragbuf
chan->fragbufs[i] = wait->fragbufs;
chan->fragbufcount[i] = wait->fragbufcount;
// Throw away wait list
Mem_Free(wait);
}
}
/* <66273> ../engine/net_chan.c:1165 */
void Netchan_AddBufferToList(fragbuf_t **pplist, fragbuf_t *pbuf)
{
// Find best slot
fragbuf_t *pprev, *n;
int id1, id2;
pbuf->next = NULL;
if (!pplist)
return;
if (!*pplist)
{
pbuf->next = *pplist;
*pplist = pbuf;
return;
}
pprev = *pplist;
while (pprev->next)
{
n = pprev->next; // Next item in list
id1 = FRAG_GETID(n->bufferid);
id2 = FRAG_GETID(pbuf->bufferid);
if (id1 > id2)
{
// Insert here
pbuf->next = n->next;
pprev->next = pbuf;
return;
}
pprev = pprev->next;
}
// Insert at end
pprev->next = pbuf;
}
/* <6554c> ../engine/net_chan.c:1211 */
fragbuf_t *Netchan_AllocFragbuf(void)
{
fragbuf_t *buf;
buf = (fragbuf_t *)Mem_ZeroMalloc(sizeof(fragbuf_t));
buf->bufferid = 0;
buf->frag_message.cursize = 0;
buf->frag_message.data = buf->frag_message_buf;
buf->frag_message.maxsize = FRAGMENT_SIZE;
buf->frag_message.buffername = "Frag Buffer Alloc'd";
buf->next = 0;
return buf;
}
/* <65302> ../engine/net_chan.c:1234 */
void Netchan_AddFragbufToTail(fragbufwaiting_t *wait, fragbuf_t *buf)
{
fragbuf_t *p;
buf->next = 0;
wait->fragbufcount++;
p = wait->fragbufs;
if (p)
{
while (p->next)
{
p = p->next;
}
p->next = buf;
}
else
{
wait->fragbufs = buf;
}
}
/* <6556c> ../engine/net_chan.c:1261 */
void Netchan_CreateFragments_(qboolean server, netchan_t *chan, sizebuf_t *msg)
{
fragbuf_t *buf;
int chunksize;
int send;
int remaining;
int pos;
int bufferid = 1;
fragbufwaiting_t *wait, *p;
if (msg->cursize == 0)
{
return;
}
// Compress if not done already
if (*(uint32 *)msg->data != MAKEID('B', 'Z', '2', '\0'))
{
unsigned char compressed[65536];
char hdr[4] = "BZ2";
unsigned int compressedSize = msg->cursize - sizeof(hdr); // we should fit in same data buffer minus 4 bytes for a header
if (!BZ2_bzBuffToBuffCompress((char *)compressed, &compressedSize, (char *)msg->data, msg->cursize, 9, 0, 30))
{
Con_DPrintf("Compressing split packet (%d -> %d bytes)\n", msg->cursize, compressedSize);
Q_memcpy(msg->data, hdr, sizeof(hdr));
Q_memcpy(msg->data + sizeof(hdr), compressed, compressedSize);
msg->cursize = compressedSize + sizeof(hdr);
}
}
#ifdef REHLDS_FIXES
chunksize = clamp(chan->pfnNetchan_Blocksize(chan->connection_status), 64, 1200);
#else
chunksize = chan->pfnNetchan_Blocksize(chan->connection_status);
#endif // REHLDS_FIXES
wait = (fragbufwaiting_t *)Mem_ZeroMalloc(sizeof(fragbufwaiting_t));
remaining = msg->cursize;
pos = 0;
while (remaining > 0)
{
send = min(remaining, chunksize);
remaining -= send;
buf = Netchan_AllocFragbuf();
if (!buf)
{
return;
}
buf->bufferid = bufferid++;
// Copy in data
SZ_Clear(&buf->frag_message);
SZ_Write(&buf->frag_message, &msg->data[pos], send);
pos += send;
Netchan_AddFragbufToTail(wait, buf);
}
// Now add waiting list item to the end of buffer queue
if (!chan->waitlist[FRAG_NORMAL_STREAM])
{
chan->waitlist[FRAG_NORMAL_STREAM] = wait;
}
else
{
p = chan->waitlist[FRAG_NORMAL_STREAM];
while (p->next)
{
p = p->next;
}
p->next = wait;
}
}
/* <6635e> ../engine/net_chan.c:1353 */
void Netchan_CreateFragments(qboolean server, netchan_t *chan, sizebuf_t *msg)
{
// Always queue any pending reliable data ahead of the fragmentation buffer
if (chan->message.cursize > 0)
{
Netchan_CreateFragments_(server, chan, &chan->message);
chan->message.cursize = 0;
}
Netchan_CreateFragments_(server, chan, msg);
}
/* <663e4> ../engine/net_chan.c:1371 */
void Netchan_CreateFileFragmentsFromBuffer(qboolean server, netchan_t *chan, const char *filename, unsigned char *uncompressed_pbuf, int uncompressed_size)
{
int chunksize;
int send;
fragbufwaiting_t *p;
fragbuf_t *buf;
unsigned char *pbuf;
signed int bCompressed;
signed int firstfragment;
signed int bufferid;
int remaining;
int pos;
unsigned int size;
fragbufwaiting_t *wait;
if (!uncompressed_size == 0)
return;
bufferid = 1;
firstfragment = TRUE;
size = uncompressed_size;
pbuf = (unsigned char *)Mem_Malloc(uncompressed_size);
if (BZ2_bzBuffToBuffCompress((char*)pbuf, &size, (char*)uncompressed_pbuf, uncompressed_size, 9, 0, 30))
{
bCompressed = FALSE;
Mem_Free(pbuf);
pbuf = uncompressed_pbuf;
size = uncompressed_size;
}
else
{
bCompressed = TRUE;
Con_DPrintf("Compressed %s for transmission (%d -> %d)\n", filename, uncompressed_size, size);
}
chunksize = chan->pfnNetchan_Blocksize(chan->connection_status);
send = chunksize;
wait = (fragbufwaiting_t *)Mem_ZeroMalloc(0xCu);
remaining = size;
pos = 0;
while (remaining > 0)
{
send = min(remaining, chunksize);
buf = (fragbuf_t *)Netchan_AllocFragbuf();
if (!buf)
{
Con_Printf("Couldn't allocate fragbuf_t\n");
Mem_Free(wait);
if (server)
SV_DropClient(host_client, 0, "Malloc problem");
else
rehlds_syserror(__FUNCTION__ "Reverse me: client-side code");
#ifdef REHLDS_FIXES
if (bCompressed) {
Mem_Free(pbuf);
}
#endif
return;
}
buf->bufferid = bufferid++;
SZ_Clear(&buf->frag_message);
if (firstfragment)
{
firstfragment = FALSE;
MSG_WriteString(&buf->frag_message, filename);
MSG_WriteString(&buf->frag_message, bCompressed ? "bz2" : "uncompressed");
MSG_WriteLong(&buf->frag_message, uncompressed_size);
send -= buf->frag_message.cursize;
}
buf->isbuffer = TRUE;
buf->isfile = TRUE;
buf->size = send;
buf->foffset = pos;
MSG_WriteBuf(&buf->frag_message, send, &pbuf[pos]);
pos += send;
remaining -= send;
Netchan_AddFragbufToTail(wait, buf);
}
if (!chan->waitlist[FRAG_FILE_STREAM]) {
chan->waitlist[FRAG_FILE_STREAM] = wait;
}
else
{
p = chan->waitlist[FRAG_FILE_STREAM];
while (p->next)
p = p->next;
p->next = wait;
}
#ifdef REHLDS_FIXES
if (bCompressed) {
Mem_Free(pbuf);
}
#endif
}
/* <66564> ../engine/net_chan.c:1500 */
int Netchan_CreateFileFragments(qboolean server, netchan_t *chan, const char *filename)
{
int chunksize;
int compressedFileTime;
FileHandle_t hfile;
signed int filesize;
int remaining;
fragbufwaiting_t *p;
int send;
fragbuf_t *buf;
char compressedfilename[MAX_PATH];
int firstfragment;
int bufferid;
int bCompressed;
int pos;
fragbufwaiting_t *wait;
int uncompressed_size;
bufferid = 1;
firstfragment = TRUE;
bCompressed = FALSE;
chunksize = chan->pfnNetchan_Blocksize(chan->connection_status);
Q_snprintf(compressedfilename, sizeof compressedfilename, "%s.ztmp", filename);
compressedFileTime = FS_GetFileTime(compressedfilename);
if (compressedFileTime >= FS_GetFileTime(filename) && (hfile = FS_Open(compressedfilename, "rb")))
{
filesize = FS_Size(hfile);
FS_Close(hfile);
bCompressed = 1;
hfile = FS_Open(filename, "rb");
if (!hfile)
{
Con_Printf("Warning: Unable to open %s for transfer\n", filename);
return 0;
}
uncompressed_size = FS_Size(hfile);
if (uncompressed_size > sv_filetransfermaxsize.value)
{
FS_Close(hfile);
Con_Printf("Warning: File %s is too big to transfer from host %s\n", filename, NET_AdrToString(chan->remote_address));
return 0;
}
}
else
{
hfile = FS_Open(filename, "rb");
if (!hfile)
{
Con_Printf("Warning: Unable to open %s for transfer\n", filename);
return 0;
}
filesize = FS_Size(hfile);
if (filesize > sv_filetransfermaxsize.value)
{
FS_Close(hfile);
Con_Printf("Warning: File %s is too big to transfer from host %s\n", filename, NET_AdrToString(chan->remote_address));
return 0;
}
uncompressed_size = filesize;
if (sv_filetransfercompression.value != 0.0)
{
unsigned char* uncompressed = (unsigned char*)Mem_Malloc(filesize);
unsigned char* compressed = (unsigned char*)Mem_Malloc(filesize);
unsigned int compressedSize = filesize;
FS_Read(uncompressed, filesize, 1, hfile);
if (BZ_OK == BZ2_bzBuffToBuffCompress((char*)compressed, &compressedSize, (char*)uncompressed, filesize, 9, 0, 30))
{
FileHandle_t destFile = FS_Open(compressedfilename, "wb");
if (destFile)
{
Con_DPrintf("Creating compressed version of file %s (%d -> %d)\n", filename, filesize, compressedSize);
FS_Write(compressed, compressedSize, 1, destFile);
FS_Close(destFile);
filesize = compressedSize;
bCompressed = 1;
}
}
Mem_Free(uncompressed);
Mem_Free(compressed);
}
}
FS_Close(hfile);
wait = (fragbufwaiting_t *)Mem_ZeroMalloc(0xCu);
remaining = filesize;
pos = 0;
while (remaining)
{
send = min(chunksize, remaining);
buf = Netchan_AllocFragbuf();
if (!buf)
{
Con_Printf("Couldn't allocate fragbuf_t\n");
Mem_Free(wait);
if (server)
{
SV_DropClient(host_client, 0, "Malloc problem");
return 0;
}
else
{
rehlds_syserror(__FUNCTION__ ": Reverse clientside code");
return 0;
}
}
buf->bufferid = bufferid++;
SZ_Clear(&buf->frag_message);
if (firstfragment)
{
firstfragment = FALSE;
MSG_WriteString(&buf->frag_message, filename);
MSG_WriteString(&buf->frag_message, bCompressed ? "bz2" : "uncompressed");
MSG_WriteLong(&buf->frag_message, uncompressed_size);
send -= buf->frag_message.cursize;
}
buf->isfile = TRUE;
buf->iscompressed = bCompressed;
buf->size = send;
buf->foffset = pos;
Q_strncpy(buf->filename, filename, MAX_PATH - 1);
buf->filename[MAX_PATH - 1] = 0;
pos += send;
remaining -= send;
Netchan_AddFragbufToTail(wait, buf);
}
if (!chan->waitlist[FRAG_FILE_STREAM])
{
chan->waitlist[FRAG_FILE_STREAM] = wait;
}
else
{
p = chan->waitlist[FRAG_FILE_STREAM];
while (p->next)
p = p->next;
p->next = wait;
}
return 1;
}
/* <6674e> ../engine/net_chan.c:1699 */
void Netchan_FlushIncoming(netchan_t *chan, int stream)
{
fragbuf_t *p, *n;
SZ_Clear(&net_message);
msg_readcount = 0;
p = chan->incomingbufs[stream];
while (p)
{
n = p->next;
Mem_Free(p);
p = n;
};
chan->incomingbufs[stream] = NULL;
chan->incomingready[stream] = FALSE;
}
/* <66786> ../engine/net_chan.c:1723 */
qboolean Netchan_CopyNormalFragments(netchan_t *chan)
{
fragbuf_t *p, *n;
if (!chan->incomingready[FRAG_NORMAL_STREAM])
return FALSE;
if (!chan->incomingbufs[FRAG_NORMAL_STREAM])
{
Con_Printf("Netchan_CopyNormalFragments: Called with no fragments readied\n");
chan->incomingready[FRAG_NORMAL_STREAM] = FALSE;
return FALSE;
}
p = chan->incomingbufs[FRAG_NORMAL_STREAM];
SZ_Clear(&net_message);
MSG_BeginReading();
#ifdef REHLDS_FIXES
bool overflowed = false;
#endif // REHLDS_FIXES
while (p)
{
n = p->next;
#ifdef REHLDS_FIXES
if (net_message.cursize + p->frag_message.cursize <= net_message.maxsize)
SZ_Write(&net_message, p->frag_message.data, p->frag_message.cursize);
else
overflowed = true;
#else // REHLDS_FIXES
SZ_Write(&net_message, p->frag_message.data, p->frag_message.cursize);
#endif // REHLDS_FIXES
Mem_Free(p);
p = n;
}
#ifdef REHLDS_FIXES
if (overflowed)
{
Con_Printf("Netchan_CopyNormalFragments: Overflowed\n");
SZ_Clear(&net_message);
chan->incomingbufs[FRAG_NORMAL_STREAM] = NULL;
chan->incomingready[FRAG_NORMAL_STREAM] = false;
return FALSE;
}
#endif // REHLDS_FIXES
if (*(uint32 *)net_message.data == MAKEID('B', 'Z', '2', '\0'))
{
char uncompressed[65536];
unsigned int uncompressedSize = 65536;
BZ2_bzBuffToBuffDecompress(uncompressed, &uncompressedSize, (char*)net_message.data + 4, net_message.cursize - 4, 1, 0);
Q_memcpy(net_message.data, uncompressed, uncompressedSize);
net_message.cursize = uncompressedSize;
}
chan->incomingbufs[FRAG_NORMAL_STREAM] = NULL;
chan->incomingready[FRAG_NORMAL_STREAM] = false;
return TRUE;
}
/* <667f7> ../engine/net_chan.c:1789 */
qboolean Netchan_CopyFileFragments(netchan_t *chan)
{
fragbuf_t *p;
int nsize;
unsigned char *buffer;
int pos;
signed int cursize;
char filename[MAX_PATH];
char compressor[32];
fragbuf_s *n;
qboolean bCompressed;
unsigned int uncompressedSize;
if (!chan->incomingready[FRAG_FILE_STREAM])
return FALSE;
p = chan->incomingbufs[FRAG_FILE_STREAM];
if (!p)
{
Con_Printf("Netchan_CopyFileFragments: Called with no fragments readied\n");
chan->incomingready[FRAG_FILE_STREAM] = FALSE;
return FALSE;
}
bCompressed = FALSE;
SZ_Clear(&net_message);
MSG_BeginReading();
SZ_Write(&net_message, p->frag_message.data, p->frag_message.cursize);
Q_strncpy(filename, MSG_ReadString(), sizeof(filename) - 1);
filename[sizeof(filename) - 1] = 0;
Q_strncpy(compressor, MSG_ReadString(), sizeof(compressor) - 1);
compressor[sizeof(compressor) - 1] = 0;
if (!Q_stricmp(compressor, "bz2"))
bCompressed = TRUE;
uncompressedSize = (unsigned int)MSG_ReadLong();
#ifdef REHLDS_FIXES
if (uncompressedSize > 1024 * 64) {
Con_Printf("Received too large file (size=%u)\nFlushing input queue\n", uncompressedSize);
Netchan_FlushIncoming(chan, 1);
return FALSE;
}
#endif
if (Q_strlen(filename) <= 0)
{
Con_Printf("File fragment received with no filename\nFlushing input queue\n");
Netchan_FlushIncoming(chan, 1);
return FALSE;
}
if (Q_strstr(filename, ".."))
{
Con_Printf("File fragment received with relative path, ignoring\n");
Netchan_FlushIncoming(chan, 1);
return FALSE;
}
if (filename[0] != '!' && !IsSafeFileToDownload(filename))
{
Con_Printf("File fragment received with bad path, ignoring\n");
Netchan_FlushIncoming(chan, 1);
return FALSE;
}
if (g_pcls.state != ca_dedicated && filename[0] != '!')
{
Con_Printf("File fragment received with bad path, ignoring (2)\n");
Netchan_FlushIncoming(chan, 1);
return FALSE;
}
Q_strncpy(chan->incomingfilename, filename, MAX_PATH - 1);
chan->incomingfilename[MAX_PATH - 1] = 0;
if (filename[0] != '!' && FS_FileExists(filename))
{
Con_Printf("Can't download %s, already exists\n", filename);
Netchan_FlushIncoming(chan, 1);
return TRUE;
}
nsize = 0;
while (p)
{
nsize += p->frag_message.cursize;
if (p == chan->incomingbufs[FRAG_FILE_STREAM])
nsize -= msg_readcount;
p = p->next;
};
buffer = (unsigned char*)Mem_ZeroMalloc(nsize + 1);
if (!buffer)
{
Con_Printf("Buffer allocation failed on %i bytes\n", nsize + 1);
Netchan_FlushIncoming(chan, 1);
return FALSE;
}
p = chan->incomingbufs[FRAG_FILE_STREAM];
pos = 0;
while (p)
{
n = p->next;
cursize = p->frag_message.cursize;
// First message has the file name, don't write that into the data stream, just write the rest of the actual data
if (p == chan->incomingbufs[FRAG_FILE_STREAM])
{
// Copy it in
cursize -= msg_readcount;
Q_memcpy(&buffer[pos], &p->frag_message.data[msg_readcount], cursize);
p->frag_message.cursize = cursize;
}
else
{
Q_memcpy(&buffer[pos], p->frag_message.data, cursize);
}
pos += p->frag_message.cursize;
Mem_Free(p);
p = n;
}
if (bCompressed)
{
unsigned char* uncompressedBuffer = (unsigned char*)Mem_Malloc(uncompressedSize);
Con_DPrintf("Decompressing file %s (%d -> %d)\n", filename, nsize, uncompressedSize);
BZ2_bzBuffToBuffDecompress((char*)uncompressedBuffer, &uncompressedSize, (char*)buffer, nsize, 1, 0);
Mem_Free(buffer);
pos = uncompressedSize;
buffer = uncompressedBuffer;
}
if (filename[0] == '!')
{
if (chan->tempbuffer)
{
Con_DPrintf("Netchan_CopyFragments: Freeing holdover tempbuffer\n");
Mem_Free(chan->tempbuffer);
}
chan->tempbuffer = buffer;
chan->tempbuffersize = pos;
}
else
{
#ifdef REHLDS_FIXES
//Don't allow to write files to FS on server
if (chan == &g_pcls.netchan)
#endif // REHLDS_FIXES
{
char filedir[MAX_PATH];
char *pszFileName;
FileHandle_t handle;
#ifdef REHLDS_CHECKS
Q_strncpy(filedir, filename, sizeof(filedir) - 1);
filedir[sizeof(filedir) - 1] = 0;
#else
Q_strncpy(filedir, filename, sizeof(filedir));
#endif // REHLDS_CHECKS
COM_FixSlashes(filedir);
pszFileName = Q_strrchr(filedir, '\\');
if (pszFileName)
{
*pszFileName = 0;
#ifdef REHLDS_FIXES
FS_CreateDirHierarchy(filedir, "GAMEDOWNLOAD");
#endif
}
#ifndef REHLDS_FIXES
FS_CreateDirHierarchy(filedir, "GAMEDOWNLOAD");
#endif
handle = FS_OpenPathID(filename, "wb", "GAMEDOWNLOAD");
if (!handle)
{
Con_Printf("File open failed %s\n", filename);
Netchan_FlushIncoming(chan, 1);
#ifdef REHLDS_FIXES
Mem_Free(buffer);
#endif
return FALSE;
}
Sys_Printf("COM_WriteFile: %s\n", filename);
FS_Write(buffer, pos, 1, handle);
FS_Close(handle);
}
Mem_Free(buffer);
}
SZ_Clear(&net_message);
chan->incomingbufs[FRAG_FILE_STREAM] = 0;
chan->incomingready[FRAG_FILE_STREAM] = 0;
msg_readcount = 0;
return TRUE;
}
/* <66ac6> ../engine/net_chan.c:2009 */
NOXREF qboolean Netchan_IsSending(netchan_t *chan)
{
int i;
for (i = 0; i < MAX_STREAMS; i++)
{
if(chan->fragbufs[i])
return TRUE;
}
return FALSE;
}
/* <66b01> ../engine/net_chan.c:2028 */
NOXREF qboolean Netchan_IsReceiving(netchan_t *chan)
{
int i;
for (i = 0; i < MAX_STREAMS; i++)
{
if(chan->incomingbufs[i])
return TRUE;
}
return FALSE;
}
/* <66b3c> ../engine/net_chan.c:2047 */
qboolean Netchan_IncomingReady(netchan_t *chan)
{
for (int i = 0; i < MAX_STREAMS; i++)
{
if (chan->incomingready[i])
return TRUE;
}
return FALSE;
}
/* <66b77> ../engine/net_chan.c:2067 */
NOXREF void Netchan_UpdateProgress(netchan_t *chan)
{
fragbuf_t *p;
int c = 0;
int total = 0;
int i;
float bestpercent = 0.0f;
float percent;
char sz[1024];
char *in;
char *out;
int len = 0;
scr_downloading.value = -1.0f;
gDownloadFile[0] = 0;
if (net_drawslider.value != 1.0f)
{
if (!chan->incomingbufs[FRAG_FILE_STREAM])
return;
}
for (i = MAX_STREAMS - 1; i >= 0; i--)
{
if (chan->incomingbufs[i])
{
p = chan->incomingbufs[i];
total = FRAG_GETCOUNT(p->bufferid);
while (p)
{
c++;
p = p->next;
}
if (total)
{
percent = (float)c * 100.0f / (float)total;
if (percent > bestpercent)
bestpercent = percent;
}
p = chan->incomingbufs[i];
if (i == FRAG_FILE_STREAM)
{
in = (char *)p->frag_message.data;
out = sz;
while (*in)
{
*out++ = *in++;
len++;
if (len > 128)
break;
}
*out = '\0';
if (Q_strlen(sz) > 0 && (*sz != '!' || !Q_strncmp(sz, "!ModuleC.dll", 11)))
{
Q_strncpy(gDownloadFile, sz, 255);
gDownloadFile[255] = 0;
}
}
}
else if (chan->fragbufs[i])
{
if (chan->fragbufcount[i])
{
percent = (float)chan->fragbufs[i]->bufferid * 100.0f / (float)chan->fragbufcount[i];
if (percent > bestpercent)
bestpercent = percent;
}
}
}
scr_downloading.value = bestpercent;
}
/* <66c7c> ../engine/net_chan.c:2170 */
void Netchan_Init(void)
{
Cvar_RegisterVariable(&net_log);
Cvar_RegisterVariable(&net_showpackets);
Cvar_RegisterVariable(&net_showdrop);
Cvar_RegisterVariable(&net_chokeloopback);
Cvar_RegisterVariable(&net_drawslider);
Cvar_RegisterVariable(&sv_filetransfercompression);
Cvar_RegisterVariable(&sv_filetransfermaxsize);
}
/* <65409> ../engine/net_chan.c:2186 */
NOXREF qboolean Netchan_CompressPacket(sizebuf_t *chan)
{
return FALSE;
}
/* <65429> ../engine/net_chan.c:2211 */
NOXREF qboolean Netchan_DecompressPacket(sizebuf_t *chan)
{
return FALSE;
}