summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--assets/ui/joinserver.menu55
-rw-r--r--assets/ui/menudef.h1
-rw-r--r--src/client/cl_main.c164
-rw-r--r--src/client/cl_ui.c11
-rw-r--r--src/client/client.h11
-rw-r--r--src/ui/ui_local.h1
-rw-r--r--src/ui/ui_main.c15
7 files changed, 233 insertions, 25 deletions
diff --git a/assets/ui/joinserver.menu b/assets/ui/joinserver.menu
index d9cd2f16..c96cc2be 100644
--- a/assets/ui/joinserver.menu
+++ b/assets/ui/joinserver.menu
@@ -43,6 +43,9 @@
#define LIST_Y ((2*BORDER)+TOP_H)
#define LIST_TOFF 5
#define HEADFOOT_H 25
+#define SERVER_H 16
+#define NUM_FEATURED 4
+#define FEATURED_H (SERVER_H*NUM_FEATURED+2)
menuDef
{
@@ -213,7 +216,7 @@
{
name server
group grpTabs
- text "Server Name"
+ text "Featured Servers"
type ITEM_TYPE_BUTTON
textscale .33
style WINDOW_STYLE_EMPTY
@@ -381,13 +384,57 @@
itemDef
{
+ name featuredlist
+ rect LIST_X (LIST_Y+HEADFOOT_H) LIST_W FEATURED_H
+ type ITEM_TYPE_LISTBOX
+ style WINDOW_STYLE_EMPTY
+ elementwidth 120
+ elementheight SERVER_H
+ textscale .25
+ elementtype LISTBOX_TEXT
+ feeder FEEDER_FEATURED
+ border WINDOW_BORDER_FULL
+ bordercolor 0.5 0.5 0.5 1
+ forecolor 1 1 1 1
+ backcolor 0.2 0.2 0.2 1
+ outlinecolor 0.1 0.1 0.1 0.5
+ visible MENU_TRUE
+ columns 4
+ LIST_TOFF ((SERVER_C*LIST_W)-(3*LIST_TOFF)) ALIGN_LEFT
+ (LIST_TOFF+((SERVER_C)*LIST_W)) ((MAP_C*LIST_W)-(3*LIST_TOFF)) ALIGN_LEFT
+ (LIST_TOFF+((SERVER_C+MAP_C)*LIST_W)) ((PLAYERS_C*LIST_W)-(3*LIST_TOFF)) ALIGN_LEFT
+ (LIST_TOFF+((SERVER_C+MAP_C+PLAYERS_C)*LIST_W)) ((PING_C*LIST_W)-(3*LIST_TOFF)) ALIGN_LEFT
+
+ doubleClick { uiScript JoinServer }
+ }
+
+ itemDef
+ {
+ name featuredseparator
+ rect LIST_X (LIST_Y+FEATURED_H+HEADFOOT_H) LIST_W HEADFOOT_H
+ type ITEM_TYPE_TEXT
+ text "Community Servers"
+ textalign ALIGN_LEFT
+ textvalign VALIGN_CENTER
+ textalignx LIST_TOFF
+ visible MENU_TRUE
+ textScale 0.33
+ border WINDOW_BORDER_FULL
+ bordercolor 0.5 0.5 0.5 1
+ forecolor 1 1 1 1
+ backcolor 0.2 0.2 0.2 1
+ outlinecolor 0.1 0.1 0.1 0.5
+ }
+
+ itemDef
+ {
name serverlist
- rect LIST_X (LIST_Y+HEADFOOT_H) LIST_W (LIST_H-(2*HEADFOOT_H))
+ rect LIST_X (LIST_Y+FEATURED_H+(HEADFOOT_H*2)) LIST_W (LIST_H-(FEATURED_H+(HEADFOOT_H*3)))
type ITEM_TYPE_LISTBOX
style WINDOW_STYLE_EMPTY
elementwidth 120
- elementheight 20
- textscale .33
+ elementheight SERVER_H
+ textscale .25
elementtype LISTBOX_TEXT
feeder FEEDER_SERVERS
border WINDOW_BORDER_FULL
diff --git a/assets/ui/menudef.h b/assets/ui/menudef.h
index 80a44e98..41412225 100644
--- a/assets/ui/menudef.h
+++ b/assets/ui/menudef.h
@@ -87,6 +87,7 @@ enum
enum
{
FEEDER_SERVERS, // servers
+ FEEDER_FEATURED, // featured servers
FEEDER_MAPS, // all maps available, in graphic format
FEEDER_ALIENTEAM_LIST, // alien team members
FEEDER_HUMANTEAM_LIST, // human team members
diff --git a/src/client/cl_main.c b/src/client/cl_main.c
index 16ddcffc..47cf0196 100644
--- a/src/client/cl_main.c
+++ b/src/client/cl_main.c
@@ -2240,6 +2240,7 @@ void CL_InitServerInfo( serverInfo_t *server, netadr_t *address ) {
server->clients = 0;
server->hostName[0] = '\0';
server->mapName[0] = '\0';
+ server->label = NULL;
server->maxClients = 0;
server->maxPing = 0;
server->minPing = 0;
@@ -2249,6 +2250,102 @@ void CL_InitServerInfo( serverInfo_t *server, netadr_t *address ) {
server->netType = 0;
}
+/*
+===================
+CL_GSRSequenceInformation
+
+Parses this packet's index and the number of packets from a master server's
+response. Updates the packet count and returns the index. Advances the data
+pointer as appropriate (but only when parsing was successful)
+
+The sequencing information isn't terribly useful at present (we can skip
+duplicate packets, but we don't bother to make sure we've got all of them).
+===================
+*/
+int CL_GSRSequenceInformation( byte **data )
+{
+ char *p = (char *)*data, *e;
+ int ind, num;
+ // '\0'-delimited fields: this packet's index, total number of packets
+ if( *p++ != '\0' )
+ return -1;
+
+ ind = strtol( p, (char **)&e, 10 );
+ if( *e++ != '\0' )
+ return -1;
+
+ num = strtol( e, (char **)&p, 10 );
+ if( *p++ != '\0' )
+ return -1;
+
+ if( num <= 0 || ind <= 0 || ind > num )
+ return -1; // nonsensical response
+
+ if( cls.numMasterPackets > 0 && num != cls.numMasterPackets )
+ {
+ // Assume we sent two getservers and somehow they changed in
+ // between - only use the results that arrive later
+ Com_DPrintf( "Master changed its mind about packet count!\n" );
+ cls.receivedMasterPackets = 0;
+ cls.numglobalservers = 0;
+ cls.numGlobalServerAddresses = 0;
+ }
+ cls.numMasterPackets = num;
+
+ // successfully parsed
+ *data = (byte *)p;
+ return ind;
+}
+
+/*
+===================
+CL_GSRFeaturedLabel
+
+Parses from the data an arbitrary text string labelling the servers in the
+following getserversresponse packet.
+Either this matches an existing label, or it is copied into a new one.
+The relevant buffer, or NULL, is returned, and *data is advanced as appropriate
+===================
+*/
+char *CL_GSRFeaturedLabel( byte **data )
+{
+ char label[ MAX_FEATLABEL_CHARS ] = { 0 }, *l = label;
+ int i;
+
+ // copy until '\0' which indicates field break
+ // or slash which indicates beginning of server list
+ while( **data && **data != '\\' && **data != '/' )
+ {
+ if( l < &label[ sizeof( label ) - 1 ] )
+ *l = **data;
+ else if( l == &label[ sizeof( label ) - 1 ] )
+ Com_DPrintf( S_COLOR_YELLOW "Warning: "
+ "CL_GSRFeaturedLabel: overflow\n" );
+ l++, (*data)++;
+ }
+
+ if( !label[ 0 ] )
+ return NULL;
+
+ // find the label in the stored array
+ for( i = 0; i < cls.numFeaturedServerLabels; i++ )
+ {
+ l = cls.featuredServerLabels[ i ];
+ if( strcmp( label, l ) == 0 )
+ return l;
+ }
+ if( i == MAX_FEATURED_LABELS )
+ {
+ Com_DPrintf( S_COLOR_YELLOW "Warning: CL_GSRFeaturedLabel: "
+ "ran out of label space, dropping %s\n", label );
+ return NULL;
+ }
+ if( i == 0 )
+ l = cls.featuredServerLabels[ 0 ];
+ Q_strncpyz( l, label, sizeof( *cls.featuredServerLabels ) );
+ return l;
+}
+
#define MAX_SERVERSPERPACKET 256
/*
@@ -2262,13 +2359,18 @@ void CL_ServersResponsePacket( const netadr_t* from, msg_t *msg, qboolean extend
int numservers;
byte* buffptr;
byte* buffend;
+ char *label = NULL;
- Com_Printf("CL_ServersResponsePacket\n");
+ Com_DPrintf("CL_ServersResponsePacket%s\n",
+ (extended) ? " (extended)" : "");
if (cls.numglobalservers == -1) {
// state to detect lack of servers or lack of response
cls.numglobalservers = 0;
cls.numGlobalServerAddresses = 0;
+ cls.numMasterPackets = 0;
+ cls.receivedMasterPackets = 0;
+ cls.numFeaturedServerLabels = 0;
}
// parse through server response string
@@ -2276,14 +2378,47 @@ void CL_ServersResponsePacket( const netadr_t* from, msg_t *msg, qboolean extend
buffptr = msg->data;
buffend = buffptr + msg->cursize;
+ // skip header
+ buffptr += 4;
+
// advance to initial token
- do
+ // I considered using strchr for this but I don't feel like relying
+ // on its behaviour with '\0'
+ while( *buffptr && *buffptr != '\\' && *buffptr != '/' )
{
- if(*buffptr == '\\' || (extended && *buffptr == '/'))
- break;
-
buffptr++;
- } while (buffptr < buffend);
+
+ if( buffptr >= buffend )
+ break;
+ }
+
+ if( *buffptr == '\0' )
+ {
+ int ind = CL_GSRSequenceInformation( &buffptr );
+ if( ind >= 0 )
+ {
+ // this denotes the start of new-syntax stuff
+ // have we already received this packet?
+ if( cls.receivedMasterPackets & ( 1 << ( ind - 1 ) ) )
+ {
+ Com_DPrintf( "CL_ServersResponsePacket: "
+ "received packet %d again, ignoring\n",
+ ind );
+ return;
+ }
+ // TODO: detect dropped packets and make another
+ // request
+ Com_DPrintf( "CL_ServersResponsePacket: packet "
+ "%d of %d\n", ind, cls.numMasterPackets );
+ cls.receivedMasterPackets |= ( 1 << ( ind - 1 ) );
+
+ label = CL_GSRFeaturedLabel( &buffptr );
+ Com_DPrintf( "CL_GSRFeaturedLabel: %s\n", label );
+ }
+ // now skip to the server list
+ for(; buffptr < buffend && *buffptr != '\\' && *buffptr != '/';
+ buffptr++ );
+ }
while (buffptr + 1 < buffend)
{
@@ -2339,6 +2474,7 @@ void CL_ServersResponsePacket( const netadr_t* from, msg_t *msg, qboolean extend
serverInfo_t *server = &cls.globalServers[count];
CL_InitServerInfo( server, &addresses[i] );
+ server->label = label;
// advance to next slot
count++;
}
@@ -3725,7 +3861,6 @@ void CL_GlobalServers_f( void ) {
netadr_t to;
int count, i, masterNum;
char command[1024], *masteraddress;
- char *cmdname;
if ((count = Cmd_Argc()) < 3 || (masterNum = atoi(Cmd_Argv(1))) < 0 || masterNum > 4)
{
@@ -3760,17 +3895,10 @@ void CL_GlobalServers_f( void ) {
cls.numglobalservers = -1;
cls.pingUpdateSource = AS_GLOBAL;
- // Use the extended query for IPv6 masters
- if (to.type == NA_IP6 || to.type == NA_MULTICAST6)
- {
- cmdname = "getserversExt " GAMENAME_FOR_MASTER;
-
- // TODO: test if we only have an IPv6 connection. If it's the case,
- // request IPv6 servers only by appending " ipv6" to the command
- }
- else
- cmdname = "getservers";
- Com_sprintf( command, sizeof(command), "%s %s", cmdname, Cmd_Argv(2) );
+ // TODO: test if we only have an IPv6 connection. If it's the case,
+ // request IPv6 servers only by appending " ipv6" to the command
+ Com_sprintf( command, sizeof(command), "getserversExt "
+ GAMENAME_FOR_MASTER " %s", Cmd_Argv(2) );
for (i=3; i < count; i++)
{
diff --git a/src/client/cl_ui.c b/src/client/cl_ui.c
index 49719824..34ae04ea 100644
--- a/src/client/cl_ui.c
+++ b/src/client/cl_ui.c
@@ -285,6 +285,8 @@ static void LAN_GetServerInfo( int source, int n, char *buf, int buflen ) {
buf[0] = '\0';
Info_SetValueForKey( info, "hostname", server->hostName);
Info_SetValueForKey( info, "mapname", server->mapName);
+ if( server->label )
+ Info_SetValueForKey( info, "label", server->label);
Info_SetValueForKey( info, "clients", va("%i",server->clients));
Info_SetValueForKey( info, "sv_maxclients", va("%i",server->maxClients));
Info_SetValueForKey( info, "ping", va("%i",server->ping));
@@ -375,6 +377,15 @@ static int LAN_CompareServers( int source, int sortKey, int sortDir, int s1, int
return 0;
}
+ // featured servers on top
+ // this is not so that they are more noticeable but that codewise it
+ // makes it much simpler to have them contiguous in the list
+ // so changing this also requires changing the feederID counting and
+ // similar code that depends on them coming first
+ res = Q_stricmpn( server1->label, server2->label, MAX_FEATLABEL_CHARS );
+ if( res )
+ return -res;
+
res = 0;
switch( sortKey ) {
case SORT_HOST:
diff --git a/src/client/client.h b/src/client/client.h
index f19c6230..5a1106b4 100644
--- a/src/client/client.h
+++ b/src/client/client.h
@@ -288,6 +288,7 @@ typedef struct {
char hostName[MAX_HOSTNAME_LENGTH];
char mapName[MAX_NAME_LENGTH];
char game[MAX_NAME_LENGTH];
+ char *label; // for featured servers, NULL otherwise
int netType;
int gameType;
int clients;
@@ -298,6 +299,9 @@ typedef struct {
qboolean visible;
} serverInfo_t;
+#define MAX_FEATURED_LABELS 8
+#define MAX_FEATLABEL_CHARS 1024
+
typedef struct {
connstate_t state; // connection status
@@ -318,6 +322,10 @@ typedef struct {
int realtime; // ignores pause
int realFrametime; // ignoring pause, so console always works
+ // master server sequence information
+ int numMasterPackets;
+ unsigned int receivedMasterPackets; // bitfield
+
int numlocalservers;
serverInfo_t localServers[MAX_OTHER_SERVERS];
@@ -330,6 +338,9 @@ typedef struct {
int numfavoriteservers;
serverInfo_t favoriteServers[MAX_OTHER_SERVERS];
+ int numFeaturedServerLabels;
+ char featuredServerLabels[ MAX_FEATURED_LABELS ][ MAX_FEATLABEL_CHARS ];
+
int pingUpdateSource; // source currently pinging or updating
// update server info
diff --git a/src/ui/ui_local.h b/src/ui/ui_local.h
index 48eea7db..938f1e23 100644
--- a/src/ui/ui_local.h
+++ b/src/ui/ui_local.h
@@ -102,6 +102,7 @@ typedef struct serverStatus_s
int currentServer;
int displayServers[MAX_DISPLAY_SERVERS];
int numDisplayServers;
+ int numFeaturedServers;
int numPlayersOnServers;
int nextDisplayRefresh;
int nextSortTime;
diff --git a/src/ui/ui_main.c b/src/ui/ui_main.c
index 441ce79a..43aaa0af 100644
--- a/src/ui/ui_main.c
+++ b/src/ui/ui_main.c
@@ -907,6 +907,8 @@ static void UI_BuildServerDisplayList( qboolean force )
if( ping > 0 )
{
trap_LAN_MarkServerVisible( ui_netSource.integer, i, qfalse );
+ if( Info_ValueForKey( info, "label" )[0] )
+ uiInfo.serverStatus.numFeaturedServers++;
numinvisible++;
}
}
@@ -1043,6 +1045,7 @@ static void UI_StartServerRefresh( qboolean full )
uiInfo.serverStatus.nextDisplayRefresh = uiInfo.uiDC.realTime + 1000;
// clear number of displayed servers
uiInfo.serverStatus.numDisplayServers = 0;
+ uiInfo.serverStatus.numFeaturedServers = 0;
uiInfo.serverStatus.numPlayersOnServers = 0;
// mark all servers as visible so we store ping updates for them
trap_LAN_MarkServerVisible( ui_netSource.integer, -1, qtrue );
@@ -3342,7 +3345,10 @@ static int UI_FeederCount( float feederID )
else if( feederID == FEEDER_MAPS )
return uiInfo.mapCount;
else if( feederID == FEEDER_SERVERS )
- return uiInfo.serverStatus.numDisplayServers;
+ return uiInfo.serverStatus.numDisplayServers -
+ uiInfo.serverStatus.numFeaturedServers;
+ else if( feederID == FEEDER_FEATURED )
+ return uiInfo.serverStatus.numFeaturedServers;
else if( feederID == FEEDER_SERVERSTATUS )
return uiInfo.serverStatusInfo.numLines;
else if( feederID == FEEDER_FINDPLAYER )
@@ -3434,12 +3440,15 @@ static const char *UI_FeederItemText( float feederID, int index, int column, qha
int actual;
return UI_SelectedMap( index, &actual );
}
- else if( feederID == FEEDER_SERVERS )
+ else if( feederID == FEEDER_SERVERS || feederID == FEEDER_FEATURED )
{
- if( index >= 0 && index < uiInfo.serverStatus.numDisplayServers )
+ if( index >= 0 && index < UI_FeederCount( feederID ) )
{
int ping;
+ if( feederID == FEEDER_SERVERS )
+ index += UI_FeederCount( FEEDER_FEATURED );
+
if( lastColumn != column || lastTime > uiInfo.uiDC.realTime + 5000 )
{
trap_LAN_GetServerInfo( ui_netSource.integer, uiInfo.serverStatus.displayServers[index],