From a97d8c119b85e2eb7fb51ad22f1e0d3b7b5182a1 Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 30 Jan 2019 03:44:48 +0700 Subject: [PATCH 01/11] network: enable new netsplit, netsplit size settings --- engine/client/cl_main.c | 17 +++++++++--- engine/client/client.h | 2 +- engine/common/net_chan.c | 30 ++++++++++++++++------ engine/common/net_ws.c | 54 +++++++++++++++++++++++++-------------- engine/common/net_ws.h | 1 + engine/common/netchan.h | 15 ++++++++--- engine/common/protocol.h | 4 +-- engine/server/sv_client.c | 19 ++++++++++++-- 8 files changed, 102 insertions(+), 40 deletions(-) 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; } /* From 097974bde2a7bf5efe1ec37d85db196e6a93cafa Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 30 Jan 2019 13:38:32 +0700 Subject: [PATCH 02/11] Check cl_dlmax sizes on client --- engine/client/cl_main.c | 4 ++++ engine/common/net_ws.c | 3 +++ 2 files changed, 7 insertions(+) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 10513687..d71b89d8 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1044,9 +1044,13 @@ void CL_SendConnectPacket( void ) } else { + if( cl_dlmax->value > FRAGMENT_MAX_SIZE || cl_dlmax->value < FRAGMENT_MIN_SIZE ) + Cvar_SetValue( "cl_dlmax", FRAGMENT_DEFAULT_SIZE ); + // remove useless userinfo keys Cvar_FullSet( "cl_maxpacket", "0", 0 ); Cvar_FullSet( "cl_maxpayload", "1000", 0 ); + Info_SetValueForKey( protinfo, "uuid", key, sizeof( protinfo )); Info_SetValueForKey( protinfo, "qport", qport, sizeof( protinfo )); Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i \"%s\" \"%s\"\n", PROTOCOL_VERSION, cls.challenge, protinfo, cls.userinfo ); diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 25ff6b29..24fc223e 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1230,6 +1230,9 @@ qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size_t *len // check for split message if( sock == NS_CLIENT && *(int *)data == NET_HEADER_SPLITPACKET ) { + int splitsize = Cvar_VariableInteger("cl_dlmax"); + if( splitsize < SPLITPACKET_MIN_SIZE || splitsize < SPLITPACKET_MAX_SIZE ) + Cvar_SetValue( "cl_dlmax", MAX_ROUTEABLE_PACKET ); return NET_GetLong( data, ret, length, Cvar_VariableInteger("cl_dlmax") ); } #endif From 23af5dcaf1609dad6a757df87c25b46cceddec43 Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 30 Jan 2019 13:56:08 +0700 Subject: [PATCH 03/11] In-game fragment size setting --- engine/server/sv_client.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index ea4366d9..4773f044 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -97,6 +97,7 @@ int SV_GetFragmentSize( void *pcl, fragsize_t mode ) 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; @@ -109,9 +110,17 @@ int SV_GetFragmentSize( void *pcl, fragsize_t mode ) if( mode != FRAGSIZE_FRAG ) return cl_frag_size; - // add window for unreliable + // get in-game fragmentation size if( cl->state == cs_spawned ) - cl_frag_size /= 2; + { + // 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; } From f3468c03216901ea8348a076e477a21dbc530c94 Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 30 Jan 2019 14:04:29 +0700 Subject: [PATCH 04/11] Fix Netchan_Setup arguments --- engine/common/net_chan.c | 2 +- engine/common/netchan.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 2ec6f6d8..2f820849 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 ); diff --git a/engine/common/netchan.h b/engine/common/netchan.h index aa4b3420..79cf49d7 100644 --- a/engine/common/netchan.h +++ b/engine/common/netchan.h @@ -268,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 ); From 31a53f484264d9d19df2d2cac03f09b1cb1d173f Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 30 Jan 2019 14:11:25 +0700 Subject: [PATCH 05/11] Fix cl_dlmax bounds --- engine/common/net_ws.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 24fc223e..1cbda9de 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1231,7 +1231,7 @@ qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size_t *len if( sock == NS_CLIENT && *(int *)data == NET_HEADER_SPLITPACKET ) { int splitsize = Cvar_VariableInteger("cl_dlmax"); - if( splitsize < SPLITPACKET_MIN_SIZE || splitsize < SPLITPACKET_MAX_SIZE ) + if( splitsize < SPLITPACKET_MIN_SIZE || splitsize > SPLITPACKET_MAX_SIZE ) Cvar_SetValue( "cl_dlmax", MAX_ROUTEABLE_PACKET ); return NET_GetLong( data, ret, length, Cvar_VariableInteger("cl_dlmax") ); } From 2b1d4f4377688521032b4bae7f81bb6976a794f6 Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 30 Jan 2019 14:48:43 +0700 Subject: [PATCH 06/11] Fix zero maxsize for reliable --- engine/common/net_chan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 2f820849..b965fc06 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -1462,6 +1462,8 @@ void Netchan_TransmitBits( netchan_t *chan, int length, byte *data ) 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 ) + (((uint)length) >> 3) > maxsize ) From 7bb8124b69205c251220d3aca5548c95cec9acab Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 30 Jan 2019 15:14:43 +0700 Subject: [PATCH 07/11] legacymode: Mark old netsplit extension legacy, fix magic number --- engine/client/cl_main.c | 6 +++--- engine/common/netchan.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index d71b89d8..ddbe0205 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1038,8 +1038,8 @@ 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 @@ -1392,7 +1392,7 @@ 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; diff --git a/engine/common/netchan.h b/engine/common/netchan.h index 79cf49d7..26d05fcc 100644 --- a/engine/common/netchan.h +++ b/engine/common/netchan.h @@ -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 From 40574d9be03fe2cd0bfb5de63336499708badfa5 Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 30 Jan 2019 16:06:32 +0700 Subject: [PATCH 08/11] Network extensions --- engine/client/cl_main.c | 31 +++++++++++++++++++++++++++++++ engine/client/client.h | 1 + engine/common/net_ws.c | 5 +---- engine/common/net_ws.h | 1 + engine/common/protocol.h | 3 +++ engine/server/server.h | 1 + engine/server/sv_client.c | 24 ++++++++++++++++++++---- 7 files changed, 58 insertions(+), 8 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index ddbe0205..96acefbc 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1044,6 +1044,8 @@ void CL_SendConnectPacket( void ) } else { + 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 ); @@ -1053,6 +1055,8 @@ void CL_SendConnectPacket( void ) 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" ); } @@ -1375,6 +1379,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 @@ -1399,6 +1421,15 @@ void CL_Reconnect( qboolean setup_netchan ) 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_LEGACY_EXT_SPLIT ) + { + Con_Reportf( "^2NET_EXT_SPLITSIZE enabled^7 (packet size is %d)\n", (int)cl_dlmax->value ); + } + } } else diff --git a/engine/client/client.h b/engine/client/client.h index 13b4c56d..b924dfb4 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -663,6 +663,7 @@ typedef struct qboolean internetservers_pending; // internetservers is waiting for dns request qboolean legacymode; // one-way 48 protocol compatibility netadr_t legacyserver; + int extensions; } client_static_t; #ifdef __cplusplus diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 1cbda9de..8130d916 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1230,10 +1230,7 @@ qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size_t *len // check for split message if( sock == NS_CLIENT && *(int *)data == NET_HEADER_SPLITPACKET ) { - int splitsize = Cvar_VariableInteger("cl_dlmax"); - if( splitsize < SPLITPACKET_MIN_SIZE || splitsize > SPLITPACKET_MAX_SIZE ) - Cvar_SetValue( "cl_dlmax", MAX_ROUTEABLE_PACKET ); - return NET_GetLong( data, ret, length, Cvar_VariableInteger("cl_dlmax") ); + return NET_GetLong( data, ret, length, CL_GetSplitSize() ); } #endif // lag the packet, if needed diff --git a/engine/common/net_ws.h b/engine/common/net_ws.h index 7901a5a8..ad4f7b8d 100644 --- a/engine/common/net_ws.h +++ b/engine/common/net_ws.h @@ -66,6 +66,7 @@ void NET_ClearLagData( qboolean bClient, qboolean bServer ); #ifndef XASH_DEDICATED qboolean CL_LegacyMode( void ); +int CL_GetSplitSize( void ); #endif #endif//NET_WS_H diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 2990a04e..7ea02024 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -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 4773f044..8e72b24b 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -108,7 +108,12 @@ int SV_GetFragmentSize( void *pcl, fragsize_t mode ) cl_frag_size = bound( FRAGMENT_MIN_SIZE, cl_frag_size, FRAGMENT_MAX_SIZE ); if( mode != FRAGSIZE_FRAG ) - return cl_frag_size; + { + 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 ) @@ -266,6 +271,7 @@ void SV_ConnectClient( netadr_t from ) int i, count = 0; int challenge; const char *s; + int extensions; if( Cmd_Argc() < 5 ) { @@ -307,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 )) { @@ -369,6 +378,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 )); @@ -379,8 +389,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; @@ -388,8 +405,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 )); From b578d44ea2f2b0c4a7ae18aca86ece064deb3bba Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 30 Jan 2019 16:15:57 +0700 Subject: [PATCH 09/11] Fix NET_EXT_SPLITSIZE console reporting --- engine/client/cl_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 96acefbc..0cf9442c 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1425,7 +1425,7 @@ void CL_Reconnect( qboolean setup_netchan ) { cls.extensions = Q_atoi( Info_ValueForKey( Cmd_Argv( 1 ), "ext" )); - if( cls.extensions & NET_LEGACY_EXT_SPLIT ) + if( cls.extensions & NET_EXT_SPLITSIZE ) { Con_Reportf( "^2NET_EXT_SPLITSIZE enabled^7 (packet size is %d)\n", (int)cl_dlmax->value ); } From f786ea65f54153af02616447df0609278bb6a5eb Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 30 Jan 2019 18:41:45 +0700 Subject: [PATCH 10/11] Fix default splitsize --- engine/common/net_chan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index b965fc06..23ad8754 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -1671,7 +1671,7 @@ void Netchan_TransmitBits( netchan_t *chan, int length, byte *data ) // send the datagram if( !CL_IsPlaybackDemo( )) { - int splitsize = 1400; + 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 ); From a169f93b9fb0cea611a583cde575d7498f43ebff Mon Sep 17 00:00:00 2001 From: mittorn Date: Wed, 30 Jan 2019 18:56:52 +0700 Subject: [PATCH 11/11] legacymode: fix netsplit settings --- engine/client/cl_main.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 0cf9442c..d5f6337e 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1025,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 ) ); @@ -1049,9 +1051,8 @@ void CL_SendConnectPacket( void ) if( cl_dlmax->value > FRAGMENT_MAX_SIZE || cl_dlmax->value < FRAGMENT_MIN_SIZE ) Cvar_SetValue( "cl_dlmax", FRAGMENT_DEFAULT_SIZE ); - // remove useless userinfo keys - Cvar_FullSet( "cl_maxpacket", "0", 0 ); - Cvar_FullSet( "cl_maxpayload", "1000", 0 ); + 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 )); @@ -2725,10 +2726,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" );