Beispiel #1
0
    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(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_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))
Beispiel #16
0
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)