Пример #1
0
	def __create_game(self, load=None, chosen_map=None):
		"""
		Actually create a game, join it, and display the lobby.

		@param load: game data tuple for creating loaded games
		@param chosen_map: the name of the map to start a new game on (overrides the gui)
		"""
		# create the game
		if load:
			mapname, gamename = load
			path = SavegameManager.get_multiplayersave_map(mapname)
			maxplayers = SavegameAccessor.get_players_num(path)
			load = SavegameAccessor.get_hash(path)
		else:
			mapindex = None
			if chosen_map is not None:
				for i, map in enumerate(self.maps_display):
					if map == chosen_map:
						mapindex = i
						break

			if mapindex is None:
				mapindex = self.current.collectData('maplist')
			mapname = self.maps_display[mapindex]
			maxplayers = self.current.collectData('playerlimit') + 2 # 1 is the first entry
			gamename = self.current.collectData('gamename')
			load = None

		game = NetworkInterface().creategame(mapname, maxplayers, gamename, load)
		if game is None:
			return

		self.__show_gamelobby()
Пример #2
0
 def __join_game(self, game=None):
     """Joins a multiplayer game. Displays lobby for that specific game"""
     if game == None:
         game = self.__get_selected_game()
     if (
         game.load is not None
         and SavegameAccessor.get_hash(SavegameManager.get_multiplayersave_map(game.mapname)) != game.load
     ):
         self.show_popup(_("Error"), self.current.findChild(name="save_missing_help_button").btn_text, size=1)
         return
     if game.get_uuid() == -1:  # -1 signals no game
         AmbientSoundComponent.play_special("error")
         return
     if game.get_version() != NetworkInterface().get_clientversion():
         self.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.get_version(), own_version=NetworkInterface().get_clientversion()),
         )
         return
         # actual join
     join_worked = NetworkInterface().joingame(game.get_uuid())
     if not join_worked:
         return
     self.__apply_new_nickname()
     self.__show_gamelobby()
Пример #3
0
    def __update_game_details(self, game=None):
        """Set map name and other misc data in a widget. Only possible in certain states"""
        if game == None:
            game = self.__get_selected_game()
            # xgettext:python-format
        self.current.findChild(name="game_map").text = _("Map: {map_name}").format(map_name=game.get_map_name())
        self.current.findChild(name="game_name").text = _("Name: {game_name}").format(game_name=game.get_name())
        # xgettext:python-format
        self.current.findChild(name="game_playersnum").text = _("Players: {player_amount}/{player_limit}").format(
            player_amount=game.get_player_count(), player_limit=game.get_player_limit()
        )
        creator_text = self.current.findChild(name="game_creator")
        # xgettext:python-format
        creator_text.text = _("Creator: {player}").format(player=game.get_creator())
        creator_text.adaptLayout()
        vbox_inner = self.current.findChild(name="game_info")
        if game.load is not None:  # work around limitations of current systems via messages
            path = SavegameManager.get_multiplayersave_map(game.mapname)
            if SavegameAccessor.get_hash(path) != game.load:
                text = ""
                btn_name = "save_missing_help_button"
                btn = vbox_inner.findChild(name=btn_name)
                if btn is None:
                    btn = pychan.widgets.Button(name=btn_name, text=_("This savegame is missing (click here)"))
                    last_elem = vbox_inner.findChild(name="game_info_last")
                    if last_elem is None:  # no last elem -> we are last
                        vbox_inner.addChild(btn)
                    else:
                        vbox_inner.insertChildBefore(btn, last_elem)
                btn_text = (
                    _(
                        u"For multiplayer load, it is currently necessary for you to ensure you have the correct savegame file."
                    )
                    + u"\n"
                )
                btn_text += _(u"This is not nice and we hope to offer a more convenient solution very soon.") + u"\n"
                btn_text += _(
                    u"Meanwhile, please request the file {path} from the game creator and put it in {map_directory} ."
                ).format(path=os.path.basename(path), map_directory=os.path.dirname(path))
                btn.btn_text = btn_text

                def show():
                    self.show_popup(_("Help"), btn_text, size=1)

                btn.capture(show)

            else:
                text = _(u"This is a savegame.")

            if text:
                self.current.findChild(name="game_isloaded").text = text
        textplayers = self.current.findChild(name="game_players")
        if textplayers is not None:
            textplayers.text = u", ".join(game.get_players())

        vbox_inner.adaptLayout()  # inner vbox always exists
        vbox = self.current.findChild(name="gamedetailsbox")
        if vbox is not None:
            vbox.adaptLayout()
