indexing description: "[ Multiplayer Waiting Room - Server ]" date: "$Date$" revision: "$Revision$" class BB_WAITING_ROOM_SERVER inherit BB_MENU redefine handle_key_down_event end SHARED_BB_SETTINGS export {NONE} all end create make feature -- Initialization make is local button: BB_MENU_BUTTON a_playerlist_request: BB_MP_PLAYERLIST_REQUEST a_label: EM_LABEL do init_menu create nickname.make_from_string (bb_settings.nickname) create connections.make start_server create new_server.make (server) new_server.set_level (bb_settings.start_level) create button.make ("Start Game") button.set_position (550, 530) button.pressed_event.subscribe (agent start_game) add_button (button) create button.make ("Back") button.set_position (50, 530) button.pressed_event.subscribe (agent back_button_pressed) add_button (button) create client_list.make_empty client_list.set_position (20, 200) client_list.set_dimension (100, 100) client_list.set_to_string_agent (agent client_list_element_to_string) add_widget (client_list) -- options create a_label.make_from_text ("Effect mode: " + bb_settings.remote_item_effect_mode) a_label.set_font (Standard_ttf_fonts.bitstream_vera_sans (18)) a_label.set_foreground_color (create {EM_COLOR}.make_white) a_label.set_position (450, 300) a_label.resize_to_optimal_dimension add_widget (a_label) create a_label.make_from_text ("Remote effect chance: " + bb_settings.remote_item_effect_chance.out + "%%") a_label.set_font (Standard_ttf_fonts.bitstream_vera_sans (18)) a_label.set_foreground_color (create {EM_COLOR}.make_white) a_label.set_position (450, 330) a_label.resize_to_optimal_dimension add_widget (a_label) create a_label.make_from_text ("Add block score: " + bb_settings.score_for_enemy_block.out) a_label.set_font (Standard_ttf_fonts.bitstream_vera_sans (18)) a_label.set_foreground_color (create {EM_COLOR}.make_white) a_label.set_position (450, 360) a_label.resize_to_optimal_dimension add_widget (a_label) create a_label.make_from_text ("Difficulty: " + bb_settings.mp_difficulty) a_label.set_font (Standard_ttf_fonts.bitstream_vera_sans (18)) a_label.set_foreground_color (create {EM_COLOR}.make_white) a_label.set_position (450, 390) a_label.resize_to_optimal_dimension add_widget (a_label) create a_label.make_from_text ("Win games: " + bb_settings.number_of_games_to_win.out) a_label.set_font (Standard_ttf_fonts.bitstream_vera_sans (18)) a_label.set_foreground_color (create {EM_COLOR}.make_white) a_label.set_position (450, 420) a_label.resize_to_optimal_dimension add_widget (a_label) create a_label.make_from_text ("Start level: " + bb_settings.start_level) a_label.set_font (Standard_ttf_fonts.bitstream_vera_sans (18)) a_label.set_foreground_color (create {EM_COLOR}.make_white) a_label.set_position (450, 450) a_label.resize_to_optimal_dimension add_widget (a_label) -- chat create chat_window.make_empty chat_window.set_position (20, 350) chat_window.set_dimension (400, 95) chat_window.disable add_widget (chat_window) create chat_input.make_from_size (50) chat_input.set_width (400) chat_input.set_position (20, 455) add_widget (chat_input) server.timeout_event.subscribe (agent on_connection_lost (?)) create client.make (47775) client.set_server_address (create {EM_INET_SOCKET_ADDRESS}.make_by_ip_string_port("127.0.0.1",Server_port)) client.subscribe_by_type_id (client.object_types.bb_mp_start_game, agent on_start_game) client.set_nickname (nickname) client.join client.enable_auto_transmit a_playerlist_request := client.object_types.create_bb_mp_playerlist_request a_playerlist_request.set_resend_time (Resend_time) a_playerlist_request.set_resends_left (Number_of_resends) client.event_2pc_id_manager.set_unique_id_for_object (a_playerlist_request) a_playerlist_request.set_group (client.standard_group) a_playerlist_request.set_player_name (bb_settings.nickname) a_playerlist_request.publish set_selected_menu_item (1) end feature -- Events handle_key_down_event (a_keyboard_event: EM_KEYBOARD_EVENT) is -- Handle keyboard events require else a_keyboard_event_not_void: a_keyboard_event /= Void local do if chat_input.has_focus and a_keyboard_event.key = a_keyboard_event.sdlk_return then send_chat_line else Precursor(a_keyboard_event) end end back_button_pressed is -- back button pressed local a_leave_game: BB_MP_LEAVE_GAME do if not was_back_button_pressed then was_back_button_pressed := True a_leave_game := server.object_types.create_bb_mp_leave_game a_leave_game.set_resend_time (Resend_time) a_leave_game.set_resends_left (Number_of_resends) server.event_2pc_id_manager.set_unique_id_for_object (a_leave_game) a_leave_game.set_group (server.standard_group) a_leave_game.publish time.add_timed_procedure (agent back_to_host_game, 1000) end end start_game is -- start game button pressed local connection_cursor: DS_LINKED_LIST_CURSOR [EM_NET_CONNECTION] do if client_list.elements.count > 1 and not start_game_button_pressed then -- start game only if there is at least one other player start_game_button_pressed := True server.disable_discovery_response server.disable_sending_heartbeats -- create groups which contain all but one client create connection_cursor.make (connections) from connections.start until connections.after loop server.create_group ("not" + connections.item_for_iteration.socket_address.host_ip_port_as_string) from connection_cursor.start until connection_cursor.after loop if not connection_cursor.item.socket_address.host_ip_port_as_string.is_equal (connections.item_for_iteration.socket_address.host_ip_port_as_string) then connection_cursor.item.join (server.last_created_group) end connection_cursor.forth end connections.forth end server.timeout_event.unsubscribe (agent on_connection_lost (?)) server.unsubscribe_by_type_id (server.object_types.Em_net_join_request, agent on_join_request (?)) server.unsubscribe_by_type_id (server.object_types.Bb_mp_playerlist_request, agent on_playerlist_request (?)) server.unsubscribe_by_type_id (server.object_types.Bb_mp_chat_message, agent on_chat_message (?)) server.unsubscribe_by_type_id (server.object_types.Bb_mp_options_request, agent on_options_request (?)) server.unsubscribe_by_type_id (server.object_types.Bb_mp_leave_game, agent on_leave_game (?)) new_server.start_game end end on_start_game is -- start the game local an_options_object: BB_MP_OPTIONS do if new_server.level_set.is_equal ("Simple") then bb_settings.set_level_set ("simple_multiplayer") else bb_settings.set_level_set ("multiplayer") end bb_settings.write_user_settings an_options_object := server.object_types.create_bb_mp_options an_options_object.set_score_for_enemy_block (bb_settings.score_for_enemy_block) an_options_object.set_remote_item_effect_chance (bb_settings.remote_item_effect_chance) an_options_object.set_remote_item_effect_mode (bb_settings.remote_item_effect_mode) an_options_object.set_difficulty (bb_settings.mp_difficulty) an_options_object.set_start_level (bb_settings.start_level) an_options_object.set_number_of_games_to_win (bb_settings.number_of_games_to_win) set_next_scene (create {BB_MP_MULTIFIELD_CLIENT_LEVEL_SCENE}.make (client, new_server, an_options_object)) new_server.refuse_joins start_next_scene end on_network_problem (error_code: INTEGER) is -- Handle network problems do io.put_string ("error: ") io.put_integer (error_code) io.put_new_line end on_join_request(a_join_request: EM_NET_JOIN_REQUEST) is -- Handle client join request local a_join_response: EM_NET_JOIN_RESPONSE do -- Move the client to the standard group if not server.standard_group.has_connection (a_join_request.updating_connection) then -- You could alswo wait a little bit until time is synchronized between the two peers. a_join_request.updating_connection.join (server.standard_group) server.create_group (a_join_request.updating_connection.socket_address.host_ip_port_as_string) a_join_request.updating_connection.join (server.last_created_group) a_join_request.updating_connection.set_max_idle_time (16000) end a_join_response := server.object_types.create_em_net_join_response a_join_response.set_resend_time (Resend_time) a_join_response.set_resends_left (Number_of_resends) server.event_2pc_id_manager.set_unique_id_for_object (a_join_response) a_join_response.accept_join a_join_response.set_group (a_join_request.group) a_join_response.publish end on_playerlist_request (a_playerlist_request: BB_MP_PLAYERLIST_REQUEST) is -- handle playerlist request do client_list.put ([a_playerlist_request.player_name, a_playerlist_request.updating_connection.socket_address.host_ip_port_as_string]) new_server.add_client (a_playerlist_request.player_name, a_playerlist_request.updating_connection.socket_address.host_ip_port_as_string) connections.force_last (a_playerlist_request.updating_connection) send_playerlist end on_options_request (an_options_request: BB_MP_OPTIONS_REQUEST) is -- handle options request local an_options_object: BB_MP_OPTIONS do an_options_object := server.object_types.create_bb_mp_options an_options_object.set_resend_time (Resend_time) an_options_object.set_resends_left (Number_of_resends) server.event_2pc_id_manager.set_unique_id_for_object (an_options_object) an_options_object.set_score_for_enemy_block (bb_settings.score_for_enemy_block) an_options_object.set_remote_item_effect_chance (bb_settings.remote_item_effect_chance) an_options_object.set_remote_item_effect_mode (bb_settings.remote_item_effect_mode) an_options_object.set_difficulty (bb_settings.mp_difficulty) an_options_object.set_start_level (bb_settings.start_level) an_options_object.set_number_of_games_to_win (bb_settings.number_of_games_to_win) an_options_object.set_level_set (new_server.level_set) an_options_object.set_group (an_options_request.group) an_options_object.publish end on_chat_message (a_chat_message: BB_MP_CHAT_MESSAGE) is -- handle chat message local chat_lines: DS_LIST [STRING] do a_chat_message.set_group (server.standard_group) a_chat_message.publish chat_lines ?= chat_window.elements check -- elements are implemnted as DS_LIST chat_lines /= Void end chat_lines.put_first (a_chat_message.nickname + ": " + a_chat_message.message) chat_window.set_changed end on_connection_lost (a_connection: EM_NET_CONNECTION) is -- remove `a_connection' and corresponding client from all lists local a_player: BB_MP_PLAYER_INFO do server.delete_connection (a_connection) server.remove_group_by_name (a_connection.socket_address.host_ip_port_as_string) a_player := new_server.client (a_connection.socket_address.host_ip_port_as_string) if a_player /= Void then new_server.remove_client (a_player.ip_port_string) from client_list.elements.start until client_list.elements.after loop if client_list.elements.item_for_iteration.item (1).is_equal (a_player.player_name) and client_list.elements.item_for_iteration.item (2).is_equal (a_player.ip_port_string) then client_list.remove (client_list.elements.item_for_iteration) client_list.elements.go_after else client_list.elements.forth end end end connections.delete (a_connection) debug ("brick_breaker") io.put_string ("Connection lost: " + a_connection.socket_address.host_ip_port_as_string) io.put_new_line end end send_chat_line is -- send text of chat_input to chat local a_chat_message: BB_MP_CHAT_MESSAGE do if not chat_input.text.is_empty then a_chat_message := server.object_types.create_bb_mp_chat_message a_chat_message.set_resend_time (Resend_time) a_chat_message.set_resends_left (Number_of_resends) server.event_2pc_id_manager.set_unique_id_for_object (a_chat_message) a_chat_message.set_nickname (nickname) a_chat_message.set_message (create {STRING}.make_from_string (chat_input.text)) on_chat_message (a_chat_message) chat_input.set_cursor_position (0) chat_input.text.make_empty end end send_playerlist is -- send current playerlist to all clients local a_playerlist: BB_MP_PLAYERLIST do a_playerlist := server.object_types.create_bb_mp_playerlist a_playerlist.set_resend_time (Resend_time) a_playerlist.set_resends_left (Number_of_resends) server.event_2pc_id_manager.set_unique_id_for_object (a_playerlist) from client_list.elements.start until client_list.elements.after loop a_playerlist.players.force_last (client_list_element_to_string (client_list.elements.item_for_iteration)) client_list.elements.forth end a_playerlist.set_group (server.standard_group) a_playerlist.publish end on_leave_game (a_received_leave_game: BB_MP_LEAVE_GAME) is -- server received a leave game local a_leave_game: BB_MP_LEAVE_GAME do a_leave_game := server.object_types.create_bb_mp_leave_game a_leave_game.set_resend_time (Resend_time) a_leave_game.set_resends_left (Number_of_resends) server.event_2pc_id_manager.set_unique_id_for_object (a_leave_game) a_leave_game.set_group (a_received_leave_game.group) a_leave_game.publish time.add_timed_procedure (agent on_connection_lost (a_received_leave_game.updating_connection), 1000) end feature {NONE} -- Implementation start_server is -- create multiplayer game server do create server.make ("brick_breaker_ce", Server_port) server.set_name (nickname) server.enable_discovery_response server.enable_auto_transmit server.enable_sending_heartbeats server.set_net_fps (1) server.create_static_connection (create {EM_INET_SOCKET_ADDRESS}.make_by_hostname_port(Masterserver_hostname,35699)) server.last_created_connection.join(server.masterserver_group) server.send_failed_event.subscribe (agent on_network_problem) server.subscribe_by_type_id (server.object_types.Em_net_join_request, agent on_join_request (?)) server.subscribe_by_type_id (server.object_types.Bb_mp_playerlist_request, agent on_playerlist_request (?)) server.subscribe_by_type_id (server.object_types.Bb_mp_chat_message, agent on_chat_message (?)) server.subscribe_by_type_id (server.object_types.Bb_mp_options_request, agent on_options_request (?)) server.subscribe_by_type_id (server.object_types.BB_mp_leave_game, agent on_leave_game (?)) end back_to_host_game is -- back to host game do server.disable_auto_transmit server.disable_discovery_response server.disable_sending_heartbeats if server.is_time_sync_enabled then server.disable_time_sync end server.close client.disable_auto_transmit client.disable_time_sync client.close set_next_scene (create {BB_HOST_GAME}.make) start_next_scene end feature {NONE} -- Implementation connections: DS_LINKED_LIST [EM_NET_CONNECTION] -- list of connections client: EM_NET_CLIENT [BB_MP_OBJECT_TYPES] -- Client server: EM_NET_SERVER [BB_MP_OBJECT_TYPES] -- Server new_server: BB_MP_MULTIFIELD_SERVER -- Server client_list: EM_TEXTLIST [TUPLE [STRING, STRING]] -- textlist with all clients by their nickname an host_ip_port_string nickname: STRING -- Nickname chat_window: EM_TEXTLIST [STRING] -- chat window chat_input: EM_TEXTBOX -- textbox to write chatmessages client_list_element_to_string (a_client_list_element: TUPLE [STRING, STRING]): STRING is -- convert `a_client_list_element' into a string for displaying in client_list (extract player name) do Result := a_client_list_element.item (1).out end start_game_button_pressed: BOOLEAN -- was the start game button already pressed? was_back_button_pressed: BOOLEAN -- was back button already pressed? end