コード例 #1
0
    def get(self, player_id, namespace):
        """
        Get full dump of game state for the current player in namespace 'namespace'
        """
        can_edit_player(player_id)

        gamestates = g.db.query(GameState) \
                         .filter(GameState.player_id == player_id,
                                 GameState.namespace == namespace) \
                         .order_by(-GameState.gamestate_id)
        if gamestates.count() == 0:
            msg = "Gamestate '%s' for player %s not found" % (namespace,
                                                              player_id)
            log.info(msg)
            abort(httplib.NOT_FOUND)

        elif gamestates.count() > 1:
            raise RuntimeError(
                "Player %s has %s game states with namespace '%s'" %
                (player_id, gamestates.count(), namespace))

        gamestate = gamestates.first()
        ret = gamestate.as_dict()
        ret["gamestatehistory_url"] = url_for("gamestate.gamestatehistorylist",
                                              player_id=player_id,
                                              namespace=namespace,
                                              _external=True)
        return ret
コード例 #2
0
    def get(self, player_id):
        """
        Get a list of all gamestates for the player
        """
        can_edit_player(player_id)

        gamestates = g.db.query(GameState) \
                         .filter(GameState.player_id == player_id) \
                         .order_by(GameState.namespace)

        ret = []
        for gamestate in gamestates:
            entry = {
                "namespace":
                gamestate.namespace,
                "gamestate_id":
                gamestate.gamestate_id,
                "gamestate_url":
                url_for("gamestate.gamestate",
                        player_id=player_id,
                        namespace=gamestate.namespace,
                        _external=True)
            }
            ret.append(entry)

        return ret
コード例 #3
0
    def get(self, player_id, namespace):
        can_edit_player(player_id)

        rows = g.db.query(GameStateHistory) \
                   .filter(GameStateHistory.player_id == player_id,
                           GameStateHistory.namespace == namespace) \
                   .order_by(-GameStateHistory.gamestatehistory_id)
        if not rows:
            abort(httplib.NOT_FOUND)
        ret = []
        for row in rows:
            entry = {
                "gamestatehistory_id":
                row.gamestatehistory_id,
                "gamestatehistoryentry_url":
                url_for("gamestate.gamestatehistoryentry",
                        player_id=player_id,
                        namespace=namespace,
                        gamestatehistory_id=row.gamestatehistory_id,
                        _external=True),
                "create_date":
                row.create_date
            }
            ret.append(entry)
        return ret
コード例 #4
0
 def get(self, player_id):
     """
     Get a list of outstanding tickets for the player
     """
     can_edit_player(player_id)
     tickets = g.db.query(Ticket) \
              .filter(Ticket.player_id == player_id, Ticket.used_date == None)
     ret = [add_ticket_links(t) for t in tickets]
     return ret
コード例 #5
0
    def get(self, player_id, journal_id):
        """
        Get a specific journal entry for the player
        """
        can_edit_player(player_id)

        entry = get_journal_entry(player_id, journal_id)
        if not entry.first():
            return json_response("Journal entry not found", httplib.NOT_FOUND)
        ret = entry.first().as_dict()
        return ret
コード例 #6
0
    def get(self, player_id, namespace, gamestatehistory_id):
        can_edit_player(player_id)

        row_gamestate = g.db.query(GameStateHistory)\
                            .filter(GameStateHistory.player_id == player_id,
                                    GameStateHistory.gamestatehistory_id == gamestatehistory_id) \
                            .first()
        if not row_gamestate:
            abort(httplib.NOT_FOUND)
        ret = row_gamestate.as_dict()
        return ret
コード例 #7
0
    def get(self, player_id):
        """
        """
        can_edit_player(player_id)
        if not get_player(player_id):
            abort(httplib.NOT_FOUND)
        summary = g.db.query(PlayerSummary).filter(
            PlayerSummary.player_id == player_id)
        ret = {}
        for row in summary:
            ret[row.name] = row.value

        return ret
