Пример #1
0
    async def handle(self, p: Player) -> None:
        unrestrcted_ids = [p.id for p in glob.players.unrestricted]
        is_online = lambda o: o in unrestrcted_ids and o != p.id

        for online in filter(is_online, self.user_ids):
            if t := glob.players.get(id=online):
                p.enqueue(packets.userStats(t))
Пример #2
0
    async def handle(self, p: Player) -> None:
        # TODO: match validation..?
        if p.silenced:
            p.enqueue(packets.matchJoinFail() + packets.notification(
                'Multiplayer is not available while silenced.'))
            return

        if not glob.matches.append(self.match):
            # failed to create match (match slots full).
            p.send('Failed to create match (no slots available).',
                   sender=glob.bot)
            p.enqueue(packets.matchJoinFail())
            return

        # create the channel and add it
        # to the global channel list as
        # an instanced channel.
        chan = Channel(name=f'#multi_{self.match.id}',
                       topic=f"MID {self.match.id}'s multiplayer channel.",
                       auto_join=False,
                       instance=True)

        glob.channels.append(chan)
        self.match.chat = chan

        await p.update_latest_activity()
        p.join_match(self.match, self.match.passwd)
        log(f'{p} created a new multiplayer match.')
Пример #3
0
async def switch_server(p: Player, c: Messageable, msg: Sequence[str]) -> str:
    """Switch servers to a specified ip address."""
    if len(msg) != 1:
        return 'Invalid syntax: !switch <endpoint>'

    p.enqueue(packets.switchTournamentServer(msg[0]))
    return 'Have a nice journey..'
Пример #4
0
    async def handle(self, p: Player) -> None:
        c = glob.channels[self.name]

        if not c or not await p.join_channel(c):
            log(f'{p} failed to join {self.name}.', Ansi.YELLOW)
            return

        # enqueue channelJoin to our player.
        p.enqueue(packets.channelJoin(c.name))
Пример #5
0
async def channelJoin(p: Player, pr: BanchoPacketReader) -> None:
    chan_name, = await pr.read(osuTypes.string)
    c = glob.channels[chan_name]

    if not c or not await p.join_channel(c):
        plog(f'{p} failed to join {chan_name}.', Ansi.YELLOW)
        return

    # Enqueue channelJoin to our player.
    p.enqueue(await packets.channelJoin(c.name))
Пример #6
0
def statsRequest(p: Player, pr: PacketReader) -> None:
    if len(pr.data) < 6:
        return

    userIDs = pr.read(osuTypes.i32_list)
    is_online = lambda o: o in glob.players.ids and o != p.id

    for online in filter(is_online, userIDs):
        target = glob.players.get_by_id(online)
        p.enqueue(packets.userStats(target))
Пример #7
0
async def statsRequest(p: Player, pr: BanchoPacketReader) -> None:
    if len(pr.data) < 6:
        return

    userIDs = await pr.read(osuTypes.i32_list)
    is_online = lambda o: o in glob.players.ids and o != p.id

    for online in filter(is_online, userIDs):
        if t := await glob.players.get_by_id(online):
            p.enqueue(await packets.userStats(t))
Пример #8
0
    async def handle(self, p: Player) -> None:
        # XXX: this only sends when the client can see > 256 players,
        # so this probably won't have much use for private servers.

        # NOTE: i'm not exactly sure how bancho implements this and whether
        # i'm supposed to filter the users presences to send back with the
        # player's presence filter; i can add it in the future perhaps.
        for t in glob.players:
            if p is not t:
                p.enqueue(packets.userPresence(t))
Пример #9
0
async def ping(p: Player, pr: BanchoPacketReader) -> None:
    # TODO: this should be last packet time, not just
    # ping.. this handler shouldn't even exist lol
    p.ping_time = int(time.time())

    # osu! seems to error when i send nothing back,
    # so perhaps the official bancho implementation
    # expects something like a stats update.. i'm
    # just gonna ping it back, as i don't really
    # want to something more expensive so often lol
    p.enqueue(b'\x04\x00\x00\x00\x00\x00\x00')
