mirror of
https://github.com/nihilvux/bancho.py.git
synced 2025-09-17 02:58:39 -07:00
Add files via upload
This commit is contained in:
138
app/objects/channel.py
Normal file
138
app/objects/channel.py
Normal file
@@ -0,0 +1,138 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import app.packets
|
||||
import app.state
|
||||
from app.constants.privileges import Privileges
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.objects.player import Player
|
||||
|
||||
|
||||
class Channel:
|
||||
"""An osu! chat channel.
|
||||
|
||||
Possibly confusing attributes
|
||||
-----------
|
||||
_name: `str`
|
||||
A name string of the channel.
|
||||
The cls.`name` property wraps handling for '#multiplayer' and
|
||||
'#spectator' when communicating with the osu! client; only use
|
||||
this attr when you need the channel's true name; otherwise you
|
||||
should use the `name` property described below.
|
||||
|
||||
instance: `bool`
|
||||
Instanced channels are deleted when all players have left;
|
||||
this is useful for things like multiplayer, spectator, etc.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
topic: str,
|
||||
read_priv: Privileges = Privileges.UNRESTRICTED,
|
||||
write_priv: Privileges = Privileges.UNRESTRICTED,
|
||||
auto_join: bool = True,
|
||||
instance: bool = False,
|
||||
) -> None:
|
||||
# TODO: think of better names than `_name` and `name`
|
||||
self._name = name # 'real' name ('#{multi/spec}_{id}')
|
||||
|
||||
if self._name.startswith("#spec_"):
|
||||
self.name = "#spectator"
|
||||
elif self._name.startswith("#multi_"):
|
||||
self.name = "#multiplayer"
|
||||
else:
|
||||
self.name = self._name
|
||||
|
||||
self.topic = topic
|
||||
self.read_priv = read_priv
|
||||
self.write_priv = write_priv
|
||||
self.auto_join = auto_join
|
||||
self.instance = instance
|
||||
|
||||
self.players: list[Player] = []
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<{self._name}>"
|
||||
|
||||
def __contains__(self, player: Player) -> bool:
|
||||
return player in self.players
|
||||
|
||||
# XXX: should this be cached differently?
|
||||
|
||||
def can_read(self, priv: Privileges) -> bool:
|
||||
if not self.read_priv:
|
||||
return True
|
||||
|
||||
return priv & self.read_priv != 0
|
||||
|
||||
def can_write(self, priv: Privileges) -> bool:
|
||||
if not self.write_priv:
|
||||
return True
|
||||
|
||||
return priv & self.write_priv != 0
|
||||
|
||||
def send(self, msg: str, sender: Player, to_self: bool = False) -> None:
|
||||
"""Enqueue `msg` to all appropriate clients from `sender`."""
|
||||
data = app.packets.send_message(
|
||||
sender=sender.name,
|
||||
msg=msg,
|
||||
recipient=self.name,
|
||||
sender_id=sender.id,
|
||||
)
|
||||
|
||||
for player in self.players:
|
||||
if sender.id not in player.blocks and (to_self or player.id != sender.id):
|
||||
player.enqueue(data)
|
||||
|
||||
def send_bot(self, msg: str) -> None:
|
||||
"""Enqueue `msg` to all connected clients from bot."""
|
||||
bot = app.state.sessions.bot
|
||||
|
||||
msg_len = len(msg)
|
||||
|
||||
if msg_len >= 31979: # TODO ??????????
|
||||
msg = f"message would have crashed games ({msg_len} chars)"
|
||||
|
||||
self.enqueue(
|
||||
app.packets.send_message(
|
||||
sender=bot.name,
|
||||
msg=msg,
|
||||
recipient=self.name,
|
||||
sender_id=bot.id,
|
||||
),
|
||||
)
|
||||
|
||||
def send_selective(
|
||||
self,
|
||||
msg: str,
|
||||
sender: Player,
|
||||
recipients: set[Player],
|
||||
) -> None:
|
||||
"""Enqueue `sender`'s `msg` to `recipients`."""
|
||||
for player in recipients:
|
||||
if player in self:
|
||||
player.send(msg, sender=sender, chan=self)
|
||||
|
||||
def append(self, player: Player) -> None:
|
||||
"""Add `player` to the channel's players."""
|
||||
self.players.append(player)
|
||||
|
||||
def remove(self, player: Player) -> None:
|
||||
"""Remove `player` from the channel's players."""
|
||||
self.players.remove(player)
|
||||
|
||||
if not self.players and self.instance:
|
||||
# if it's an instance channel and this
|
||||
# is the last member leaving, just remove
|
||||
# the channel from the global list.
|
||||
app.state.sessions.channels.remove(self)
|
||||
|
||||
def enqueue(self, data: bytes, immune: Sequence[int] = []) -> None:
|
||||
"""Enqueue `data` to all connected clients not in `immune`."""
|
||||
for player in self.players:
|
||||
if player.id not in immune:
|
||||
player.enqueue(data)
|
Reference in New Issue
Block a user