Exemplo n.º 1
0
	def create_blizzard_account(self, hi, lo, battletag):
		blizzard_account = BlizzardAccount(
			account_hi=hi,
			account_lo=lo,
			region=BnetRegion.from_account_hi(hi),
		)
		changes = [{"added": {}}]
		return blizzard_account, changes
Exemplo n.º 2
0
	def get_rows(self, qs):
		rows = []
		for pack in qs:
			cards = PackCard.objects.filter(pack=pack)
			region = BnetRegion.from_account_hi(pack.account_hi)
			row = [
				pack.id, anonymize(pack.user_id), anonymize(pack.account_lo),
				pack.booster_type.name, pack.date.isoformat(),
				region.name
			]
			for card in cards:
				row += [card.card_id, int(card.premium)]
			rows.append(row)

		return rows
Exemplo n.º 3
0
def update_global_players(global_game, entity_tree, meta, upload_event):
	# Fill the player metadata and objects
	players = {}

	for player in entity_tree.players:
		player_meta = meta.get("player%i" % (player.player_id), {})

		is_spectated_replay = meta.get("spectator_mode", False)
		is_friendly_player = player.player_id == meta["friendly_player"]
		decklist_from_meta = player_meta.get("deck")
		decklist_from_replay = [c.card_id for c in player.initial_deck if c.card_id]

		meta_decklist_is_superset = _is_decklist_superset(
			decklist_from_meta,
			decklist_from_replay
		)

		if not decklist_from_meta or is_spectated_replay or not meta_decklist_is_superset:
			# Spectated replays never know more than is in the replay data
			# But may have erroneous data from the spectator's client's memory
			# Read from before they entered the spectated game
			decklist = decklist_from_replay
		else:
			decklist = decklist_from_meta

		name, real_name = get_player_names(player)
		player_hero_id = player._hero.card_id

		try:
			deck, _ = Deck.objects.get_or_create_from_id_list(
				decklist,
				hero_id=player_hero_id,
				game_type=global_game.game_type,
				classify_into_archetype=True
			)
			log.debug("Prepared deck %i (created=%r)", deck.id, _)
		except IntegrityError as e:
			# This will happen if cards in the deck are not in the DB
			# For example, during a patch release
			influx_metric("replay_deck_create_failure", {"global_game_id": global_game.id})
			log.exception("Could not create deck for player %r", player)
			global_game.tainted_decks = True
			# Replace with an empty deck
			deck, _ = Deck.objects.get_or_create_from_id_list([])

		# Create the PegasusAccount first
		defaults = {
			"region": BnetRegion.from_account_hi(player.account_hi),
			"battletag": name,
		}

		if not is_spectated_replay and not player.is_ai and is_friendly_player:
			user = upload_event.token.user if upload_event.token else None
			if user and not user.is_fake:
				# and user.battletag and user.battletag.startswith(player.name):
				defaults["user"] = user

		pegasus_account, created = PegasusAccount.objects.get_or_create(
			account_hi=player.account_hi, account_lo=player.account_lo,
			defaults=defaults
		)
		if not created and not pegasus_account.user and "user" in defaults:
			# Set PegasusAccount.user if it's an available claim for the user
			influx_metric("pegasus_account_claimed", {"count": 1})
			pegasus_account.user = defaults["user"]
			pegasus_account.save()

		log.debug("Prepared PegasusAccount %r", pegasus_account)

		# Now create the GlobalGamePlayer object
		common = {
			"game": global_game,
			"player_id": player.player_id,
		}
		defaults = {
			"account_hi": player.account_hi,
			"account_lo": player.account_lo,
			"is_first": player.tags.get(GameTag.FIRST_PLAYER, False),
			"is_ai": player.is_ai,
			"hero_id": player_hero_id,
			"hero_premium": player._hero.tags.get(GameTag.PREMIUM, False),
			"final_state": player.tags.get(GameTag.PLAYSTATE, 0),
			"deck_list": deck,
		}

		update = {
			"name": name,
			"real_name": real_name,
			"pegasus_account": pegasus_account,
			"rank": player_meta.get("rank"),
			"legend_rank": player_meta.get("legend_rank"),
			"stars": player_meta.get("stars"),
			"wins": player_meta.get("wins"),
			"losses": player_meta.get("losses"),
			"deck_id": player_meta.get("deck_id") or None,
			"cardback_id": player_meta.get("cardback"),
		}

		defaults.update(update)
		game_player, created = GlobalGamePlayer.objects.get_or_create(defaults=defaults, **common)
		log.debug("Prepared player %r (%i) (created=%r)", game_player, game_player.id, created)

		if not created:
			# Go through the update dict and update values on the player
			# This gets us extra data we might not have had when the player was first created
			updated = False
			for k, v in update.items():
				if v and getattr(game_player, k) != v:
					setattr(game_player, k, v)
					updated = True

			# Skip updating the deck if we already have a bigger one
			# TODO: We should make deck_list nullable and only create it here
			if len(decklist) > game_player.deck_list.size:
				# XXX: Maybe we should also check friendly_player_id for good measure
				game_player.deck_list = deck
				updated = True

			if updated:
				log.debug("Saving updated player to the database.")
				game_player.save()

		players[player.player_id] = game_player

	return players
