make menu QC aware of player list; add extension DP_SV_QCSTATUS to allow QC to overri...
authordivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Fri, 8 Aug 2008 08:13:13 +0000 (08:13 +0000)
committerdivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Fri, 8 Aug 2008 08:13:13 +0000 (08:13 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@8439 d7cf8633-e32d-0410-b094-e92efae38249

mvm_cmds.c
netconn.c
netconn.h
progsvm.h
prvm_edict.c
svvm_cmds.c

index ddf72ae..0581a1c 100644 (file)
@@ -371,6 +371,12 @@ void VM_M_setserverlistmaskstring( void )
                case SLIF_NAME:
                        strlcpy( mask->info.name, PRVM_G_STRING( OFS_PARM2 ), sizeof(mask->info.name)  );
                        break;
+               case SLIF_QCSTATUS:
+                       strlcpy( mask->info.qcstatus, PRVM_G_STRING( OFS_PARM2 ), sizeof(mask->info.qcstatus)  );
+                       break;
+               case SLIF_PLAYERS:
+                       strlcpy( mask->info.players, PRVM_G_STRING( OFS_PARM2 ), sizeof(mask->info.players)  );
+                       break;
                case SLIF_MAP:
                        strlcpy( mask->info.map, PRVM_G_STRING( OFS_PARM2 ), sizeof(mask->info.map)  );
                        break;
@@ -497,6 +503,12 @@ void VM_M_getserverliststring(void)
                case SLIF_NAME:
                        PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( cache->info.name );
                        break;
+               case SLIF_QCSTATUS:
+                       PRVM_G_INT (OFS_RETURN ) = PRVM_SetEngineString (cache->info.qcstatus );
+                       break;
+               case SLIF_PLAYERS:
+                       PRVM_G_INT (OFS_RETURN ) = PRVM_SetEngineString (cache->info.players );
+                       break;
                case SLIF_GAME:
                        PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( cache->info.game );
                        break;
@@ -624,6 +636,10 @@ void VM_M_getserverlistindexforkey( void )
                PRVM_G_FLOAT( OFS_RETURN ) = SLIF_MAP;
        else if( !strcmp( key, "name" ) )
                PRVM_G_FLOAT( OFS_RETURN ) = SLIF_NAME;
+       else if( !strcmp( key, "qcstatus" ) )
+               PRVM_G_FLOAT( OFS_RETURN ) = SLIF_QCSTATUS;
+       else if( !strcmp( key, "players" ) )
+               PRVM_G_FLOAT( OFS_RETURN ) = SLIF_PLAYERS;
        else if( !strcmp( key, "maxplayers" ) )
                PRVM_G_FLOAT( OFS_RETURN ) = SLIF_MAXPLAYERS;
        else if( !strcmp( key, "numplayers" ) )
index d114d7c..3abc226 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -208,6 +208,12 @@ static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_ent
                case SLIF_NAME:
                        result = strcasecmp( B->info.name, A->info.name );
                        break;
+               case SLIF_QCSTATUS:
+                       result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
+                       break;
+               case SLIF_PLAYERS:
+                       result = strcasecmp( B->info.players, A->info.qcstatus ); // not really THAT useful, though
+                       break;
                default:
                        Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
                        break;
@@ -240,6 +246,8 @@ static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
                case SLMO_GREATEREQUAL:
                case SLMO_CONTAINS:
                case SLMO_NOTCONTAIN:
+               case SLMO_STARTSWITH:
+               case SLMO_NOTSTARTSWITH:
                        return A >= B;
                default:
                        Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
@@ -250,9 +258,10 @@ static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
 static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
 {
        int i;
-       char bufferA[ 256 ], bufferB[ 256 ]; // should be more than enough
-       for (i = 0;i < (int)sizeof(bufferA)-1 && A[i];i++)
-               bufferA[i] = (A[i] >= 'A' && A[i] <= 'Z') ? (A[i] + 'a' - 'A') : A[i];
+       char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
+       COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
+       for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
+               bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
        bufferA[i] = 0;
        for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
                bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
@@ -265,6 +274,11 @@ static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, c
                        return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
                case SLMO_NOTCONTAIN:
                        return !*bufferB || !strstr( bufferA, bufferB );
+               case SLMO_STARTSWITH:
+                       //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
+                       return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
+               case SLMO_NOTSTARTSWITH:
+                       return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
                case SLMO_LESS:
                        return strcmp( bufferA, bufferB ) < 0;
                case SLMO_LESSEQUAL:
@@ -314,6 +328,12 @@ static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info
        if( *mask->info.name
                && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
                return false;
+       if( *mask->info.qcstatus
+               && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
+               return false;
+       if( *mask->info.players
+               && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
+               return false;
        return true;
 }
 
@@ -1403,6 +1423,58 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        M_Update_Return_Reason(rejectreason);
                        return true;
                }
+               if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
+               {
+                       serverlist_info_t *info;
+                       char *p;
+                       int n;
+
+                       string += 15;
+                       // search the cache for this server and update it
+                       n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
+                       if (n < 0)
+                               return true;
+
+                       info = &serverlist_cache[n].info;
+                       info->game[0] = 0;
+                       info->mod[0]  = 0;
+                       info->map[0]  = 0;
+                       info->name[0] = 0;
+                       info->qcstatus[0] = 0;
+                       info->players[0] = 0;
+                       info->protocol = -1;
+                       info->numplayers = 0;
+                       info->numbots = -1;
+                       info->maxplayers  = 0;
+                       info->gameversion = 0;
+
+                       p = strchr(string, '\n');
+                       if(p)
+                       {
+                               *p = 0; // cut off the string there
+                               ++p;
+                       }
+                       else
+                               Con_Printf("statusResponse without players block?\n");
+
+                       if ((s = SearchInfostring(string, "gamename"     )) != NULL) strlcpy(info->game, s, sizeof (info->game));
+                       if ((s = SearchInfostring(string, "modname"      )) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
+                       if ((s = SearchInfostring(string, "mapname"      )) != NULL) strlcpy(info->map , s, sizeof (info->map ));
+                       if ((s = SearchInfostring(string, "hostname"     )) != NULL) strlcpy(info->name, s, sizeof (info->name));
+                       if ((s = SearchInfostring(string, "protocol"     )) != NULL) info->protocol = atoi(s);
+                       if ((s = SearchInfostring(string, "clients"      )) != NULL) info->numplayers = atoi(s);
+                       if ((s = SearchInfostring(string, "bots"         )) != NULL) info->numbots = atoi(s);
+                       if ((s = SearchInfostring(string, "sv_maxclients")) != NULL) info->maxplayers = atoi(s);
+                       if ((s = SearchInfostring(string, "gameversion"  )) != NULL) info->gameversion = atoi(s);
+                       if ((s = SearchInfostring(string, "qcstatus"     )) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
+                       if (p                                               != NULL) strlcpy(info->players, p, sizeof(info->players));
+                       info->numhumans = info->numplayers - max(0, info->numbots);
+                       info->freeslots = info->maxplayers - info->numplayers;
+
+                       NetConn_ClientParsePacket_ServerList_UpdateCache(n);
+
+                       return true;
+               }
                if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
                {
                        serverlist_info_t *info;
@@ -1419,11 +1491,14 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        info->mod[0]  = 0;
                        info->map[0]  = 0;
                        info->name[0] = 0;
+                       info->qcstatus[0] = 0;
+                       info->players[0] = 0;
                        info->protocol = -1;
                        info->numplayers = 0;
                        info->numbots = -1;
                        info->maxplayers  = 0;
                        info->gameversion = 0;
+
                        if ((s = SearchInfostring(string, "gamename"     )) != NULL) strlcpy(info->game, s, sizeof (info->game));
                        if ((s = SearchInfostring(string, "modname"      )) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
                        if ((s = SearchInfostring(string, "mapname"      )) != NULL) strlcpy(info->map , s, sizeof (info->map ));
@@ -1433,6 +1508,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        if ((s = SearchInfostring(string, "bots"         )) != NULL) info->numbots = atoi(s);
                        if ((s = SearchInfostring(string, "sv_maxclients")) != NULL) info->maxplayers = atoi(s);
                        if ((s = SearchInfostring(string, "gameversion"  )) != NULL) info->gameversion = atoi(s);
+                       if ((s = SearchInfostring(string, "qcstatus"     )) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
                        info->numhumans = info->numplayers - max(0, info->numbots);
                        info->freeslots = info->maxplayers - info->numplayers;
 
@@ -1738,7 +1814,7 @@ void NetConn_QueryQueueFrame(void)
                        else
                        {
                                for (socket     = 0; socket     < cl_numsockets ;       socket++)
-                                       NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getinfo", &address);
+                                       NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
                        }
 
                        //      update the entry fields
@@ -1831,11 +1907,15 @@ static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
        buffer[i] = 0;
 }
 
+// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
 {
+       const char *qcstatus = NULL;
        unsigned int nb_clients = 0, nb_bots = 0, i;
        int length;
 
+       SV_VM_Begin();
+
        // How many clients are there?
        for (i = 0;i < (unsigned int)svs.maxclients;i++)
        {
@@ -1847,27 +1927,50 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
                }
        }
 
+       if(prog->globaloffsets.worldstatus >= 0)
+       {
+               const char *str = PRVM_G_STRING(prog->globaloffsets.worldstatus);
+               if(str && *str)
+               {
+                       char *p;
+                       const char *q;
+                       qcstatus = p = Mem_Alloc(tempmempool, strlen(str) + 1);
+                       for(q = str; *q; ++q)
+                               if(*q != '\\')
+                                       *p++ = *q;
+                       *p = 0;
+               }
+       }
+
        // TODO: we should add more information for the full status string
        length = dpsnprintf(out_msg, out_size,
                                                "\377\377\377\377%s\x0A"
                                                "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
                                                "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
                                                "%s%s"
+                                               "%s%s"
                                                "%s",
                                                fullstatus ? "statusResponse" : "infoResponse",
                                                gamename, com_modname, gameversion.integer, svs.maxclients,
                                                nb_clients, nb_bots, sv.name, hostname.string, NET_PROTOCOL_VERSION,
+                                               qcstatus ? "\\qcstatus\\" : "", qcstatus ? qcstatus : "",
                                                challenge ? "\\challenge\\" : "", challenge ? challenge : "",
                                                fullstatus ? "\n" : "");
 
+       if(qcstatus)
+               Mem_Free((char *)qcstatus);
+
        // Make sure it fits in the buffer
        if (length < 0)
-               return false;
+               goto bad;
 
        if (fullstatus)
        {
                char *ptr;
                int left;
+               int savelength;
+
+               savelength = length;
 
                ptr = out_msg + length;
                left = (int)out_size - length;
@@ -1900,19 +2003,57 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
                                        pingvalue = bound(1, pingvalue, 9999);
                                else
                                        pingvalue = 0;
-                               length = dpsnprintf(ptr, left, "%d %d \"%s\"\n",
-                                                                       cl->frags,
-                                                                       pingvalue,
-                                                                       cleanname);
+
+                               if(prog->fieldoffsets.clientstatus >= 0)
+                               {
+                                       const char *str = PRVM_E_STRING(PRVM_EDICT_NUM(i + 1), prog->fieldoffsets.clientstatus);
+                                       if(str && *str)
+                                       {
+                                               char *p;
+                                               const char *q;
+                                               qcstatus = p = Mem_Alloc(tempmempool, strlen(str) + 1);
+                                               for(q = str; *q; ++q)
+                                                       if(*q != '\\' && *q != ' ')
+                                                               *p++ = *q;
+                                               *p = 0;
+                                       }
+                               }
+
+                               if(qcstatus)
+                               {
+                                       length = dpsnprintf(ptr, left, "%s %d \"%s\"\n",
+                                                                               qcstatus,
+                                                                               pingvalue,
+                                                                               cleanname);
+                                       Mem_Free((char *)qcstatus);
+                               }
+                               else
+                                       length = dpsnprintf(ptr, left, "%d %d \"%s\"\n",
+                                                                               cl->frags,
+                                                                               pingvalue,
+                                                                               cleanname);
+
                                if(length < 0)
-                                       return false;
+                               {
+                                       // out of space?
+                                       // turn it into an infoResponse!
+                                       out_msg[savelength] = 0;
+                                       memcpy(out_msg + 4, "infoResponse\x0A", 13);
+                                       memmove(out_msg + 17, out_msg + 19, savelength - 19);
+                                       break;
+                               }
                                left -= length;
                                ptr += length;
                        }
                }
        }
 
+       SV_VM_End();
        return true;
+
+bad:
+       SV_VM_End();
+       return false;
 }
 
 static qboolean NetConn_PreventConnectFlood(lhnetaddress_t *peeraddress)
@@ -2586,7 +2727,7 @@ void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
                                SZ_Clear(&net_message);
 
                                // search LAN for DarkPlaces servers
-                               NetConn_WriteString(cl_sockets[i], "\377\377\377\377getinfo", &broadcastaddress);
+                               NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
 
                                // build the getservers message to send to the dpmaster master servers
                                dpsnprintf(request, sizeof(request), "\377\377\377\377getservers %s %u empty full\x0A", gamename, NET_PROTOCOL_VERSION);
index a755d28..30bb90e 100755 (executable)
--- a/netconn.h
+++ b/netconn.h
@@ -227,7 +227,9 @@ typedef enum serverlist_maskop_e
        SLMO_EQUAL,
        SLMO_GREATER,
        SLMO_GREATEREQUAL,
-       SLMO_NOTEQUAL
+       SLMO_NOTEQUAL,
+       SLMO_STARTSWITH,
+       SLMO_NOTSTARTSWITH
 } serverlist_maskop_t;
 
 // struct with all fields that you can search for or sort by
@@ -245,6 +247,10 @@ typedef struct serverlist_info_s
        char map[32];
        // name of the session
        char name[128];
+       // qc-defined short status string
+       char qcstatus[128];
+       // frags/ping/name list (if they fit in the packet)
+       char players[1400];
        // max client number
        int maxplayers;
        // number of currently connected players (including bots)
@@ -277,6 +283,8 @@ typedef enum
        SLIF_NUMBOTS,
        SLIF_NUMHUMANS,
        SLIF_FREESLOTS,
+       SLIF_QCSTATUS,
+       SLIF_PLAYERS,
        SLIF_COUNT
 } serverlist_infofield_t;
 
index 8386e99..90b5511 100644 (file)
--- a/progsvm.h
+++ b/progsvm.h
@@ -217,6 +217,7 @@ typedef struct prvm_prog_fieldoffsets_s
        int viewzoom; // ssqc
        int yaw_speed; // ssqc / csqc
        int clientcamera; // ssqc
+       int clientstatus; // ssqc
 }
 prvm_prog_fieldoffsets_t;
 
@@ -253,6 +254,7 @@ typedef struct prvm_prog_globaloffsets_s
        int sb_showscores; // csqc
        int drawfont; // csqc / menu
        int require_spawnfunc_prefix; // ssqc
+       int worldstatus; // ssqc
 }
 prvm_prog_globaloffsets_t;
 
