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) )
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) )
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), )
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))