Ejemplo n.º 1
0
    def handle(self, tree: Node) -> Optional[Node]:
        """
        Given a packet from a game, handle it and return a response.

        Parameters:
            tree - A Node representing the root of a tree. Expected to
                   come from an external game.

        Returns:
            A Node representing the root of a response tree, or None if
            we had a problem parsing or generating a response.
        """
        self.log("Received request:\n{}", tree)

        if tree.name != 'call':
            # Invalid request
            self.log("Invalid root node {}", tree.name)
            return None

        if len(tree.children) != 1:
            # Invalid request
            self.log("Invalid number of children for root node")
            return None

        modelstring = tree.attribute('model')
        model = Model.from_modelstring(modelstring)
        pcbid = tree.attribute('srcid')

        # If we are enforcing, bail out if we don't recognize thie ID
        pcb = self.__data.local.machine.get_machine(pcbid)
        if self.__config['server']['enforce_pcbid'] and pcb is None:
            self.log("Unrecognized PCBID {}", pcbid)
            raise UnrecognizedPCBIDException(
                pcbid, modelstring, self.__config['client']['address'])

        # If we don't have a Machine, but we aren't enforcing, we must create it
        if pcb is None:
            pcb = self.__data.local.machine.create_machine(pcbid)

        request = tree.children[0]

        config = copy.copy(self.__config)
        config['machine'] = {
            'pcbid': pcbid,
            'arcade': pcb.arcade,
        }

        # If the machine we looked up is in an arcade, override the global
        # paseli settings with the arcade paseli settings.
        if pcb.arcade is not None:
            arcade = self.__data.local.machine.get_arcade(pcb.arcade)
            if arcade is not None:
                config['paseli']['enabled'] = arcade.data.get_bool(
                    'paseli_enabled')
                config['paseli']['infinite'] = arcade.data.get_bool(
                    'paseli_infinite')
                if arcade.data.get_bool('mask_services_url'):
                    # Mask the address, no matter what the server settings are
                    config['server']['uri'] = None
        # If we don't have a server URI, we should add the default
        if 'uri' not in config['server']:
            config['server']['uri'] = None

        game = Base.create(self.__data, config, model)
        method = request.attribute('method')
        response = None

        # If we are enforcing, make sure the PCBID isn't specified to be
        # game-specific
        if self.__config['server']['enforce_pcbid'] and pcb.game is not None:
            if pcb.game != game.game:
                self.log(
                    "PCBID {} assigned to game {}, but connected from game {}",
                    pcbid, pcb.game, game.game)
                raise UnrecognizedPCBIDException(
                    pcbid, modelstring, self.__config['client']['address'])
            if pcb.version is not None:
                if pcb.version > 0 and pcb.version != game.version:
                    self.log(
                        "PCBID {} assigned to game {} version {}, but connected from game {} version {}",
                        pcbid,
                        pcb.game,
                        pcb.version,
                        game.game,
                        game.version,
                    )
                    raise UnrecognizedPCBIDException(
                        pcbid, modelstring, self.__config['client']['address'])
                if pcb.version < 0 and (-pcb.version) < game.version:
                    self.log(
                        "PCBID {} assigned to game {} maximum version {}, but connected from game {} version {}",
                        pcbid,
                        pcb.game,
                        -pcb.version,
                        game.game,
                        game.version,
                    )
                    raise UnrecognizedPCBIDException(
                        pcbid, modelstring, self.__config['client']['address'])

        # First, try to handle with specific service/method function
        try:
            handler = getattr(game, f'handle_{request.name}_{method}_request')
        except AttributeError:
            handler = None
        if handler is not None:
            response = handler(request)

        if response is None:
            # Now, try to pass it off to a generic service handler
            try:
                handler = getattr(game, f'handle_{request.name}_request')
            except AttributeError:
                handler = None
            if handler is not None:
                response = handler(request)

        if response is None:
            # Unrecognized handler
            self.log(f"Unrecognized service {request.name} method {method}")
            return None

        # Make sure we have a status value if one wasn't provided
        if 'status' not in response.attributes:
            response.set_attribute('status', str(Status.SUCCESS))

        root = Node.void('response')
        root.add_child(response)
        root.set_attribute('dstid', pcbid)

        self.log("Sending response:\n{}", root)

        return root
