Exemplo n.º 1
0
    def enqueue_state(self, lobby: bool = True) -> None:
        """Enqueue `self`'s state to players in the match & lobby."""
        # TODO: hmm this is pretty bad, writes twice

        # send password only to users currently in the match.
        self.chat.enqueue(packets.updateMatch(self, send_pw=True))

        if lobby and (lchan := glob.channels['#lobby']) and lchan.players:
            lchan.enqueue(packets.updateMatch(self, send_pw=False))
Exemplo n.º 2
0
class MatchNotReady(BanchoPacket, type=Packets.OSU_MATCH_NOT_READY):
    async def handle(self, p: Player) -> None:
        if not (m := p.match):
            return

        m.get_slot(p).status = SlotStatus.not_ready
        m.enqueue(packets.updateMatch(m), lobby=False)
Exemplo n.º 3
0
class MatchHasBeatmap(BanchoPacket, type=Packets.OSU_MATCH_HAS_BEATMAP):
    async def handle(self, p: Player) -> None:
        if not (m := p.match):
            return

        m.get_slot(p).status = SlotStatus.not_ready
        m.enqueue(packets.updateMatch(m))
Exemplo n.º 4
0
class MatchNoBeatmap(ClientPacket, type=ClientPacketType.MATCH_NO_BEATMAP):
    async def handle(self, p: Player) -> None:
        if not (m := p.match):
            return

        m.get_slot(p).status = SlotStatus.no_map
        m.enqueue(packets.updateMatch(m))
Exemplo n.º 5
0
class MatchReady(ClientPacket, type=ClientPacketType.MATCH_READY):
    async def handle(self, p: Player) -> None:
        if not (m := p.match):
            return

        m.get_slot(p).status = SlotStatus.ready
        m.enqueue(packets.updateMatch(m))
Exemplo n.º 6
0
async def mp_unlock(p: Player, m: Match, msg: Sequence[str]) -> str:
    """Unlock locked slots in `m`."""
    for slot in m.slots:
        if slot.status == SlotStatus.locked:
            slot.status = SlotStatus.open

    m.enqueue(packets.updateMatch(m))
    return 'All locked slots unlocked.'
Exemplo n.º 7
0
class MatchChangePassword(BanchoPacket,
                          type=Packets.OSU_MATCH_CHANGE_PASSWORD):
    match: osuTypes.match

    async def handle(self, p: Player) -> None:
        if not (m := p.match):
            return

        m.passwd = self.match.passwd
        m.enqueue(packets.updateMatch(m), lobby=False)
Exemplo n.º 8
0
class MatchChangePassword(ClientPacket,
                          type=ClientPacketType.MATCH_CHANGE_PASSWORD):
    passwd: osuTypes.string

    async def handle(self, p: Player) -> None:
        if not (m := p.match):
            return

        m.passwd = self.passwd
        m.enqueue(packets.updateMatch(m), lobby=False)
Exemplo n.º 9
0
async def mp_abort(p: Player, m: Match, msg: Sequence[str]) -> str:
    """Abort an in-progress multiplayer match."""
    if not m.in_progress:
        return 'Abort what?'

    m.unready_players(expected=SlotStatus.playing)

    m.in_progress = False
    m.enqueue(packets.matchAbort())
    m.enqueue(packets.updateMatch(m))
    return 'Match aborted.'
Exemplo n.º 10
0
async def mp_abort(p: Player, m: Match, msg: Sequence[str]) -> str:
    if not m.in_progress:
        return 'Abort what?'

    for s in m.slots:
        if s.status & SlotStatus.playing:
            s.status = SlotStatus.not_ready

    m.in_progress = False
    m.enqueue(packets.updateMatch(m))
    m.enqueue(packets.matchAbort())
    return 'Match aborted.'