index d74abbd..998c225 100644 (file)
@@ -1457,6 +1457,7 @@ void PRVM_FindOffsets(void)
        prog->fieldoffsets.viewzoom                       = PRVM_ED_FindFieldOffset("viewzoom");
        prog->fieldoffsets.yaw_speed                      = PRVM_ED_FindFieldOffset("yaw_speed");
        prog->fieldoffsets.clientcamera                   = PRVM_ED_FindFieldOffset("clientcamera");
+       prog->fieldoffsets.clientstatus                   = PRVM_ED_FindFieldOffset("clientstatus");
        prog->funcoffsets.CSQC_ConsoleCommand             = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
        prog->funcoffsets.CSQC_Ent_Remove                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
        prog->funcoffsets.CSQC_Ent_Update                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
@@ -1509,6 +1510,7 @@ void PRVM_FindOffsets(void)
        prog->globaloffsets.sb_showscores                 = PRVM_ED_FindGlobalOffset("sb_showscores");
        prog->globaloffsets.drawfont                      = PRVM_ED_FindGlobalOffset("drawfont");
        prog->globaloffsets.require_spawnfunc_prefix      = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
+       prog->globaloffsets.worldstatus                   = PRVM_ED_FindGlobalOffset("worldstatus");
 
        // menu qc only uses some functions, nothing else
        prog->funcoffsets.m_draw                          = PRVM_ED_FindFunctionOffset("m_draw");
index d979e2f..d7fe26d 100644 (file)
@@ -127,6 +127,7 @@ char *vm_sv_extensions =
 "DP_SV_PRECACHEANYTIME "
 "DP_SV_PRINT "
 "DP_SV_PUNCHVECTOR "
+"DP_SV_QCSTATUS "
 "DP_SV_ROTATINGBMODEL "
 "DP_SV_SETCOLOR "
 "DP_SV_SHUTDOWN "