foxygit / doom Log in
commit b11030cbfee0d4d4a46673001992e94abc146c8c
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Thu Dec 2 20:11:24 2010 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Thu Dec 2 20:11:24 2010 +0000

    More refactoring of querying code, to not be specific to the purpose of
    printing out a list.

    Subversion-branch: /trunk/chocolate-doom
    Subversion-revision: 2184
---
 src/d_main.c    |  13 +-
 src/net_query.c | 372 ++++++++++++++++++++++++++++++--------------------------
 src/net_query.h |  11 +-
 3 files changed, 218 insertions(+), 178 deletions(-)

diff --git a/src/d_main.c b/src/d_main.c
index 22fdb142..c0804b5d 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -852,7 +852,10 @@ void D_DoomMain (void)

     if (M_CheckParm("-masterquery"))
     {
-        NET_MasterQuery();
+        printf("\nSearching for servers on Internet ...\n\n");
+        p = NET_MasterQuery(NET_QueryPrintCallback, NULL);
+        printf("%i server(s) found.\n", p);
+        exit(0);
     }

     //!
@@ -868,6 +871,7 @@ void D_DoomMain (void)
     if (p > 0)
     {
         NET_QueryAddress(myargv[p+1]);
+        exit(0);
     }

     //!
@@ -877,7 +881,12 @@ void D_DoomMain (void)
     //

     if (M_CheckParm("-search"))
-        NET_LANQuery();
+    {
+        printf("\nSearching for servers on local LAN ...\n");
+        p = NET_LANQuery(NET_QueryPrintCallback, NULL);
+        printf("\n%i server(s) found.\n", p);
+        exit(0);
+    }

 #endif

diff --git a/src/net_query.c b/src/net_query.c
index 681411c2..3dee1b00 100644
--- a/src/net_query.c
+++ b/src/net_query.c
@@ -71,6 +71,7 @@ static net_context_t *query_context;
 static query_target_t *targets;
 static int num_targets;

+static boolean query_loop_running = false;
 static boolean printed_header = false;

 // Resolve the master server address.
@@ -205,7 +206,9 @@ static void NET_Query_SendQuery(net_addr_t *addr)
     NET_FreePacket(request);
 }

-static void NET_Query_ParseResponse(net_addr_t *addr, net_packet_t *packet)
+static void NET_Query_ParseResponse(net_addr_t *addr, net_packet_t *packet,
+                                    net_query_callback_t callback,
+                                    void *user_data)
 {
     unsigned int packet_type;
     net_querydata_t querydata;
@@ -231,8 +234,15 @@ static void NET_Query_ParseResponse(net_addr_t *addr, net_packet_t *packet)

     target = GetTargetForAddr(addr, true);

-    target->state = QUERY_TARGET_RESPONDED;
-    memcpy(&target->data, &querydata, sizeof(net_querydata_t));
+    if (target->state != QUERY_TARGET_RESPONDED)
+    {
+        target->state = QUERY_TARGET_RESPONDED;
+        memcpy(&target->data, &querydata, sizeof(net_querydata_t));
+
+        // Invoke callback to signal that we have a new address.
+
+        callback(addr, &target->data, user_data);
+    }
 }

 // Parse a response packet from the master server.
@@ -282,7 +292,9 @@ static void NET_Query_ParseMasterResponse(net_addr_t *master_addr,
     target->state = QUERY_TARGET_RESPONDED;
 }

-static void NET_Query_ParsePacket(net_addr_t *addr, net_packet_t *packet)
+static void NET_Query_ParsePacket(net_addr_t *addr, net_packet_t *packet,
+                                  net_query_callback_t callback,
+                                  void *user_data)
 {
     query_target_t *target;

@@ -296,18 +308,19 @@ static void NET_Query_ParsePacket(net_addr_t *addr, net_packet_t *packet)
     }
     else
     {
-        NET_Query_ParseResponse(addr, packet);
+        NET_Query_ParseResponse(addr, packet, callback, user_data);
     }
 }

-static void NET_Query_GetResponse(void)
+static void NET_Query_GetResponse(net_query_callback_t callback,
+                                  void *user_data)
 {
     net_addr_t *addr;
     net_packet_t *packet;

     if (NET_RecvPacket(query_context, &addr, &packet))
     {
-        NET_Query_ParsePacket(addr, packet);
+        NET_Query_ParsePacket(addr, packet, callback, user_data);
         NET_FreePacket(packet);
     }
 }
