Пример #1
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.'
Пример #2
0
async def mp_start(p: Player, m: Match, msg: Sequence[str]) -> str:
    for s in m.slots:
        if s.status & SlotStatus.has_player \
        and not s.status & SlotStatus.no_map:
            s.status = SlotStatus.playing

    m.in_progress = True
    m.enqueue(await packets.matchStart(m))
    return 'Good luck!'
Пример #3
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(await packets.updateMatch(m))
    m.enqueue(await packets.matchAbort())
    return 'Match aborted.'
Пример #4
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.'
Пример #5
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.'
Пример #6
0
    def join_match(self, m: Match, passwd: str) -> bool:
        if self.match:
            printlog(f'{self} tried to join multiple matches?')
            self.enqueue(packets.matchJoinFail(m))
            return False

        if m.chat:  # Match already exists, we're simply joining.
            if passwd != m.passwd:  # eff: could add to if? or self.create_m..
                printlog(f'{self} tried to join {m} with incorrect passwd.')
                self.enqueue(packets.matchJoinFail(m))
                return False
            if (slotID := m.get_free()) is None:
                printlog(f'{self} tried to join a full match.')
                self.enqueue(packets.matchJoinFail(m))
                return False
Пример #7
0
    async def add(self, m: Match) -> None:
        if m in self.matches:
            await plog(f'{m} already in matches list!')
            return

        if (free := self.get_free()) is not None:
            m.id = free
            await plog(f'Adding {m} to matches list.')
            self.matches[free] = m
Пример #8
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.'
Пример #9
0
    def append(self, m: Match) -> bool:
        """Append `m` to the list."""
        if (free := self.get_free()) is not None:
            # set the id of the match to the lowest available free.
            m.id = free
            self[free] = m

            if glob.app.debug:
                log(f'{m} added to matches list.')

            return True
Пример #10
0
    async def add(self, m: Match) -> None:
        if m in self.matches:
            plog(f'{m} double-added to matches list?')
            return

        if (free := self.get_free()) is not None:
            # set the id of the match
            # to our free slot found.
            m.id = free
            self.matches[free] = m

            if glob.config.debug:
                plog(f'{m} added to matches list.')
Пример #11
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.'
Пример #12
0
async def mp_abort(_, __, match: Match) -> str:
    """Abort current multiplayer session"""
    if not match.in_prog:
        return

    match.unready_players(wanted=slotStatus.playing)
    match.in_prog = False

    match.enqueue(writer.matchAbort())
    match.enqueue_state()

    return 'Match aborted.'
Пример #13
0
async def mp_mods(_, args: list, match: Match) -> str:
    """Set the mods of the lobby"""
    if len(args) < 1:
        return 'You must provide the mods to set!'

    if args[0].isdecimal():
        mods = Mods(args[0])
    elif isinstance(args[0], str):
        mods = Mods.convert_str(args[0])
    else:
        return 'Invalid mods.'

    if match.fm:
        match.mods = mods & Mods.SPEED_MODS

        for slot in match.slots:
            if slot.status & slotStatus.has_player:
                slot.mods = mods & ~Mods.SPEED_MODS
    else:
        match.mods = match.mods = mods

    match.enqueue_state()
    return 'Updated mods.'
