diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index e1d86ee8..10513687 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -65,6 +65,8 @@ convar_t *cl_showevents; convar_t *cl_cmdrate; convar_t *cl_interp; convar_t *cl_dlmax; +convar_t *cl_upmax; + convar_t *cl_lw; convar_t *cl_charset; convar_t *cl_trace_messages; @@ -197,12 +199,18 @@ void CL_CheckClientState( void ) } } -int CL_GetFragmentSize( void *unused ) +int CL_GetFragmentSize( void *unused, fragsize_t mode ) { + if( mode == FRAGSIZE_SPLIT ) + return 0; + + if( mode == FRAGSIZE_UNRELIABLE ) + return NET_MAX_MESSAGE; + if( Netchan_IsLocal( &cls.netchan )) return FRAGMENT_LOCAL_SIZE; - return FRAGMENT_MIN_SIZE; + return cl_upmax->value; } /* @@ -1876,7 +1884,7 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) // too many fails use default connection method Con_Printf( "hi-speed connection is failed, use default method\n" ); Netchan_OutOfBandPrint( NS_CLIENT, from, "getchallenge\n" ); - Cvar_SetValue( "cl_dlmax", FRAGMENT_MIN_SIZE ); + Cvar_SetValue( "cl_dlmax", FRAGMENT_DEFAULT_SIZE ); cls.connect_time = host.realtime; return; } @@ -2671,7 +2679,8 @@ void CL_InitLocal( void ) name = Cvar_Get( "name", Sys_GetCurrentUser(), FCVAR_USERINFO|FCVAR_ARCHIVE|FCVAR_PRINTABLEONLY, "player name" ); model = Cvar_Get( "model", "", FCVAR_USERINFO|FCVAR_ARCHIVE, "player model ('player' is a singleplayer model)" ); cl_updaterate = Cvar_Get( "cl_updaterate", "20", FCVAR_USERINFO|FCVAR_ARCHIVE, "refresh rate of server messages" ); - cl_dlmax = Cvar_Get( "cl_dlmax", "0", FCVAR_USERINFO|FCVAR_ARCHIVE, "max allowed fragment size on download resources" ); + cl_dlmax = Cvar_Get( "cl_dlmax", "0", FCVAR_USERINFO|FCVAR_ARCHIVE, "max allowed outcoming fragment size" ); + cl_upmax = Cvar_Get( "cl_upmax", "1200", FCVAR_ARCHIVE, "max allowed incoming fragment size" ); rate = Cvar_Get( "rate", "3500", FCVAR_USERINFO|FCVAR_ARCHIVE, "player network rate" ); topcolor = Cvar_Get( "topcolor", "0", FCVAR_USERINFO|FCVAR_ARCHIVE, "player top color" ); bottomcolor = Cvar_Get( "bottomcolor", "0", FCVAR_USERINFO|FCVAR_ARCHIVE, "player bottom color" ); diff --git a/engine/client/client.h b/engine/client/client.h index a281512d..13b4c56d 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -776,7 +776,7 @@ void CL_SendCommand( void ); void CL_Disconnect_f( void ); void CL_ProcessFile( qboolean successfully_received, const char *filename ); void CL_WriteUsercmd( sizebuf_t *msg, int from, int to ); -int CL_GetFragmentSize( void *unused ); +int CL_GetFragmentSize( void *unused , fragsize_t mode ); qboolean CL_PrecacheResources( void ); void CL_SetupOverviewParams( void ); void CL_UpdateFrameLerp( void ); diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index b2db3dbf..2ec6f6d8 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -710,7 +710,7 @@ static void Netchan_CreateFragments_( netchan_t *chan, sizebuf_t *msg ) return; if( chan->pfnBlockSize != NULL ) - chunksize = chan->pfnBlockSize( chan->client ); + chunksize = chan->pfnBlockSize( chan->client, FRAGSIZE_FRAG ); else chunksize = FRAGMENT_MAX_SIZE; // fallback wait = (fragbufwaiting_t *)Mem_Calloc( net_mempool, sizeof( fragbufwaiting_t )); @@ -871,7 +871,7 @@ void Netchan_CreateFileFragmentsFromBuffer( netchan_t *chan, const char *filenam if( !size ) return; if( chan->pfnBlockSize != NULL ) - chunksize = chan->pfnBlockSize( chan->client ); + chunksize = chan->pfnBlockSize( chan->client, FRAGSIZE_FRAG ); else chunksize = FRAGMENT_MAX_SIZE; // fallback if( !LZSS_IsCompressed( pbuf )) @@ -969,7 +969,7 @@ int Netchan_CreateFileFragments( netchan_t *chan, const char *filename ) } if( chan->pfnBlockSize != NULL ) - chunksize = chan->pfnBlockSize( chan->client ); + chunksize = chan->pfnBlockSize( chan->client, FRAGSIZE_FRAG ); else chunksize = FRAGMENT_MAX_SIZE; // fallback Q_strncpy( compressedfilename, filename, sizeof( compressedfilename )); @@ -1458,9 +1458,13 @@ void Netchan_TransmitBits( netchan_t *chan, int length, byte *data ) if( send_from_regular && ( send_from_frag[FRAG_NORMAL_STREAM] )) { send_from_regular = false; + int maxsize = MAX_RELIABLE_PAYLOAD; + + if( chan->pfnBlockSize ) + maxsize = chan->pfnBlockSize( chan->client, FRAGSIZE_SPLIT ); // if the reliable buffer has gotten too big, queue it at the end of everything and clear out buffer - if( MSG_GetNumBytesWritten( &chan->message ) > MAX_RELIABLE_PAYLOAD ) + if( MSG_GetNumBytesWritten( &chan->message ) + (((uint)length) >> 3) > maxsize ) { Netchan_CreateFragments_( chan, &chan->message ); MSG_Clear( &chan->message ); @@ -1627,9 +1631,16 @@ void Netchan_TransmitBits( netchan_t *chan, int length, byte *data ) chan->last_reliable_sequence = chan->outgoing_sequence - 1; } - if( MSG_GetNumBitsLeft( &send ) >= length ) - MSG_WriteBits( &send, data, length ); - else Con_Printf( S_WARN "Netchan_Transmit: unreliable message overflow\n" ); + if( length ) + { + int maxsize = NET_MAX_MESSAGE; + if( chan->pfnBlockSize ) + maxsize = chan->pfnBlockSize( chan->client, FRAGSIZE_UNRELIABLE ); + + if( MSG_GetNumBytesWritten( &send ) + length >> 3 <= maxsize ) + MSG_WriteBits( &send, data, length ); + else Con_Printf( S_WARN "Netchan_Transmit: unreliable message overflow: %d\n", MSG_GetNumBytesWritten( &send ) ); + } // deal with packets that are too small for some networks if( MSG_GetNumBytesWritten( &send ) < 16 && !NET_IsLocalAddress( chan->remote_address )) // packet too small for some networks @@ -1658,7 +1669,10 @@ void Netchan_TransmitBits( netchan_t *chan, int length, byte *data ) // send the datagram if( !CL_IsPlaybackDemo( )) { - NET_SendPacket( chan->sock, MSG_GetNumBytesWritten( &send ), MSG_GetData( &send ), chan->remote_address ); + int splitsize = 1400; + if( chan->pfnBlockSize ) + splitsize = chan->pfnBlockSize( chan->client, FRAGSIZE_SPLIT ); + NET_SendPacketEx( chan->sock, MSG_GetNumBytesWritten( &send ), MSG_GetData( &send ), chan->remote_address, splitsize ); } if( SV_Active() && sv_lan.value && sv_lan_rate.value > 1000.0 ) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 08e03261..25ff6b29 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -35,15 +35,16 @@ GNU General Public License for more details. #include "netchan.h" #include "mathlib.h" -// #define NET_USE_FRAGMENTS +#define NET_USE_FRAGMENTS #define PORT_ANY -1 #define MAX_LOOPBACK 4 #define MASK_LOOPBACK (MAX_LOOPBACK - 1) #define MAX_ROUTEABLE_PACKET 1400 -#define SPLIT_SIZE ( MAX_ROUTEABLE_PACKET - sizeof( SPLITPACKET )) -#define NET_MAX_FRAGMENTS ( NET_MAX_FRAGMENT / SPLIT_SIZE ) +#define SPLITPACKET_MIN_SIZE 508 // RFC 791: 576(min ip packet) - 60 (ip header) - 8 (udp header) +#define SPLITPACKET_MAX_SIZE 64000 +#define NET_MAX_FRAGMENTS ( NET_MAX_FRAGMENT / (SPLITPACKET_MIN_SIZE - sizeof( SPLITPACKET )) ) #ifndef _WIN32 // it seems we need to use WS2 to support it #define HAVE_GETADDRINFO @@ -1105,13 +1106,17 @@ NET_GetLong receive long packet from network ================== */ -qboolean NET_GetLong( byte *pData, int size, int *outSize ) +qboolean NET_GetLong( byte *pData, int size, int *outSize, int splitsize ) { int i, sequence_number, offset; SPLITPACKET *pHeader = (SPLITPACKET *)pData; int packet_number; int packet_count; short packet_id; + int body_size = splitsize - sizeof( SPLITPACKET ); + + if( body_size < 0 ) + return false; if( size < sizeof( SPLITPACKET )) { @@ -1149,7 +1154,7 @@ qboolean NET_GetLong( byte *pData, int size, int *outSize ) if( net.split_flags[packet_number] != sequence_number ) { if( packet_number == ( packet_count - 1 )) - net.split.total_size = size + SPLIT_SIZE * ( packet_count - 1 ); + net.split.total_size = size + body_size * ( packet_count - 1 ); net.split.split_count--; net.split_flags[packet_number] = sequence_number; @@ -1162,7 +1167,7 @@ qboolean NET_GetLong( byte *pData, int size, int *outSize ) Con_DPrintf( "NET_GetLong: Ignoring duplicated split packet %i of %i ( %i bytes )\n", packet_number + 1, packet_count, size ); } - offset = (packet_number * SPLIT_SIZE); + offset = (packet_number * body_size); memcpy( net.split.buffer + offset, pData + sizeof( SPLITPACKET ), size ); // have we received all of the pieces to the packet? @@ -1221,13 +1226,13 @@ qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size_t *len #ifndef XASH_DEDICATED if( CL_LegacyMode() ) return NET_LagPacket( true, sock, from, length, data ); -#endif + // check for split message - if( *(int *)data == NET_HEADER_SPLITPACKET ) + if( sock == NS_CLIENT && *(int *)data == NET_HEADER_SPLITPACKET ) { - return NET_GetLong( data, ret, length ); + return NET_GetLong( data, ret, length, Cvar_VariableInteger("cl_dlmax") ); } - +#endif // lag the packet, if needed return NET_LagPacket( true, sock, from, length, data ); } @@ -1288,15 +1293,16 @@ NET_SendLong Fragment long packets, send short directly ================== */ -int NET_SendLong( netsrc_t sock, int net_socket, const char *buf, int len, int flags, const struct sockaddr *to, int tolen ) +int NET_SendLong( netsrc_t sock, int net_socket, const char *buf, size_t len, int flags, const struct sockaddr *to, size_t tolen, size_t splitsize ) { #ifdef NET_USE_FRAGMENTS // do we need to break this packet up? - if( sock == NS_SERVER && len > MAX_ROUTEABLE_PACKET ) + if( splitsize > sizeof( SPLITPACKET ) && sock == NS_SERVER && len > splitsize ) { - char packet[MAX_ROUTEABLE_PACKET]; + char packet[SPLITPACKET_MAX_SIZE]; int total_sent, size, packet_count; int ret, packet_number; + int body_size = splitsize - sizeof( SPLITPACKET ); SPLITPACKET *pPacket; net.sequence_number++; @@ -1308,13 +1314,13 @@ int NET_SendLong( netsrc_t sock, int net_socket, const char *buf, int len, int f pPacket->net_id = NET_HEADER_SPLITPACKET; packet_number = 0; total_sent = 0; - packet_count = (len + SPLIT_SIZE - 1) / SPLIT_SIZE; + packet_count = (len + body_size - 1) / body_size; while( len > 0 ) { - size = Q_min( SPLIT_SIZE, len ); + size = Q_min( body_size, len ); pPacket->packet_id = (packet_number << 8) + packet_count; - memcpy( packet + sizeof( SPLITPACKET ), buf + ( packet_number * SPLIT_SIZE ), size ); + memcpy( packet + sizeof( SPLITPACKET ), buf + ( packet_number * body_size ), size ); if( net_showpackets && net_showpackets->value == 3.0f ) { @@ -1349,10 +1355,10 @@ int NET_SendLong( netsrc_t sock, int net_socket, const char *buf, int len, int f /* ================== -NET_SendPacket +NET_SendPacketEx ================== */ -void NET_SendPacket( netsrc_t sock, size_t length, const void *data, netadr_t to ) +void NET_SendPacketEx( netsrc_t sock, size_t length, const void *data, netadr_t to, size_t splitsize ) { int ret; struct sockaddr addr; @@ -1382,7 +1388,7 @@ void NET_SendPacket( netsrc_t sock, size_t length, const void *data, netadr_t to NET_NetadrToSockadr( &to, &addr ); - ret = NET_SendLong( sock, net_socket, data, length, 0, &addr, sizeof( addr )); + ret = NET_SendLong( sock, net_socket, data, length, 0, &addr, sizeof( addr ), splitsize ); if( NET_IsSocketError( ret )) { @@ -1412,6 +1418,16 @@ void NET_SendPacket( netsrc_t sock, size_t length, const void *data, netadr_t to } +/* +================== +NET_SendPacket +================== +*/ +void NET_SendPacket( netsrc_t sock, size_t length, const void *data, netadr_t to ) +{ + return NET_SendPacketEx( sock, length, data, to, 0 ); +} + /* ==================== NET_BufferToBufferCompress diff --git a/engine/common/net_ws.h b/engine/common/net_ws.h index c8f45d6f..7901a5a8 100644 --- a/engine/common/net_ws.h +++ b/engine/common/net_ws.h @@ -61,6 +61,7 @@ qboolean NET_GetPacket( netsrc_t sock, netadr_t *from, byte *data, size_t *lengt qboolean NET_BufferToBufferCompress( char *dest, uint *destLen, char *source, uint sourceLen ); qboolean NET_BufferToBufferDecompress( char *dest, uint *destLen, char *source, uint sourceLen ); void NET_SendPacket( netsrc_t sock, size_t length, const void *data, netadr_t to ); +void NET_SendPacketEx( netsrc_t sock, size_t length, const void *data, netadr_t to, size_t splitsize ); void NET_ClearLagData( qboolean bClient, qboolean bServer ); #ifndef XASH_DEDICATED diff --git a/engine/common/netchan.h b/engine/common/netchan.h index 9d588faa..aa4b3420 100644 --- a/engine/common/netchan.h +++ b/engine/common/netchan.h @@ -59,10 +59,10 @@ GNU General Public License for more details. // { // byte (on/off) // int (fragment id) -// short (startpos) -// short (length) +// int (startpos) +// int (length) // } -#define HEADER_BYTES ( 8 + MAX_STREAMS * 9 ) +#define HEADER_BYTES ( 8 + MAX_STREAMS * 13 ) // Pad this to next higher 16 byte boundary // This is the largest packet that can come in/out over the wire, before processing the header @@ -182,6 +182,13 @@ typedef struct fbufqueue_s fragbuf_t *fragbufs; // the actual buffers } fragbufwaiting_t; +typedef enum fragsize_e +{ + FRAGSIZE_FRAG, + FRAGSIZE_SPLIT, + FRAGSIZE_UNRELIABLE +} fragsize_t; + // Network Connection Channel typedef struct netchan_s { @@ -205,7 +212,7 @@ typedef struct netchan_s // callback to get actual framgment size void *client; - int (*pfnBlockSize)( void *cl ); + int (*pfnBlockSize)( void *cl, fragsize_t mode ); // staging and holding areas sizebuf_t message; diff --git a/engine/common/protocol.h b/engine/common/protocol.h index d9c6486e..2990a04e 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -174,8 +174,8 @@ GNU General Public License for more details. #define MAX_RESOURCES (MAX_MODELS+MAX_SOUNDS+MAX_CUSTOM+MAX_EVENTS) #define MAX_RESOURCE_BITS 13 // 13 bits 8192 resource (4096 models + 2048 sounds + 1024 events + 1024 files) - -#define FRAGMENT_MIN_SIZE 1200 // default MTU +#define FRAGMENT_MIN_SIZE 508 // RFC 791: 576(min ip packet) - 60 (ip header) - 8 (udp header) +#define FRAGMENT_DEFAULT_SIZE 1200 // default MTU #define FRAGMENT_MAX_SIZE 64000 // maximal fragment size #define FRAGMENT_LOCAL_SIZE FRAGMENT_MAX_SIZE // local connection diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 5b4f4cd8..ea4366d9 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -87,7 +87,7 @@ void SV_GetChallenge( netadr_t from ) Netchan_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "challenge %i", svs.challenges[i].challenge ); } -int SV_GetFragmentSize( void *pcl ) +int SV_GetFragmentSize( void *pcl, fragsize_t mode ) { sv_client_t *cl = (sv_client_t*)pcl; int cl_frag_size; @@ -95,10 +95,25 @@ int SV_GetFragmentSize( void *pcl ) if( Netchan_IsLocal( &cl->netchan )) return FRAGMENT_LOCAL_SIZE; + if( mode == FRAGSIZE_UNRELIABLE ) + { + cl_frag_size = Q_atoi( Info_ValueForKey( cl->userinfo, "cl_urmax" )); + if( cl_frag_size == 0 ) + return NET_MAX_MESSAGE; + return bound( FRAGMENT_MAX_SIZE, cl_frag_size, NET_MAX_MESSAGE ); + } + cl_frag_size = Q_atoi( Info_ValueForKey( cl->userinfo, "cl_dlmax" )); cl_frag_size = bound( FRAGMENT_MIN_SIZE, cl_frag_size, FRAGMENT_MAX_SIZE ); - return cl_frag_size; + if( mode != FRAGSIZE_FRAG ) + return cl_frag_size; + + // add window for unreliable + if( cl->state == cs_spawned ) + cl_frag_size /= 2; + + return cl_frag_size - HEADER_BYTES; } /*