@@ -354,25 +367,6 @@ static void SendOneQuery(void)
     targets[i].query_time = I_GetTimeMS();
 }

-// Search the targets list and find a target that has responded.
-// If none have responded yet, returns NULL.
-
-static query_target_t *FindFirstResponder(void)
-{
-    unsigned int i;
-
-    for (i = 0; i < num_targets; ++i)
-    {
-        if (targets[i].type == QUERY_TARGET_SERVER
-         && targets[i].state == QUERY_TARGET_RESPONDED)
-        {
-            return &targets[i];
-        }
-    }
-
-    return NULL;
-}
-
 // Time out servers that have been queried and not responded.

 static void CheckTargetTimeouts(void)
@@ -410,197 +404,165 @@ static boolean AllTargetsDone(void)
     return true;
 }

-static void formatted_printf(int wide, char *s, ...)
-{
-    va_list args;
-    int i;
-
-    va_start(args, s);
-    i = vprintf(s, args);
-    va_end(args);
+// Stop the query loop

-    while (i < wide)
-    {
-        putchar(' ');
-        ++i;
-    }
+static void NET_Query_ExitLoop(void)
+{
+    query_loop_running = false;
 }

-static char *GameDescription(GameMode_t mode, GameMission_t mission)
+// Loop waiting for responses.
+// The specified callback is invoked when a new server responds.
+
+static void NET_Query_QueryLoop(net_query_callback_t callback,
+                                void *user_data)
 {
-    switch (mode)
+    query_loop_running = true;
+
+    while (query_loop_running && !AllTargetsDone())
     {
-        case shareware:
-            return "shareware";
-        case registered:
-            return "registered";
-        case retail:
-            return "ultimate";
-        case commercial:
-            if (mission == doom2)
-                return "doom2";
-            else if (mission == pack_tnt)
-                return "tnt";
-            else if (mission == pack_plut)
-                return "plutonia";
-        default:
-            return "unknown";
-    }
-}
+        // Send a query.  This will only send a single query.
+        // Because of the delay below, this is therefore rate limited.

-static void PrintHeader(void)
-{
-    int i;
+        SendOneQuery();

-    formatted_printf(18, "Address");
-    formatted_printf(8, "Players");
-    puts("Description");
+        // Check for a response

-    for (i=0; i<70; ++i)
-        putchar('=');
-    putchar('\n');
+        NET_Query_GetResponse(callback, user_data);
+
+        // Don't thrash the CPU
+
+        I_Sleep(100);
+
+        CheckTargetTimeouts();
+    }
 }

-static void PrintResponse(query_target_t *target)
+void NET_Query_Init(void)
 {
-    formatted_printf(18, "%s: ", NET_AddrToString(target->addr));
-    formatted_printf(8, "%i/%i", target->data.num_players,
-                                 target->data.max_players);
+    query_context = NET_NewContext();
+    NET_AddModule(query_context, &net_sdl_module);
+    net_sdl_module.InitClient();

-    if (target->data.gamemode != indetermined)
-    {
-        printf("(%s) ", GameDescription(target->data.gamemode,
-                                        target->data.gamemission));
-    }
+    targets = NULL;
+    num_targets = 0;

-    if (target->data.server_state)
-    {
-        printf("(game running) ");
-    }
+    printed_header = false;
+}
+
+// Callback that exits the query loop when the first server is found.

-    NET_SafePuts(target->data.description);
+static void NET_Query_ExitCallback(net_addr_t *addr, net_querydata_t *data,
+                                   void *user_data)
+{
+    NET_Query_ExitLoop();
 }

-// Check for printing information about servers that have responded.
+// Search the targets list and find a target that has responded.
+// If none have responded, returns NULL.

-static void CheckPrintOutput(void)
+static query_target_t *FindFirstResponder(void)
 {
     unsigned int i;

     for (i = 0; i < num_targets; ++i)
     {
         if (targets[i].type == QUERY_TARGET_SERVER
-         && targets[i].state == QUERY_TARGET_RESPONDED
-         && !targets[i].printed)
+         && targets[i].state == QUERY_TARGET_RESPONDED)
         {
-            if (!printed_header)
-            {
-                PrintHeader();
-                printed_header = true;
-            }
-
-            PrintResponse(&targets[i]);
-            targets[i].printed = true;
+            return &targets[i];
         }
     }