コード例 #8
0
    def _patch(self, player_id, ticket_id):
        """
        Claim a ticket
        """
        args = request.json
        journal_id = args.get("journal_id")

        if not can_edit_player(player_id):
            abort(httplib.METHOD_NOT_ALLOWED,
                  message="That is not your player!")

        ticket = get_ticket(player_id, ticket_id)
        if not ticket:
            abort(404, message="Ticket was not found")

        if ticket.used_date:
            abort(404, message="Ticket has already been claimed")

        log.info(
            "Player %s is claiming ticket %s of type '%s' with journal_id %s",
            player_id, ticket_id, ticket.ticket_type, journal_id)

        ticket.used_date = datetime.datetime.utcnow()
        ticket.journal_id = journal_id
        g.db.commit()

        log_event(player_id, "event.player.ticketclaimed",
                  {"ticket_id": ticket_id})

        return add_ticket_links(ticket)
コード例 #9
0
    def delete(self, player_id, namespace):
        """
        Remove a gamestate from the player (it will still exist in the history table)
        """
        can_edit_player(player_id)
        gamestates = g.db.query(GameState) \
                         .filter(GameState.player_id == player_id,
                                 GameState.namespace == namespace)

        gamestates.delete()
        g.db.commit()

        log.info("Gamestate '%s' for player %s has been deleted", namespace,
                 player_id)

        return "OK"
コード例 #10
0
ファイル: endpoints.py プロジェクト: 1939Games/drift-base
    def get(self, player_id, ticket_id):
        """
        Get information about any past or ongoing battle initiated by
        the current player against the other player
        """
        if not can_edit_player(player_id):
            abort(httplib.METHOD_NOT_ALLOWED, message="That is not your player!")

        ticket = get_ticket(player_id, ticket_id)
        if not ticket:
            abort(404, message="Ticket was not found")
        return add_ticket_links(ticket)
コード例 #11
0
    def get(self, player_id):
        """
        Get a list of recent journal entries for the player
        """
        DEFAULT_ROWS = 100
        args = self.get_args.parse_args()
        can_edit_player(player_id)

        # TODO: Custom filters
        query = g.db.query(PlayerJournal)
        query = query.filter(PlayerJournal.player_id == player_id)
        if not getattr(args, "include_deleted", False):
            query = query.filter(PlayerJournal.deleted == False)
        query = query.order_by(-PlayerJournal.journal_id, -PlayerJournal.sequence_id)
        query = query.limit(args.rows or DEFAULT_ROWS)

        ret = []
        for entry in query:
            e = entry.as_dict()
            ret.append(e)
        return ret
コード例 #12
0
    def put(self, player_id):
        """
        Full update of summary fields, deletes fields from db that are not included
        """
        if not can_edit_player(player_id):
            abort(httplib.METHOD_NOT_ALLOWED,
                  message="That is not your player!")

        if not get_player(player_id):
            abort(httplib.NOT_FOUND)

        old_summary = g.db.query(PlayerSummary).filter(
            PlayerSummary.player_id == player_id).all()

        new_summary = []
        updated_ids = set()
        for name, val in request.json.iteritems():
            for row in old_summary:
                if row.name == name:
                    updated_ids.add(row.id)
                    if val != row.value:
                        row.value = val
                    break
            else:
                log.info("Adding a new summary field, '%s' with value '%s'",
                         name, val)
                summary_row = PlayerSummary(player_id=player_id,
                                            name=name,
                                            value=val)
                g.db.add(summary_row)
                g.db.flush()
                updated_ids.add(summary_row.id)
        g.db.commit()

        for row in old_summary:
            if row.id not in updated_ids:
                log.info(
                    "Deleting summary field '%s' with id %s which had the value '%s'",
                    row.name, row.id, row.value)
                g.db.delete(row)
        g.db.commit()

        new_summary = g.db.query(PlayerSummary).filter(
            PlayerSummary.player_id == player_id).all()

        request_txt = ""
        for k, v in request.json.iteritems():
            request_txt += "%s = %s, " % (k, v)
        if request_txt:
            request_txt = request_txt[:-2]

        new_summary_txt = ""
        for row in new_summary:
            new_summary_txt += "%s = %s, " % (row.name, row.value)
        if new_summary_txt:
            new_summary_txt = new_summary_txt[:-2]
        log.info(
            "Updating summary for player %s. Request is '%s'. New summary is '%s'",
            player_id, request_txt, new_summary_txt)

        ret = []
        return ret
