indexing description: "[ Simple scene to scan for LAN and/or internet servers (using a masterserver). When ok is clicked you can get the last selected server from the query: ]" date: "$Date$" revision: "$Revision$" class EM_SERVER_LOOKUP_SCENE inherit EM_COMPONENT_SCENE redefine handle_update_event, initialize_scene end EM_NETWORK_HELPER_FUNCTIONS export {ANY} is_valid_ip_string {NONE} all end create make_for_lan_only, make_with_master_server feature -- Initialisation make_for_lan_only is -- Make for lan do -- First make the widget scene make_component_scene create_widgets -- Setup the scanner client: create scanner.make ("em_net_sample",35701) -- Setup a broadcast group scanner.create_group("BCAST") broadcast_group := scanner.last_created_group scanner.create_connection(create {EM_INET_SOCKET_ADDRESS}.make_by_ip_port(0xFFFFFFFF,35700)) scanner.last_created_connection.join (broadcast_group) -- Event subscription scanner.subscribe_by_type_id (scanner.object_types.em_net_server_discovery_response, agent on_server_discovery_response(?)) end make_with_master_server(a_host, a_path: STRING) is -- Init require a_host_not_void: a_host /= Void a_path_not_void: a_path /= Void do make_for_lan_only masterserver_hostname := a_host masterserver_path := a_path end feature -- Basic operations initialize_scene is -- Initialize scene do Precursor video_subsystem.video_surface.fill ( create {EM_COLOR}.make_with_rgb(200,200,200)) end feature -- Event handling handle_update_event is -- outside event do Precursor scanner.send_update_to_clients end feature {NONE} -- Implementation on_server_discovery_response(a_response: EM_NET_SERVER_DISCOVERY_RESPONSE) is -- server response event handling routine do a_response.set_ping (time.ticks-last_scan_ticks) server_selection.put (a_response) end on_scan_master_servers is -- Scan master servers button clicked require masterserver_hostname_not_void: masterserver_hostname /= Void masterserver_path_not_void: masterserver_path /= Void do server_selection.wipe_out scan_master_servers.disable scan_lan.disable scan_both.disable last_scan_ticks := time.ticks create http.make http.set_hostname (masterserver_hostname) http.set_path (masterserver_path) http.connection_established_event.subscribe (agent on_connection_established) http.connection_failed_event.subscribe (agent on_connection_failed) http.connection_closed_event.subscribe (agent on_connection_closed) http.data_received_event.subscribe (agent on_master_server_data(?)) http.connect end on_connection_failed is -- Handle connection failed event. do scan_both.enable scan_lan.enable scan_master_servers.enable end on_connection_closed is -- Handle connection closed event. local a_discovery_response: EM_NET_SERVER_DISCOVERY_RESPONSE current_entry: DS_HASH_TABLE[STRING,STRING] do generate_server_list from server_list.start until server_list.after loop current_entry := server_list.item_for_iteration if is_current_entry_valid(current_entry) then a_discovery_response := scanner.object_types.create_em_net_server_discovery_response a_discovery_response.set_server_name (current_entry.item("servername")) a_discovery_response.set_game_name (current_entry.item("game")) a_discovery_response.set_ip (convert_ip_string_to_ip(current_entry.item("address"))) a_discovery_response.set_port (current_entry.item("port").to_integer) end on_server_discovery_response (a_discovery_response) server_list.forth end scan_both.enable scan_lan.enable scan_master_servers.enable end is_current_entry_valid(current_entry: DS_HASH_TABLE [STRING,STRING]): BOOLEAN is -- Is current entry valid? require current_entry_not_void: current_entry /= Void do if current_entry.has ("servername") and current_entry.has ("game") and current_entry.has("address") and current_entry.has ("port") and current_entry.has ("game") then Result := is_valid_ip_string (current_entry.item ("address")) and current_entry.item ("port").is_integer else Result := False end end on_connection_established is -- Handle connection established event. do create http_response.make_empty http.get end on_master_server_data (the_data: STRING) is -- Append data to buffer. do http_response.append_string (the_data) end on_scan_lan is -- Scan lan button clicked local discovery: EM_NET_SERVER_DISCOVERY do server_selection.wipe_out last_scan_ticks := time.ticks discovery := scanner.object_types.create_em_net_server_discovery discovery.set_game_name(scanner.game_identifier) discovery.set_group (broadcast_group) discovery.publish --another way: scanner.broadcast_group.publish(discovery) end on_scan_both is -- Scan LAN and contact masterservers for server information. do on_scan_lan on_scan_master_servers end create_widgets is -- create the widghets do create server_selection.make_from_list (create {DS_LINKED_LIST[EM_NET_SERVER_DISCOVERY_RESPONSE]}.make) server_selection.set_background_color (create {EM_COLOR}.make_with_rgb(200,255,200)) server_selection.set_position (15, 15) server_selection.set_dimension (570, 570) server_selection.set_to_string_agent (agent discovery_response_pretty_printer(?)) add_component (server_selection) create scan_master_servers.make_from_text ("contact master servers") scan_master_servers.set_background_color(create {EM_COLOR}.make_with_rgb(20,50,20)) scan_master_servers.set_position (590, 15) scan_master_servers.set_dimension (200, 20) scan_master_servers.clicked_event.subscribe (agent on_scan_master_servers) add_component (scan_master_servers) create scan_lan.make_from_text ("LAN broadcast scan") scan_lan.set_background_color (create {EM_COLOR}.make_with_rgb(20,50,20)) scan_lan.set_position (590, 45) scan_lan.set_dimension (200, 20) scan_lan.clicked_event.subscribe (agent on_scan_lan) add_component (scan_lan) create scan_both.make_from_text ("update both") scan_both.set_background_color (create {EM_COLOR}.make_with_rgb(20,50,20)) scan_both.set_position (590, 75) scan_both.set_dimension (200, 20) scan_both.clicked_event.subscribe (agent on_scan_both) add_component (scan_both) create connect.make_from_text ("Connect ...") connect.set_background_color (create {EM_COLOR}.make_with_rgb(20,50,20)) connect.set_position (590, 150) connect.set_dimension (200, 20) add_component (connect) create cancel.make_from_text ("Cancel") cancel.set_background_color (create {EM_COLOR}.make_with_rgb(20,50,20)) cancel.set_position (590, 180) cancel.set_dimension (200, 20) add_component (cancel) end feature -- Attributes has_selected_server: BOOLEAN is -- Has last selected server? do Result := server_selection.has_selected_element end selected_server: EM_INET_SOCKET_ADDRESS is -- Last selected server require has_selected_server: has_selected_server do create Result.make_by_ip_port(server_selection.selected_element.ip, server_selection.selected_element.port) end server_list: DS_LINKED_LIST[DS_HASH_TABLE[STRING, STRING]] connect: EM_BUTTON -- connect button cancel: EM_BUTTON -- cancel button feature {NONE} -- Implementation generate_server_list is -- Generate the server list. local sections: LIST[STRING] keys: ARRAY[STRING] values: ARRAY[STRING] split: INTEGER num_of_servers: INTEGER new_server: DS_HASH_TABLE[STRING, STRING] i, j, index: INTEGER current_value, current_key: STRING do create server_list.make num_of_servers := num_of_servers.max_value sections := http_response.split ('&') create keys.make(1, sections.count) create values.make(1, sections.count) from sections.start until sections.after loop if sections.item.has ('=') then split := sections.item.index_of ('=', 1) keys.put (sections.item.substring (1, split - 1), sections.index) values.put (sections.item.substring(split + 1, sections.item.count), sections.index) num_of_servers := num_of_servers.min (values.item (sections.index).occurrences ('|') + 1) end sections.forth end if num_of_servers = num_of_servers.max_value then num_of_servers := 0 end from i := 1 until i > num_of_servers loop create new_server.make (keys.count) from j := 1 until j > keys.count loop index := values.item(j).index_of ('|', 1) current_value := values.item(j) current_key := keys.item(j) if index /= 0 then new_server.put (current_value.substring (1, index - 1), current_key) values.put (current_value.substring (index + 1, current_value.count), j) else new_server.put (current_value, current_key) end j := j + 1 end server_list.put_last (new_server) i := i + 1 end end discovery_response_pretty_printer(a_response: EM_NET_SERVER_DISCOVERY_RESPONSE): STRING is -- Print a_response in a pretty form to a string. do create Result.make_empty Result.append( a_response.ping.out + " :: " + a_response.server_name + " :: ") Result.append (a_response.ip.bit_shift_right (24).bit_and (255).out + ".") Result.append (a_response.ip.bit_shift_right (16).bit_and (255).out + ".") Result.append (a_response.ip.bit_shift_right (8).bit_and (255).out + ".") Result.append (a_response.ip.bit_and (255).out) Result.append(":"+a_response.port.out) end server_selection: EM_TEXTLIST[EM_NET_SERVER_DISCOVERY_RESPONSE] -- the server selection list http: EM_HTTP_PROTOCOL -- HTTP Client http_response: STRING -- HTTP response string scan_master_servers: EM_BUTTON -- Scan master servers button scan_lan: EM_BUTTON -- Scan LAN button scan_both: EM_BUTTON -- Scan both (LAN and master server) button last_scan_ticks: INTEGER -- Last scan ticks -- Used to compute the ping for servers. scanner: EM_NET_SERVER [MY_OBJECT_TYPES] -- a scanner broadcast_group: EM_NET_GROUP -- the broadcast group masterserver_hostname: STRING -- masterserver hostname masterserver_path: STRING -- masterserver path invariant invariant_clause: True -- Your invariant here end