Exemplo n.º 11
0
async def mp_teams(p: Player, m: Match, msg: Sequence[str]) -> str:
    if len(msg) != 1 or msg[0] not in ('head-to-head', 'tag-coop',
                                       'team-vs', 'tag-team-vs'):
        return 'Invalid syntax: !mp teams <mode>'

    m.team_type = {
        'head-to-head': MatchTeamTypes.head_to_head,
        'tag-coop': MatchTeamTypes.tag_coop,
        'team-vs': MatchTeamTypes.team_vs,
        'tag-team-vs': MatchTeamTypes.tag_team_vs
    }[msg[0]]

    m.enqueue(packets.updateMatch(m))
    return 'Match team type updated.'
Exemplo n.º 12
0
async def mp_condition(p: Player, m: Match, msg: Sequence[str]) -> str:
    if len(msg) != 1 or msg[0] not in ('score', 'accuracy',
                                       'combo', 'scorev2'):
        return 'Invalid syntax: !mp condition <mode>'

    m.match_scoring = {
        'score': MatchScoringTypes.score,
        'accuracy': MatchScoringTypes.accuracy,
        'combo': MatchScoringTypes.combo,
        'scorev2': MatchScoringTypes.scorev2
    }[msg[0]]

    m.enqueue(packets.updateMatch(m))
    return 'Match win condition updated.'
Exemplo n.º 13
0
class MatchChangeTeam(BanchoPacket, type=Packets.OSU_MATCH_CHANGE_TEAM):
    async def handle(self, p: Player) -> None:
        if not (m := p.match):
            return

        for s in m.slots:
            if p == s.player:
                s.team = Teams.blue if s.team != Teams.blue else Teams.red
                break
        else:
            log(f'{p} tried changing team outside of a match? (2)')
            return

        m.enqueue(packets.updateMatch(m), lobby=False)
Exemplo n.º 14
0
class MatchComplete(BanchoPacket, type=Packets.OSU_MATCH_COMPLETE):
    async def handle(self, p: Player) -> None:
        if not (m := p.match):
            return

        m.get_slot(p).status = SlotStatus.complete

        # check if there are any players that haven't finished.
        if any(s.status == SlotStatus.playing for s in m.slots):
            return

        m.unready_players(expected=SlotStatus.complete)

        m.in_progress = False
        m.enqueue(packets.matchComplete())
        m.enqueue(packets.updateMatch(m))
Exemplo n.º 15
0
class MatchChangeMods(BanchoPacket, type=Packets.OSU_MATCH_CHANGE_MODS):
    mods: osuTypes.i32

    async def handle(self, p: Player) -> None:
        if not (m := p.match):
            return

        if m.freemods:
            if p.id == m.host.id:
                # allow host to set speed-changing mods.
                m.mods = self.mods & Mods.SPEED_CHANGING

            # set slot mods
            m.get_slot(p).mods = self.mods & ~Mods.SPEED_CHANGING
        else:
            # not freemods, set match mods.
            m.mods = self.mods

        m.enqueue(packets.updateMatch(m))
Exemplo n.º 16
0
async def mp_mods(p: Player, m: Match, msg: Sequence[str]) -> str:
    """Set `m`'s mods, from string form."""
    if len(msg) != 1 or not ~len(msg[0]) & 1: # len(msg[0]) % 2 == 0
        return 'Invalid syntax: !mp mods <mods>'

    mods = Mods.from_str(msg[0])

    if m.freemods:
        if p.id == m.host.id:
            # allow host to set speed-changing mods.
            m.mods = mods & Mods.SPEED_CHANGING

        # set slot mods
        m.get_slot(p).mods = mods & ~Mods.SPEED_CHANGING
    else:
        # not freemods, set match mods.
        m.mods = mods

    m.enqueue(packets.updateMatch(m))
    return 'Match mods updated.'
