예제 #1
0
    async def delete_entry(_id: str, db: AioRedis):
        """
        Deletes a rate limiter entry.

        @param host: (string) the hostname of the rate limiter entry to delete
        @param db: (object) db connection
        """
        await asyncio.gather(
            db.delete(_id),
            RateLimiter._clear_indexes(_id, db),
            db.srem(entry_set, _id)
        )
예제 #2
0
    async def delete_rule(_id: str, db: AioRedis):
        """
        Deletes a rate limiter rule.

        @param id: (string) the ID of the rate limiter rule to delete
        @param db: (object) db connection
        """
        await asyncio.gather(
            db.delete(_id),
            RateLimiter._clear_indexes(_id, db),
            db.srem(rules_set, _id)
        )
예제 #3
0
    async def delete(_id: str, db: AioRedis):
        """
        deletes a endpoint cache rule.

        @param id: (string) id of endpoint cache to delete
        @param db: (object) db connection
        """
        await asyncio.gather(
            db.delete(_id),
            EndpointCacher._clear_indexes(_id, db),
            db.srem(endpoint_cache_set, _id),
        )
예제 #4
0
class Redis(KeyValueStore):
    """Implementation for Redis"""

    user_groupid_key = "users:{!s}:groupid"
    user_ready_key = "users:{!s}:ready"
    user_partyid_key = "users:{!s}:partyid"
    group_state_key = "groups:{!s}:state"
    group_members_key = "groups:{!s}:members"
    group_gameid_key = "groups:{!s}:gameid"
    group_slotid_key = "groups:{!s}:slotid"
    group_partyid_key = "groups:{!s}:partyid"
    game_queue_key = "queues:{!s}"
    slot_players_key = "slots:{!s}:players"
    slot_groups_key = "slots:{!s}:groups"
    party_gameid_key = "parties:{!s}:gameid"
    party_slotid_key = "parties:{!s}:slotid"
    party_host_key = "parties:{!s}:host"
    party_ports_key = "parties:{!s}:ports"
    msgqueue_key = "msgqueues:{!s}:{!s}"

    def __init__(self, redis_pool):
        self.redis = AIORedis(redis_pool)

    async def revoke_token(self, token) -> None:
        await self.redis.zremrangebyscore("trl", 0, int(time()))
        await self.redis.zadd("trl", token["exp"], token["jti"])

    async def is_token_revoked(self, token_id) -> bool:
        return await self.redis.zscore("trl", token_id) != None

    async def get_user(self, userid):
        groupid = await self.redis.get(Redis.user_groupid_key.format(userid))
        if groupid is None:
            raise PlayerNotInGroup()
        partyid = await self.redis.get(Redis.user_partyid_key.format(userid))
        ready = await self.redis.get(Redis.user_ready_key.format(userid))

        return UserKVS(UUID(groupid.decode()),
                       partyid and UUID(partyid.decode()),
                       ready == b"1")


    async def create_group(self, userid, gameid):
        user_groupid_key = Redis.user_groupid_key.format(userid)
        groupid = await self.redis.get(user_groupid_key)
        if groupid is not None:
            raise PlayerInGroupAlready()

        if (await RDB.get_game_by_id(gameid)) is None:
            raise GameDoesntExist()

        groupid = uuid4()
        await self.redis.set(user_groupid_key, str(groupid))
        await self.redis.set(Redis.user_ready_key.format(userid), b"0")
        await self.redis.set(
            Redis.group_state_key.format(groupid), State.GROUP_CHECK.value)
        await self.redis.set(
            Redis.group_gameid_key.format(groupid), str(gameid))
        await self.redis.sadd(
            Redis.group_members_key.format(groupid), str(userid))

        return groupid

    async def join_group(self, groupid, userid):
        user_groupid_key = Redis.user_groupid_key.format(userid)
        if (await self.redis.get(user_groupid_key)) is not None:
            raise PlayerInGroupAlready()

        await self.redis.set(Redis.user_ready_key.format(userid), b"0")

        gameid = await self.redis.get(Redis.group_gameid_key.format(groupid))
        if gameid is None:
            raise GroupDoesntExist()

        state = State(await self.redis.get(
            Redis.group_state_key.format(groupid)))
        if state != State.GROUP_CHECK:
            raise WrongGroupState(state, State.GROUP_CHECK)

        game = await RDB.get_game_by_id(int(gameid))
        group_members_key = Redis.group_members_key.format(groupid)
        group_size = await self.redis.scard(group_members_key)
        if group_size + 1 > game.capacity:
            raise GroupIsFull()

        await self.redis.set(user_groupid_key, str(groupid))
        await self.redis.sadd(group_members_key, str(userid))
        await self.redis.set(Redis.user_ready_key.format(userid), b"0")

    async def leave_group(self, userid):
        user_groupid_key = Redis.user_groupid_key.format(userid)
        groupid = await self.redis.get(user_groupid_key)
        if groupid is None:
            raise PlayerNotInGroup()
        groupid = groupid.decode()

        state = State(await self.redis.get(
            Redis.group_state_key.format(groupid)))
        valids = [State.GROUP_CHECK, State.IN_QUEUE]
        if state not in valids:
            raise WrongGroupState(state, valids)
        if state == State.IN_QUEUE:
            await self.leave_queue(groupid)

        await self.redis.delete(user_groupid_key)
        await self.redis.delete(Redis.user_ready_key.format(userid))
        await self.redis.srem(
            Redis.group_members_key.format(groupid), str(userid))

        ensure_future(self.group_cleanup(groupid))

    async def group_cleanup(self, groupid):
        """Remove keys from redis"""
        if (await self.redis.scard(Redis.group_members_key.format(groupid))) == 0:
            await gather(
                self.redis.delete(Redis.group_gameid_key.format(groupid)),
                self.redis.delete(Redis.group_state_key.format(groupid)),
                self.redis.delete(Redis.group_slotid_key.format(groupid)),
                self.redis.delete(Redis.group_partyid_key.format(groupid))
            )

    async def get_group(self, groupid):
        state = await self.redis.get(Redis.group_state_key.format(groupid))
        if state is None:
            raise GroupDoesntExist()
        gameid = await self.redis.get(Redis.group_gameid_key.format(groupid))
        slotid = await self.redis.get(Redis.group_slotid_key.format(groupid))
        partyid = await self.redis.get(Redis.group_partyid_key.format(groupid))
        members = await self.redis.smembers(Redis.group_members_key.format(groupid))

        return Group(State(state),
                     [UUID(userid.decode()) for userid in members],
                     int(gameid),
                     slotid and UUID(slotid.decode()),
                     partyid and UUID(partyid.decode()))

    async def mark_as_ready(self, userid):
        user_groupid_key = Redis.user_groupid_key.format(userid)
        groupid = await self.redis.get(user_groupid_key)
        if groupid is None:
            raise PlayerNotInGroup()
        groupid = groupid.decode()

        state = State(await self.redis.get(
            Redis.group_state_key.format(groupid)))
        valids = [State.GROUP_CHECK, State.PARTY_CHECK]
        if state not in valids:
            raise WrongGroupState(state, valids)

        await self.redis.set(Redis.user_ready_key.format(userid), b"1")

    async def mark_as_not_ready(self, userid):
        user_groupid_key = Redis.user_groupid_key.format(userid)
        groupid = await self.redis.get(user_groupid_key)
        if groupid is None:
            raise PlayerNotInGroup()
        groupid = groupid.decode()

        state = State(await self.redis.get(
            Redis.group_state_key.format(groupid)))
        valids = [State.GROUP_CHECK, State.IN_QUEUE, State.PARTY_CHECK]
        if state not in valids:
            raise WrongGroupState(state, valids)
        if state == State.IN_QUEUE:
            await self.leave_queue(groupid)

        await self.redis.set(Redis.user_ready_key.format(userid), b"0")

    async def is_user_ready(self, userid):
        return (await self.redis.get(Redis.user_ready_key.format(userid))) == b"1"

    async def is_group_ready(self, groupid):
        """Check the readiness of each member of a group"""
        return all(await gather(*[
            self.is_user_ready(userid.decode()) for userid in
            await self.redis.smembers(Redis.group_members_key.format(groupid))]))


    async def join_queue(self, groupid):
        gameid = await self.redis.get(Redis.group_gameid_key.format(groupid))
        if gameid is None:
            raise GroupDoesntExist()
        gameid = int(gameid)

        group_state_key = Redis.group_state_key.format(groupid)
        state = State(await self.redis.get(group_state_key))
        if state != State.GROUP_CHECK:
            raise WrongGroupState(state, State.GROUP_CHECK)
        if not await self.is_group_ready(groupid):
            raise GroupNotReady()
        await self.redis.set(group_state_key, State.IN_QUEUE.value)

        game = await RDB.get_game_by_id(gameid)
        group_members_key = Redis.group_members_key.format(groupid)
        group_members = await self.redis.smembers(group_members_key)

        game_queue_key = Redis.game_queue_key.format(gameid)
        slotids = await self.redis.lrange(
            game_queue_key, 0, await self.redis.llen(game_queue_key))
        for slotid in [UUID(slotid.decode()) for slotid in slotids]:
            slot_players_key = Redis.slot_players_key.format(slotid)
            slot_size = await self.redis.scard(slot_players_key)
            if slot_size + len(group_members) <= game.capacity:
                await self.redis.set(
                    Redis.group_slotid_key.format(groupid), str(slotid))
                await self.redis.sunionstore(
                    slot_players_key, slot_players_key, group_members_key)
                await self.redis.sadd(
                    Redis.slot_groups_key.format(slotid), str(groupid))

                if slot_size + len(group_members) == game.capacity:
                    ensure_future(self.start_game(gameid, slotid))
                break
        else:
            slotid = uuid4()
            await self.redis.set(
                Redis.group_slotid_key.format(groupid), str(slotid))
            await self.redis.sunionstore(
                Redis.slot_players_key.format(slotid), group_members_key)
            await self.redis.sadd(Redis.slot_groups_key.format(slotid), str(groupid))
            await self.redis.rpush(
                Redis.game_queue_key.format(gameid), str(slotid))
            if len(group_members) == game.capacity:
                ensure_future(self.start_game(gameid, slotid))

    async def leave_queue(self, groupid):
        gameid = await self.redis.get(Redis.group_gameid_key.format(groupid))
        if gameid is None:
            raise GroupDoesntExist()
        gameid = int(gameid)

        group_state_key = Redis.group_state_key.format(groupid)
        state = State(await self.redis.get(group_state_key))
        if state != State.IN_QUEUE:
            raise WrongGroupState(state, State.IN_QUEUE)

        group_slotid_key = Redis.group_slotid_key.format(groupid)
        slotid = (await self.redis.get(group_slotid_key)).decode()
        slot_players_key = Redis.slot_players_key.format(slotid)
        await self.redis.srem(Redis.slot_groups_key.format(slotid), str(groupid))
        await self.redis.sdiffstore(
            slot_players_key, slot_players_key, Redis.group_members_key.format(groupid))
        if (await self.redis.scard(slot_players_key)) == 0:
            self.redis.lrem(Redis.game_queue_key.format(gameid), 1, slotid)
        await self.redis.set(group_state_key, State.GROUP_CHECK.value)

    async def start_game(self, gameid, slotid):
        partyid = str(uuid4())
        payload = {"type": "game:starting", "partyid": str(partyid)}

        groups = await self.redis.smembers(Redis.slot_groups_key.format(slotid))
        for groupid in map(methodcaller("decode"), groups):
            await MSG.send_message(MsgQueueType.GROUP, groupid, payload)
            await self.redis.set(
                Redis.group_state_key.format(groupid), State.PLAYING.value)
            await self.redis.set(
                Redis.group_partyid_key.format(groupid), partyid)

        users = await self.redis.smembers(Redis.slot_players_key.format(slotid))
        for userid in map(methodcaller("decode"), users):
            await self.redis.set(
                Redis.user_partyid_key.format(userid), partyid)

        await self.redis.lrem(Redis.game_queue_key.format(gameid), 1, str(slotid))

        game = await RDB.get_game_by_id(gameid)
        party = Party(gameid, slotid, config.webapi.GAME_HOST,
                      sorted(sample(range(config.webapi.GAME_PORT_RANGE_START,
                                          config.webapi.GAME_PORT_RANGE_STOP),
                                    len(game.ports))))
        await self.redis.set(
            Redis.party_gameid_key.format(partyid), str(party.gameid))
        await self.redis.set(
            Redis.party_slotid_key.format(partyid), str(party.slotid))
        await self.redis.set(
            Redis.party_host_key.format(partyid), party.host)
        await self.redis.sadd(
            Redis.party_ports_key.format(partyid), *party.ports)

        ensure_future(CTR.launch_game(gameid, game, partyid, party))

    async def get_party(self, partyid):
        gameid = await self.redis.get(Redis.party_gameid_key.format(partyid))
        if gameid is None:
            raise PartyDoesntExist()
        slotid = await self.redis.get(Redis.party_slotid_key.format(partyid))
        host = await self.redis.get(Redis.party_host_key.format(partyid))
        ports = await self.redis.get(Redis.party_ports_key.format(partyid))

        return Party(gameid=UUID(gameid.decode()),
                     slotid=UUID(slotid.decode()),
                     host=host.decode(),
                     ports=list(map(int, ports)))

    async def end_game(self, partyid):
        slotid = await self.redis.get(Redis.party_slotid_key.format(partyid))
        slotid = UUID(slotid.decode())

        groups = await self.redis.smembers(Redis.slot_groups_key.format(slotid))
        for groupid in map(methodcaller("decode"), groups):
            await self.redis.set(
                Redis.group_state_key.format(groupid), State.GROUP_CHECK.value)
            await self.redis.delete(Redis.group_partyid_key.format(groupid))

        users = await self.redis.smembers(Redis.slot_players_key.format(slotid))
        for userid in map(methodcaller("decode"), users):
            await self.redis.delete(Redis.user_partyid_key.format(userid))

        await self.redis.delete(Redis.party_gameid_key.format(partyid))
        await self.redis.delete(Redis.party_slotid_key.format(partyid))
        await self.redis.delete(Redis.party_host_key.format(partyid))
        await self.redis.delete(Redis.party_ports_key.format(partyid))
        await self.redis.delete(Redis.slot_players_key.format(slotid))
        await self.redis.delete(Redis.slot_groups_key.format(slotid))