diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index a732208d..d869ddae 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; } /* @@ -1017,10 +1025,12 @@ void CL_SendConnectPacket( void ) { // set related userinfo keys if( cl_dlmax->value >= 40000 || cl_dlmax->value < 100 ) - Cvar_FullSet( "cl_maxpacket", "1400", FCVAR_USERINFO ); + Info_SetValueForKey( cls.userinfo, "cl_maxpacket", "1400", sizeof( cls.userinfo ) ); else - Cvar_FullSet( "cl_maxpacket", cl_dlmax->string, FCVAR_USERINFO ); - Cvar_FullSet( "cl_maxpayload", "1000", FCVAR_USERINFO ); + Info_SetValueForKey( cls.userinfo, "cl_maxpacket", cl_dlmax->string, sizeof( cls.userinfo ) ); + + if( !*Info_ValueForKey( cls.userinfo,"cl_maxpayload") ) + Info_SetValueForKey( cls.userinfo, "cl_maxpayload", "1000", sizeof( cls.userinfo ) ); /// TODO: add input devices list //Info_SetValueForKey( protinfo, "d", va( "%d", input_devices ), sizeof( protinfo ) ); @@ -1030,17 +1040,24 @@ void CL_SendConnectPacket( void ) Info_SetValueForKey( protinfo, "a", Q_buildarch(), sizeof( protinfo ) ); Info_SetValueForKey( protinfo, "i", ID_GetMD5(), sizeof( protinfo ) ); - Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i %i \"%s\" 2 \"%s\"\n", - PROTOCOL_LEGACY_VERSION, Q_atoi( qport ), cls.challenge, cls.userinfo, protinfo ); + Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i %i \"%s\" %d \"%s\"\n", + PROTOCOL_LEGACY_VERSION, Q_atoi( qport ), cls.challenge, cls.userinfo, NET_LEGACY_EXT_SPLIT, protinfo ); Con_Printf( "Trying to connect by legacy protocol\n" ); } else { - // remove useless userinfo keys - Cvar_FullSet( "cl_maxpacket", "0", 0 ); - Cvar_FullSet( "cl_maxpayload", "1000", 0 ); + int extensions = NET_EXT_SPLITSIZE; + + if( cl_dlmax->value > FRAGMENT_MAX_SIZE || cl_dlmax->value < FRAGMENT_MIN_SIZE ) + Cvar_SetValue( "cl_dlmax", FRAGMENT_DEFAULT_SIZE ); + + Info_RemoveKey( cls.userinfo,"cl_maxpacket" ); + Info_RemoveKey( cls.userinfo, "cl_maxpayload" ); + Info_SetValueForKey( protinfo, "uuid", key, sizeof( protinfo )); Info_SetValueForKey( protinfo, "qport", qport, sizeof( protinfo )); + Info_SetValueForKey( protinfo, "ext", va("%d", extensions), sizeof( protinfo )); + Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i \"%s\" \"%s\"\n", PROTOCOL_VERSION, cls.challenge, protinfo, cls.userinfo ); Con_Printf( "Trying to connect by modern protocol\n" ); } @@ -1364,6 +1381,24 @@ void CL_SendDisconnectMessage( void ) Netchan_TransmitBits( &cls.netchan, MSG_GetNumBitsWritten( &buf ), MSG_GetData( &buf )); } +int CL_GetSplitSize( void ) +{ + int splitsize; + + if( Host_IsDedicated() ) + return 0; + + if( !(cls.extensions & NET_EXT_SPLITSIZE) ) + return 1400; + + splitsize = cl_dlmax->value; + + if( splitsize < FRAGMENT_MIN_SIZE || splitsize > FRAGMENT_MAX_SIZE ) + Cvar_SetValue( "cl_dlmax", FRAGMENT_DEFAULT_SIZE ); + + return cl_dlmax->value; +} + /* ===================== CL_Reconnect @@ -1381,13 +1416,22 @@ void CL_Reconnect( qboolean setup_netchan ) { unsigned int extensions = Q_atoi( Cmd_Argv( 1 ) ); - if( extensions & NET_EXT_SPLIT ) + if( extensions & NET_LEGACY_EXT_SPLIT ) { // only enable incoming split for legacy mode cls.netchan.split = true; Con_Reportf( "^2NET_EXT_SPLIT enabled^7 (packet sizes is %d/%d)\n", (int)cl_dlmax->value, 65536 ); } } + else + { + cls.extensions = Q_atoi( Info_ValueForKey( Cmd_Argv( 1 ), "ext" )); + + if( cls.extensions & NET_EXT_SPLITSIZE ) + { + Con_Reportf( "^2NET_EXT_SPLITSIZE enabled^7 (packet size is %d)\n", (int)cl_dlmax->value ); + } + } } else @@ -1892,7 +1936,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; } @@ -2698,7 +2742,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" ); @@ -2708,10 +2753,6 @@ void CL_InitLocal( void ) Cvar_Get( "team", "", FCVAR_USERINFO, "player team" ); Cvar_Get( "skin", "", FCVAR_USERINFO, "player skin" ); - // legacy mode cvars (need this to add it to userinfo) - Cvar_Get( "cl_maxpacket", "0", 0, "legacy server compatibility" ); - Cvar_Get( "cl_maxpayload", "1000", 0, "legacy server compatibility" ); - cl_showfps = Cvar_Get( "cl_showfps", "1", FCVAR_ARCHIVE, "show client fps" ); cl_nosmooth = Cvar_Get( "cl_nosmooth", "0", FCVAR_ARCHIVE, "disable smooth up stair climbing and interpolate position in multiplayer" ); cl_smoothtime = Cvar_Get( "cl_smoothtime", "0", FCVAR_ARCHIVE, "time to smooth up" ); diff --git a/engine/client/client.h b/engine/client/client.h index aad6123a..ebdeae01 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -665,6 +665,7 @@ typedef struct netadr_t legacyserver; netadr_t legacyservers[MAX_LEGACY_SERVERS]; int legacyservercount; + int extensions; } client_static_t; #ifdef __cplusplus @@ -778,7 +779,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..23ad8754 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -302,7 +302,7 @@ Netchan_Setup called to open a channel to a remote system ============== */ -void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, void *client, int (*pfnBlockSize)(void * )) +void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, void *client, int (*pfnBlockSize)(void *, fragsize_t mode )) { Netchan_Clear( chan ); @@ -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,15 @@ 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( maxsize == 0 ) + maxsize = MAX_RELIABLE_PAYLOAD; // 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 +1633,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 +1671,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 = 0; + 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 ceb31644..4a811398 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, CL_GetSplitSize() ); } - +#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 e104820b..af5680e1 100644 --- a/engine/common/net_ws.h +++ b/engine/common/net_ws.h @@ -61,10 +61,12 @@ 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 qboolean CL_LegacyMode( void ); +int CL_GetSplitSize( void ); #endif void HTTP_AddCustomServer( const char *url ); diff --git a/engine/common/netchan.h b/engine/common/netchan.h index 9d588faa..26d05fcc 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 @@ -84,7 +84,7 @@ GNU General Public License for more details. #define NUM_PACKET_ENTITIES 256 // 170 Mb for multiplayer with 32 players #define MAX_CUSTOM_BASELINES 64 -#define NET_EXT_SPLIT (1U<<1) +#define NET_LEGACY_EXT_SPLIT (1U<<1) #define NETSPLIT_BACKUP 8 #define NETSPLIT_BACKUP_MASK (NETSPLIT_BACKUP - 1) #define NETSPLIT_HEADER_SIZE 18 @@ -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; @@ -261,7 +268,7 @@ extern int net_drop; void Netchan_Init( void ); void Netchan_Shutdown( void ); -void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, void *client, int (*pfnBlockSize)(void * ) ); +void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, void *client, int (*pfnBlockSize)(void *, fragsize_t mode ) ); void Netchan_CreateFileFragmentsFromBuffer( netchan_t *chan, const char *filename, byte *pbuf, int size ); qboolean Netchan_CopyNormalFragments( netchan_t *chan, sizebuf_t *msg, size_t *length ); qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg ); diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 4e9e54ee..a3221c75 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 @@ -243,6 +243,9 @@ GNU General Public License for more details. extern const char *svc_strings[svc_lastmsg+1]; extern const char *clc_strings[clc_lastmsg+1]; +// FWGS extensions +#define NET_EXT_SPLITSIZE (1U<<0) // set splitsize by cl_dlmax + // legacy protocol definitons #define PROTOCOL_LEGACY_VERSION 48 #define svc_legacy_modelindex 31 // [index][modelpath] diff --git a/engine/server/server.h b/engine/server/server.h index 96d09cf1..3a6d3034 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -252,6 +252,7 @@ typedef struct sv_client_s int challenge; // challenge of this user, randomly generated int userid; // identifying number on server + int extensions; } sv_client_t; /* diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 6bd8f435..3a37d1de 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,39 @@ int SV_GetFragmentSize( void *pcl ) if( Netchan_IsLocal( &cl->netchan )) return FRAGMENT_LOCAL_SIZE; + if( mode == FRAGSIZE_UNRELIABLE ) + { + // allow setting unreliable limit with "setinfo cl_urmax" + 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 ) + { + if( cl->extensions & NET_EXT_SPLITSIZE ) + return cl_frag_size; + else + return 0; // original engine behaviour + } + + // get in-game fragmentation size + if( cl->state == cs_spawned ) + { + // allow setting in-game fragsize with "setinfo cl_frmax" + int frmax = Q_atoi( Info_ValueForKey( cl->userinfo, "cl_frmax" )); + + if( frmax < FRAGMENT_MIN_SIZE || frmax > FRAGMENT_MAX_SIZE ) + cl_frag_size = frmax; + else + cl_frag_size /= 2;// add window for unreliable + } + + return cl_frag_size - HEADER_BYTES; } /* @@ -242,6 +271,7 @@ void SV_ConnectClient( netadr_t from ) int i, count = 0; int challenge; const char *s; + int extensions; if( Cmd_Argc() < 5 ) { @@ -283,6 +313,9 @@ void SV_ConnectClient( netadr_t from ) return; } + extensions = Q_atoi( Info_ValueForKey( protinfo, "ext" ) ); + + // LAN servers restrict to class b IP addresses if( !SV_CheckIPRestrictions( from )) { @@ -346,6 +379,7 @@ void SV_ConnectClient( netadr_t from ) newcl->frames = (client_frame_t *)Z_Calloc( sizeof( client_frame_t ) * SV_UPDATE_BACKUP ); newcl->userid = g_userid++; // create unique userid newcl->state = cs_connected; + newcl->extensions = extensions & (NET_EXT_SPLITSIZE); // reset viewentities (from previous level) memset( newcl->viewentity, 0, sizeof( newcl->viewentity )); @@ -356,8 +390,15 @@ void SV_ConnectClient( netadr_t from ) Netchan_Setup( NS_SERVER, &newcl->netchan, from, qport, newcl, SV_GetFragmentSize ); MSG_Init( &newcl->datagram, "Datagram", newcl->datagram_buf, sizeof( newcl->datagram_buf )); // datagram buf + Q_strncpy( newcl->hashedcdkey, Info_ValueForKey( protinfo, "uuid" ), 32 ); + newcl->hashedcdkey[32] = '\0'; + + // build protinfo answer + protinfo[0] = '\0'; + Info_SetValueForKey( protinfo, "ext", va( "%d",newcl->extensions ), sizeof( protinfo ) ); + // send the connect packet to the client - Netchan_OutOfBandPrint( NS_SERVER, from, "client_connect" ); + Netchan_OutOfBandPrint( NS_SERVER, from, "client_connect %s", protinfo ); newcl->upstate = us_inactive; newcl->connection_started = host.realtime; @@ -365,8 +406,7 @@ void SV_ConnectClient( netadr_t from ) newcl->delta_sequence = -1; newcl->flags = 0; - Q_strncpy( newcl->hashedcdkey, Info_ValueForKey( protinfo, "uuid" ), 32 ); - newcl->hashedcdkey[32] = '\0'; + // reset any remaining events memset( &newcl->events, 0, sizeof( newcl->events ));