Пример #4
0
	def end(self, keep_map=False, remove_savegame=True):
		"""
		Clean up temporary files.
		"""
		super(SPTestSession, self).end()

		# Find all islands in the map first
		savegame_db = SavegameAccessor(self.savegame)
		if not keep_map:
			for (island_file, ) in savegame_db('SELECT file FROM island'):
				if not island_file.startswith('random:'): # random islands don't exist as files
					os.remove(island_file)

		# Finally remove savegame
		savegame_db.close()
		if remove_savegame:
			os.remove(self.savegame)
Пример #5
0
	def __send_toggle_ready(self):
		game = NetworkInterface().get_game()

		if game.load is not None and \
		SavegameAccessor.get_hash(SavegameManager.get_multiplayersave_map(game.mapname)) != game.load:
			self.__print_event_message("You are fetching savegame data. You must wait for it")
			return

		NetworkInterface().send_toggle_ready(NetworkInterface().get_client_name())
Пример #6
0
	def load(self, savegame, players, is_scenario=False, campaign={}):
		"""Loads a map.
		@param savegame: path to the savegame database.
		@param players: iterable of dictionaries containing id, name, color and local
		@param is_scenario: Bool whether the loaded map is a scenario or not
		"""
		if is_scenario:
			# savegame is a yaml file, that contains reference to actual map file
			self.scenario_eventhandler = ScenarioEventHandler(self, savegame)
			savegame = os.path.join(SavegameManager.maps_dir, \
			                        self.scenario_eventhandler.get_map_file())
		self.campaign = campaign

		self.log.debug("Session: Loading from %s", savegame)
		savegame_db = SavegameAccessor(savegame) # Initialize new dbreader
		try:
			# load how often the game has been saved (used to know the difference between
			# a loaded and a new game)
			self.savecounter = SavegameManager.get_metadata(savegame)['savecounter']
		except KeyError:
			self.savecounter = 0

		self.world = World(self) # Load horizons.world module (check horizons/world/__init__.py)
		self.world._init(savegame_db)
		self.view.load(savegame_db) # load view
		if not self.is_game_loaded():
			# NOTE: this must be sorted before iteration, cause there is no defined order for
			#       iterating a dict, and it must happen in the same order for mp games.
			for i in sorted(players):
				self.world.setup_player(i['id'], i['name'], i['color'], i['local'])
			center = self.world.init_new_world()
			self.view.center(center[0], center[1])
		else:
			# try to load scenario data
			self.scenario_eventhandler.load(savegame_db)
		self.manager.load(savegame_db) # load the manager (there might me old scheduled ticks).
		self.ingame_gui.load(savegame_db) # load the old gui positions and stuff

		for instance_id in savegame_db("SELECT id FROM selected WHERE `group` IS NULL"): # Set old selected instance
			obj = WorldObject.get_object_by_id(instance_id[0])
			self.selected_instances.add(obj)
			obj.select()
		for group in xrange(len(self.selection_groups)): # load user defined unit groups
			for instance_id in savegame_db("SELECT id FROM selected WHERE `group` = ?", group):
				self.selection_groups[group].add(WorldObject.get_object_by_id(instance_id[0]))

		# cursor has to be inited last, else player interacts with a not inited world with it.
		self.cursor = SelectionTool(self)
		self.cursor.apply_select() # Set cursor correctly, menus might need to be opened.

		assert hasattr(self.world, "player"), 'Error: there is no human player'
		"""
Пример #7
0
	def load(self, savegame, players):
		"""
		Stripped version of the original code. We don't need to load selections,
		or a scenario, setting up the gui or view.
		"""
		self.savegame = savegame
		self.savegame_db = SavegameAccessor(self.savegame)

		self.world = World(self)
		self.world._init(self.savegame_db)
		for i in sorted(players):
			self.world.setup_player(i['id'], i['name'], i['color'], i['local'], i['is_ai'], i['difficulty'])
		self.manager.load(self.savegame_db)
Пример #8
0
	def __update_game_details(self, game=None):
		"""Set map name and other misc data in a widget. Only possible in certain states"""
		if game == None:
			game = self.__get_selected_game()
		#xgettext:python-format
		self.current.findChild(name="game_map").text = _("Map: {map_name}").format(map_name=game.get_map_name())
		self.current.findChild(name="game_name").text = _("Name: {game_name}").format(game_name=game.get_name())
		#xgettext:python-format
		self.current.findChild(name="game_playersnum").text = _("Players: {player_amount}/{player_limit}").format(
		                           player_amount=game.get_player_count(),
		                           player_limit=game.get_player_limit())
		vbox_inner = self.current.findChild(name="game_info")
		if game.load is not None: # work around limitations of current systems via messages
			path = SavegameManager.get_multiplayersave_map(game.mapname)
			btn_name = "save_missing_help_button"
			btn = vbox_inner.findChild(name=btn_name)
			if SavegameAccessor.get_hash(path) != game.load:
				text = ""
				if btn is None:
					btn = pychan.widgets.Button(name=btn_name,
					                            text=_("This savegame is missing (click here)"))
					last_elem = vbox_inner.findChild(name="game_info_last")
					if last_elem is None: # no last elem -> we are last
						vbox_inner.addChild( btn )
					else:
						vbox_inner.insertChildBefore( btn, last_elem )
				btn_text = _(u"For multiplayer load, it is necessary for you to have the correct savegame file.") + u"\n"
				btn_text += _(u"The file will be downloaded when you join the game.")
				btn.btn_text = btn_text
				def show():
					self.show_popup(_("Help"), btn_text, size=1)
				btn.capture(show)

			else:
				text = _(u"This is a savegame.")
				if btn is not None:
					btn.hide()

			if text:
				self.current.findChild(name="game_isloaded").text = text

		self.__update_players_box(game)

		vbox_inner.adaptLayout() # inner vbox always exists
		vbox = self.current.findChild(name="gamedetailsbox")
		if vbox is not None:
			vbox.adaptLayout()
Пример #9
0
	def __join_game(self, game=None):
		"""Joins a multiplayer game. Displays lobby for that specific game"""
		if game == None:
			game = self.__get_selected_game()
		if game.load is not None and SavegameAccessor.get_hash(SavegameManager.get_multiplayersave_map(game.mapname)) != game.load:
			NetworkInterface().send_fetch_game(NetworkInterface().get_clientversion(), game.get_uuid())

		if game.get_uuid() == -1: # -1 signals no game
			AmbientSoundComponent.play_special('error')
			return
		if game.get_version() != NetworkInterface().get_clientversion():
			self.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.get_version(),
			                  own_version=NetworkInterface().get_clientversion()))
			return

		# actual join
		if game.password:
			self.__enter_password_dialog(game)

		else:
			self.__actual_join(game)
Пример #10
0
	def load(self, savegame, players, trader_enabled, pirate_enabled,
	         natural_resource_multiplier, is_scenario=False, campaign=None,
	         force_player_id=None, disasters_enabled=True):
		"""Loads a map. Key method for starting a game.
		@param savegame: path to the savegame database.
		@param players: iterable of dictionaries containing id, name, color, local, ai, and difficulty
		@param is_scenario: Bool whether the loaded map is a scenario or not
		@param force_player_id: the worldid of the selected human player or default if None (debug option)
		"""
		"""
		TUTORIAL: Here you see how the vital game elements (and some random things that are also required)
		are initialised
		"""
		if is_scenario:
			# savegame is a yaml file, that contains reference to actual map file
			self.scenario_eventhandler = ScenarioEventHandler(self, savegame)
			# scenario maps can be normal maps or scenario maps:
			map_filename = self.scenario_eventhandler.get_map_file()
			savegame = os.path.join(SavegameManager.scenario_maps_dir, map_filename)
			if not os.path.exists(savegame):
				savegame = os.path.join(SavegameManager.maps_dir, map_filename)
		self.campaign = {} if not campaign else campaign

		self.log.debug("Session: Loading from %s", savegame)
		savegame_db = SavegameAccessor(savegame) # Initialize new dbreader
		savegame_data = SavegameManager.get_metadata(savegame)

		# load how often the game has been saved (used to know the difference between
		# a loaded and a new game)
		self.savecounter = 0 if not 'savecounter' in savegame_data else savegame_data['savecounter']

		if savegame_data.get('rng_state', None):
			rng_state_list = json.loads( savegame_data['rng_state'] )
			# json treats tuples as lists, but we need tuples here, so convert back
			def rec_list_to_tuple(x):
				if isinstance(x, list):
					return tuple( rec_list_to_tuple(i) for i in x )
				else:
					return x
			rng_state_tuple = rec_list_to_tuple(rng_state_list)
			# changing the rng is safe for mp, as all players have to have the same map
			self.random.setstate( rng_state_tuple )

		self.world = World(self) # Load horizons.world module (check horizons/world/__init__.py)
		self.world._init(savegame_db, force_player_id, disasters_enabled=disasters_enabled)
		self.view.load(savegame_db) # load view
		if not self.is_game_loaded():
			# NOTE: this must be sorted before iteration, cause there is no defined order for
			#       iterating a dict, and it must happen in the same order for mp games.
			for i in sorted(players, lambda p1, p2: cmp(p1['id'], p2['id'])):
				self.world.setup_player(i['id'], i['name'], i['color'], i['local'], i['ai'], i['difficulty'])
			self.world.set_forced_player(force_player_id)
			center = self.world.init_new_world(trader_enabled, pirate_enabled, natural_resource_multiplier)
			self.view.center(center[0], center[1])
		else:
			# try to load scenario data
			self.scenario_eventhandler.load(savegame_db)
		self.manager.load(savegame_db) # load the manager (there might me old scheduled ticks).
		self.world.init_fish_indexer() # now the fish should exist
		self.ingame_gui.load(savegame_db) # load the old gui positions and stuff

		for instance_id in savegame_db("SELECT id FROM selected WHERE `group` IS NULL"): # Set old selected instance
			obj = WorldObject.get_object_by_id(instance_id[0])
			self.selected_instances.add(obj)
			obj.get_component(SelectableComponent).select()
		for group in xrange(len(self.selection_groups)): # load user defined unit groups
			for instance_id in savegame_db("SELECT id FROM selected WHERE `group` = ?", group):
				self.selection_groups[group].add(WorldObject.get_object_by_id(instance_id[0]))

		# cursor has to be inited last, else player interacts with a not inited world with it.
		self.current_cursor = 'default'
		self.cursor = SelectionTool(self)
		# Set cursor correctly, menus might need to be opened.
		# Open menus later, they may need unit data not yet inited
		self.cursor.apply_select()
		if self.is_game_loaded():
			LastActivePlayerSettlementManager().load(savegame_db)

		Scheduler().before_ticking()
		savegame_db.close()

		assert hasattr(self.world, "player"), 'Error: there is no human player'
		"""
