diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/d_main.c | 12 | ||||
-rw-r--r-- | src/net_query.c | 472 | ||||
-rw-r--r-- | src/net_query.h | 7 |
3 files changed, 388 insertions, 103 deletions
diff --git a/src/d_main.c b/src/d_main.c index 378cc5f3..22fdb142 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -844,6 +844,18 @@ void D_DoomMain (void) } //! + // @category net + // + // Query the Internet master server for a global list of active + // servers. + // + + if (M_CheckParm("-masterquery")) + { + NET_MasterQuery(); + } + + //! // @arg <address> // @category net // diff --git a/src/net_query.c b/src/net_query.c index deb3744a..681411c2 100644 --- a/src/net_query.c +++ b/src/net_query.c @@ -38,17 +38,40 @@ #define MASTER_SERVER_ADDRESS "master.chocolate-doom.org" -typedef struct +typedef enum { + QUERY_TARGET_SERVER, // Normal server target. + QUERY_TARGET_MASTER, // The master server. + QUERY_TARGET_BROADCAST // Send a broadcast query +} query_target_type_t; + +typedef enum +{ + QUERY_TARGET_QUEUED, // Query not yet sent + QUERY_TARGET_QUERIED, // Query sent, waiting response + QUERY_TARGET_RESPONDED, // Response received + QUERY_TARGET_TIMED_OUT +} query_target_state_t; + +typedef struct +{ + query_target_type_t type; + query_target_state_t state; net_addr_t *addr; net_querydata_t data; -} queryresponse_t; + unsigned int query_time; + boolean printed; +} query_target_t; + +// Transmit a query packet static boolean registered_with_master = false; static net_context_t *query_context; -static queryresponse_t *responders; -static int num_responses; +static query_target_t *targets; +static int num_targets; + +static boolean printed_header = false; // Resolve the master server address. @@ -116,40 +139,49 @@ void NET_Query_MasterResponse(net_packet_t *packet) } } -// Add a new address to the list of hosts that has responded +// Send a query to the master server. -static queryresponse_t *AddResponder(net_addr_t *addr, - net_querydata_t *data) +static void NET_Query_SendMasterQuery(net_addr_t *addr) { - queryresponse_t *response; - - responders = realloc(responders, - sizeof(queryresponse_t) * (num_responses + 1)); - - response = &responders[num_responses]; - response->addr = addr; - response->data = *data; - ++num_responses; + net_packet_t *packet; - return response; + packet = NET_NewPacket(10); + NET_WriteInt16(packet, NET_MASTER_PACKET_TYPE_QUERY); + NET_SendPacket(addr, packet); + NET_FreePacket(packet); } -// Returns true if the reply is from a host that has not previously -// responded. +// Given the specified address, find the target associated. If no +// target is found, and 'create' is true, a new target is created. -static boolean CheckResponder(net_addr_t *addr) +static query_target_t *GetTargetForAddr(net_addr_t *addr, boolean create) { + query_target_t *target; int i; - for (i=0; i<num_responses; ++i) + for (i=0; i<num_targets; ++i) { - if (responders[i].addr == addr) + if (targets[i].addr == addr) { - return false; + return &targets[i]; } } - return true; + if (!create) + { + return NULL; + } + + targets = realloc(targets, sizeof(query_target_t) * (num_targets + 1)); + + target = &targets[num_targets]; + target->type = QUERY_TARGET_SERVER; + target->state = QUERY_TARGET_QUEUED; + target->printed = false; + target->addr = addr; + ++num_targets; + + return target; } // Transmit a query packet @@ -173,20 +205,225 @@ 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) +{ + unsigned int packet_type; + net_querydata_t querydata; + query_target_t *target; + + // Read the header + + if (!NET_ReadInt16(packet, &packet_type) + || packet_type != NET_PACKET_TYPE_QUERY_RESPONSE) + { + return; + } + + // Read query data + + if (!NET_ReadQueryData(packet, &querydata)) + { + return; + } + + // Find the target that responded, or potentially add a new target + // if it was not already known (for LAN broadcast search) + + target = GetTargetForAddr(addr, true); + + target->state = QUERY_TARGET_RESPONDED; + memcpy(&target->data, &querydata, sizeof(net_querydata_t)); +} + +// Parse a response packet from the master server. + +static void NET_Query_ParseMasterResponse(net_addr_t *master_addr, + net_packet_t *packet) +{ + unsigned int packet_type; + query_target_t *target; + char *addr_str; + net_addr_t *addr; + + // Read the header. We are only interested in query responses. + + if (!NET_ReadInt16(packet, &packet_type) + || packet_type != NET_MASTER_PACKET_TYPE_QUERY_RESPONSE) + { + return; + } + + // Read a list of strings containing the addresses of servers + // that the master knows about. + + for (;;) + { + addr_str = NET_ReadString(packet); + + if (addr_str == NULL) + { + break; + } + + // Resolve address and add to targets list if it is not already + // there. + + addr = NET_ResolveAddress(query_context, addr_str); + + if (addr != NULL) + { + GetTargetForAddr(addr, true); + } + } + + // Mark the master as having responded. + + target = GetTargetForAddr(master_addr, true); + target->state = QUERY_TARGET_RESPONDED; +} + +static void NET_Query_ParsePacket(net_addr_t *addr, net_packet_t *packet) +{ + query_target_t *target; + + // This might be the master server responding. + + target = GetTargetForAddr(addr, false); + + if (target != NULL && target->type == QUERY_TARGET_MASTER) + { + NET_Query_ParseMasterResponse(addr, packet); + } + else + { + NET_Query_ParseResponse(addr, packet); + } +} + +static void NET_Query_GetResponse(void) +{ + net_addr_t *addr; + net_packet_t *packet; + + if (NET_RecvPacket(query_context, &addr, &packet)) + { + NET_Query_ParsePacket(addr, packet); + NET_FreePacket(packet); + } +} + +// Find a target we have not yet queried and send a query. + +static void SendOneQuery(void) +{ + unsigned int i; + + for (i = 0; i < num_targets; ++i) + { + if (targets[i].state == QUERY_TARGET_QUEUED) + { + break; + } + } + + if (i >= num_targets) + { + return; + } + + // Found a target to query. Send a query; how to do this depends on + // the target type. + + switch (targets[i].type) + { + case QUERY_TARGET_SERVER: + NET_Query_SendQuery(targets[i].addr); + break; + + case QUERY_TARGET_BROADCAST: + NET_Query_SendQuery(NULL); + break; + + case QUERY_TARGET_MASTER: + NET_Query_SendMasterQuery(targets[i].addr); + break; + } + + //printf("Queried %s\n", NET_AddrToString(targets[i].addr)); + targets[i].state = QUERY_TARGET_QUERIED; + 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) +{ + unsigned int i; + unsigned int now; + + now = I_GetTimeMS(); + + for (i = 0; i < num_targets; ++i) + { + if (targets[i].state == QUERY_TARGET_QUERIED + && now - targets[i].query_time > 5000) + { + targets[i].state = QUERY_TARGET_TIMED_OUT; + } + } +} + +// If all targets have responded or timed out, returns true. + +static boolean AllTargetsDone(void) +{ + unsigned int i; + + for (i = 0; i < num_targets; ++i) + { + if (targets[i].state != QUERY_TARGET_RESPONDED + && targets[i].state != QUERY_TARGET_TIMED_OUT) + { + return false; + } + } + + 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); - while (i < wide) + while (i < wide) { putchar(' '); ++i; - } + } } static char *GameDescription(GameMode_t mode, GameMission_t mission) @@ -224,115 +461,104 @@ static void PrintHeader(void) putchar('\n'); } -static void PrintResponse(queryresponse_t *response) +static void PrintResponse(query_target_t *target) { - formatted_printf(18, "%s: ", NET_AddrToString(response->addr)); - formatted_printf(8, "%i/%i", response->data.num_players, - response->data.max_players); + formatted_printf(18, "%s: ", NET_AddrToString(target->addr)); + formatted_printf(8, "%i/%i", target->data.num_players, + target->data.max_players); - if (response->data.gamemode != indetermined) + if (target->data.gamemode != indetermined) { - printf("(%s) ", GameDescription(response->data.gamemode, - response->data.gamemission)); + printf("(%s) ", GameDescription(target->data.gamemode, + target->data.gamemission)); } - if (response->data.server_state) + if (target->data.server_state) { printf("(game running) "); } - NET_SafePuts(response->data.description); + NET_SafePuts(target->data.description); } -static void NET_Query_ParsePacket(net_addr_t *addr, net_packet_t *packet) -{ - unsigned int packet_type; - net_querydata_t querydata; - queryresponse_t *response; - - // Have we already received a packet from this host? +// Check for printing information about servers that have responded. - if (!CheckResponder(addr)) - { - return; - } - - // Read the header - - if (!NET_ReadInt16(packet, &packet_type) - || packet_type != NET_PACKET_TYPE_QUERY_RESPONSE) - { - return; - } - - // Read query data - - if (!NET_ReadQueryData(packet, &querydata)) - { - return; - } +static void CheckPrintOutput(void) +{ + unsigned int i; - if (num_responses <= 0) + for (i = 0; i < num_targets; ++i) { - // If this is the first response, print the table header + if (targets[i].type == QUERY_TARGET_SERVER + && targets[i].state == QUERY_TARGET_RESPONDED + && !targets[i].printed) + { + if (!printed_header) + { + PrintHeader(); + printed_header = true; + } - PrintHeader(); + PrintResponse(&targets[i]); + targets[i].printed = true; + } } - - response = AddResponder(addr, &querydata); - - PrintResponse(response); } -static void NET_Query_GetResponse(void) -{ - net_addr_t *addr; - net_packet_t *packet; +// Loop waiting for responses. - if (NET_RecvPacket(query_context, &addr, &packet)) - { - NET_Query_ParsePacket(addr, packet); - NET_FreePacket(packet); - } -} - -static net_addr_t *NET_Query_QueryLoop(net_addr_t *addr, - boolean find_one) +static net_addr_t *NET_Query_QueryLoop(boolean find_first, + boolean silent) { + query_target_t *responder; int start_time; int last_send_time; last_send_time = -1; start_time = I_GetTimeMS(); - while (I_GetTimeMS() < start_time + 5000) + while (!AllTargetsDone()) { - // Send a query once every second + // Send a query. This will only send a single query. + // Because of the delay below, this is therefore rate limited. - if (last_send_time < 0 || I_GetTimeMS() > last_send_time + 1000) - { - NET_Query_SendQuery(addr); - last_send_time = I_GetTimeMS(); - } + SendOneQuery(); // Check for a response NET_Query_GetResponse(); + // Output the responses + + if (!silent) + { + CheckPrintOutput(); + } + // Found a response? - if (find_one && num_responses > 0) + if (find_first && FindFirstResponder()) + { break; - + } + // Don't thrash the CPU - + I_Sleep(100); + + CheckTargetTimeouts(); } - if (num_responses > 0) - return responders[0].addr; + responder = FindFirstResponder(); + + if (responder != NULL) + { + return responder->addr; + } else + { return NULL; + } } void NET_Query_Init(void) @@ -341,14 +567,16 @@ void NET_Query_Init(void) NET_AddModule(query_context, &net_sdl_module); net_sdl_module.InitClient(); - responders = NULL; - num_responses = 0; + targets = NULL; + num_targets = 0; + + printed_header = false; } void NET_QueryAddress(char *addr) { net_addr_t *net_addr; - + NET_Query_Init(); net_addr = NET_ResolveAddress(query_context, addr); @@ -358,9 +586,13 @@ void NET_QueryAddress(char *addr) I_Error("NET_QueryAddress: Host '%s' not found!", addr); } + // Add the address to the list of targets. + + GetTargetForAddr(net_addr, true); + printf("\nQuerying '%s'...\n\n", addr); - if (!NET_Query_QueryLoop(net_addr, true)) + if (!NET_Query_QueryLoop(true, false)) { I_Error("No response from '%s'", addr); } @@ -370,18 +602,32 @@ void NET_QueryAddress(char *addr) net_addr_t *NET_FindLANServer(void) { + query_target_t *target; + NET_Query_Init(); - return NET_Query_QueryLoop(NULL, true); + // Add a broadcast target to the list. + + target = GetTargetForAddr(NULL, true); + target->type = QUERY_TARGET_BROADCAST; + + return NET_Query_QueryLoop(true, true); } void NET_LANQuery(void) { + query_target_t *target; + NET_Query_Init(); printf("\nSearching for servers on local LAN ...\n\n"); - if (!NET_Query_QueryLoop(NULL, false)) + // Add a broadcast target to the list. + + target = GetTargetForAddr(NULL, true); + target->type = QUERY_TARGET_BROADCAST; + + if (!NET_Query_QueryLoop(false, false)) { I_Error("No servers found"); } @@ -389,4 +635,30 @@ void NET_LANQuery(void) exit(0); } +void NET_MasterQuery(void) +{ + net_addr_t *master; + query_target_t *target; + + NET_Query_Init(); + + printf("\nSearching for servers on Internet ...\n\n"); + + // Resolve master address and add to targets list. + + master = NET_Query_ResolveMaster(query_context); + + if (master == NULL) + { + I_Error("Failed to resolve master server address"); + } + + target = GetTargetForAddr(master, true); + target->type = QUERY_TARGET_MASTER; + + if (!NET_Query_QueryLoop(false, false)) + { + I_Error("No servers found"); + } +} diff --git a/src/net_query.h b/src/net_query.h index 0ed098f1..21c89ecb 100644 --- a/src/net_query.h +++ b/src/net_query.h @@ -30,10 +30,11 @@ extern void NET_QueryAddress(char *addr); extern void NET_LANQuery(void); extern net_addr_t *NET_FindLANServer(void); +extern void NET_MasterQuery(void); -net_addr_t *NET_Query_ResolveMaster(net_context_t *context); -void NET_Query_AddToMaster(net_addr_t *master_addr); -void NET_Query_MasterResponse(net_packet_t *packet); +extern net_addr_t *NET_Query_ResolveMaster(net_context_t *context); +extern void NET_Query_AddToMaster(net_addr_t *master_addr); +extern void NET_Query_MasterResponse(net_packet_t *packet); #endif /* #ifndef NET_QUERY_H */ |