Пример #14
0
async def mp_start(_, args: list, match: Match) -> Optional[str]:
    """Starts the current match, either forcefully or on a timer"""
    if len(args) < 1:
        return "Please provide either a timer to start or cancel/force"

    if not args[0]:  # start now
        if any([s.status == slotStatus.not_ready for s in match.slots]):
            return (
                "Not all players are ready. You can force start with `!mp start force`"
            )

    elif args[0] == "force":
        match.start()
        match.chat.send(glob.bot, "Starting match. Have fun!", send_self=False)

    elif args[0].isdecimal():

        def start_timer():
            match.start()
            match.chat.send(glob.bot,
                            "Starting match. Have fun!",
                            send_self=False)

        def alert_timer(remaining):
            match.chat.send(
                glob.bot,
                f"Starting match in {remaining} seconds!",
                send_self=False,
            )

        loop = asyncio.get_event_loop()
        timer = int(args[0])
        match.start_task = loop.call_later(timer, start_timer)
        match.alert_tasks = [
            loop.call_later(
                timer - countdown,
                lambda countdown=countdown: alert_timer(countdown),
            ) for countdown in (30, 15, 5, 4, 3, 2, 1) if countdown < timer
        ]

        return f"Starting match in {timer} seconds"

    elif args[0] == "cancel":
        if not match.start_task:
            return

        match.start_task.cancel()
        for alert in match.alert_tasks:
            alert.cancel()

        match.start_task = None
        match.alert_tasks = None

        return "Cancelled timer."

    else:
        return "Unknown argument. Please use seconds/force/cancel"
Пример #15
0
async def mp_fm(user: Player, args: list, match: Match) -> str:
    if len(args) < 1:
        return 'Please provide whether to turn freemod on or off!'

    if args[0] == 'on':
        match.fm = True

        for slot in match.slots:
            if slot.status & slotStatus.has_player:
                slot.mods = match.mods & ~Mods.SPEED_MODS

        match.mods &= Mods.SPEED_MODS
    else:
        match.fm = False

        match.mods &= Mods.SPEED_MODS
        match.mods |= (match.get_slot(user)).mods

        for slot in match.slots:
            if slot.status & slotStatus.has_player:
                slot.mods = Mods.NOMOD

    match.enqueue_state()
    return 'Freemod state toggled.'
Пример #16
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.'
Пример #17
0
async def read_match(data: bytearray) -> Tuple[Match, int]:
    """ Read an osu! match from `data`. """
    m = Match()

    # Ignore match id (i32) & inprogress (i8).
    offset = 3

    # Read type & match mods.
    m.type, m.mods = struct.unpack('<bI', data[offset:offset + 5])
    offset += 5

    # Read match name & password.
    m.name, offs = await read_string(data[offset:])
    offset += offs
    m.passwd, offs = await read_string(data[offset:])
    offset += offs

    # Ignore map's name.
    offset += 1
    if data[offset - 1] == 0x0b:
        offset += sum(await read_uleb128(data[offset:]))

    # Read beatmap information (id & md5).
    map_id = int.from_bytes(data[offset:offset + 4], 'little')
    offset += 4

    map_md5, offs = await read_string(data[offset:])
    offset += offs

    # Get beatmap object for map selected.
    m.bmap = await Beatmap.from_md5(map_md5)
    if not m.bmap and map_id != (1 << 32) - 1:
        # If they pick an unsubmitted map,
        # just give them Vivid [Insane] lol.
        vivid_md5 = '1cf5b2c2edfafd055536d2cefcb89c0e'
        m.bmap = await Beatmap.from_md5(vivid_md5)

    # Read slot statuses.
    for s in m.slots:
        s.status = data[offset]
        offset += 1

    # Read slot teams.
    for s in m.slots:
        s.team = data[offset]
        offset += 1

    for s in m.slots:
        if s.status & SlotStatus.has_player:
            # Dont think we need this?
            offset += 4

    # Read match host.
    user_id = int.from_bytes(data[offset:offset + 4], 'little')
    m.host = await glob.players.get_by_id(user_id)
    offset += 4

    # Read match mode, match scoring,
    # team type, and freemods.
    m.game_mode = data[offset]
    offset += 1
    m.match_scoring = data[offset]
    offset += 1
    m.team_type = data[offset]
    offset += 1
    m.freemods = data[offset]
    offset += 1

    # If we're in freemods mode,
    # read individual slot mods.
    if m.freemods:
        for s in m.slots:
            s.mods = int.from_bytes(data[offset:offset + 4], 'little')
            offset += 4

    # Read the seed from multi.
    # XXX: Used for mania random mod.
    m.seed = int.from_bytes(data[offset:offset + 4], 'little')
    return m, offset + 4