Пример #11
0
class SPTestSession(SPSession):

	def __init__(self, db, rng_seed=None):
		"""
		Unfortunately, right now there is no other way to setup Dummy versions of the GUI,
		View etc., unless we want to patch the references in the session module.
		"""
		super(LivingObject, self).__init__()
		self.gui = Dummy()
		self.db = db
		self.savecounter = 0	# this is a new game.
		self.is_alive = True

		WorldObject.reset()
		NamedObject.reset()
		AIPlayer.clear_caches()

		# Game
		self.current_tick = 0
		self.random = self.create_rng(rng_seed)
		self.timer = self.create_timer()
		Scheduler.create_instance(self.timer)
		ExtScheduler.create_instance(Dummy)
		self.manager = self.create_manager()
		self.view = Dummy()
		self.view.renderer = Dummy()
		Entities.load(self.db)
		self.scenario_eventhandler = Dummy()
		self.campaign = {}
		self.selected_instances = []

		# GUI
		self.gui.session = self
		self.ingame_gui = Dummy()

		GAME_SPEED.TICKS_PER_SECOND = 16

	def load(self, savegame, players):
		"""
		Stripped version of the original code. We don't need to load selections,
		or a scenario, setting up the gui or view.
		"""
		self.savegame = savegame
		self.savegame_db = SavegameAccessor(self.savegame)

		self.world = World(self)
		self.world._init(self.savegame_db)
		for i in sorted(players):
			self.world.setup_player(i['id'], i['name'], i['color'], i['local'], i['is_ai'], i['difficulty'])
		self.manager.load(self.savegame_db)

	def end(self):
		"""
		Clean up temporary files.
		"""
		super(SPTestSession, self).end()
		# Find all islands in the map first
		random_map = False
		for (island_file, ) in self.savegame_db('SELECT file FROM island'):
			if island_file[:7] != 'random:': # random islands don't exist as files
				os.remove(island_file)
			else:
				random_map = True
				break
		# Finally remove savegame
		self.savegame_db.close()
		if not random_map:
			os.remove(self.savegame)

	def run(self, ticks=1, seconds=None):
		"""
		Run the scheduler the given count of ticks or (in-game) seconds. Default is 1 tick,
		if seconds are passed, they will overwrite the tick count.
		"""
		if seconds:
			ticks = self.timer.get_ticks(seconds)

		for i in range(ticks):
			Scheduler().tick(self.current_tick)
			self.current_tick += 1
