Skip to content

Commit

Permalink
server: optimized gamestate acknowledge
Browse files Browse the repository at this point in the history
server: comment out loopback code for dedicated binaries
net: implemented unified packet queue with different release times
  • Loading branch information
ec- committed Jul 12, 2024
1 parent 14e33fb commit f0c43d3
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 66 deletions.
2 changes: 1 addition & 1 deletion code/client/cl_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,7 @@ void CL_WritePacket( int repeat ) {
CL_Netchan_Transmit( &clc.netchan, &buf );
} else {
CL_Netchan_Enqueue( &clc.netchan, &buf, repeat + 1 );
NET_FlushPacketQueue( 1 );
NET_FlushPacketQueue( 0 );
}
}

Expand Down
11 changes: 6 additions & 5 deletions code/qcommon/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -2830,11 +2830,13 @@ Returns last event time
*/
int Com_EventLoop( void ) {
sysEvent_t ev;
netadr_t evFrom;

#ifndef DEDICATED
byte bufData[ MAX_MSGLEN_BUF ];
msg_t buf;

MSG_Init( &buf, bufData, MAX_MSGLEN );
#endif // !DEDICATED

while ( 1 ) {
ev = Com_GetEvent();
Expand All @@ -2843,21 +2845,20 @@ int Com_EventLoop( void ) {
if ( ev.evType == SE_NONE ) {
// manually send packet events for the loopback channel
#ifndef DEDICATED
netadr_t evFrom;
while ( NET_GetLoopPacket( NS_CLIENT, &evFrom, &buf ) ) {
CL_PacketEvent( &evFrom, &buf );
}
#endif
while ( NET_GetLoopPacket( NS_SERVER, &evFrom, &buf ) ) {
// if the server just shut down, flush the events
if ( com_sv_running->integer ) {
Com_RunAndTimeServerPacket( &evFrom, &buf );
}
}

#endif // !DEDICATED
return ev.evTime;
}


switch ( ev.evType ) {
#ifndef DEDICATED
case SE_KEY:
Expand All @@ -2872,7 +2873,7 @@ int Com_EventLoop( void ) {
case SE_JOYSTICK_AXIS:
CL_JoystickEvent( ev.evValue, ev.evValue2, ev.evTime );
break;
#endif
#endif // !DEDICATED
case SE_CONSOLE:
Cbuf_AddText( (char *)ev.evPtr );
Cbuf_AddText( "\n" );
Expand Down
136 changes: 91 additions & 45 deletions code/qcommon/net_chan.c
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ static void Netchan_EnqueueFragments( const netchan_t *chan, const int length, c
MSG_WriteData( &send, data + unsentFragmentStart, fragmentLength );

// enqueue the datagram
NET_QueuePacket( 1 /*queue index*/, chan->sock, send.cursize, send.data, &chan->remoteAddress, 0 /*offset*/ );
NET_QueuePacket( chan->sock, send.cursize, send.data, &chan->remoteAddress, 0 /*offset*/ );

// TODO: add showpackets debug info

Expand Down Expand Up @@ -316,7 +316,7 @@ void Netchan_Enqueue( netchan_t *chan, int length, const byte *data ) {
MSG_WriteData( &send, data, length );

// enqueue the datagram
NET_QueuePacket( 1 /*queue index*/, chan->sock, send.cursize, send.data, &chan->remoteAddress, 0 /*offset*/ );
NET_QueuePacket( chan->sock, send.cursize, send.data, &chan->remoteAddress, 0 /*offset*/ );

// TODO: add showpackets debug info
}
Expand Down Expand Up @@ -508,10 +508,11 @@ LOOPBACK BUFFERS FOR LOCAL PLAYER
=============================================================================
*/
#ifndef DEDICATED

// there needs to be enough loopback messages to hold a complete
// gamestate of maximum size
#define MAX_LOOPBACK 16
#define MAX_LOOPBACK 32

typedef struct {
byte data[MAX_PACKETLEN];
Expand Down Expand Up @@ -564,6 +565,8 @@ static void NET_SendLoopPacket( netsrc_t sock, int length, const void *data )
loop->msgs[i].datalen = length;
}

#endif // !DEDICATED

//=============================================================================

typedef struct packetQueue_s {
Expand All @@ -576,11 +579,82 @@ typedef struct packetQueue_s {
int release;
} packetQueue_t;

static packetQueue_t *packetQueue[2] = { NULL, NULL };
static packetQueue_t *packetQueue = NULL;

static packetQueue_t *list_remove( packetQueue_t *head, packetQueue_t *item ) {
if ( item->next != item ) {
item->next->prev = item->prev;
item->prev->next = item->next;
} else {
item->next = item->prev = NULL;
}
return item == head ? item->next : head;
}


void NET_QueuePacket( int index, netsrc_t sock, int length, const void *data, const netadr_t *to, int offset )
static packetQueue_t *list_insert( packetQueue_t *head, packetQueue_t *item )
{
packetQueue_t *new, *next = packetQueue[index];
if ( head ) {
packetQueue_t *prev = head->prev;
packetQueue_t *next = head;
prev->next = item;
next->prev = item;
item->prev = prev;
item->next = next;
return head;
} else {
item->prev = item->next = item;
return item;
}
}


static packetQueue_t *list_process( packetQueue_t *head, const int time_diff )
{
packetQueue_t *item = head;
int do_break = 0;
int now;
do {
if ( head == NULL ) {
break;
}
if ( head->prev == item ) {
do_break = 1;
}
now = Sys_Milliseconds();
if ( now - item->release >= time_diff ) {
packetQueue_t *next = item->next;
#ifndef DEDICATED
if ( item->to.type == NA_LOOPBACK )
NET_SendLoopPacket( item->sock, item->length, item->data );
else
#endif
Sys_SendPacket( item->length, item->data, &item->to );
head = list_remove( head, item );
Z_Free( item );
item = next;
} else {
item = item->next;
}
} while ( do_break == 0 );

return head;
}


void NET_QueuePacket( netsrc_t sock, int length, const void *data, const netadr_t *to, int offset )
{
packetQueue_t *new;

if ( to->type == NA_BOT ) {
return;
}
if ( to->type == NA_BAD ) {
return;
}
if ( com_timescale->value == 0.0f ) {
return;
}

if ( offset > 999 ) {
offset = 999;
Expand All @@ -592,45 +666,16 @@ void NET_QueuePacket( int index, netsrc_t sock, int length, const void *data, co
new->length = length;
new->to = *to;
new->sock = sock;
new->release = Sys_Milliseconds() + (int)((float)offset / com_timescale->value);
new->release = Sys_Milliseconds() + (int)( (float)offset / com_timescale->value );
new->next = NULL;

if (packetQueue[index] == NULL) {
packetQueue[index] = new;
return;
}

while (next) {
if (!next->next) {
next->next = new;
return;
}
next = next->next;
}
packetQueue = list_insert( packetQueue, new );
}


void NET_FlushPacketQueue( int index )
void NET_FlushPacketQueue( int time_diff )
{
packetQueue_t *last;
int now;

while ( packetQueue[index] ) {
now = Sys_Milliseconds();
if ( packetQueue[index]->release - now > 0 ) {
break;
}

if ( index == 0 ) {
Sys_SendPacket( packetQueue[index]->length, packetQueue[index]->data, &packetQueue[index]->to );
} else {
NET_SendPacket( packetQueue[index]->sock, packetQueue[index]->length, packetQueue[index]->data, &packetQueue[index]->to );
}

last = packetQueue[index];
packetQueue[index] = packetQueue[index]->next;
Z_Free( last );
}
packetQueue = list_process( packetQueue, time_diff );
}


Expand All @@ -641,10 +686,6 @@ void NET_SendPacket( netsrc_t sock, int length, const void *data, const netadr_t
Com_Printf ("send packet %4i\n", length);
}

if ( to->type == NA_LOOPBACK ) {
NET_SendLoopPacket( sock, length, data );
return;
}
if ( to->type == NA_BOT ) {
return;
}
Expand All @@ -653,12 +694,17 @@ void NET_SendPacket( netsrc_t sock, int length, const void *data, const netadr_t
}
#ifndef DEDICATED
if ( sock == NS_CLIENT && cl_packetdelay->integer > 0 ) {
NET_QueuePacket( 0, sock, length, data, to, cl_packetdelay->integer );
NET_QueuePacket( sock, length, data, to, cl_packetdelay->integer );
} else
#endif
if ( sock == NS_SERVER && sv_packetdelay->integer > 0 ) {
NET_QueuePacket( 0, sock, length, data, to, sv_packetdelay->integer );
NET_QueuePacket( sock, length, data, to, sv_packetdelay->integer );
}
#ifndef DEDICATED
else if ( to->type == NA_LOOPBACK ) {
NET_SendLoopPacket( sock, length, data );
}
#endif
else {
Sys_SendPacket( length, data, to );
}
Expand Down
6 changes: 4 additions & 2 deletions code/qcommon/qcommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,8 @@ typedef struct {

void NET_Init( void );
void NET_Shutdown( void );
void NET_FlushPacketQueue( int index );
void NET_QueuePacket( int index, netsrc_t sock, int length, const void *data, const netadr_t *to, int offset );
void NET_FlushPacketQueue( int time_diff );
void NET_QueuePacket( netsrc_t sock, int length, const void *data, const netadr_t *to, int offset );
void NET_SendPacket( netsrc_t sock, int length, const void *data, const netadr_t *to );
void QDECL NET_OutOfBandPrint( netsrc_t net_socket, const netadr_t *adr, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
void NET_OutOfBandCompress( netsrc_t sock, const netadr_t *adr, const byte *data, int len );
Expand All @@ -208,7 +208,9 @@ qboolean NET_IsLocalAddress( const netadr_t *adr );
const char *NET_AdrToString( const netadr_t *a );
const char *NET_AdrToStringwPort( const netadr_t *a );
int NET_StringToAdr( const char *s, netadr_t *a, netadrtype_t family );
#ifndef DEDICATED
qboolean NET_GetLoopPacket( netsrc_t sock, netadr_t *net_from, msg_t *net_message );
#endif
#ifdef USE_IPV6
void NET_JoinMulticast6( void );
void NET_LeaveMulticast6( void );
Expand Down
9 changes: 8 additions & 1 deletion code/server/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,13 @@ struct leakyBucket_s {
leakyBucket_t *prev, *next;
};

typedef enum {
GSA_INIT = 0, // gamestate never sent with current sv.serverId
GSA_SENT_ONCE, // gamestate sent once, client can reply with any (messageAcknowledge - gamestateMessageNum) >= 0 and correct serverId
GSA_SENT_MANY, // gamestate sent many times, client must reply with exact gamestateMessageNum == gamestateMessageNum and correct serverId
GSA_ACKED // gamestate acknowledged, no retansmissions needed
} gameStateAck_t;

typedef struct client_s {
clientState_t state;
char userinfo[MAX_INFO_STRING]; // name, etc
Expand All @@ -167,7 +174,7 @@ typedef struct client_s {
sharedEntity_t *gentity; // SV_GentityNum(clientnum)
char name[MAX_NAME_LENGTH]; // extracted from userinfo, high bits masked

qboolean gamestateAcked; // set to qtrue when serverId = sv.serverId & messageAcknowledge = gamestateMessageNum
gameStateAck_t gamestateAck;
qboolean downloading; // set at "download", reset at gamestate retransmission
// int serverId; // last acknowledged serverId

Expand Down
30 changes: 19 additions & 11 deletions code/server/sv_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1028,7 +1028,12 @@ static void SV_SendClientGameState( client_t *client ) {

client->state = CS_PRIMED;

client->gamestateAcked = qfalse;
if ( client->gamestateAck == GSA_INIT ) {
client->gamestateAck = GSA_SENT_ONCE;
} else {
client->gamestateAck = GSA_SENT_MANY;
}

client->downloading = qfalse;

client->pureAuthentic = qfalse;
Expand Down Expand Up @@ -1135,8 +1140,8 @@ void SV_ClientEnterWorld( client_t *client ) {
}

client->state = CS_ACTIVE;
client->gamestateAck = GSA_ACKED;

client->gamestateAcked = qtrue;
client->oldServerTime = 0;

// resend all configstrings using the cs commands since these are
Expand Down Expand Up @@ -1281,7 +1286,7 @@ static void SV_BeginDownload_f( client_t *cl ) {
cl->gentity = NULL;

cl->downloading = qtrue;
cl->gamestateAcked = qfalse;
cl->gamestateAck = GSA_SENT_MANY; // expect exact messageAcknowledge next time
}


Expand Down Expand Up @@ -2282,14 +2287,17 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
}
return;
}
} else if ( !cl->gamestateAcked ) {
} else if ( cl->gamestateAck != GSA_ACKED ) {
// early check for gamestate acknowledge
if ( serverId == sv.serverId && cl->messageAcknowledge == cl->gamestateMessageNum ) {
cl->gamestateAcked = qtrue;
// this client has acknowledged the new gamestate so it's
// safe to start sending it the real time again
Com_DPrintf( "%s acknowledged gamestate\n", cl->name );
cl->oldServerTime = 0;
if ( serverId == sv.serverId ) {
const int delta = cl->messageAcknowledge - cl->gamestateMessageNum;
if ( delta == 0 || ( delta > 0 && cl->gamestateAck == GSA_SENT_ONCE ) ) {
cl->gamestateAck = GSA_ACKED;
// this client has acknowledged the new gamestate so it's
// safe to start sending it the real time again
Com_DPrintf( "%s acknowledged gamestate with delta %i\n", cl->name, delta );
cl->oldServerTime = 0;
}
}
}
// else if ( cl->state == CS_PRIMED ) {
Expand All @@ -2312,7 +2320,7 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
}
} while ( 1 );

if ( !cl->gamestateAcked ) {
if ( cl->gamestateAck != GSA_ACKED ) {
// late check for gamestate resend
if ( cl->state == CS_PRIMED && cl->messageAcknowledge - cl->gamestateMessageNum > 0 ) {
Com_DPrintf( "%s: dropped gamestate, resending\n", cl->name );
Expand Down
4 changes: 3 additions & 1 deletion code/server/sv_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ void SV_SpawnServer( const char *mapname, qboolean killBots ) {
SV_DropClient( &svs.clients[i], denied );
} else {
if ( !isBot ) {
svs.clients[i].gamestateAcked = qfalse;
svs.clients[i].gamestateAck = GSA_INIT; // resend gamestate, accept first correct serverId
// when we get the next packet from a connected client,
// the new gamestate will be sent
svs.clients[i].state = CS_CONNECTED;
Expand Down Expand Up @@ -851,6 +851,8 @@ static void SV_FinalMessage( const char *message ) {
}
}
}

NET_FlushPacketQueue( 99999 );
}


Expand Down

0 comments on commit f0c43d3

Please sign in to comment.