Ejemplo n.º 2
0
    def handle_cardmng_request(self, request: Node) -> Optional[Node]:
        """
        Handle a request for card management. This is independent of a game's profile handling,
        but still gives the game information as to whether or not a profile exists for a game.
        These methods handle looking up a card, handling binding a profile to a game version,
        returning whether a game profile exists or should be migrated, and creating a new account
        when no account is associated with a card.
        """
        method = request.attribute('method')

        if method == 'inquire':
            # Given a cardid, look up the dataid/refid (same thing in this system).
            # If the card doesn't exist or isn't allowed, return a status specifying this
            # instead of the results of the dataid/refid lookup.
            cardid = request.attribute('cardid')
            modelstring = request.attribute('model')
            userid = self.data.local.user.from_cardid(cardid)

            if userid is None:
                # This user doesn't exist, force system to create new account
                root = Node.void('cardmng')
                root.set_attribute('status', str(Status.NOT_REGISTERED))
                return root

            # Special handling for looking up whether the previous game's profile existed
            bound = self.has_profile(userid)
            expired = False
            if bound is False:
                if modelstring is not None:
                    model = Model.from_modelstring(modelstring)
                    oldgame = Base.create(self.data, self.config, model,
                                          self.model)
                    if oldgame is None:
                        bound = False
                    else:
                        bound = oldgame.has_profile(userid)
                        expired = True

            refid = self.data.local.user.get_refid(self.game, self.version,
                                                   userid)
            paseli_enabled = self.supports_paseli(
            ) and self.config['paseli']['enabled']

            root = Node.void('cardmng')
            root.set_attribute('refid', refid)
            root.set_attribute('dataid', refid)
            root.set_attribute('newflag', '1')  # Always seems to be set to 1
            root.set_attribute(
                'binded', '1' if bound else
                '0')  # Whether we've bound to this version of the game or not
            root.set_attribute(
                'expired', '1' if expired else '0')  # Whether we're expired
            root.set_attribute(
                'ecflag',
                '1' if paseli_enabled else '0')  # Whether to allow paseli
            root.set_attribute('useridflag', '1')
            root.set_attribute('extidflag', '1')
            return root

        elif method == 'authpass':
            # Given a dataid/refid previously found via inquire, verify the pin
            refid = request.attribute('refid')
            pin = request.attribute('pass')
            userid = self.data.local.user.from_refid(self.game, self.version,
                                                     refid)
            if userid is not None:
                valid = self.data.local.user.validate_pin(userid, pin)
            else:
                valid = False
            root = Node.void('cardmng')
            root.set_attribute(
                'status', str(Status.SUCCESS if valid else Status.INVALID_PIN))
            return root

        elif method == 'getrefid':
            # Given a cardid and a pin, register the card with the system and generate a new dataid/refid + extid
            cardid = request.attribute('cardid')
            pin = request.attribute('passwd')
            userid = self.data.local.user.create_account(cardid, pin)
            if userid is None:
                # This user can't be created
                root = Node.void('cardmng')
                root.set_attribute('status', str(Status.NOT_ALLOWED))
                return root

            refid = self.data.local.user.create_refid(self.game, self.version,
                                                      userid)
            root = Node.void('cardmng')
            root.set_attribute('dataid', refid)
            root.set_attribute('refid', refid)
            return root

        elif method == 'bindmodel':
            # Given a refid, bind the user's card to the current version of the game
            refid = request.attribute('refid')
            userid = self.data.local.user.from_refid(self.game, self.version,
                                                     refid)
            self.bind_profile(userid)
            root = Node.void('cardmng')
            root.set_attribute('dataid', refid)
            return root

        elif method == 'getkeepspan':
            # Unclear what this method does, return an arbitrary span
            root = Node.void('cardmng')
            root.set_attribute('keepspan', '30')
            return root

        elif method == 'getdatalist':
            # Unclear what this method does, return a dummy response
            root = Node.void('cardmng')
            return root

        # Invalid method
        return None