Exemplo n.º 17
0
class MatchLock(ClientPacket, type=ClientPacketType.MATCH_LOCK):
    slot_id: osuTypes.i32

    async def handle(self, p: Player) -> None:
        if not (m := p.match):
            return

        # read new slot ID
        if self.slot_id not in range(16):
            return

        slot = m.slots[self.slot_id]

        if slot.status & SlotStatus.locked:
            slot.status = SlotStatus.open
        else:
            if slot.player:
                slot.reset()
            slot.status = SlotStatus.locked

        m.enqueue(packets.updateMatch(m))
Exemplo n.º 18
0
class MatchLock(BanchoPacket, type=Packets.OSU_MATCH_LOCK):
    slot_id: osuTypes.i32

    async def handle(self, p: Player) -> None:
        if not (m := p.match):
            return

        # read new slot ID
        if not 0 <= self.slot_id < 16:
            return

        slot = m.slots[self.slot_id]

        if slot.status == SlotStatus.locked:
            slot.status = SlotStatus.open
        else:
            if slot.player:
                slot.reset()
            slot.status = SlotStatus.locked

        m.enqueue(packets.updateMatch(m))
Exemplo n.º 19
0
class MatchChangeSlot(ClientPacket, type=ClientPacketType.MATCH_CHANGE_SLOT):
    slot_id: osuTypes.i32

    async def handle(self, p: Player) -> None:
        if not (m := p.match):
            return

        # read new slot ID
        if self.slot_id not in range(16):
            return

        if m.slots[self.slot_id].status & SlotStatus.has_player:
            log(f'{p} tried to switch to slot {self.slot_id} which has a player.'
                )
            return

        # swap with current slot.
        s = m.get_slot(p)
        m.slots[self.slot_id].copy(s)
        s.reset()
        m.enqueue(packets.updateMatch(m))
Exemplo n.º 20
0
async def mp_freemods(p: Player, m: Match, msg: Sequence[str]) -> str:
    if len(msg) != 1 or msg[0] not in ('on', 'off'):
        return 'Invalid syntax: !mp freemods <on/off>'

    if msg[0] == 'on':
        # central mods -> all players mods.
        m.freemods = True

        for s in m.slots:
            if s.status & SlotStatus.has_player:
                s.mods = m.mods & ~Mods.SPEED_CHANGING

        m.mods = m.mods & Mods.SPEED_CHANGING
    else:
        # host mods -> central mods.
        m.freemods = False
        for s in m.slots:
            if s.player and s.player.id == m.host.id:
                m.mods = s.mods | (m.mods & Mods.SPEED_CHANGING)
                break

    m.enqueue(packets.updateMatch(m))
    return 'Match freemod status updated.'
Exemplo n.º 21
0
class MatchChangeSlot(BanchoPacket, type=Packets.OSU_MATCH_CHANGE_SLOT):
    slot_id: osuTypes.i32

    async def handle(self, p: Player) -> None:
        if not (m := p.match):
            return

        # read new slot ID
        if not 0 <= self.slot_id < 16:
            return

        if m.slots[self.slot_id].status & SlotStatus.has_player:
            log(f'{p} tried to move into a slot with another player.')
            return

        if m.slots[self.slot_id].status == SlotStatus.locked:
            log(f'{p} tried to move to into locked slot.')
            return

        # swap with current slot.
        s = m.get_slot(p)
        m.slots[self.slot_id].copy(s)
        s.reset()
        m.enqueue(packets.updateMatch(m))
Exemplo n.º 22
0
            m.chat = glob.channels[f'#multi_{m.id}']

        if not await self.join_channel(m.chat):
            log(f'{self} failed to join {m.chat}.')
            return False

        if (lobby := glob.channels['#lobby']) in self.channels:
            await self.leave_channel(lobby)

        slot = m.slots[0 if slotID == -1 else slotID]

        slot.status = SlotStatus.not_ready
        slot.player = self
        self.match = m
        self.enqueue(packets.matchJoinSuccess(m))
        m.enqueue(packets.updateMatch(m))

        return True

    async def leave_match(self) -> None:
        """Attempt to remove `self` from their match."""
        if not self.match:
            if glob.config.debug:
                log(f"{self} tried leaving a match they're not in?")
            return

        for s in self.match.slots:
            if self == s.player:
                s.reset()
                break