Пример #10
0
async def channelJoin(p: Player, pr: PacketReader) -> None:
    chan_name, = await pr.read(osuTypes.string)
    c = glob.channels.get(chan_name)

    if not c or not await p.join_channel(c):
        await plog(f'{p} failed to join {chan_name}.', Ansi.YELLOW)
        return

    # Enqueue new channelinfo (playercount) to a ll players.
    #glob.players.enqueue(await packets.channelInfo(*c.basic_info))

    # Enqueue channelJoin to our player.
    p.enqueue(await packets.channelJoin(c.name))
Пример #11
0
    async def handle(self, p: Player) -> None:
        if not 0 <= self.match_id < 64:
            if self.match_id >= 64:
                # NOTE: this function is unrelated to mp.
                await check_menu_option(p, self.match_id)

            p.enqueue(packets.matchJoinFail())
            return

        if not (m := glob.matches[self.match_id]):
            log(f'{p} tried to join a non-existant mp lobby?')
            p.enqueue(packets.matchJoinFail())
            return
Пример #12
0
async def join_match(user: Player, p: bytes) -> None:
    d = reader.handle_packet(p, (('id', osuTypes.i32), ('pw', osuTypes.string),))
    _id = d['id']
    pw = d['pw']

    if _id >= 1000:
        if not (menu := glob.menus.get(_id)): # TODO: use pw instead of id
            return user.enqueue(writer.matchJoinFail())

        ret = await menu.handle(user)

        # if we don't return a join failure also, its gonna think we are still in lobby
        if isinstance(ret, str): # return string message?
            user.enqueue(
                writer.sendMessage(
                    fromname = glob.bot.name,
                    msg = ret,
                    tarname = user.name,
                    fromid = glob.bot.id
                )
            )

            return user.enqueue(writer.matchJoinFail())

        user.enqueue(writer.matchJoinFail())
        return ret
Пример #13
0
    async def handle(self, p: Player) -> None:
        if not glob.matches.append(self.match):
            # failed to create match (match slots full).
            await p.send(glob.bot,
                         'Failed to create match (no slots available).')
            p.enqueue(packets.matchJoinFail())
            return

        # create the channel and add it
        # to the global channel list as
        # an instanced channel.
        chan = Channel(name=f'#multi_{self.match.id}',
                       topic=f"MID {self.match.id}'s multiplayer channel.",
                       auto_join=False,
                       instance=True)

        glob.channels.append(chan)
        self.match.chat = chan

        await p.update_latest_activity()
        await p.join_match(self.match, self.match.passwd)
        log(f'{p} created a new multiplayer match.')
Пример #14
0
async def create_match(user: Player, p: bytes) -> None:
    match = (reader.handle_packet(p, (('match', osuTypes.match),)))['match']

    glob.matches[match.id] = match
    if not glob.matches.get(match.id):
        return user.enqueue(writer.matchJoinFail())

    mp_chan = Channel(name='#multiplayer', desc=f'Multiplayer channel for match ID {match.id}', auto=False, perm=False)
    glob.channels[f'#multi_{match.id}'] = mp_chan
    match.chat = mp_chan

    user.join_match(match, match.pw)
    log(f'{user.name} created a new multiplayer lobby.', Ansi.LBLUE)