コード例 #13
0
    def patch(self, player_id):
        """
        Partial update of summary fields.
        """
        if not can_edit_player(player_id):
            abort(httplib.METHOD_NOT_ALLOWED,
                  message="That is not your player!")

        if not get_player(player_id):
            abort(httplib.NOT_FOUND)
        old_summary = g.db.query(PlayerSummary).filter(
            PlayerSummary.player_id == player_id).all()
        old_summary_txt = ""
        for row in old_summary:
            old_summary_txt += "%s = %s, " % (row.name, row.value)

        changes = {}
        for name, val in request.json.iteritems():
            for row in old_summary:
                if row.name == name:
                    if val != row.value:
                        changes[name] = {"old": row.value, "new": val}
                        row.value = val
                    break
            else:
                log.info("Adding a new summary field, '%s' with value '%s'",
                         name, val)
                changes[name] = {"old": None, "new": val}
                summary_row = PlayerSummary(player_id=player_id,
                                            name=name,
                                            value=val)
                g.db.add(summary_row)

            # if this summary stat changes we write it into our history log
            if name in changes:
                summaryhistory_row = PlayerSummaryHistory(player_id=player_id,
                                                          name=name,
                                                          value=val)
                g.db.add(summaryhistory_row)

            g.db.commit()

        new_summary = g.db.query(PlayerSummary).filter(
            PlayerSummary.player_id == player_id).all()

        log_event(player_id, "event.player.summarychanged", changes)
        request_txt = ""
        for k, v in request.json.iteritems():
            request_txt += "%s = %s, " % (k, v)
        if request_txt:
            request_txt = request_txt[:-2]

        if old_summary_txt:
            old_summary_txt = old_summary_txt[:-2]
        new_summary_txt = ""
        for row in new_summary:
            new_summary_txt += "%s = %s, " % (row.name, row.value)
        if new_summary_txt:
            new_summary_txt = new_summary_txt[:-2]
        log.info(
            "Updating summary. Request is '%s'. Old summary is '%s'. New summary is '%s'",
            request_txt, old_summary_txt, new_summary_txt)

        return [r.as_dict() for r in new_summary]
