From 5d9af5dacee16fe68e30455732e73a0b35bbc05b Mon Sep 17 00:00:00 2001 From: polaris Date: Wed, 9 Apr 2014 09:09:29 -0400 Subject: [PATCH] Matchmaking working slightly better This code needs to be cleaned up later. Figured out the proper response to handle not finding a server. Still buggy: - Mario Kart DS can get in-game but lags out during the peer-to-peer game phase. Doesn't seem to be an issue with the server because at that point the games are directly communicating? - Phantasy Star Zero can't find any hits in matchmaking, even if server info is returned. --- gs_server_database.py | 1 + natneg_server.py | 19 +++++-- server_browser.py | 116 +++++++++++++++++++++--------------------- 3 files changed, 73 insertions(+), 63 deletions(-) diff --git a/gs_server_database.py b/gs_server_database.py index df4ae7d..48e59b7 100644 --- a/gs_server_database.py +++ b/gs_server_database.py @@ -326,6 +326,7 @@ def delete_server(gameid, session): return # Remove all servers hosted by the given session id. + print "Deleting all %s servers where session = %d" % (gameid, session) server_list[gameid] = [x for x in server_list[gameid] if x['__session__'] != session] def find_server_by_address(ip, port, gameid = None): diff --git a/natneg_server.py b/natneg_server.py index b62415a..4675dc5 100644 --- a/natneg_server.py +++ b/natneg_server.py @@ -47,13 +47,13 @@ while 1: session_list[gameid][session_id][client_id]['addr'] = addr clients = len(session_list[gameid][session_id]) - if client_id in session_list[gameid][session_id]: - clients -= 1 + #if client_id in session_list[gameid][session_id]: + # clients -= 1 if clients > 0: # Someone else is waiting to connect, send message for client in session_list[gameid][session_id]: - if session_list[gameid][session_id][client]['connected'] == True: + if session_list[gameid][session_id][client]['connected'] == True or client == client_id: continue output = bytearray(recv_data[0:12]) @@ -68,6 +68,8 @@ while 1: s.sendto(output, (session_list[gameid][session_id][client]['addr'])) print "Sent connection request to %s:%d..." % (session_list[gameid][session_id][client]['addr'][0], session_list[gameid][session_id][client]['addr'][1]) + utils.print_hex(output) + print "" #session_list[gameid][session_id][client_id]['connected'] = True output = bytearray(recv_data[0:14]) @@ -86,7 +88,16 @@ while 1: if client_id not in session_list[gameid][session_id]: pass - session_list[gameid][session_id][client_id]['connected'] = True + #session_list[gameid][session_id][client_id]['connected'] = True + + elif recv_data[7] == '\x0d': + client_id = "%02x" % ord(recv_data[13]) + utils.print_log("Received report command from %s:%s..." % (addr[0], addr[1])) + + utils.print_hex(bytearray(recv_data)) + + output[7] = 0x0e # Report response + s.sendto(output, addr) else: # Was able to connect utils.print_log("Received unknown command %02x from %s:%s..." % (ord(recv_data[7]), addr[0], addr[1])) diff --git a/server_browser.py b/server_browser.py index 077f5fe..08dab5e 100644 --- a/server_browser.py +++ b/server_browser.py @@ -43,16 +43,16 @@ def generate_server_list_data(address, fields, server_info): # Write the port output += utils.get_bytes_from_short_be(address.port) - if len(server_info) > 0: + #if len(server_info) > 0: + if True: # Write number of fields that will be returned. - key_count = len(server_info['requested']) + key_count = len(fields) output += utils.get_bytes_from_short(key_count) if key_count != len(fields): # For some reason we didn't get all of the expected data. print "key_count[%d] != len(fields)[%d]" % (key_count, len(fields)) print fields - return flags_buffer = bytearray() @@ -62,35 +62,35 @@ def generate_server_list_data(address, fields, server_info): # Start server loop here instead of including all of the fields and stuff again flags = 0 - if key_count > 0: + if len(server_info) != 0: flags |= ServerListFlags.HAS_KEYS_FLAG - if "natneg" in server_info: - flags |= ServerListFlags.CONNECT_NEGOTIATE_FLAG + if "natneg" in server_info: + flags |= ServerListFlags.CONNECT_NEGOTIATE_FLAG - flags_buffer += utils.get_bytes_from_int(int(server_info['publicip'])) + flags_buffer += utils.get_bytes_from_int(int(server_info['publicip'])) - flags |= ServerListFlags.NONSTANDARD_PORT_FLAG - flags_buffer += utils.get_bytes_from_short_be(int(server_info['publicport'])) + flags |= ServerListFlags.NONSTANDARD_PORT_FLAG + flags_buffer += utils.get_bytes_from_short_be(int(server_info['publicport'])) - if "localip0" in server_info: - flags |= ServerListFlags.PRIVATE_IP_FLAG - flags_buffer += bytearray([int(x) for x in server_info['localip0'].split('.')]) + if "localip0" in server_info: + flags |= ServerListFlags.PRIVATE_IP_FLAG + flags_buffer += bytearray([int(x) for x in server_info['localip0'].split('.')]) - if "localport" in server_info: - flags |= ServerListFlags.NONSTANDARD_PRIVATE_PORT_FLAG - flags_buffer += utils.get_bytes_from_short_be(int(server_info['localport'])) + if "localport" in server_info: + flags |= ServerListFlags.NONSTANDARD_PRIVATE_PORT_FLAG + flags_buffer += utils.get_bytes_from_short_be(int(server_info['localport'])) - flags |= ServerListFlags.ICMP_IP_FLAG - flags_buffer += bytearray([int(x) for x in "0.0.0.0".split('.')]) + flags |= ServerListFlags.ICMP_IP_FLAG + flags_buffer += bytearray([int(x) for x in "0.0.0.0".split('.')]) - output += bytearray([flags & 0xff]) - output += flags_buffer + output += bytearray([flags & 0xff]) + output += flags_buffer - if (flags & ServerListFlags.HAS_KEYS_FLAG): - # Write data for associated fields - for field in fields: - output += '\xff' + bytearray(server_info['requested'][field]) + '\0' + if (flags & ServerListFlags.HAS_KEYS_FLAG): + # Write data for associated fields + for field in fields: + output += '\xff' + bytearray(server_info['requested'][field]) + '\0' output += '\0' output += utils.get_bytes_from_int(-1) @@ -103,12 +103,6 @@ class Session(LineReceiver): self.addr = addr self.forward_to_client = False self.forward_client = () - self.is_searching = False - - # Amount of time (in seconds) we can search for a match before getting kicked off. - # Set to -1 to allow keep searching forever until a server appears. - #self.timeout = 60 * 10 - self.timeout = -1 def rawDataReceived(self, data): # First 2 bytes are the packet size. @@ -136,15 +130,13 @@ class Session(LineReceiver): # Get server based on ip/port server = server_manager.find_server_by_address(ip, self.forward_client[1])._getvalue() - print self.server_list + print server - if self.server_list == None: + if server == None: pass #print "%s %s" % (ip, server['publicip']) if server['publicip'] == ip and server['publicport'] == str(self.forward_client[1]): - print server - # Send command to server to get it to connect to natneg natneg_session = int(utils.generate_random_hex_str(8), 16) # Quick and lazy way to get a random 32bit integer. Replace with something else late.r @@ -210,15 +202,15 @@ class Session(LineReceiver): #print "%08x" % options #print "%d %08x" % (max_servers, source_ip) - if self.is_searching == False: - self.is_searching = True - - # I still want the connection to close properly when the client disconnects, so put the server search - # part into its own thread. - # The game will create a new connection for every search query, so don't worry about setting - # self.is_searching to False. - t = Thread(target = self.find_server, args=(query_game, filter, fields, max_servers, game_name, challenge)) - t.start() + # Requesting ip and port of client, not server + if filter == "" or fields == "": + output = bytearray([int(x) for x in self.addr.host.split('.')]) + output += utils.get_bytes_from_short_be(self.addr.port) + self.transport.write(bytes(output)) + print "Responding with own IP and port..." + utils.print_hex(output) + elif: + self.find_server(query_game, filter, fields, max_servers, game_name, challenge) @@ -228,6 +220,7 @@ class Session(LineReceiver): dest = (dest_addr, dest_port) utils.print_log("Received send message request from %s:%s to %s:%d..." % (self.addr.host, self.addr.port, dest_addr, dest_port)) + utils.print_hex(bytearray(data)) self.forward_to_client = True self.forward_client = dest @@ -236,30 +229,34 @@ class Session(LineReceiver): utils.print_log("Received keep alive from %s:%s..." % (self.addr.host, self.addr.port)) else: - utils.print_log("Received unknown command (%02x) from %s:%s... %s" % (ord(data[2]), self.addr.host, self.addr.port, data)) + utils.print_log("Received unknown command (%02x) from %s:%s..." % (ord(data[2]), self.addr.host, self.addr.port)) + utils.print_hex(bytearray(data)) + utils.print_hex(data) def find_server(self, query_game, filter, fields, max_servers, game_name, challenge): # Get dictionary from master server list server. - # The game sends a search query only once so we must loop until something is found. - # Search for the specified amount of time, or if self.timeout is set to -1, until a match is found. + print "Searching for server matching '%s' with the fields '%s'" % (filter, fields) + start = time.time() - self.server_list = [] - while True: - self.server_list = get_server_list(query_game, filter, fields, max_servers)._getvalue() + self.server_list = get_server_list(query_game, filter, fields, max_servers)._getvalue() - if self.server_list != []: - break - - time.sleep(1) # Sleep 1 second - - if self.timeout != -1 and time.time() - start > self.timeout: - break - - # Generate encrypted server list and send to client. + print "Found server(s):" print self.server_list - for server in self.server_list: + + if self.server_list == []: + self.server_list.append({}) + + for _server in self.server_list: + server = _server + if len(server) > 0 and len(fields) > 0 and server['requested'] == {}: + # If the requested fields weren't found then don't return a server. + # This fixes a bug with Mario Kart DS. + print "Requested was empty" + server = {} + # Generate binary server list data data = generate_server_list_data(self.addr, fields, server) + utils.print_hex(data) # Encrypt data enc = gs_utils.EncTypeX() @@ -268,7 +265,8 @@ class Session(LineReceiver): # Send to client self.transport.write(bytes(data)) utils.print_log("Sent server list message to %s:%s..." % (self.addr.host, self.addr.port)) - break + break + class SessionFactory(Factory):