+
+    return NULL;
 }

-// Loop waiting for responses.
+// Return a count of the number of responses.

-static net_addr_t *NET_Query_QueryLoop(boolean find_first,
-                                       boolean silent)
+static int GetNumResponses(void)
 {
-    query_target_t *responder;
-    int start_time;
-    int last_send_time;
+    unsigned int i;
+    int result;

-    last_send_time = -1;
-    start_time = I_GetTimeMS();
+    result = 0;

-    while (!AllTargetsDone())
+    for (i = 0; i < num_targets; ++i)
     {
-        // Send a query.  This will only send a single query.
-        // Because of the delay below, this is therefore rate limited.
+        if (targets[i].type == QUERY_TARGET_SERVER
+         && targets[i].state == QUERY_TARGET_RESPONDED)
+        {
+            ++result;
+        }
+    }

-        SendOneQuery();
+    return result;
+}

-        // Check for a response
+void NET_QueryAddress(char *addr_str)
+{
+    net_addr_t *addr;
+    query_target_t *target;

-        NET_Query_GetResponse();
+    NET_Query_Init();

-        // Output the responses
+    addr = NET_ResolveAddress(query_context, addr_str);

-        if (!silent)
-        {
-            CheckPrintOutput();
-        }
+    if (addr == NULL)
+    {
+        I_Error("NET_QueryAddress: Host '%s' not found!", addr_str);
+    }

-        // Found a response?
+    // Add the address to the list of targets.

-        if (find_first && FindFirstResponder())
-        {
-            break;
-        }
+    target = GetTargetForAddr(addr, true);

-        // Don't thrash the CPU
+    printf("\nQuerying '%s'...\n", addr_str);

-        I_Sleep(100);
+    // Run query loop.

-        CheckTargetTimeouts();
-    }
+    NET_Query_QueryLoop(NET_Query_ExitCallback, NULL);

-    responder = FindFirstResponder();
+    // Check if the target responded.

-    if (responder != NULL)
+    if (target->state == QUERY_TARGET_RESPONDED)
     {
-        return responder->addr;
+        NET_QueryPrintCallback(addr, &target->data, NULL);
     }
     else
     {
-        return NULL;
+        I_Error("No response from '%s'", addr_str);
     }
 }

-void NET_Query_Init(void)
-{
-    query_context = NET_NewContext();
-    NET_AddModule(query_context, &net_sdl_module);
-    net_sdl_module.InitClient();
-
-    targets = NULL;
-    num_targets = 0;
-
-    printed_header = false;
-}
-
-void NET_QueryAddress(char *addr)
+net_addr_t *NET_FindLANServer(void)
 {
-    net_addr_t *net_addr;
+    query_target_t *target;
+    query_target_t *responder;

     NET_Query_Init();

-    net_addr = NET_ResolveAddress(query_context, addr);
+    // Add a broadcast target to the list.

-    if (net_addr == NULL)
-    {
-        I_Error("NET_QueryAddress: Host '%s' not found!", addr);
-    }
+    target = GetTargetForAddr(NULL, true);
+    target->type = QUERY_TARGET_BROADCAST;

-    // Add the address to the list of targets.
+    // Run the query loop, and stop at the first target found.

-    GetTargetForAddr(net_addr, true);
+    NET_Query_QueryLoop(NET_Query_ExitCallback, NULL);

-    printf("\nQuerying '%s'...\n\n", addr);
+    responder = FindFirstResponder();

-    if (!NET_Query_QueryLoop(true, false))
+    if (responder != NULL)
     {
-        I_Error("No response from '%s'", addr);
+        return responder->addr;
+    }
+    else
+    {
+        return NULL;
     }
-
-    exit(0);
 }

-net_addr_t *NET_FindLANServer(void)
+int NET_LANQuery(net_query_callback_t callback, void *user_data)
 {
     query_target_t *target;

@@ -611,54 +573,116 @@ net_addr_t *NET_FindLANServer(void)
     target = GetTargetForAddr(NULL, true);
     target->type = QUERY_TARGET_BROADCAST;

-    return NET_Query_QueryLoop(true, true);
+    NET_Query_QueryLoop(callback, user_data);
+
+    return GetNumResponses();
 }

