def __init__(self, windows): super(SingleplayerMenu, self).__init__(windows) self._mode = None self._gui = load_uh_widget('singleplayermenu.xml') self._gui.mapEvents({ 'cancel': self._windows.close, 'okay': self.act, 'scenario': Callback(self._select_mode, 'scenario'), 'random': Callback(self._select_mode, 'random'), 'free_maps': Callback(self._select_mode, 'free_maps') }) self._playerdata = PlayerDataSelection() self._aidata = AIDataSelection() self._gui.findChild(name="playerdataselectioncontainer").addChild( self._playerdata.get_widget()) self._gui.findChild(name="aidataselectioncontainer").addChild( self._aidata.get_widget())
def show(self): if not self._check_connection(): return self._gui = load_uh_widget('multiplayermenu.xml') self._gui.mapEvents({ 'cancel' : self._windows.close, 'join' : self._join_game, 'create' : self._create_game, 'refresh': Callback(self._refresh, play_sound=True) }) self._gui.findChild(name='gamelist').capture(self._update_game_details) self._playerdata = PlayerDataSelection() self._gui.findChild(name="playerdataselectioncontainer").addChild(self._playerdata.get_widget()) refresh_worked = self._refresh() if not refresh_worked: self._windows.close() return # FIXME workaround for multiple callback registrations # this happens because subscription is done when the window is showed, unsubscription # only when it is closed. if new windows appear and disappear, show is called multiple # times. the error handler is used throughout the entire mp menu, that's why we can't # unsubscribe in hide. need to find a better solution. NetworkInterface().discard("error", self._on_error) NetworkInterface().subscribe("error", self._on_error) self._gui.show() # TODO Remove once loading a game is implemented again self._gui.findChild(name='load').parent.hide()
def show(self): if not self._check_connection(): return self._gui = load_uh_widget('multiplayermenu.xml') self._gui.mapEvents({ 'cancel' : self._windows.close, 'join' : self._join_game, 'create' : self._create_game, 'refresh': Callback(self._refresh, play_sound=True) }) self._gui.findChild(name='gamelist').capture(self._update_game_details) self._playerdata = PlayerDataSelection() self._gui.findChild(name="playerdataselectioncontainer").addChild(self._playerdata.get_widget()) refresh_worked = self._refresh() if not refresh_worked: self._windows.close() return NetworkInterface().subscribe("game_prepare", self._prepare_game) NetworkInterface().subscribe("error", self._on_error) self._gui.show() # TODO Remove once loading a game is implemented again self._gui.findChild(name='load').parent.hide()
def show_single(self, show='scenario'): # show scenarios to highlight tutorials """ @param show: string, which type of games to show """ # save player name before removing playerdata container self._save_player_name() self.hide() # start with default single player settings self.widgets.reload('singleplayermenu') self._switch_current_widget('singleplayermenu', center=True) self.active_right_side = None for mode in ('random', 'scenario', 'free_maps'): self.widgets.reload('sp_%s' % mode) right_side = self.widgets['sp_%s' % mode] self.current.findChild(name="right_side_box").addChild(right_side) right_side.parent.hideChild(right_side) # create and add permanent left side widgets self.current.playerdata = PlayerDataSelection(self.current, self.widgets) self.current.aidata = AIDataSelection(self.current, self.widgets) self.map_preview = MapPreview(lambda: self.current) self._select_single(show)
def _show_change_player_details_popup(self, game): """Shows a dialog where the player can change its name and/or color""" assigned = [ p["color"] for p in NetworkInterface().get_game().get_player_list() if p["name"] != NetworkInterface().get_client_name() ] unused_colors = set(Color) - set(assigned) playerdata = PlayerDataSelection(color_palette=unused_colors) playerdata.set_player_name(NetworkInterface().get_client_name()) playerdata.set_color(NetworkInterface().get_client_color()) dialog = load_uh_widget('set_player_details.xml') dialog.findChild(name="playerdataselectioncontainer").addChild( playerdata.get_widget()) def _change_playerdata(): NetworkInterface().change_name(playerdata.get_player_name()) NetworkInterface().change_color(playerdata.get_player_color().id) dialog.hide() self._update_game_details() def _cancel(): dialog.hide() dialog.mapEvents({ OkButton.DEFAULT_NAME: _change_playerdata, CancelButton.DEFAULT_NAME: _cancel }) dialog.show()
def __init__(self, mainmenu, windows): super(MultiplayerMenu, self).__init__(windows) self._mainmenu = mainmenu self._gui = load_uh_widget('multiplayermenu.xml') self._gui.mapEvents({ 'cancel': self._windows.close, 'join': self._join_game, 'create': self._create_game, 'refresh': Callback(self._refresh, play_sound=True) }) self._gui.findChild(name='gamelist').capture(self._update_game_details) self._playerdata = PlayerDataSelection() self._gui.findChild(name="playerdataselectioncontainer").addChild( self._playerdata.get_widget()) # to track if the menu window is opened or not. self._is_open = False
def _show_change_player_details_popup(self, game): """Shows a dialog where the player can change its name and/or color""" assigned = [p["color"] for p in NetworkInterface().get_game().get_player_list() if p["name"] != NetworkInterface().get_client_name()] unused_colors = set(Color) - set(assigned) playerdata = PlayerDataSelection(color_palette=unused_colors) playerdata.set_player_name(NetworkInterface().get_client_name()) playerdata.set_color(NetworkInterface().get_client_color()) dialog = load_uh_widget('set_player_details.xml') dialog.findChild(name="playerdataselectioncontainer").addChild(playerdata.get_widget()) def _change_playerdata(): NetworkInterface().change_name(playerdata.get_player_name()) NetworkInterface().change_color(playerdata.get_player_color().id) dialog.hide() self._update_game_details() def _cancel(): dialog.hide() dialog.mapEvents({ OkButton.DEFAULT_NAME: _change_playerdata, CancelButton.DEFAULT_NAME: _cancel }) dialog.show()
def __init__(self, mainmenu, windows): super(MultiplayerMenu, self).__init__(windows) self._mainmenu = mainmenu self._gui = load_uh_widget('multiplayermenu.xml') self._gui.mapEvents({ 'cancel' : self._windows.close, 'join' : self._join_game, 'create' : self._create_game, 'refresh': Callback(self._refresh, play_sound=True) }) self._gui.findChild(name='gamelist').capture(self._update_game_details) self._playerdata = PlayerDataSelection() self._gui.findChild(name="playerdataselectioncontainer").addChild(self._playerdata.get_widget()) # to track if the menu window is opened or not. self._is_open = False
def __init__(self, windows): super(SingleplayerMenu, self).__init__(windows) self._mode = None self._gui = load_uh_widget('singleplayermenu.xml') self._gui.mapEvents({ 'cancel' : self._windows.close, 'okay' : self.act, 'scenario' : Callback(self._select_mode, 'scenario'), 'random' : Callback(self._select_mode, 'random'), 'free_maps': Callback(self._select_mode, 'free_maps') }) self._playerdata = PlayerDataSelection() self._aidata = AIDataSelection() self._gui.findChild(name="playerdataselectioncontainer").addChild(self._playerdata.get_widget()) self._gui.findChild(name="aidataselectioncontainer").addChild(self._aidata.get_widget())
def __show_change_player_details_popup(self): """Shows a dialog where the player can change its name and/or color""" def _get_unused_colors(): """Returns unused colors list in a game """ assigned = [ p["color"] for p in NetworkInterface().get_game().get_player_list() if p["name"] != NetworkInterface().get_client_name() ] available = set(Color) - set(assigned) return available set_player_details_dialog = self.widgets['set_player_details'] #remove all children of color and name pop-up and then show them set_player_details_dialog.findChild( name="playerdataselectioncontainer").removeAllChildren() #assign playerdata to self.current.playerdata to use self.__apply_new_color() and __apply_new_nickname() self.current.playerdata = PlayerDataSelection( set_player_details_dialog, self.widgets, color_palette=_get_unused_colors()) self.current.playerdata.set_player_name( NetworkInterface().get_client_name()) self.current.playerdata.set_color( NetworkInterface().get_client_color()) def _change_playerdata(): self.__apply_new_nickname() self.__apply_new_color() set_player_details_dialog.hide() self.__update_game_details() def _cancel(): set_player_details_dialog.hide() events = { OkButton.DEFAULT_NAME: _change_playerdata, CancelButton.DEFAULT_NAME: _cancel } self.on_escape = _cancel set_player_details_dialog.mapEvents(events) set_player_details_dialog.show()
class SingleplayerMenu(object): def show_single(self, show='scenario'): # tutorial """ @param show: string, which type of games to show """ assert show in ('random', 'scenario', 'campaign', 'free_maps') self.hide() # reload since the gui is changed at runtime self.widgets.reload('singleplayermenu') self._switch_current_widget('singleplayermenu', center=True) eventMap = { 'cancel': self.show_main, 'okay': self.start_single, 'showScenario': Callback(self.show_single, show='scenario'), 'showCampaign': Callback(self.show_single, show='campaign'), 'showRandom': Callback(self.show_single, show='random'), 'showMaps': Callback(self.show_single, show='free_maps') } adjust_widget_black_background(self.widgets['singleplayermenu']) # init gui for subcategory if show == 'random': del eventMap['showRandom'] self.current.findChild(name="showRandom").marked = True to_remove = self.current.findChild(name="map_list_area") to_remove.parent.removeChild(to_remove) to_remove = self.current.findChild(name="choose_map_lbl") to_remove.parent.removeChild(to_remove) # need to add some options here (generation algo, size, ... ) else: if show == 'free_maps': del eventMap['showMaps'] self.current.findChild(name="showMaps").marked = True self.current.files, maps_display = SavegameManager.get_maps() elif show == 'campaign': del eventMap['showCampaign'] self.current.findChild(name="showCampaign").marked = True self.current.files, maps_display = SavegameManager.get_campaigns( ) else: # scenario del eventMap['showScenario'] self.current.findChild(name="showScenario").marked = True choosable_locales = ['en', horizons.main.fife.get_locale()] self.current.files, maps_display = SavegameManager.get_available_scenarios( locales=choosable_locales) # get the map files and their display names self.current.distributeInitialData({ 'maplist': maps_display, }) if len(maps_display) > 0: # select first entry self.current.distributeData({ 'maplist': 0, }) if show == 'scenario': # update infos for scenario from horizons.scenario import ScenarioEventHandler, InvalidScenarioFileFormat def _update_infos(): """Fill in infos of selected scenario to label""" try: difficulty = ScenarioEventHandler.get_difficulty_from_file( self.__get_selected_map()) desc = ScenarioEventHandler.get_description_from_file( self.__get_selected_map()) author = ScenarioEventHandler.get_author_from_file( self.__get_selected_map()) except InvalidScenarioFileFormat, e: self.__show_invalid_scenario_file_popup(e) return self.current.findChild( name="map_difficulty" ).text = _("Difficulty: ") + unicode(difficulty) self.current.findChild( name="map_author" ).text = _("Author: ") + unicode(author) self.current.findChild( name="map_desc" ).text = _("Description: ") + unicode(desc) #self.current.findChild(name="map_desc").parent.adaptLayout() elif show == 'campaign': # update infos for campaign def _update_infos(): """Fill in infos of selected campaign to label""" campaign_info = SavegameManager.get_campaign_info( file=self.__get_selected_map()) if not campaign_info: # TODO : an "invalid campaign popup" self.__show_invalid_scenario_file_popup(e) return self.current.findChild( name="map_difficulty" ).text = _("Difficulty: ") + unicode( campaign_info.get('difficulty', '')) self.current.findChild( name="map_author").text = _("Author: ") + unicode( campaign_info.get('author', '')) self.current.findChild( name="map_desc" ).text = _("Description: ") + unicode( campaign_info.get('description', '')) if show in ('scenario', 'campaign'): self.current.findChild( name="maplist").capture(_update_infos) _update_infos() self.current.mapEvents(eventMap) self.current.playerdata = PlayerDataSelection(self.current, self.widgets) self.current.show() self.on_escape = self.show_main
class SingleplayerMenu(Window): def __init__(self, windows): super(SingleplayerMenu, self).__init__(windows) self._mode = None self._gui = load_uh_widget('singleplayermenu.xml') self._gui.mapEvents({ 'cancel' : self._windows.close, 'okay' : self.act, 'scenario' : Callback(self._select_mode, 'scenario'), 'random' : Callback(self._select_mode, 'random'), 'free_maps': Callback(self._select_mode, 'free_maps') }) self._playerdata = PlayerDataSelection() self._aidata = AIDataSelection() self._gui.findChild(name="playerdataselectioncontainer").addChild(self._playerdata.get_widget()) self._gui.findChild(name="aidataselectioncontainer").addChild(self._aidata.get_widget()) def hide(self): # Save the player-data on hide so that other menus gets updated data self._playerdata.save_settings() self._gui.hide() def show(self): self._playerdata.update_data() self._gui.findChild(name='scenario').marked = True self._select_mode('scenario') def on_return(self): self.act() def _select_mode(self, mode): self._gui.hide() modes = { 'random': RandomMapWidget, 'free_maps': FreeMapsWidget, 'scenario': ScenarioMapWidget, } # remove old widget if self._mode: self._mode.end() self._gui.findChild(name="right_side_box").removeChild(self._mode.get_widget()) self._mode = modes[mode](self._windows, self, self._aidata) self._mode.show() self._gui.findChild(name="right_side_box").addChild(self._mode.get_widget()) self._gui.show() def act(self): """Start the game. Called when OK button is pressed.""" player_color = self._playerdata.get_player_color() player_name = self._playerdata.get_player_name() if not player_name: self._windows.open_popup(_("Invalid player name"), _("You entered an invalid playername.")) return horizons.globals.fife.set_uh_setting("Nickname", player_name) self._windows.close() self._mode.act(player_name, player_color)
def show_multi(self): """Shows main multiplayer menu""" if enet is None: headline = _(u"Unable to find pyenet") descr = _( u'The multiplayer feature requires the library "pyenet", ' u"which could not be found on your system.") advice = _( u"Linux users: Try to install pyenet through your package manager." ) self.show_error_popup(headline, descr, advice) return if NetworkInterface() is None: try: NetworkInterface.create_instance() except RuntimeError as e: headline = _(u"Failed to initialize networking.") descr = _( "Network features could not be initialized with the current configuration." ) advice = _( "Check the settings you specified in the network section.") self.show_error_popup(headline, descr, advice, unicode(e)) return if not NetworkInterface().isconnected(): connected = self.__connect_to_server() if not connected: return if NetworkInterface().isjoined(): if not NetworkInterface().leavegame(): return event_map = { 'cancel': self.__cancel, 'join': self.__join_game, 'create': self.__show_create_game, 'load': self.__show_load_game, 'refresh': Callback(self.__refresh, play_sound=True) } # store old name and color self.__apply_new_nickname() self.__apply_new_color() # reload because changing modes (not yet implemented) will need it self.widgets.reload('multiplayermenu') self._switch_current_widget('multiplayermenu', center=True, event_map=event_map, hide_old=True) refresh_worked = self.__refresh() if not refresh_worked: self.show_main() return self.current.findChild(name='gamelist').capture( self.__update_game_details) self.current.findChild(name='showonlyownversion').capture( self.__show_only_own_version_toggle) self.current.playerdata = PlayerDataSelection(self.current, self.widgets) self.current.show() self.on_escape = event_map['cancel']
class MultiplayerMenu(Window): def __init__(self, mainmenu, windows): super(MultiplayerMenu, self).__init__(windows) self._mainmenu = mainmenu self._gui = load_uh_widget('multiplayermenu.xml') self._gui.mapEvents({ 'cancel': self._windows.close, 'join': self._join_game, 'create': self._create_game, 'refresh': Callback(self._refresh, play_sound=True) }) self._gui.findChild(name='gamelist').capture(self._update_game_details) self._playerdata = PlayerDataSelection() self._gui.findChild(name="playerdataselectioncontainer").addChild( self._playerdata.get_widget()) # to track if the menu window is opened or not. self._is_open = False def hide(self): # Save the player-data on hide so that other menus gets updated data self._playerdata.save_settings() self._gui.hide() ExtScheduler().rem_all_classinst_calls(self) def show(self): if not self._check_connection(): return if not self._refresh(): self._windows.close() return if not self._is_open: self._is_open = True # subscribe "error" when this menu window is firstly opened # only unsubscribe if this menu window is closed NetworkInterface().subscribe("error", self._on_error) # get updated player data self._playerdata.update_data() self._gui.show() # TODO: Remove once loading a game is implemented again self._gui.findChild(name='load').parent.hide() ExtScheduler().add_new_object(self._refresh, self, run_in=5, loops=-1) def close(self): # if the window is not open (due to connection errors), just do nothing if not self._is_open: return self.hide() NetworkInterface().unsubscribe("error", self._on_error) self._is_open = False # the window is also closed when a game starts, don't disconnect in that case if NetworkInterface( ).is_connected and not NetworkInterface().is_joined: NetworkInterface().disconnect() def on_return(self): self._join_game() def _check_connection(self): """ Check if all dependencies for multiplayer games are met and we can connect to the master server. If any dependency is not met, the window is closed. """ # It is important to close this window before showing the error popup. # Otherwise closing the popup will trigger `show` again, a new attempt # to connect is made, which ends up in an endless loop. if enet is None: self._windows.close() headline = _("Unable to find pyenet") descr = _('The multiplayer feature requires the library "pyenet", ' "which could not be found on your system.") advice = _( "Linux users: Try to install pyenet through your package manager." ) self._windows.open_error_popup(headline, descr, advice) return False if NetworkInterface() is None: try: NetworkInterface.create_instance() except RuntimeError as e: self._windows.close() headline = _("Failed to initialize networking.") descr = _( "Network features could not be initialized with the current configuration." ) advice = _( "Check the settings you specified in the network section.") self._windows.open_error_popup(headline, descr, advice, unicode(e)) return False if not NetworkInterface().is_connected: try: NetworkInterface().connect() except Exception as err: self._windows.close() headline = _("Fatal Network Error") descr = _("Could not connect to master server.") advice = _( "Please check your Internet connection. If it is fine, " "it means our master server is temporarily down.") self._windows.open_error_popup(headline, descr, advice, unicode(err)) return False if NetworkInterface().is_joined: if not NetworkInterface().leavegame(): self._windows.close() return False return True def _on_error(self, exception, fatal=True): """Error callback""" if not fatal: self._windows.open_popup(_("Error"), unicode(exception)) else: self._windows.open_popup( _("Fatal Network Error"), _("Something went wrong with the network:") + u'\n' + unicode(exception)) # FIXME: this shouldn't be necessary, the main menu window is still somewhere # in the stack and we just need to get rid of all MP related windows self._mainmenu.show_main() def _display_game_name(self, game): same_version = game.version == NetworkInterface().get_clientversion() template = u"{password}{gamename}: {name} ({players}, {limit}){version}" return template.format( password="******" if game.has_password else "", name=game.map_name, gamename=game.name, players=game.player_count, limit=game.player_limit, version=u" " + _("Version differs!") if not same_version else u"") def _refresh(self, play_sound=False): """Refresh list of games. @param play_sound: whether to play the refresh sound @return bool, whether refresh worked """ if play_sound: AmbientSoundComponent.play_special('refresh') self._games = NetworkInterface().get_active_games() if self._games is None: return False gamelist = [self._display_game_name(g) for g in self._games] self._gui.distributeInitialData({'gamelist': gamelist}) self._gui.distributeData({'gamelist': 0}) self._update_game_details() return True def _update_game_details(self): """Set map name and other misc data in a widget.""" try: index = self._gui.collectData('gamelist') game = self._games[index] except IndexError: return self._gui.findChild( name="game_map").text = _("Map: {map_name}").format( map_name=game.map_name) self._gui.findChild( name="game_name").text = _("Name: {game_name}").format( game_name=game.name) self._gui.findChild( name="game_creator").text = _("Creator: {game_creator}").format( game_creator=game.creator) self._gui.findChild(name="game_playersnum").text = _( "Players: {player_amount}/{player_limit}").format( player_amount=game.player_count, player_limit=game.player_limit) vbox_inner = self._gui.findChild(name="game_info") vbox_inner.adaptLayout() def _join_game(self): """Joins a multiplayer game. Displays lobby for that specific game""" try: index = self._gui.collectData('gamelist') game = self._games[index] except IndexError: return if game.uuid == -1: # -1 signals no game AmbientSoundComponent.play_special('error') return if game.version != NetworkInterface().get_clientversion(): self._windows.open_popup( _("Wrong version"), _("The game's version differs from your version. " "Every player in a multiplayer game must use the same version. " "This can be fixed by every player updating to the latest version. " "Game version: {game_version} Your version: {own_version}"). format(game_version=game.version, own_version=NetworkInterface().get_clientversion())) return NetworkInterface().change_name(self._playerdata.get_player_name()) NetworkInterface().change_color(self._playerdata.get_player_color().id) if game.password: # ask the player for the password popup = PasswordInput(self._windows) password = self._windows.open(popup) if password is None: return password = hashlib.sha1(password).hexdigest() success = NetworkInterface().joingame(game.uuid, password) if not success: return elif not NetworkInterface().joingame(game.uuid, ''): return window = GameLobby(self._windows) self._windows.open(window) def _create_game(self): NetworkInterface().change_name(self._playerdata.get_player_name()) NetworkInterface().change_color(self._playerdata.get_player_color().id) self._windows.open(CreateGame(self._windows))
class SingleplayerMenu(Window): def __init__(self, windows): super(SingleplayerMenu, self).__init__(windows) self._mode = None self._gui = load_uh_widget('singleplayermenu.xml') self._gui.mapEvents({ 'cancel': self._windows.close, 'okay': self.act, 'scenario': Callback(self._select_mode, 'scenario'), 'random': Callback(self._select_mode, 'random'), 'free_maps': Callback(self._select_mode, 'free_maps') }) self._playerdata = PlayerDataSelection() self._aidata = AIDataSelection() self._gui.findChild(name="playerdataselectioncontainer").addChild( self._playerdata.get_widget()) self._gui.findChild(name="aidataselectioncontainer").addChild( self._aidata.get_widget()) def hide(self): # Save the player-data on hide so that other menus gets updated data self._playerdata.save_settings() self._gui.hide() def show(self): self._playerdata.update_data() self._gui.findChild(name='scenario').marked = True self._select_mode('scenario') def on_return(self): self.act() def _select_mode(self, mode): self._gui.hide() modes = { 'random': RandomMapWidget, 'free_maps': FreeMapsWidget, 'scenario': ScenarioMapWidget, } # remove old widget if self._mode: self._mode.end() self._gui.findChild(name="right_side_box").removeChild( self._mode.get_widget()) self._mode = modes[mode](self._windows, self, self._aidata) self._mode.show() self._gui.findChild(name="right_side_box").addChild( self._mode.get_widget()) self._gui.show() def act(self): """Start the game. Called when OK button is pressed.""" player_color = self._playerdata.get_player_color() player_name = self._playerdata.get_player_name() if not player_name: self._windows.open_popup(_("Invalid player name"), _("You entered an invalid playername.")) return horizons.globals.fife.set_uh_setting("Nickname", player_name) self._windows.close() self._mode.act(player_name, player_color)
class MultiplayerMenu(object): def __init__(self, mainmenu): self._mainmenu = mainmenu def hide(self): self._gui.hide() def show(self): if not self._check_connection(): return self._mainmenu.hide() self._mainmenu.current = self self._mainmenu.on_escape = self.close self._gui = load_uh_widget('multiplayermenu.xml') self._gui.mapEvents({ 'cancel' : self.close, 'join' : self._join_game, 'create' : self._create_game, 'refresh': Callback(self._refresh, play_sound=True) }) self._gui.findChild(name='gamelist').capture(self._update_game_details) self._gui.findChild(name='showonlyownversion').capture(self._refresh) self._playerdata = PlayerDataSelection() self._gui.findChild(name="playerdataselectioncontainer").addChild(self._playerdata.get_widget()) refresh_worked = self._refresh() if not refresh_worked: self.cancel() return NetworkInterface().subscribe("game_prepare", self._prepare_game) NetworkInterface().subscribe("game_starts", self._start_game) NetworkInterface().subscribe("error", self._on_error) self._gui.show() # TODO Remove once loading a game is implemented again self._gui.findChild(name='load').parent.hide() def close(self): self.hide() NetworkInterface().unsubscribe("game_prepare", self._prepare_game) NetworkInterface().unsubscribe("game_starts", self._start_game) NetworkInterface().unsubscribe("error", self._on_error) if NetworkInterface().is_connected: NetworkInterface().disconnect() NetworkInterface().change_name(self._playerdata.get_player_name()) NetworkInterface().change_color(self._playerdata.get_player_color().id) self._mainmenu.show_main() def _check_connection(self): """ Check if all dependencies for multiplayer games are met and we can connect to the master server. """ if enet is None: headline = _(u"Unable to find pyenet") descr = _(u'The multiplayer feature requires the library "pyenet", ' u"which could not be found on your system.") advice = _(u"Linux users: Try to install pyenet through your package manager.") self._mainmenu.show_error_popup(headline, descr, advice) return False if NetworkInterface() is None: try: NetworkInterface.create_instance() except RuntimeError as e: headline = _(u"Failed to initialize networking.") descr = _("Network features could not be initialized with the current configuration.") advice = _("Check the settings you specified in the network section.") self._mainmenu.show_error_popup(headline, descr, advice, unicode(e)) return False if not NetworkInterface().is_connected: try: NetworkInterface().connect() except Exception as err: headline = _(u"Fatal Network Error") descr = _(u"Could not connect to master server.") advice = _(u"Please check your Internet connection. If it is fine, " u"it means our master server is temporarily down.") self._mainmenu.show_error_popup(headline, descr, advice, unicode(err)) return False if NetworkInterface().is_joined: if not NetworkInterface().leavegame(): return False return True def _on_error(self, exception, fatal=True): """Error callback""" if fatal and self._mainmenu.session is not None: self._mainmenu.session.timer.ticks_per_second = 0 if not fatal: self._mainmenu.show_popup(_("Error"), unicode(exception)) else: self._mainmenu.show_popup(_("Fatal Network Error"), _("Something went wrong with the network:") + u'\n' + unicode(exception) ) self._mainmenu.quit_session(force=True) def _display_game_name(self, game): same_version = game.version == NetworkInterface().get_clientversion() template = u"{password}{gamename}: {name} ({players}, {limit}){version}" return template.format( password="******" if game.has_password else "", name=game.map_name, gamename=game.name, players=game.player_count, limit=game.player_limit, version=u" " + _("Version differs!") if not same_version else u"") def _refresh(self, play_sound=False): """Refresh list of games. @param play_sound: whether to play the refresh sound @return bool, whether refresh worked """ if play_sound: AmbientSoundComponent.play_special('refresh') only_this_version_allowed = self._gui.findChild(name='showonlyownversion').marked self._games = NetworkInterface().get_active_games(only_this_version_allowed) if self._games is None: return False gamelist = [self._display_game_name(g) for g in self._games] self._gui.distributeInitialData({'gamelist': gamelist}) self._gui.distributeData({'gamelist': 0}) self._update_game_details() return True def _update_game_details(self): """Set map name and other misc data in a widget.""" try: index = self._gui.collectData('gamelist') game = self._games[index] except IndexError: return #xgettext:python-format self._gui.findChild(name="game_map").text = _("Map: {map_name}").format(map_name=game.map_name) self._gui.findChild(name="game_name").text = _("Name: {game_name}").format(game_name=game.name) self._gui.findChild(name="game_creator").text = _("Creator: {game_creator}").format(game_creator=game.creator) #xgettext:python-format self._gui.findChild(name="game_playersnum").text = _("Players: {player_amount}/{player_limit}").format( player_amount=game.player_count, player_limit=game.player_limit) vbox_inner = self._gui.findChild(name="game_info") vbox_inner.adaptLayout() def _join_game(self): """Joins a multiplayer game. Displays lobby for that specific game""" try: index = self._gui.collectData('gamelist') game = self._games[index] except IndexError: return if game.uuid == -1: # -1 signals no game AmbientSoundComponent.play_special('error') return if game.version != NetworkInterface().get_clientversion(): self._mainmenu.show_popup(_("Wrong version"), #xgettext:python-format _("The game's version differs from your version. " "Every player in a multiplayer game must use the same version. " "This can be fixed by every player updating to the latest version. " "Game version: {game_version} Your version: {own_version}").format( game_version=game.version, own_version=NetworkInterface().get_clientversion())) return password = "" if game.password: # Repeatedly ask the player for the password success = False while not success: password = self._request_game_password(game) if password is None: break password = hashlib.sha1(password).hexdigest() success = NetworkInterface().joingame(game.uuid, password) if not success: return else: if not NetworkInterface().joingame(game.uuid, password): return self.show_lobby() def _request_game_password(self, game): """Show dialog to ask player for a password.""" dialog = load_uh_widget('set_password.xml') bind = {OkButton.DEFAULT_NAME: True, CancelButton.DEFAULT_NAME: False} retval = self._mainmenu.show_dialog(dialog, bind, modal=True, focus="password") if retval: return dialog.collectData("password") else: return None def _create_game(self): window = CreateGame(self._mainmenu, self) window.show() def show_lobby(self): window = GameLobby(self._mainmenu, self) window.show() def _prepare_game(self, game): self._mainmenu.show_loading_screen() horizons.main.prepare_multiplayer(game) def _start_game(self, game): # TODO listening for this event could be moved to MPSession horizons.main.start_multiplayer(game)
class MultiplayerMenu(Window): def __init__(self, mainmenu, windows): super(MultiplayerMenu, self).__init__(windows) self._mainmenu = mainmenu def hide(self): self._gui.hide() def show(self): if not self._check_connection(): return self._gui = load_uh_widget('multiplayermenu.xml') self._gui.mapEvents({ 'cancel' : self._windows.close, 'join' : self._join_game, 'create' : self._create_game, 'refresh': Callback(self._refresh, play_sound=True) }) self._gui.findChild(name='gamelist').capture(self._update_game_details) self._playerdata = PlayerDataSelection() self._gui.findChild(name="playerdataselectioncontainer").addChild(self._playerdata.get_widget()) refresh_worked = self._refresh() if not refresh_worked: self._windows.close() return # FIXME workaround for multiple callback registrations # this happens because subscription is done when the window is showed, unsubscription # only when it is closed. if new windows appear and disappear, show is called multiple # times. the error handler is used throughout the entire mp menu, that's why we can't # unsubscribe in hide. need to find a better solution. NetworkInterface().discard("error", self._on_error) NetworkInterface().subscribe("error", self._on_error) self._gui.show() # TODO Remove once loading a game is implemented again self._gui.findChild(name='load').parent.hide() def close(self): # when the connection to the master server fails, the window will be closed before # anything has been setup if not hasattr(self, '_gui'): return self.hide() NetworkInterface().unsubscribe("error", self._on_error) # the window is also closed when a game starts, don't disconnect in that case if NetworkInterface().is_connected and not NetworkInterface().is_joined: NetworkInterface().disconnect() NetworkInterface().change_name(self._playerdata.get_player_name()) NetworkInterface().change_color(self._playerdata.get_player_color().id) def on_return(self): self._join_game() def _check_connection(self): """ Check if all dependencies for multiplayer games are met and we can connect to the master server. If any dependency is not met, the window is closed. """ # It is important to close this window before showing the error popup. # Otherwise closing the popup will trigger `show` again, a new attempt # to connect is made, which ends up in an endless loop. if enet is None: self._windows.close() headline = _("Unable to find pyenet") descr = _('The multiplayer feature requires the library "pyenet", ' "which could not be found on your system.") advice = _("Linux users: Try to install pyenet through your package manager.") self._windows.show_error_popup(headline, descr, advice) return False if NetworkInterface() is None: try: NetworkInterface.create_instance() except RuntimeError as e: self._windows.close() headline = _("Failed to initialize networking.") descr = _("Network features could not be initialized with the current configuration.") advice = _("Check the settings you specified in the network section.") self._windows.show_error_popup(headline, descr, advice, unicode(e)) return False if not NetworkInterface().is_connected: try: NetworkInterface().connect() except Exception as err: self._windows.close() headline = _("Fatal Network Error") descr = _("Could not connect to master server.") advice = _("Please check your Internet connection. If it is fine, " "it means our master server is temporarily down.") self._windows.show_error_popup(headline, descr, advice, unicode(err)) return False if NetworkInterface().is_joined: if not NetworkInterface().leavegame(): self._windows.close() return False return True def _on_error(self, exception, fatal=True): """Error callback""" if not fatal: self._windows.show_popup(_("Error"), unicode(exception)) else: self._windows.show_popup(_("Fatal Network Error"), _("Something went wrong with the network:") + u'\n' + unicode(exception) ) # FIXME this shouldn't be necessary, the main menu window is still somewhere # in the stack and we just need to get rid of all MP related windows self._mainmenu.show_main() def _display_game_name(self, game): same_version = game.version == NetworkInterface().get_clientversion() template = u"{password}{gamename}: {name} ({players}, {limit}){version}" return template.format( password="******" if game.has_password else "", name=game.map_name, gamename=game.name, players=game.player_count, limit=game.player_limit, version=u" " + _("Version differs!") if not same_version else u"") def _refresh(self, play_sound=False): """Refresh list of games. @param play_sound: whether to play the refresh sound @return bool, whether refresh worked """ if play_sound: AmbientSoundComponent.play_special('refresh') self._games = NetworkInterface().get_active_games() if self._games is None: return False gamelist = [self._display_game_name(g) for g in self._games] self._gui.distributeInitialData({'gamelist': gamelist}) self._gui.distributeData({'gamelist': 0}) self._update_game_details() return True def _update_game_details(self): """Set map name and other misc data in a widget.""" try: index = self._gui.collectData('gamelist') game = self._games[index] except IndexError: return self._gui.findChild(name="game_map").text = _("Map: {map_name}").format(map_name=game.map_name) self._gui.findChild(name="game_name").text = _("Name: {game_name}").format(game_name=game.name) self._gui.findChild(name="game_creator").text = _("Creator: {game_creator}").format(game_creator=game.creator) self._gui.findChild(name="game_playersnum").text = _("Players: {player_amount}/{player_limit}").format( player_amount=game.player_count, player_limit=game.player_limit) vbox_inner = self._gui.findChild(name="game_info") vbox_inner.adaptLayout() def _join_game(self): """Joins a multiplayer game. Displays lobby for that specific game""" try: index = self._gui.collectData('gamelist') game = self._games[index] except IndexError: return if game.uuid == -1: # -1 signals no game AmbientSoundComponent.play_special('error') return if game.version != NetworkInterface().get_clientversion(): self._windows.show_popup(_("Wrong version"), _("The game's version differs from your version. " "Every player in a multiplayer game must use the same version. " "This can be fixed by every player updating to the latest version. " "Game version: {game_version} Your version: {own_version}").format( game_version=game.version, own_version=NetworkInterface().get_clientversion())) return NetworkInterface().change_name(self._playerdata.get_player_name()) NetworkInterface().change_color(self._playerdata.get_player_color().id) if game.password: # ask the player for the password popup = PasswordInput(self._windows) password = self._windows.show(popup) if password is None: return password = hashlib.sha1(password).hexdigest() success = NetworkInterface().joingame(game.uuid, password) if not success: return elif not NetworkInterface().joingame(game.uuid, ''): return window = GameLobby(self._windows) self._windows.show(window) def _create_game(self): NetworkInterface().change_name(self._playerdata.get_player_name()) NetworkInterface().change_color(self._playerdata.get_player_color().id) self._windows.show(CreateGame(self._windows)),
class SingleplayerMenu(object): def __init__(self, mainmenu): self._mainmenu = mainmenu self._mode = None self._gui = load_uh_widget('singleplayermenu.xml') self._gui.mapEvents({ 'cancel' : self.cancel, 'okay' : self.act, 'scenario' : Callback(self._select_mode, 'scenario'), 'random' : Callback(self._select_mode, 'random'), 'free_maps': Callback(self._select_mode, 'free_maps') }) self._playerdata = PlayerDataSelection() self._aidata = AIDataSelection() self._gui.findChild(name="playerdataselectioncontainer").addChild(self._playerdata.get_widget()) self._gui.findChild(name="aidataselectioncontainer").addChild(self._aidata.get_widget()) def hide(self): self._gui.hide() def cancel(self): self._mainmenu.show_main() def show(self): self._mainmenu.hide() self._mainmenu.current = self self._mainmenu.on_escape = self.cancel self._gui.findChild(name='scenario').marked = True self._select_mode('scenario') def _select_mode(self, mode): self._gui.hide() modes = { 'random': RandomMapWidget, 'free_maps': FreeMapsWidget, 'scenario': ScenarioMapWidget, } # remove old widget if self._mode: self._mode.end() self._gui.findChild(name="right_side_box").removeChild(self._mode.get_widget()) self._mode = modes[mode](self._mainmenu, self, self._aidata) self._mode.show() self._gui.findChild(name="right_side_box").addChild(self._mode.get_widget()) self._gui.show() def act(self): """Start the game. Called when OK button is pressed.""" player_color = self._playerdata.get_player_color() player_name = self._playerdata.get_player_name() if not player_name: self._mainmenu.show_popup(_("Invalid player name"), _("You entered an invalid playername.")) return horizons.globals.fife.set_uh_setting("Nickname", player_name) self._mode.act(player_name, player_color)