Пример #18
0
    async def read_match(self) -> Match:
        """Read an osu! match from the internal buffer."""
        m = Match()

        # ignore match id (i16) and inprogress (i8).
        self._buf = self._buf[3:]

        m.type = MatchTypes(await self.read_i8())
        m.mods = Mods(await self.read_i32())

        m.name = await self.read_string()
        m.passwd = await self.read_string()

        # ignore the map's name, we're going
        # to get all it's info from the md5.
        await self.read_string()

        map_id = await self.read_i32()
        map_md5 = await self.read_string()

        m.bmap = await Beatmap.from_md5(map_md5)
        if not m.bmap and map_id != (1 << 32) - 1:
            # if they pick an unsubmitted map,
            # just give them vivid [insane] lol.
            vivid_md5 = '1cf5b2c2edfafd055536d2cefcb89c0e'
            m.bmap = await Beatmap.from_md5(vivid_md5)

        for slot in m.slots:
            slot.status = await self.read_i8()

        for slot in m.slots:
            slot.team = Teams(await self.read_i8())

        for slot in m.slots:
            if slot.status & SlotStatus.has_player:
                # we don't need this, ignore it.
                self._buf = self._buf[4:]

        host_id = await self.read_i32()
        m.host = await glob.players.get_by_id(host_id)

        m.mode = GameMode(await self.read_i8())
        m.match_scoring = MatchScoringTypes(await self.read_i8())
        m.team_type = MatchTeamTypes(await self.read_i8())
        m.freemods = await self.read_i8() == 1

        # if we're in freemods mode,
        # read individual slot mods.
        if m.freemods:
            for slot in m.slots:
                slot.mods = Mods(await self.read_i32())

        # read the seed (used for mania)
        m.seed = await self.read_i32()

        return m
Пример #19
0
    def read_match(self) -> Match:
        """Read an osu! match from the internal buffer."""
        m = Match()

        # ignore match id (i16) and inprogress (i8).
        self.body_view = self.body_view[3:]

        self.read_i8()  # powerplay unused

        m.mods = Mods(self.read_i32())

        m.name = self.read_string()
        m.passwd = self.read_string()

        m.map_name = self.read_string()
        m.map_id = self.read_i32()
        m.map_md5 = self.read_string()

        for slot in m.slots:
            slot.status = SlotStatus(self.read_i8())

        for slot in m.slots:
            slot.team = MatchTeams(self.read_i8())

        for slot in m.slots:
            if slot.status & SlotStatus.has_player:
                # we don't need this, ignore it.
                self.body_view = self.body_view[4:]

        host_id = self.read_i32()
        m.host = glob.players.get(id=host_id)

        m.mode = GameMode(self.read_i8())
        m.win_condition = MatchWinConditions(self.read_i8())
        m.team_type = MatchTeamTypes(self.read_i8())
        m.freemods = self.read_i8() == 1

        # if we're in freemods mode,
        # read individual slot mods.
        if m.freemods:
            for slot in m.slots:
                slot.mods = Mods(self.read_i32())

        # read the seed (used for mania)
        m.seed = self.read_i32()

        return m