コード例 #14
0
    def post(self, player_id):
        """
        Add a journal entry
        """
        if not can_edit_player(player_id):
            abort(httplib.METHOD_NOT_ALLOWED, message="That is not your player!")

        args_list = request.json
        if not isinstance(args_list, list):
            raise RuntimeError("Arguments should be a list")
        for a in args_list:
            if "journal_id" not in a:
                abort(httplib.BAD_REQUEST)
        ret = []
        now = datetime.datetime.utcnow()
        MAX_DRIFT = 60
        args_list.sort(key=itemgetter('journal_id'))
        client_current_time = args_list[0].get("client_current_time")
        if not client_current_time:
            log.warning("Client is uploading journal entries without a client_current_time")
        else:
            client_current_time = parser.parse(client_current_time)
            diff = (client_current_time.replace(tzinfo=None) - now).total_seconds()
            if abs(diff) > MAX_DRIFT:
                log.warning("Client's clock is %.0f seconds out of sync. "
                            "Client system time: '%s', Server time: '%s'",
                            diff, client_current_time, now)
        for args in args_list:
            # Special handling if this is a rollback event.
            # We mark all journal entries higher than the event to rollback to
            # as deleted (as an optimization) and then add the rollback event itself.
            if "rollback_to_journal_id" in args:
                to_journal_id = int(args.get("rollback_to_journal_id"))
                # TODO: Check if there are any gamestates persisted after the id
                gamestate = get_player_gamestate(player_id)
                if not gamestate:
                    log.warning("Player is rebasing journal entries but doesn't"
                                "have any gamestate.")

                elif gamestate.journal_id > to_journal_id:
                    return json_response("Journal has already been persisted into home base!",
                                         httplib.BAD_REQUEST)

                entry = get_journal_entry(player_id, to_journal_id)
                if not entry.first():
                    log.warning("Rolling back to journal entry %s which doesn't exist")
                elif entry.first().deleted:
                    log.warning("Rolling back to journal entry %s which has been rolled back")

                g.db.query(PlayerJournal).filter(PlayerJournal.player_id == player_id,
                                                 PlayerJournal.journal_id > to_journal_id) \
                                         .update({"deleted": True})

            # report if the client's clock is out of sync with the server
            timestamp = parser.parse(args["timestamp"])
            diff = (timestamp.replace(tzinfo=None) - now).total_seconds()
            if abs(diff) > MAX_DRIFT:
                log.info("Client is sending journal info for journal entry %s '%s' which "
                         "is %.0f seconds out of sync. "
                         "Client journal timestamp: '%s', Server timestamp: '%s'",
                         args["journal_id"], args["action"], diff, args["timestamp"], now)

            try:
                journal = write_journal(player_id, args["action"], args["journal_id"],
                                        args["timestamp"],
                                        details=args.get("details"), steps=args.get("steps"),
                                        actor_id=current_user["player_id"])
            except JournalError as e:
                # TODO: We now reject the journal entry and subsequent entries instead of
                # rolling the entire thing back. Is that what we want?
                log.warning("Error writing to journal. Rejecting entry. Error was: %s", e)
                abort(httplib.BAD_REQUEST, description=e.message)

            ret.append({"journal_id": journal["journal_id"],
                        "url": url_for("journal.entry",
                                       player_id=player_id,
                                       journal_id=journal["journal_id"])
                        })
        return ret, httplib.CREATED
コード例 #15
0
    def put(self, player_id, namespace):
        """
        Upload the gamestate state to the server
        """
        can_edit_player(player_id)

        args = request.json
        data = args["gamestate"]
        journal_id = None
        if args.get("journal_id"):
            journal_id = int(args["journal_id"])

        if journal_id:
            journal_row = g.db.query(PlayerJournal) \
                .filter(PlayerJournal.player_id == player_id,
                        PlayerJournal.journal_id == journal_id,
                        PlayerJournal.deleted != True) \
                .first()
            if not journal_row:
                # Note: this might happen normally unless we serialize on the
                # client to ensure all journal entries
                # are acked before sending up the new state
                msg = "Journal entry %s for player %s not found!" % (
                    journal_id, player_id)
                log.warning(msg)
                return json_response(msg, httplib.BAD_REQUEST)

        gamestate = g.db.query(GameState)\
            .filter(GameState.player_id == player_id, GameState.namespace == namespace) \
            .order_by(-GameState.gamestate_id).first()

        if gamestate:
            if journal_id and journal_id <= gamestate.journal_id:
                # TODO: Raise here?
                log.warning(
                    "Writing a new gamestate with an older journal_id, %s "
                    "than the current one",
                    journal_id,
                    extra=gamestate.as_dict())
            gamestate.version += 1
            gamestate.data = data
            gamestate.journal_id = journal_id
            log.info(
                "Updated gamestate for player %s to version %s. journal_id = %s",
                player_id, gamestate.version, journal_id)
        else:
            gamestate = GameState(player_id=player_id,
                                  data=data,
                                  journal_id=journal_id,
                                  namespace=namespace)
            g.db.add(gamestate)
            g.db.flush()
            log.info("Added new gamestate for player")

        # write new gamestate to the history table for safe keeping
        gamestatehistory_row = GameStateHistory(
            player_id=player_id,
            version=gamestate.version,
            data=gamestate.data,
            namespace=gamestate.namespace,
            journal_id=gamestate.journal_id)
        g.db.add(gamestatehistory_row)
        g.db.flush()

        gamestatehistory_id = gamestatehistory_row.gamestatehistory_id
        gamestate.gamestatehistory_id = gamestatehistory_id
        g.db.commit()

        return gamestate.as_dict()