-void NET_LANQuery(void)
+int NET_MasterQuery(net_query_callback_t callback, void *user_data)
 {
+    net_addr_t *master;
     query_target_t *target;

     NET_Query_Init();

-    printf("\nSearching for servers on local LAN ...\n\n");
+    // Resolve master address and add to targets list.

-    // Add a broadcast target to the list.
+    master = NET_Query_ResolveMaster(query_context);

-    target = GetTargetForAddr(NULL, true);
-    target->type = QUERY_TARGET_BROADCAST;
+    if (master == NULL)
+    {
+        return 0;
+    }
+
+    target = GetTargetForAddr(master, true);
+    target->type = QUERY_TARGET_MASTER;

-    if (!NET_Query_QueryLoop(false, false))
+    NET_Query_QueryLoop(callback, user_data);
+
+    return GetNumResponses();
+}
+
+static void formatted_printf(int wide, char *s, ...)
+{
+    va_list args;
+    int i;
+
+    va_start(args, s);
+    i = vprintf(s, args);
+    va_end(args);
+
+    while (i < wide)
     {
-        I_Error("No servers found");
+        putchar(' ');
+        ++i;
     }
+}

-    exit(0);
+static char *GameDescription(GameMode_t mode, GameMission_t mission)
+{
+    switch (mode)
+    {
+        case shareware:
+            return "shareware";
+        case registered:
+            return "registered";
+        case retail:
+            return "ultimate";
+        case commercial:
+            if (mission == doom2)
+                return "doom2";
+            else if (mission == pack_tnt)
+                return "tnt";
+            else if (mission == pack_plut)
+                return "plutonia";
+        default:
+            return "unknown";
+    }
 }

-void NET_MasterQuery(void)
+static void PrintHeader(void)
 {
-    net_addr_t *master;
-    query_target_t *target;
+    int i;

-    NET_Query_Init();
+    putchar('\n');
+    formatted_printf(18, "Address");
+    formatted_printf(8, "Players");
+    puts("Description");

-    printf("\nSearching for servers on Internet ...\n\n");
+    for (i=0; i<70; ++i)
+        putchar('=');
+    putchar('\n');
+}

-    // Resolve master address and add to targets list.
+// Callback function that just prints information in a table.

-    master = NET_Query_ResolveMaster(query_context);
+void NET_QueryPrintCallback(net_addr_t *addr,
+                            net_querydata_t *data,
+                            void *user_data)
+{
+    // If this is the first server, print the header.

-    if (master == NULL)
+    if (!printed_header)
     {
-        I_Error("Failed to resolve master server address");
+        PrintHeader();
+        printed_header = true;
     }

-    target = GetTargetForAddr(master, true);
-    target->type = QUERY_TARGET_MASTER;
+    formatted_printf(18, "%s: ", NET_AddrToString(addr));
+    formatted_printf(8, "%i/%i", data->num_players,
+                                 data->max_players);

-    if (!NET_Query_QueryLoop(false, false))
+    if (data->gamemode != indetermined)
     {
-        I_Error("No servers found");
+        printf("(%s) ", GameDescription(data->gamemode,
+                                        data->gamemission));
     }
+
+    if (data->server_state)
+    {
+        printf("(game running) ");
+    }
+
+    NET_SafePuts(data->description);
 }

diff --git a/src/net_query.h b/src/net_query.h
index 21c89ecb..98931593 100644
--- a/src/net_query.h
+++ b/src/net_query.h
@@ -27,10 +27,17 @@

 #include "net_defs.h"

+typedef void (*net_query_callback_t)(net_addr_t *addr,
+                                     net_querydata_t *querydata,
+                                     void *user_data);
+
+extern int NET_LANQuery(net_query_callback_t callback, void *user_data);
+extern int NET_MasterQuery(net_query_callback_t callback, void *user_data);
 extern void NET_QueryAddress(char *addr);
-extern void NET_LANQuery(void);
 extern net_addr_t *NET_FindLANServer(void);
-extern void NET_MasterQuery(void);
+
+extern void NET_QueryPrintCallback(net_addr_t *addr, net_querydata_t *data,
+                                   void *user_data);

 extern net_addr_t *NET_Query_ResolveMaster(net_context_t *context);
 extern void NET_Query_AddToMaster(net_addr_t *master_addr);