Пример #15
0
async def lastFM(p: Player, conn: AsyncConnection) -> Optional[bytes]:
    if conn.args['b'][0] != 'a':
        # not anticheat related, tell the
        # client not to send any more for now.
        return b'-3'

    flags = ClientFlags(int(conn.args['b'][1:]))

    if flags & (ClientFlags.HQAssembly | ClientFlags.HQFile):
        # Player is currently running hq!osu; could possibly
        # be a separate client, buuuut prooobably not lol.

        await p.ban(glob.bot, f'hq!osu running ({flags})')
        return b'-3'

    if flags & ClientFlags.RegistryEdits:
        # Player has registry edits left from
        # hq!osu's multiaccounting tool. This
        # does not necessarily mean they are
        # using it now, but they have in the past.

        if random.randrange(32) == 0:
            # Random chance (1/32) for a ban.
            await p.ban(glob.bot, f'hq!osu relife 1/32')
            return b'-3'

        p.enqueue(packets.notification('\n'.join([
            "Hey!",
            "It appears you have hq!osu's multiaccounting tool (relife) enabled.",
            "This tool leaves a change in your registry that the osu! client can detect.",
            "Please re-install relife and disable the program to avoid possible ban."
        ])))

        await p.logout()
        return b'-3'

    """ These checks only worked for ~5 hours from release. rumoi's quick!
Пример #16
0
async def create_match(user: Player, p: bytes) -> None:
    match = (reader.handle_packet(p, (("match", osuTypes.match),)))["match"]

    glob.matches[match.id] = match
    if not glob.matches.get(match.id):
        return user.enqueue(writer.matchJoinFail())

    mp_chan = Channel(
        name="#multiplayer",
        desc=f"Multiplayer channel for match ID {match.id}",
        auto=False,
        perm=False,
    )
    glob.channels[f"#multi_{match.id}"] = mp_chan
    match.chat = mp_chan

    user.join_match(match, match.pw)
    base_info(f"{user.name} created a new multiplayer lobby.")
Пример #17
0
 async def handle(self, p: Player) -> None:
     p.enqueue(packets.userStats(p))
Пример #18
0
async def request_stats(user: Player, p: bytes) -> None:
    uids = (reader.handle_packet(p, (('uids', osuTypes.i32_list),)))['uids']

    for o in glob.players.unrestricted_users:
        if o.id != user.id and o.id in uids:
            user.enqueue(writer.userStats(o))
Пример #19
0
 async def handle(self, p: Player) -> None:
     for pid in self.user_ids:
         if t := await glob.players.get(id=pid):
             p.enqueue(packets.userPresence(t))
Пример #20
0
    async def handle(self, p: Player) -> None:
        is_online = lambda o: o in glob.players.ids and o != p.id

        for online in filter(is_online, self.user_ids):
            if t := await glob.players.get(id=online):
                p.enqueue(packets.userStats(t))
Пример #21
0
async def lobbyJoin(p: Player, pr: BanchoPacketReader) -> None:
    p.in_lobby = True

    for m in filter(lambda m: m is not None, glob.matches):
        p.enqueue(await packets.newMatch(m))
Пример #22
0
async def presence_request_all(user: Player, _) -> None:
    for o in glob.players:
        if o.id != user.id:
            user.enqueue(writer.userPresence(o))
Пример #23
0
    async def handle(self, p: Player) -> None:
        if p.silenced:
            log(f'{p} sent a message while silenced.', Ansi.LYELLOW)
            return

        # remove leading/trailing whitespace
        msg = self.msg.msg.strip()
        target = self.msg.target

        if target == '#spectator':
            if p.spectating:
                # we are spectating someone
                spec_id = p.spectating.id
            elif p.spectators:
                # we are being spectated
                spec_id = p.id
            else:
                return

            t_chan = glob.channels[f'#spec_{spec_id}']
        elif target == '#multiplayer':
            if not p.match:
                # they're not in a match?
                return

            t_chan = p.match.chat
        else:
            t_chan = glob.channels[target]

        if not t_chan:
            log(f'{p} wrote to non-existent {target}.', Ansi.LYELLOW)
            return

        if p.priv & t_chan.write_priv != t_chan.write_priv:
            log(f'{p} wrote to {target} with insufficient privileges.')
            return

        # limit message length to 2k chars
        # perhaps this could be dangerous with !py..?
        if len(msg) > 2000:
            msg = f'{msg[:2000]}... (truncated)'
            p.enqueue(
                packets.notification('Your message was truncated\n'
                                     '(exceeded 2000 characters).'))

        cmd = (msg.startswith(glob.config.command_prefix)
               and await commands.process_commands(p, t_chan, msg))

        if cmd:
            # a command was triggered.
            if not cmd['hidden']:
                t_chan.send(msg, sender=p)
                if 'resp' in cmd:
                    t_chan.send_bot(cmd['resp'])
            else:
                staff = glob.players.staff
                t_chan.send_selective(msg=msg, sender=p, targets=staff - {p})
                if 'resp' in cmd:
                    t_chan.send_selective(msg=cmd['resp'],
                                          sender=glob.bot,
                                          targets=staff | {p})

        else:
            # no commands were triggered

            # check if the user is /np'ing a map.
            # even though this is a public channel,
            # we'll update the player's last np stored.
            if match := regexes.now_playing.match(msg):
                # the player is /np'ing a map.
                # save it to their player instance
                # so we can use this elsewhere owo..
                bmap = await Beatmap.from_bid(int(match['bid']))

                if bmap:
                    # parse mode_vn int from regex
                    if match['mode_vn'] is not None:
                        mode_vn = {
                            'Taiko': 1,
                            'CatchTheBeat': 2,
                            'osu!mania': 3
                        }[match['mode_vn']]
                    else:
                        # use beatmap mode if not specified
                        mode_vn = bmap.mode.as_vanilla

                    p.last_np = {
                        'bmap': bmap,
                        'mode_vn': mode_vn,
                        'timeout': time.time() + 300  # 5mins
                    }
                else:
                    # time out their previous /np
                    p.last_np['timeout'] = 0

            t_chan.send(msg, sender=p)
Пример #24
0
                        msg = 'Could not find map.'

                        # time out their previous /np
                        p.last_np['timeout'] = 0

                    p.send(msg, sender=t)

        else:
            # target is not aika, send the message normally if online
            if t.online:
                t.send(msg, sender=p)
            else:
                # inform user they're offline, but
                # will receive the mail @ next login.
                p.enqueue(
                    packets.notification(
                        f'{t.name} is currently offline, but will '
                        'receive your messsage on their next login.'))

            # insert mail into db,
            # marked as unread.
            await glob.db.execute(
                'INSERT INTO `mail` '
                '(`from_id`, `to_id`, `msg`, `time`) '
                'VALUES (%s, %s, %s, UNIX_TIMESTAMP())', [p.id, t.id, msg])

        await p.update_latest_activity()
        log(f'{p} @ {t}: {msg}', Ansi.LCYAN, fd='.data/logs/chat.log')


@register
class LobbyPart(BanchoPacket, type=Packets.OSU_PART_LOBBY):
Пример #25
0
async def switch(p: Player, c: Messageable, msg: Sequence[str]) -> str:
    if len(msg) != 1:
        return 'Invalid syntax: !switch <ip>'

    p.enqueue(await packets.switchTournamentServer(msg[0]))
    return 'Have a nice journey..'
Пример #26
0
async def userPresenceRequest(p: Player, pr: BanchoPacketReader) -> None:
    for pid in await pr.read(osuTypes.i32_list):
        if t := await glob.players.get_by_id(pid):
            p.enqueue(await packets.userPresence(t))
Пример #27
0
    async def handle(self, p: Player) -> None:
        p.in_lobby = True

        for m in [_m for _m in glob.matches if _m]:
            p.enqueue(packets.newMatch(m))
Пример #28
0
        # add to `stats` table.
        await glob.db.execute('INSERT INTO stats '
                              '(id) VALUES (%s)', [user_id])

        p = Player(id=user_id,
                   name=username,
                   silence_end=0,
                   priv=Privileges.Normal,
                   osu_version=osu_ver)

        plog(f'{p} has registered!', Ansi.LGREEN)

        # enqueue registration message to the user.
        _msg = registration_msg.format(glob.players.staff)
        p.enqueue(await packets.sendMessage(glob.bot.name, _msg, p.name, p.id))

    data = bytearray(
        await packets.userID(p.id) + await packets.protocolVersion(19) +
        await packets.banchoPrivileges(p.bancho_priv) +
        await packets.notification(
            f'Welcome back to the gulag!\nCurrent build: {glob.version}') +

        # tells osu! to load channels from config, I believe?
        await packets.channelInfoEnd())

    # channels
    for c in glob.channels:
        if not p.priv & c.read:
            continue  # no priv to read
Пример #29
0
async def presence_request(user: Player, p: bytes) -> None:
    uids = (reader.handle_packet(p, (('uids', osuTypes.i32_list),)))['uids']

    for u in uids:
        if o := await glob.players.get(id=u):
            user.enqueue(writer.userPresence(o))
Пример #30
0
async def statsUpdateRequest(p: Player, pr: BanchoPacketReader) -> None:
    p.enqueue(await packets.userStats(p))