Пример #12
0
class SPTestSession(SPSession):

	def __init__(self, db, rng_seed=None):
		"""
		Unfortunately, right now there is no other way to setup Dummy versions of the GUI,
		View etc., unless we want to patch the references in the session module.
		"""
		super(LivingObject, self).__init__()
		self.gui = Dummy()
		self.db = db
		self.savecounter = 0	# this is a new game.
		self.is_alive = True

		WorldObject.reset()
		NamedComponent.reset()
		AIPlayer.clear_caches()

		# Game
		self.random = self.create_rng(rng_seed)
		self.timer = self.create_timer()
		Scheduler.create_instance(self.timer)
		ExtScheduler.create_instance(Dummy)
		self.manager = self.create_manager()
		self.view = Dummy()
		self.view.renderer = Dummy()
		Entities.load(self.db)
		self.scenario_eventhandler = Dummy()
		self.campaign = {}

		self.message_bus = MessageBus()
		self.status_icon_manager = StatusIconManager(self)


		# GUI
		self.gui.session = self
		self.ingame_gui = Dummy()
		LastActivePlayerSettlementManager.create_instance(self)

		self.selected_instances = set()
		self.selection_groups = [set()] * 10 # List of sets that holds the player assigned unit groups.

		GAME_SPEED.TICKS_PER_SECOND = 16

	def save(self, *args, **kwargs):
		"""
		Wrapper around original save function to fix some things.
		"""
		# SavegameManager.write_metadata tries to create a screenshot and breaks when
		# accessing fife properties
		with mock.patch('horizons.spsession.SavegameManager'):
			# We need to covert Dummy() objects to a sensible value that can be stored
			# in the database
			with _dbreader_convert_dummy_objects():
				return super(SPTestSession, self).save(*args, **kwargs)

	def load(self, savegame, players):
		"""
		Stripped version of the original code. We don't need to load selections,
		or a scenario, setting up the gui or view.
		"""
		self.savegame = savegame
		self.savegame_db = SavegameAccessor(self.savegame)

		self.savecounter = 1

		self.world = World(self)
		self.world._init(self.savegame_db)
		for i in sorted(players):
			self.world.setup_player(i['id'], i['name'], i['color'], i['local'], i['is_ai'], i['difficulty'])
		self.manager.load(self.savegame_db)

	def end(self, keep_map=False, remove_savegame=True):
		"""
		Clean up temporary files.
		"""
		super(SPTestSession, self).end()
		# Find all islands in the map first
		if not keep_map:
			for (island_file, ) in self.savegame_db('SELECT file FROM island'):
				if island_file[:7] != 'random:': # random islands don't exist as files
					os.remove(island_file)

		self.savegame_db.close()
		# Finally remove savegame
		if remove_savegame:
			os.remove(self.savegame)

	@classmethod
	def cleanup(cls):
		"""
		If a test uses manual session management, we cannot be sure that session.end was
		called before a crash, leaving the game in an unclean state. This method should
		return the game to a valid state.
		"""
		Scheduler.destroy_instance()

	def run(self, ticks=1, seconds=None):
		"""
		Run the scheduler the given count of ticks or (in-game) seconds. Default is 1 tick,
		if seconds are passed, they will overwrite the tick count.
		"""
		if seconds:
			ticks = self.timer.get_ticks(seconds)

		for i in range(ticks):
			Scheduler().tick( Scheduler().cur_tick + 1 )