Exemplo n.º 23
0
        return 'Could not find a user by that name.'

    await t.join_match(m)
    return 'Welcome.'


# set the current beatmap (by id).
async def mp_map(p: Player, m: Match, msg: Sequence[str]) -> str:
    if len(msg) < 1 or not msg[0].isdecimal():
        return 'Invalid syntax: !mp map <beatmapid>'

    if not (bmap := await Beatmap.from_bid(int(msg[0]))):
        return 'Beatmap not found.'

    m.bmap = bmap
    m.enqueue(packets.updateMatch(m))
    return f'Map selected: {bmap.embed}.'


_mp_triggers = defaultdict(
    lambda: None, {
        'force': {
            'callback': mp_force,
            'priv': Privileges.Admin
        },
        'abort': {
            'callback': mp_abort,
            'priv': Privileges.Normal
        },
        'start': {
            'callback': mp_start,
Exemplo n.º 24
0
class MatchChangeSettings(BanchoPacket,
                          type=Packets.OSU_MATCH_CHANGE_SETTINGS):
    new: osuTypes.match

    async def handle(self, p: Player) -> None:
        if not (m := p.match):
            return

        if self.new.freemods != m.freemods:
            # freemods status has been changed.
            if self.new.freemods:
                # switching to freemods.
                # central mods -> all players mods.
                for s in m.slots:
                    if s.status & SlotStatus.has_player:
                        s.mods = m.mods & ~Mods.SPEED_CHANGING

                m.mods = m.mods & Mods.SPEED_CHANGING
            else:
                # switching to centralized mods.
                # host mods -> central mods.
                for s in m.slots:
                    if s.player and s.player.id == m.host.id:
                        m.mods = s.mods | (m.mods & Mods.SPEED_CHANGING)
                        break

        if not self.new.bmap:
            # map being changed, unready players.
            m.unready_players(expected=SlotStatus.ready)
        elif not m.bmap:
            # new map has been chosen, send to match chat.
            await m.chat.send(glob.bot,
                              f'Map selected: {self.new.bmap.embed}.')

        # copy basic match info into our match.
        m.bmap = self.new.bmap
        m.freemods = self.new.freemods
        m.mode = self.new.mode

        if m.team_type != self.new.team_type:
            # team type is changing, find the new appropriate default team.
            # if it's head vs. head, the default should be red, otherwise neutral.
            if self.new.team_type in (MatchTeamTypes.head_to_head,
                                      MatchTeamTypes.tag_coop):
                new_t = Teams.red
            else:
                new_t = Teams.neutral

            # change each active slots team to
            # fit the correspoding team type.
            for s in m.slots:
                if s.player:
                    s.team = new_t

            # change the matches'.
            m.team_type = self.new.team_type

        m.match_scoring = self.new.match_scoring
        m.name = self.new.name

        m.enqueue(packets.updateMatch(m))
Exemplo n.º 25
0
    async def handle(self, p: Player) -> None:
        if not (m := p.match):
            return

        # read new slot ID
        if not 0 <= self.slot_id < 16:
            return

        if not (t := m[self.slot_id].player):
            log(f'{p} tried to transfer host to an empty slot?')
            return

        m.host = t
        m.host.enqueue(packets.matchTransferHost())
        m.enqueue(packets.updateMatch(m), lobby=False)


@register
class FriendAdd(BanchoPacket, type=Packets.OSU_FRIEND_ADD):
    user_id: osuTypes.i32

    async def handle(self, p: Player) -> None:
        if not (t := await glob.players.get_by_id(self.user_id)):
            log(f'{p} tried to add a user who is not online! ({self.user_id})')
            return

        if t.id in (1, p.id):
            # trying to add the bot, or themselves.
            # these are already appended to the friends list
            # on login, so disallow the user from *actually*