Пример #20
0
    async def read_match(self) -> Match:
        """Read an osu! match from the internal buffer."""
        m = Match()

        # ignore match id (i16) and inprogress (i8).
        self._buf = self._buf[3:]

        #m.type = MatchTypes(await self.read_i8())
        if await self.read_i8() == 1:
            point_of_interest() # what is powerplay

        m.mods = Mods(await self.read_i32())

        m.name = await self.read_string()
        m.passwd = await self.read_string()

        m.map_name = await self.read_string()
        m.map_id = await self.read_i32()
        m.map_md5 = await self.read_string()

        for slot in m.slots:
            slot.status = await self.read_i8()

        for slot in m.slots:
            slot.team = MatchTeams(await self.read_i8())

        for slot in m.slots:
            if slot.status & SlotStatus.has_player:
                # we don't need this, ignore it.
                self._buf = self._buf[4:]

        host_id = await self.read_i32()
        m.host = await glob.players.get(id=host_id)

        m.mode = GameMode(await self.read_i8())
        m.win_condition = MatchWinConditions(await self.read_i8())
        m.team_type = MatchTeamTypes(await self.read_i8())
        m.freemods = await self.read_i8() == 1

        # if we're in freemods mode,
        # read individual slot mods.
        if m.freemods:
            for slot in m.slots:
                slot.mods = Mods(await self.read_i32())

        # read the seed (used for mania)
        m.seed = await self.read_i32()

        return m
Пример #21
0
    user.enqueue(
        writer.sendMessage(fromname=glob.bot.name,
                           msg='Request accepted! Creating match...',
                           tarname=user.name,
                           fromid=glob.bot.id))

    owner.enqueue(
        writer.sendMessage(
            fromname=glob.bot.name,
            msg=
            f'{user.name} accepted your request to battle their clan {user.clan.name}! Creating match...',
            tarname=owner.name,
            fromid=glob.bot.id))

    match = Match()

    match.name = f'Clan Battle: ({clan.name}) vs ({user.clan.name})'
    match.clan_battle = True

    match.clan_1 = clan
    match.clan_2 = user.clan

    match.host = owner
    match.type = teamTypes.team

    glob.matches[match.id] = match

    mp_chan = Channel(name='#multiplayer',
                      desc=f'Multiplayer channel for match ID {match.id}',
                      auto=False,
Пример #22
0
async def mp_randpw(p: Player, m: Match, msg: Sequence[str]) -> str:
    """Randomize `m`'s password."""
    m.passwd = cmyui.rstring(16)
    return 'Match password randomized.'
Пример #23
0
def read_match(data: bytearray) -> Tuple[Match, int]:
    """ Read an osu! match from `data`. """
    m = Match()

    # Ignore match id (i32) & inprogress (i8).
    offset = 3

    # Read type & match mods.
    m.type, m.mods = struct.unpack('<bI', data[offset:offset + 5])
    offset += 5

    # Read match name & password.
    m.name, offs = read_string(data[offset:])
    offset += offs
    m.passwd, offs = read_string(data[offset:])
    offset += offs

    # Ignore map's name.
    offset += 1
    if data[offset - 1] == 0x0b:
        offset += sum(read_uleb128(data[offset:]))

    # Ignore map's id.
    offset += 4

    # Read map's md5 (only thing needed to get map).
    map_md5, offs = read_string(data[offset:])
    offset += offs

    # Get beatmap object for map selected.
    m.bmap = Beatmap.from_md5(map_md5)

    # Read slot statuses.
    for s in m.slots:
        s.status = data[offset]
        offset += 1

    # Read slot teams.
    for s in m.slots:
        s.team = data[offset]
        offset += 1

    for s in m.slots:
        if s.status & SlotStatus.has_player:
            # Dont think we need this?
            offset += 4

    # Read match host.
    m.host = glob.players.get_by_id(
        int.from_bytes(data[offset:offset + 4], 'little'))
    offset += 4

    # Read match mode, match scoring,
    # team type, and freemods.
    m.game_mode = data[offset]
    offset += 1
    m.match_scoring = data[offset]
    offset += 1
    m.team_type = data[offset]
    offset += 1
    m.freemods = data[offset]
    offset += 1

    # If we're in freemods mode,
    # read individual slot mods.
    if m.freemods:
        for s in m.slots:
            s.mods = int.from_bytes(data[offset:offset + 4], 'little')
            offset += 4

    # Read the seed from multi.
    # XXX: Used for mania random mod.
    m.seed = int.from_bytes(data[offset:offset + 4], 'little')
    return m, offset + 4