Exemplo n.º 4
0
def update_global_players(global_game, entity_tree, meta, upload_event,
                          exporter):
    # Fill the player metadata and objects
    players = {}
    played_cards = exporter.export_played_cards()

    is_spectated_replay = meta.get("spectator_mode", False)
    is_dungeon_run = meta.get("scenario_id", 0) == 2663

    for player in entity_tree.players:
        is_friendly_player = player.player_id == meta["friendly_player"]
        player_meta = meta.get("player%i" % (player.player_id), {})

        decklist_from_meta = player_meta.get("deck")
        decklist_from_replay = [
            c.initial_card_id for c in player.initial_deck if c.card_id
        ]

        meta_decklist_is_superset = _is_decklist_superset(
            decklist_from_meta, decklist_from_replay)

        # We disregard the meta decklist if it's not matching the replay decklist
        # We always want to use it in dungeon run though, since the initial deck is garbage
        disregard_meta = not meta_decklist_is_superset and (
            not is_dungeon_run or not is_friendly_player)

        if not decklist_from_meta or is_spectated_replay or disregard_meta:
            # Spectated replays never know more than is in the replay data
            # But may have erroneous data from the spectator's client's memory
            # Read from before they entered the spectated game
            decklist = decklist_from_replay
        else:
            decklist = decklist_from_meta

        name, real_name = get_player_names(player)
        player_hero_id = player._hero.card_id

        try:
            deck, _ = Deck.objects.get_or_create_from_id_list(
                decklist,
                hero_id=player_hero_id,
                game_type=global_game.game_type,
                classify_archetype=True)
            log.debug("Prepared deck %i (created=%r)", deck.id, _)
        except IntegrityError as e:
            # This will happen if cards in the deck are not in the DB
            # For example, during a patch release
            influx_metric(
                "replay_deck_create_failure", {
                    "count": 1,
                    "build": meta["build"],
                    "global_game_id": global_game.id,
                    "server_ip": meta.get("server_ip", ""),
                    "upload_ip": upload_event.upload_ip,
                    "error": str(e),
                })
            log.exception("Could not create deck for player %r", player)
            global_game.tainted_decks = True
            # Replace with an empty deck
            deck, _ = Deck.objects.get_or_create_from_id_list([])

        capture_played_card_stats(
            global_game, [c.dbf_id for c in played_cards[player.player_id]],
            is_friendly_player)

        eligible_formats = [FormatType.FT_STANDARD, FormatType.FT_WILD]
        is_eligible_format = global_game.format in eligible_formats

        deck_prediction_enabled = getattr(settings,
                                          "FULL_DECK_PREDICTION_ENABLED", True)
        if deck_prediction_enabled and is_eligible_format and settings.ENV_AWS:
            try:
                player_class = Deck.objects._convert_hero_id_to_player_class(
                    player_hero_id)
                tree = deck_prediction_tree(player_class, global_game.format)
                played_cards_for_player = played_cards[player.player_id]

                # 5 played cards partitions a 14 day window into buckets of ~ 500 or less
                # We can search through ~ 2,000 decks in 100ms so that gives us plenty of headroom
                min_played_cards = tree.max_depth - 1

                # We can control via settings the minumum number of cards we need
                # To know about in the deck list before we attempt to guess the full deck
                min_observed_cards = settings.DECK_PREDICTION_MINIMUM_CARDS

                played_card_dbfs = [c.dbf_id for c in played_cards_for_player
                                    ][:min_played_cards]
                played_card_names = [c.name for c in played_cards_for_player
                                     ][:min_played_cards]

                if deck.size is not None:
                    deck_size = deck.size
                else:
                    deck_size = sum(i.count for i in deck.includes.all())

                has_enough_observed_cards = deck_size >= min_observed_cards
                has_enough_played_cards = len(
                    played_card_dbfs) >= min_played_cards

                if deck_size == 30:
                    tree.observe(deck.id, deck.dbf_map(), played_card_dbfs)
                    # deck_id == proxy_deck_id for complete decks
                    deck.guessed_full_deck = deck
                    deck.save()
                elif has_enough_observed_cards and has_enough_played_cards:
                    res = tree.lookup(
                        deck.dbf_map(),
                        played_card_dbfs,
                    )
                    predicted_deck_id = res.predicted_deck_id

                    fields = {
                        "actual_deck_id":
                        deck.id,
                        "deck_size":
                        deck_size,
                        "game_id":
                        global_game.id,
                        "sequence":
                        "->".join("[%s]" % c for c in played_card_names),
                        "predicted_deck_id":
                        res.predicted_deck_id,
                        "match_attempts":
                        res.match_attempts,
                        "tie":
                        res.tie
                    }

                    if settings.DETAILED_PREDICTION_METRICS:
                        fields["actual_deck"] = repr(deck)

                        if res.predicted_deck_id:
                            predicted_deck = Deck.objects.get(
                                id=res.predicted_deck_id)
                            fields["predicted_deck"] = repr(predicted_deck)

                    if res.node:
                        fields["depth"] = res.node.depth

                        if settings.DETAILED_PREDICTION_METRICS:
                            node_labels = []
                            for path_dbf_id in res.path():
                                if path_dbf_id == "ROOT":
                                    path_str = path_dbf_id
                                else:
                                    path_card = Card.objects.get(
                                        dbf_id=path_dbf_id)
                                    path_str = path_card.name
                                node_labels.append("[%s]" % path_str)
                            fields["node"] = "->".join(node_labels)

                            popularity = res.popularity_distribution.popularity(
                                res.predicted_deck_id)
                            fields["predicted_deck_popularity"] = popularity

                            deck_count = res.popularity_distribution.size()
                            fields["distribution_deck_count"] = deck_count

                            observation_count = res.popularity_distribution.observations(
                            )
                            fields[
                                "distribution_observation_count"] = observation_count

                    tree_depth = res.node.depth if res.node else None
                    influx_metric(
                        "deck_prediction",
                        fields,
                        missing_cards=30 - deck_size,
                        player_class=CardClass(int(player_class)).name,
                        format=FormatType(int(global_game.format)).name,
                        tree_depth=tree_depth,
                        made_prediction=predicted_deck_id is not None)

                    if predicted_deck_id:
                        deck.guessed_full_deck = Deck.objects.get(
                            id=predicted_deck_id)
                        deck.save()
            except Exception as e:
                error_handler(e)

        # Create the BlizzardAccount first
        defaults = {
            "region": BnetRegion.from_account_hi(player.account_hi),
            "battletag": name,
        }

        if not is_spectated_replay and not player.is_ai and is_friendly_player:
            user = upload_event.token.user if upload_event.token else None
            if user and not user.is_fake:
                # and user.battletag and user.battletag.startswith(player.name):
                defaults["user"] = user

        blizzard_account, created = BlizzardAccount.objects.get_or_create(
            account_hi=player.account_hi,
            account_lo=player.account_lo,
            defaults=defaults)
        if not created and not blizzard_account.user and "user" in defaults:
            # Set BlizzardAccount.user if it's an available claim for the user
            influx_metric(
                "pegasus_account_claimed", {
                    "count": 1,
                    "account": str(blizzard_account.id),
                    "region": str(blizzard_account.region),
                    "account_lo": str(blizzard_account.account_lo),
                    "game": str(global_game.id)
                })
            blizzard_account.user = defaults["user"]
            blizzard_account.save()

        log.debug("Prepared BlizzardAccount %r", blizzard_account)

        # Now create the GlobalGamePlayer object
        common = {
            "game": global_game,
            "player_id": player.player_id,
        }
        defaults = {
            "is_first": player.tags.get(GameTag.FIRST_PLAYER, False),
            "is_ai": player.is_ai,
            "hero_id": player_hero_id,
            "hero_premium": player._hero.tags.get(GameTag.PREMIUM, False),
            "final_state": player.tags.get(GameTag.PLAYSTATE, 0),
            "extra_turns": player.tags.get(GameTag.EXTRA_TURNS_TAKEN_THIS_GAME,
                                           0),
            "deck_list": deck,
        }

        update = {
            "name": name,
            "real_name": real_name,
            "pegasus_account": blizzard_account,
            "rank": player_meta.get("rank"),
            "legend_rank": player_meta.get("legend_rank"),
            "stars": player_meta.get("stars"),
            "wins": player_meta.get("wins"),
            "losses": player_meta.get("losses"),
            "deck_id": player_meta.get("deck_id") or None,
            "cardback_id": player_meta.get("cardback"),
        }

        defaults.update(update)
        game_player, created = GlobalGamePlayer.objects.get_or_create(
            defaults=defaults, **common)
        log.debug("Prepared player %r (%i) (created=%r)", game_player,
                  game_player.id, created)

        if not created:
            # Go through the update dict and update values on the player
            # This gets us extra data we might not have had when the player was first created
            updated = False
            for k, v in update.items():
                if v and getattr(game_player, k) != v:
                    setattr(game_player, k, v)
                    updated = True

            # Skip updating the deck if we already have a bigger one
            # TODO: We should make deck_list nullable and only create it here
            if game_player.deck_list.size is None or len(
                    decklist) > game_player.deck_list.size:
                # XXX: Maybe we should also check friendly_player_id for good measure
                game_player.deck_list = deck
                updated = True

            if updated:
                log.debug("Saving updated player to the database.")
                game_player.save()

        players[player.player_id] = game_player

    return players