mirror of
https://github.com/rehlds/rehlds.git
synced 2025-04-12 04:20:06 +03:00
1287 lines
28 KiB
C++
1287 lines
28 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"
|
|
|
|
NetChannel::NetChannel()
|
|
{
|
|
for (int i = 0; i < MAX_STREAMS; i++)
|
|
{
|
|
m_waitlist[i] = nullptr;
|
|
m_fragbufs[i] = nullptr;
|
|
m_incomingbufs[i] = nullptr;
|
|
|
|
m_frag_length[i] = 0;
|
|
m_frag_startpos[i] = 0;
|
|
m_fragbufcount[i] = 0;
|
|
|
|
m_reliable_fragid[i] = 0;
|
|
m_reliable_fragment[i] = 0;
|
|
}
|
|
|
|
m_Socket = nullptr;
|
|
m_tempBuffer = nullptr;
|
|
}
|
|
|
|
void NetChannel::UnlinkFragment(fragbuf_t *buf, int stream)
|
|
{
|
|
fragbuf_t *search;
|
|
fragbuf_t **list = &m_fragbufs[stream];
|
|
|
|
if (list == nullptr)
|
|
{
|
|
m_System->DPrintf("Netchan_UnlinkFragment: Asked to unlink fragment from empty list, ignored\n");
|
|
return;
|
|
}
|
|
|
|
if (*list == buf)
|
|
{
|
|
*list = buf->next;
|
|
free(buf);
|
|
return;
|
|
}
|
|
|
|
search = *list;
|
|
while (search->next)
|
|
{
|
|
if (search->next == buf)
|
|
{
|
|
search->next = buf->next;
|
|
free(buf);
|
|
return;
|
|
}
|
|
|
|
search = search->next;
|
|
}
|
|
|
|
m_System->DPrintf("Netchan_UnlinkFragment: Couldn't find fragment\n");
|
|
}
|
|
|
|
void NetChannel::OutOfBandPrintf(const char *format, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[NET_MAX_MESSAGE];
|
|
BitBuffer data(string, sizeof(string));
|
|
|
|
if (m_Socket)
|
|
{
|
|
*(int *)string = CONNECTIONLESS_HEADER;
|
|
|
|
va_start(argptr, format);
|
|
_vsnprintf(&string[4], sizeof(string) - 4, format, argptr);
|
|
va_end(argptr);
|
|
|
|
data.SkipBytes(strlen(string));
|
|
m_Socket->SendPacket(&m_remote_address, data.GetData(), data.CurrentSize());
|
|
}
|
|
}
|
|
|
|
void NetChannel::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;
|
|
free(buf);
|
|
buf = n;
|
|
}
|
|
|
|
*ppbuf = nullptr;
|
|
}
|
|
|
|
void NetChannel::ClearFragments()
|
|
{
|
|
fragbufwaiting_t *wait, *next;
|
|
for (int i = 0; i < MAX_STREAMS; i++)
|
|
{
|
|
wait = m_waitlist[i];
|
|
while (wait)
|
|
{
|
|
next = wait->next;
|
|
ClearFragbufs(&wait->fragbufs);
|
|
free(wait);
|
|
wait = next;
|
|
}
|
|
m_waitlist[i] = nullptr;
|
|
|
|
ClearFragbufs(&m_fragbufs[i]);
|
|
FlushIncoming(i);
|
|
}
|
|
}
|
|
|
|
void NetChannel::FlushIncoming(int stream)
|
|
{
|
|
fragbuf_t *p, *n;
|
|
|
|
NetPacket *in;
|
|
while ((in = (NetPacket *)m_incomingPackets.RemoveTail())) {
|
|
FreePacket(in);
|
|
}
|
|
|
|
p = m_incomingbufs[stream];
|
|
while (p)
|
|
{
|
|
n = p->next;
|
|
free(p);
|
|
p = n;
|
|
}
|
|
|
|
m_incomingbufs[stream] = nullptr;
|
|
}
|
|
|
|
void NetChannel::Reset()
|
|
{
|
|
m_keep_alive = true;
|
|
m_crashed = false;
|
|
m_connected = false;
|
|
|
|
m_connect_time = m_System->GetTime();
|
|
|
|
SetTimeOut(30);
|
|
SetRate(10000);
|
|
SetUpdateRate(20);
|
|
|
|
m_incoming_sequence = 0;
|
|
m_incoming_acknowledged = 0;
|
|
m_incoming_reliable_acknowledged = 0;
|
|
m_incoming_reliable_sequence = 0;
|
|
m_outgoing_sequence = 1;
|
|
m_reliable_sequence = 0;
|
|
m_last_reliable_sequence = 0;
|
|
}
|
|
|
|
void NetChannel::Clear()
|
|
{
|
|
NetPacket *packet;
|
|
while ((packet = (NetPacket *)m_incomingPackets.RemoveHead())) {
|
|
FreePacket(packet);
|
|
}
|
|
|
|
ClearFragments();
|
|
|
|
for (int i = 0; i < MAX_STREAMS; i++)
|
|
{
|
|
m_reliable_fragid[i] = 0;
|
|
m_reliable_fragment[i] = 0;
|
|
m_fragbufcount[i] = 0;
|
|
m_frag_startpos[i] = 0;
|
|
m_frag_length[i] = 0;
|
|
}
|
|
|
|
if (m_tempBuffer)
|
|
{
|
|
free(m_tempBuffer);
|
|
m_tempBuffer = nullptr;
|
|
}
|
|
|
|
m_tempBufferSize = 0;
|
|
m_reliableOutSize = 0;
|
|
|
|
memset(m_reliableOutBuffer, 0, sizeof(m_reliableOutBuffer));
|
|
memset(m_flow, 0, sizeof(m_flow));
|
|
|
|
m_reliableStream.Clear();
|
|
m_unreliableStream.Clear();
|
|
|
|
m_last_send =
|
|
m_last_received =
|
|
m_cleartime = m_System->GetTime();
|
|
|
|
m_loss = 0;
|
|
}
|
|
|
|
bool NetChannel::Create(IBaseSystem *system, INetSocket *netsocket, NetAddress *adr)
|
|
{
|
|
m_System = system;
|
|
m_incomingPackets.Init();
|
|
m_blocksize = FRAGMENT_S2C_MAX_SIZE;
|
|
|
|
if (!m_reliableStream.Resize(MAX_MSGLEN))
|
|
{
|
|
m_System->Errorf("NetChannel::Create: m_reliableStream out of memory.\n");
|
|
return false;
|
|
}
|
|
|
|
if (!m_unreliableStream.Resize(MAX_MSGLEN))
|
|
{
|
|
m_System->Errorf("NetChannel::Create: m_unreliableStream out of memory.\n");
|
|
return false;
|
|
}
|
|
|
|
m_remote_address.FromNetAddress(adr);
|
|
m_Socket = netsocket;
|
|
if (m_Socket)
|
|
{
|
|
if (!m_Socket->AddChannel(this))
|
|
{
|
|
m_System->Errorf("NetChannel::Create: could not bound to NetSocket\n");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_System->DPrintf("Creating fake network channel.\n");
|
|
}
|
|
|
|
Clear();
|
|
Reset();
|
|
|
|
return true;
|
|
}
|
|
|
|
void NetChannel::Close()
|
|
{
|
|
if (m_Socket) {
|
|
m_Socket->RemoveChannel(this);
|
|
}
|
|
|
|
Clear();
|
|
m_reliableStream.Free();
|
|
m_unreliableStream.Free();
|
|
}
|
|
|
|
bool NetChannel::IsReadyToSend()
|
|
{
|
|
if (m_System->GetTime() > m_cleartime) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void NetChannel::UpdateFlow(int stream)
|
|
{
|
|
int bytes = 0;
|
|
float faccumulatedtime = 0.0f;
|
|
flow_t *pflow = &m_flow[stream];
|
|
|
|
if (pflow->nextcompute > m_System->GetTime()) {
|
|
return;
|
|
}
|
|
|
|
pflow->nextcompute = m_System->GetTime() + 0.5f;
|
|
|
|
flowstats_t *pstat;
|
|
int start = pflow->current - 1;
|
|
for (int i = 1; i < (MAX_LATENT / 2); i++)
|
|
{
|
|
pstat = &pflow->stats[(start - i) & 0x1f];
|
|
bytes += pstat->size;
|
|
}
|
|
|
|
faccumulatedtime = m_System->GetTime() - pstat->time;
|
|
|
|
pflow->kbytespersec = (faccumulatedtime == 0.0f) ? 0.0f : bytes / faccumulatedtime / 1024.0f;
|
|
pflow->avgkbytespersec = pflow->avgkbytespersec * (2.0 / 3) + pflow->kbytespersec * (1.0 / 3);
|
|
}
|
|
|
|
void NetChannel::TransmitOutgoing()
|
|
{
|
|
byte send_buf[NET_MAX_MESSAGE];
|
|
BitBuffer data(send_buf, sizeof(send_buf));
|
|
|
|
bool send_reliable;
|
|
bool send_reliable_fragment;
|
|
bool send_resending = false;
|
|
unsigned w1, w2;
|
|
int i, j;
|
|
|
|
if (IsFakeChannel())
|
|
{
|
|
m_outgoing_sequence++;
|
|
|
|
m_last_send = m_System->GetTime();
|
|
m_cleartime = m_last_send + m_send_interval;
|
|
|
|
m_reliableStream.FastClear();
|
|
m_unreliableStream.FastClear();
|
|
|
|
FakeAcknowledgement();
|
|
return;
|
|
}
|
|
|
|
// check for reliable message overflow
|
|
if (m_reliableStream.IsOverflowed())
|
|
{
|
|
m_System->DPrintf("Transmit:Outgoing m_reliableStream overflow (%s)\n", m_remote_address.ToString());
|
|
m_reliableStream.Clear();
|
|
return;
|
|
}
|
|
|
|
// check for unreliable message overflow
|
|
if (m_unreliableStream.IsOverflowed())
|
|
{
|
|
m_System->DPrintf("Transmit:Outgoing m_unreliableStream overflow (%s)\n", m_remote_address.ToString());
|
|
m_unreliableStream.Clear();
|
|
}
|
|
|
|
// if the remote side dropped the last reliable message, resend it
|
|
send_reliable = false;
|
|
|
|
if (m_incoming_acknowledged > m_last_reliable_sequence && m_incoming_reliable_acknowledged != m_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 (!m_reliableOutSize)
|
|
{
|
|
bool 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 m_waitlist(s) into the current queue(s)
|
|
FragSend();
|
|
|
|
// Sending regular payload
|
|
send_from_regular = m_reliableStream.CurrentSize() ? 1 : 0;
|
|
|
|
// Check to see if we are sending a frag payload
|
|
for (i = 0; i < MAX_STREAMS; i++)
|
|
{
|
|
if (m_fragbufs[i])
|
|
{
|
|
send_from_frag[i] = 1;
|
|
}
|
|
}
|
|
|
|
/*if (m_reliableStream.CurrentSize() > sizeof(send_buf))
|
|
{
|
|
CreateFragmentsFromBuffer(m_reliableStream.GetData(), m_reliableStream.CurrentSize(), FRAG_NORMAL_STREAM);
|
|
m_reliableStream.FastClear();
|
|
}*/
|
|
|
|
// 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 (m_reliableStream.CurrentSize() > MAX_RELIABLE_PAYLOAD)
|
|
{
|
|
CreateFragmentsFromBuffer(m_reliableStream.GetData(), m_reliableStream.CurrentSize(), FRAG_NORMAL_STREAM);
|
|
m_reliableStream.FastClear();
|
|
}
|
|
}
|
|
|
|
// Startpos will be zero if there is no regular payload
|
|
for (i = 0; i < MAX_STREAMS; i++)
|
|
{
|
|
m_frag_startpos[i] = 0;
|
|
|
|
// Assume no fragment is being sent
|
|
m_reliable_fragment[i] = 0;
|
|
m_reliable_fragid[i] = 0;
|
|
m_frag_length[i] = 0;
|
|
|
|
if (send_from_frag[i])
|
|
{
|
|
send_frag = true;
|
|
}
|
|
}
|
|
|
|
if (send_from_regular || send_frag)
|
|
{
|
|
m_reliable_sequence ^= 1u;
|
|
send_reliable = true;
|
|
}
|
|
|
|
if (send_from_regular)
|
|
{
|
|
memcpy(m_reliableOutBuffer, m_reliableStream.GetData(), m_reliableStream.CurrentSize());
|
|
|
|
m_reliableOutSize = m_reliableStream.CurrentSize();
|
|
m_reliableStream.FastClear();
|
|
|
|
// If we send fragments, this is where they'll start
|
|
for (i = 0; i < MAX_STREAMS; i++) {
|
|
m_frag_startpos[i] = m_reliableOutSize;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MAX_STREAMS; i++)
|
|
{
|
|
int fragment_size = 0;
|
|
|
|
// Is there something in the fragbuf?
|
|
pbuf = m_fragbufs[i];
|
|
if (pbuf)
|
|
{
|
|
fragment_size = pbuf->size;
|
|
|
|
// 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 && (m_reliableOutSize + fragment_size) < MAX_RELIABLE_PAYLOAD)
|
|
{
|
|
m_reliable_fragid[i] = MAKE_FRAGID(pbuf->bufferId, m_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) {
|
|
m_System->Printf("TODO! NetChannel::Transmit: system file support\n");
|
|
}
|
|
|
|
memcpy(m_reliableOutBuffer + m_reliableOutSize, pbuf->data, pbuf->size);
|
|
|
|
m_reliableOutSize += pbuf->size;
|
|
m_frag_length[i] = pbuf->size;
|
|
|
|
// Unlink pbuf
|
|
UnlinkFragment(pbuf, i);
|
|
m_reliable_fragment[i] = 1;
|
|
|
|
// Offset the rest of the starting positions
|
|
for (j = i + 1; j < MAX_STREAMS; j++)
|
|
{
|
|
m_frag_startpos[j] += m_frag_length[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Prepare the packet header
|
|
w1 = m_outgoing_sequence | (send_reliable << 31);
|
|
w2 = m_incoming_sequence | (m_incoming_reliable_sequence << 31);
|
|
|
|
send_reliable_fragment = false;
|
|
|
|
for (i = 0; i < MAX_STREAMS; i++)
|
|
{
|
|
if (m_reliable_fragment[i])
|
|
{
|
|
send_reliable_fragment = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (send_reliable && send_reliable_fragment) {
|
|
w1 |= (1 << 30);
|
|
}
|
|
|
|
m_outgoing_sequence++;
|
|
|
|
data.Clear();
|
|
data.WriteLong(w1);
|
|
data.WriteLong(w2);
|
|
|
|
if (send_reliable && send_reliable_fragment)
|
|
{
|
|
for (i = 0; i < MAX_STREAMS; i++)
|
|
{
|
|
if (m_reliable_fragment[i])
|
|
{
|
|
data.WriteByte(1);
|
|
data.WriteLong(m_reliable_fragid[i]);
|
|
data.WriteShort(m_frag_startpos[i]);
|
|
data.WriteShort(m_frag_length[i]);
|
|
}
|
|
else
|
|
{
|
|
data.WriteByte(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy the reliable message to the packet first
|
|
if (send_reliable) {
|
|
data.WriteBuf(m_reliableOutBuffer, m_reliableOutSize);
|
|
m_last_reliable_sequence = m_outgoing_sequence - 1;
|
|
}
|
|
|
|
// Is there room for the unreliable payload?
|
|
int max_send_size = send_resending ? MAX_ROUTEABLE_PACKET : NET_MAX_MESSAGE;
|
|
if ((max_send_size - data.CurrentSize()) >= m_unreliableStream.CurrentSize()) {
|
|
data.ConcatBuffer(&m_unreliableStream);
|
|
}
|
|
else {
|
|
m_System->DPrintf("WARNING! TransmitOutgoing: Unreliable would overfow, ignoring.\n");
|
|
}
|
|
|
|
m_unreliableStream.FastClear();
|
|
|
|
// Deal with packets that are too small for some networks
|
|
// Packet too small for some networks
|
|
if (data.CurrentSize() < 16)
|
|
{
|
|
// Go ahead and pad a full 16 extra bytes -- this only happens during authentication / signon
|
|
for (int i = data.CurrentSize(); i < 16; i++)
|
|
{
|
|
// Note that the server can parse svc_nop, too.
|
|
data.WriteByte(svc_nop);
|
|
}
|
|
}
|
|
|
|
int statId = m_flow[FLOW_OUTGOING].current & 0x1f;
|
|
m_flow[FLOW_OUTGOING].stats[statId].size = data.CurrentSize() + UDP_HEADER_SIZE;
|
|
m_flow[FLOW_OUTGOING].stats[statId].time = m_System->GetTime();
|
|
m_flow[FLOW_OUTGOING].current++;
|
|
|
|
COM_Munge2(data.GetData() + 8, data.CurrentSize() - 8, (unsigned char)(m_outgoing_sequence - 1));
|
|
|
|
if (m_Socket) {
|
|
m_Socket->SendPacket(&m_remote_address, data.GetData(), data.CurrentSize());
|
|
}
|
|
|
|
m_last_send = m_System->GetTime();
|
|
m_cleartime = max(m_send_interval, (data.CurrentSize() + UDP_HEADER_SIZE) * (1.0 / m_max_bandwidth_rate)) + m_last_send;
|
|
}
|
|
|
|
NetChannel::fragbuf_t *NetChannel::FindBufferById(fragbuf_t **pplist, int id, bool allocate)
|
|
{
|
|
fragbuf_t *list = *pplist;
|
|
fragbuf_t *pnewbuf;
|
|
|
|
while (list)
|
|
{
|
|
if (list->bufferId == id)
|
|
return list;
|
|
|
|
list = list->next;
|
|
}
|
|
|
|
if (!allocate)
|
|
return nullptr;
|
|
|
|
// Create new entry
|
|
pnewbuf = (fragbuf_t *)Mem_ZeroMalloc(sizeof(fragbuf_t));
|
|
pnewbuf->bufferId = id;
|
|
AddBufferToList(pplist, pnewbuf);
|
|
|
|
return pnewbuf;
|
|
}
|
|
|
|
bool NetChannel::CheckForCompletion(int stream, int intotalbuffers)
|
|
{
|
|
int c;
|
|
int size;
|
|
int id;
|
|
fragbuf_t *p;
|
|
|
|
size = 0;
|
|
c = 0;
|
|
|
|
if (stream != FRAG_NORMAL_STREAM && stream != FRAG_FILE_STREAM) {
|
|
m_System->DPrintf("ERROR! NetChannel::CheckForCompletion: invalid stream number %i.\n");
|
|
return false;
|
|
}
|
|
|
|
p = m_incomingbufs[stream];
|
|
if (!p) {
|
|
return false;
|
|
}
|
|
|
|
while (p)
|
|
{
|
|
size += p->size;
|
|
c++;
|
|
|
|
id = FRAG_GETID(p->bufferId);
|
|
if (id != c)
|
|
{
|
|
m_System->DPrintf("WARNING! NetChannel::CheckForCompletion: lost/dropped fragment Lost/dropped fragment would cause stall, retrying connection\n");
|
|
m_crashed = true;
|
|
return false;
|
|
}
|
|
|
|
p = p->next;
|
|
}
|
|
|
|
// Received final message
|
|
if (c == intotalbuffers)
|
|
{
|
|
switch (stream)
|
|
{
|
|
case FRAG_NORMAL_STREAM:
|
|
CopyNormalFragments();
|
|
break;
|
|
case FRAG_FILE_STREAM:
|
|
m_System->Printf("TODO! NetChannel::CheckForCompletion: create file from fragments.\n");
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void NetChannel::ProcessIncoming(unsigned char *data, int size)
|
|
{
|
|
BitBuffer message(data, size);
|
|
|
|
int i;
|
|
unsigned int sequence, sequence_ack;
|
|
unsigned int reliable_ack, reliable_message;
|
|
unsigned int fragid[MAX_STREAMS] = { 0, 0 };
|
|
|
|
bool frag_message[MAX_STREAMS] = { false, false };
|
|
int frag_offset[MAX_STREAMS] = { 0, 0 };
|
|
int m_frag_length[MAX_STREAMS] = { 0, 0 };
|
|
|
|
bool message_contains_fragments;
|
|
int net_drop;
|
|
float newLoss;
|
|
float weight;
|
|
|
|
// get sequence numbers
|
|
sequence = message.ReadLong();
|
|
if (sequence == CONNECTIONLESS_HEADER)
|
|
{
|
|
NetPacket *p = new NetPacket;
|
|
|
|
p->connectionless = true;
|
|
p->time = m_System->GetTime();
|
|
p->seqnr = -1;
|
|
|
|
p->address.FromNetAddress(&m_remote_address);
|
|
p->data.Resize(size - 4);
|
|
p->data.WriteBuf(data + 4, size - 4);
|
|
p->data.Reset();
|
|
|
|
m_incomingPackets.AddHead(p);
|
|
return;
|
|
}
|
|
|
|
if (!m_connected) {
|
|
return;
|
|
}
|
|
|
|
sequence_ack = message.ReadLong();
|
|
|
|
COM_UnMunge2(message.GetData() + 8, size - 8, sequence & 0xFF);
|
|
|
|
reliable_message = sequence >> 31;
|
|
reliable_ack = sequence_ack >> 31;
|
|
message_contains_fragments = sequence & (1 << 30) ? true : false;
|
|
|
|
// TODO: Looks like need to move it above COM_UnMunge2
|
|
if (sequence_ack & 0x40000000)
|
|
{
|
|
m_crashed = true;
|
|
return;
|
|
}
|
|
|
|
if (message_contains_fragments)
|
|
{
|
|
for (i = 0; i < MAX_STREAMS; i++)
|
|
{
|
|
if (message.ReadByte())
|
|
{
|
|
frag_message[i] = true;
|
|
fragid[i] = message.ReadLong();
|
|
frag_offset[i] = message.ReadShort();
|
|
m_frag_length[i] = message.ReadShort();
|
|
}
|
|
}
|
|
}
|
|
|
|
sequence &= ~(1 << 31);
|
|
sequence &= ~(1 << 30);
|
|
|
|
sequence_ack &= ~(1 << 31);
|
|
sequence_ack &= ~(1 << 30);
|
|
|
|
if (sequence <= (unsigned int)m_incoming_sequence)
|
|
{
|
|
if (sequence == (unsigned int)m_incoming_sequence)
|
|
m_System->DPrintf("NetChannel::ProcessIncoming: duplicate packet %i at %i from %s\n", sequence, m_incoming_sequence, m_remote_address.ToString());
|
|
else
|
|
m_System->DPrintf("NetChannel::ProcessIncoming: out of order packet %i at %i from %s\n", sequence, m_incoming_sequence, m_remote_address.ToString());
|
|
|
|
return;
|
|
}
|
|
|
|
// dropped packets don't keep the message from being used
|
|
net_drop = sequence - (m_incoming_sequence + 1);
|
|
if (net_drop < 0) {
|
|
net_drop = 0;
|
|
}
|
|
|
|
newLoss = (net_drop + 1) * (1.0f / 200.0f);
|
|
if (newLoss < 1.0f)
|
|
{
|
|
weight = (float)net_drop / (float)(net_drop + 1);
|
|
m_loss = (1.0 - newLoss) * m_loss + weight * newLoss;
|
|
}
|
|
else
|
|
m_loss = 1;
|
|
|
|
// if the current outgoing reliable message has been acknowledged
|
|
// clear the buffer to make way for the next
|
|
if (reliable_ack == (unsigned int)m_reliable_sequence)
|
|
{
|
|
if (m_incoming_acknowledged + 1 >= m_last_reliable_sequence)
|
|
{
|
|
// it has been received
|
|
m_reliableOutSize = 0;
|
|
}
|
|
}
|
|
|
|
// if this message contains a reliable message, bump incoming_reliable_sequence
|
|
m_incoming_sequence = sequence;
|
|
m_incoming_acknowledged = sequence_ack;
|
|
m_incoming_reliable_acknowledged = reliable_ack;
|
|
|
|
if (reliable_message) {
|
|
m_incoming_reliable_sequence ^= 1u;
|
|
}
|
|
|
|
int statId = m_flow[FLOW_INCOMING].current & 0x1f;
|
|
m_flow[FLOW_INCOMING].stats[statId].size = size + UDP_HEADER_SIZE;
|
|
m_flow[FLOW_INCOMING].stats[statId].time = m_System->GetTime();
|
|
m_flow[FLOW_INCOMING].current++;
|
|
|
|
m_last_received = m_System->GetTime();
|
|
|
|
if (message_contains_fragments)
|
|
{
|
|
for (i = 0; i <= 1; ++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])
|
|
{
|
|
pbuf = FindBufferById(&m_incomingbufs[i], fragid[i], true);
|
|
if (pbuf)
|
|
{
|
|
memcpy(pbuf->data, message.GetData() + message.CurrentSize() + frag_offset[i], m_frag_length[i]);
|
|
pbuf->size = m_frag_length[i];
|
|
}
|
|
else
|
|
{
|
|
m_System->Printf("NetChannel::ProcessIncoming: couldn't allocate or find buffer %i\n", inbufferid);
|
|
}
|
|
|
|
// Count # of incoming bufs we've queued? are we done?
|
|
CheckForCompletion(i, intotalbuffers);
|
|
}
|
|
|
|
// Rearrange incoming data to not have the frag stuff in the middle of it
|
|
int wpos = message.CurrentSize() + frag_offset[i];
|
|
int rpos = wpos + m_frag_length[i];
|
|
|
|
memmove(message.GetData() + wpos, message.GetData() + rpos, message.GetMaxSize() - rpos);
|
|
message.m_MaxSize -= m_frag_length[i];
|
|
|
|
for (j = i + 1; j < MAX_STREAMS; j++)
|
|
{
|
|
// fragments order already validated
|
|
frag_offset[j] -= m_frag_length[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
int curLen = message.GetMaxSize() - message.CurrentSize();
|
|
if (curLen > 0)
|
|
{
|
|
NetPacket *p = new NetPacket;
|
|
p->connectionless = 0;
|
|
p->hasReliableData = reliable_message != 0;
|
|
p->time = m_System->GetTime();
|
|
p->seqnr = m_incoming_sequence;
|
|
p->address.FromNetAddress(&m_remote_address);
|
|
p->data.Resize(curLen);
|
|
p->data.WriteBuf(message.m_CurByte, curLen);
|
|
p->data.Reset();
|
|
m_incomingPackets.AddHead(p);
|
|
}
|
|
}
|
|
|
|
void NetChannel::FragSend()
|
|
{
|
|
fragbufwaiting_t *wait;
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_STREAMS; i++)
|
|
{
|
|
// Already something queued up, just leave in waitlist
|
|
if (m_fragbufs[i]) {
|
|
continue;
|
|
}
|
|
|
|
wait = m_waitlist[i];
|
|
|
|
// Nothing to queue?
|
|
if (!wait) {
|
|
continue;
|
|
}
|
|
|
|
m_waitlist[i] = wait->next;
|
|
wait->next = nullptr;
|
|
|
|
// Copy in to fragbuf
|
|
m_fragbufs[i] = wait->fragbufs;
|
|
m_fragbufcount[i] = wait->fragbufcount;
|
|
|
|
// Throw away wait list
|
|
free(wait);
|
|
}
|
|
}
|
|
|
|
void NetChannel::AddBufferToList(fragbuf_t **pplist, fragbuf_t *pbuf)
|
|
{
|
|
// Find best slot
|
|
fragbuf_t *pprev, *n;
|
|
int id1, id2;
|
|
|
|
pbuf->next = nullptr;
|
|
|
|
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;
|
|
}
|
|
|
|
bool NetChannel::CreateFragmentsFromBuffer(void *buffer, int size, int streamtype, char *filename)
|
|
{
|
|
fragbuf_t *buf;
|
|
int chunksize;
|
|
int sendsize;
|
|
int remaining;
|
|
int pos;
|
|
int bufferid = 1;
|
|
bool firstfragment = true;
|
|
fragbufwaiting_t *wait, *p;
|
|
|
|
if (IsFakeChannel())
|
|
{
|
|
m_System->Printf("NetChannel::CreateFragmentsFromBuffer: IsFakeChannel()\n");
|
|
return true;
|
|
}
|
|
|
|
if (size == 0) {
|
|
return true;
|
|
}
|
|
|
|
unsigned char compressed[65536];
|
|
char hdr[4] = "BZ2";
|
|
|
|
unsigned int header_size = sizeof(hdr);
|
|
unsigned int compressedSize = size - sizeof(hdr); // we should fit in same data buffer minus 4 bytes for a header
|
|
|
|
if (streamtype == FRAG_FILE_STREAM) {
|
|
compressedSize -= 4;
|
|
}
|
|
|
|
if (!BZ2_bzBuffToBuffCompress((char *)compressed, &compressedSize, (char *)buffer, size, 9, 0, 30))
|
|
{
|
|
m_System->DPrintf("Compressing split packet (%d -> %d bytes)\n", size, compressedSize);
|
|
memcpy(buffer, hdr, sizeof(hdr));
|
|
|
|
if (streamtype == FRAG_FILE_STREAM) {
|
|
memcpy((char *)buffer + 4, &size, sizeof(int));
|
|
header_size = 8;
|
|
}
|
|
|
|
memcpy((char *)buffer + header_size, compressed, compressedSize);
|
|
size = header_size + compressedSize;
|
|
}
|
|
|
|
chunksize = FRAGMENT_S2C_MIN_SIZE;
|
|
wait = (fragbufwaiting_t *)Mem_ZeroMalloc(sizeof(fragbufwaiting_t));
|
|
|
|
remaining = size;
|
|
pos = 0;
|
|
while (remaining > 0)
|
|
{
|
|
sendsize = min(remaining, chunksize);
|
|
remaining -= sendsize;
|
|
|
|
buf = (fragbuf_t *)Mem_ZeroMalloc(sizeof(fragbuf_t));
|
|
if (!buf)
|
|
{
|
|
m_System->Printf("NetChannel::CreateFragmentsFromBuffer:Couldn't allocate fragbuf_t\n");
|
|
free(wait);
|
|
return false;
|
|
}
|
|
|
|
if (firstfragment && filename)
|
|
{
|
|
firstfragment = false;
|
|
|
|
unsigned int len = strlen(filename) + 1;
|
|
memcpy(buf->data, filename, len);
|
|
sendsize -= len;
|
|
|
|
memcpy(&buf->data[len], (char *)buffer + pos, sendsize);
|
|
buf->size = len + sendsize;
|
|
}
|
|
else
|
|
{
|
|
memcpy(buf->data, (char *)buffer + pos, sendsize);
|
|
buf->size = sendsize;
|
|
}
|
|
|
|
buf->bufferId = bufferid++;
|
|
buf->isfile = filename ? true : false;
|
|
buf->isbuffer = true;
|
|
|
|
// Copy in data
|
|
pos += sendsize;
|
|
|
|
AddFragbufToTail(wait, buf);
|
|
}
|
|
|
|
// Now add waiting list item to the end of buffer queue
|
|
if (!m_waitlist[streamtype])
|
|
{
|
|
m_waitlist[streamtype] = wait;
|
|
}
|
|
else
|
|
{
|
|
p = m_waitlist[streamtype];
|
|
while (p->next) {
|
|
p = p->next;
|
|
}
|
|
|
|
p->next = wait;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void NetChannel::AddFragbufToTail(fragbufwaiting_t *wait, fragbuf_t *buf)
|
|
{
|
|
fragbuf_t *p;
|
|
|
|
buf->next = nullptr;
|
|
wait->fragbufcount++;
|
|
|
|
p = wait->fragbufs;
|
|
if (p)
|
|
{
|
|
while (p->next) {
|
|
p = p->next;
|
|
}
|
|
|
|
p->next = buf;
|
|
}
|
|
else
|
|
{
|
|
wait->fragbufs = buf;
|
|
}
|
|
}
|
|
|
|
NetPacket *NetChannel::GetPacket()
|
|
{
|
|
return (NetPacket *)m_incomingPackets.RemoveTail();
|
|
}
|
|
|
|
void NetChannel::FreePacket(NetPacket *packet)
|
|
{
|
|
if (packet) {
|
|
delete packet;
|
|
}
|
|
}
|
|
|
|
void NetChannel::SetUpdateRate(int newupdaterate)
|
|
{
|
|
m_updaterate = newupdaterate;
|
|
if (newupdaterate > 100) {
|
|
m_updaterate = 100;
|
|
} else if (newupdaterate < 0) {
|
|
m_updaterate = 1;
|
|
}
|
|
|
|
m_send_interval = (1.0f / m_updaterate);
|
|
}
|
|
|
|
void NetChannel::CopyNormalFragments()
|
|
{
|
|
fragbuf_t *p, *n;
|
|
int totalSize;
|
|
|
|
if (!m_incomingbufs[FRAG_NORMAL_STREAM]) {
|
|
m_System->DPrintf("WARNING! NetChannel::CopyNormalFragments: called with no fragments readied.\n");
|
|
return;
|
|
}
|
|
|
|
totalSize = 0;
|
|
p = m_incomingbufs[FRAG_NORMAL_STREAM];
|
|
while (p)
|
|
{
|
|
totalSize += p->size;
|
|
p = p->next;
|
|
}
|
|
|
|
NetPacket *packet = new NetPacket;
|
|
packet->seqnr = m_incoming_sequence;
|
|
packet->connectionless = false;
|
|
packet->time = m_System->GetTime();
|
|
packet->address.FromNetAddress(&m_remote_address);
|
|
packet->data.Resize(totalSize);
|
|
|
|
p = m_incomingbufs[FRAG_NORMAL_STREAM];
|
|
while (p)
|
|
{
|
|
n = p->next;
|
|
packet->data.WriteBuf(p->data, p->size);
|
|
|
|
free(p);
|
|
p = n;
|
|
}
|
|
|
|
if (*(uint32 *)packet->data.GetData() == MAKEID('B', 'Z', '2', '\0'))
|
|
{
|
|
char uncompressed[65536];
|
|
unsigned int uncompressedSize = 65536;
|
|
|
|
BZ2_bzBuffToBuffDecompress(uncompressed, &uncompressedSize, (char *)packet->data.GetData() + 4, totalSize - 4, 1, 0);
|
|
|
|
packet->data.Resize(uncompressedSize);
|
|
packet->data.WriteBuf(uncompressed, uncompressedSize);
|
|
}
|
|
|
|
packet->data.Reset();
|
|
|
|
m_incomingPackets.AddHead(packet);
|
|
m_incomingbufs[FRAG_NORMAL_STREAM] = nullptr;
|
|
}
|
|
|
|
void NetChannel::SetConnected(bool flag)
|
|
{
|
|
m_connected = flag;
|
|
}
|
|
|
|
void NetChannel::SetRate(int newRate)
|
|
{
|
|
m_max_bandwidth_rate = clamp(newRate, 1000, 20000);
|
|
}
|
|
|
|
void NetChannel::GetFlowStats(float *avgInKBSec, float *avgOutKBSec)
|
|
{
|
|
UpdateFlow(FLOW_OUTGOING);
|
|
UpdateFlow(FLOW_INCOMING);
|
|
|
|
*avgInKBSec = m_flow[FLOW_OUTGOING].avgkbytespersec;
|
|
*avgOutKBSec = m_flow[FLOW_INCOMING].avgkbytespersec;
|
|
}
|
|
|
|
void NetChannel::SetKeepAlive(bool flag)
|
|
{
|
|
m_keep_alive = flag;
|
|
}
|
|
|
|
bool NetChannel::KeepAlive()
|
|
{
|
|
return m_keep_alive;
|
|
}
|
|
|
|
NetAddress *NetChannel::GetTargetAddress()
|
|
{
|
|
return &m_remote_address;
|
|
}
|
|
|
|
bool NetChannel::IsFakeChannel()
|
|
{
|
|
if (m_Socket) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool NetChannel::IsConnected()
|
|
{
|
|
return m_connected;
|
|
}
|
|
|
|
void NetChannel::SetTimeOut(float time)
|
|
{
|
|
if (time > 0) {
|
|
m_timeout = time;
|
|
}
|
|
}
|
|
|
|
bool NetChannel::IsTimedOut()
|
|
{
|
|
if (m_Socket && m_System->GetTime() > m_timeout + m_last_received) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
float NetChannel::GetIdleTime()
|
|
{
|
|
return m_System->GetTime() - m_last_send;
|
|
}
|
|
|
|
int NetChannel::GetRate()
|
|
{
|
|
return m_max_bandwidth_rate;
|
|
}
|
|
|
|
int NetChannel::GetUpdateRate()
|
|
{
|
|
return m_updaterate;
|
|
}
|
|
|
|
float NetChannel::GetLoss()
|
|
{
|
|
return m_loss;
|
|
}
|
|
|
|
void NetChannel::FakeAcknowledgement()
|
|
{
|
|
m_incoming_sequence = 0;
|
|
m_incoming_acknowledged = m_outgoing_sequence - 1;
|
|
m_incoming_reliable_acknowledged = m_reliable_sequence;
|
|
m_reliableOutSize = 0;
|
|
|
|
m_last_received = m_System->GetTime();
|
|
}
|
|
|
|
bool NetChannel::CreateFragmentsFromFile(char *fileName)
|
|
{
|
|
if (IsFakeChannel()) {
|
|
m_System->Printf("NetChannel::CreateFragmentsFromBuffer: IsFakeChannel()\n");
|
|
return true;
|
|
}
|
|
|
|
m_System->Printf("WARNING! Ignoring file request %s.\n", fileName);
|
|
return false;
|
|
}
|
|
|
|
bool NetChannel::IsCrashed()
|
|
{
|
|
return m_crashed;
|
|
}
|
|
|
|
bool NetChannel::CopyFileFragments()
|
|
{
|
|
fragbuf_t *p;
|
|
fragbuf_s *n;
|
|
char filename[MAX_PATH];
|
|
int totalSize = 0;
|
|
|
|
if (!m_incomingbufs[FRAG_FILE_STREAM]) {
|
|
m_System->DPrintf("WARNING! NetChannel::CopyFileFragments: called with no fragments readied.\n");
|
|
return false;
|
|
}
|
|
|
|
totalSize = 0;
|
|
p = m_incomingbufs[FRAG_FILE_STREAM];
|
|
while (p)
|
|
{
|
|
totalSize += p->size;
|
|
p = p->next;
|
|
}
|
|
|
|
BitBuffer filecontent(totalSize);
|
|
p = m_incomingbufs[FRAG_FILE_STREAM];
|
|
while (p)
|
|
{
|
|
n = p->next;
|
|
filecontent.WriteBuf(p->data, p->size);
|
|
free(p);
|
|
p = n;
|
|
}
|
|
|
|
filecontent.Reset();
|
|
strcopy(filename, filecontent.ReadString());
|
|
|
|
if (!strlen(filename)) {
|
|
m_System->Printf("File fragment received with no filename\n");
|
|
FlushIncoming(FRAG_FILE_STREAM);
|
|
return false;
|
|
}
|
|
|
|
if (strstr(filename, "..")) {
|
|
m_System->Printf("File fragment received with relative path, ignoring\n");
|
|
FlushIncoming(FRAG_FILE_STREAM);
|
|
return false;
|
|
}
|
|
|
|
// TODO: Here is the missing code.
|
|
// TODO: Check me, value of return function only false.
|
|
|
|
totalSize -= strlen(filename) - 1;
|
|
m_incomingbufs[FRAG_FILE_STREAM] = nullptr;
|
|
|
|
return false;
|
|
}
|