Priority update for listeners

pull/147/head
bobloy 4 years ago
parent 06af229a62
commit a2eaf55515

@ -9,10 +9,10 @@ import discord
from redbot.core import commands from redbot.core import commands
from redbot.core.bot import Red from redbot.core.bot import Red
from .builder import parse_code from werewolf.builder import parse_code
from .player import Player from werewolf.player import Player
from .role import Role from werewolf.role import Role
from .votegroup import VoteGroup from werewolf.votegroup import VoteGroup
log = logging.getLogger("red.fox_v3.werewolf.game") log = logging.getLogger("red.fox_v3.werewolf.game")
@ -480,7 +480,7 @@ class Game:
async def _notify(self, event, **kwargs): async def _notify(self, event, **kwargs):
for i in range(1, 7): # action guide 1-6 (0 is no action) for i in range(1, 7): # action guide 1-6 (0 is no action)
tasks = [] tasks = []
for event in self.listeners.get(event, []): for event in self.listeners.get(event, {}).get(i, []):
tasks.append(asyncio.ensure_future(event(**kwargs), loop=self.loop)) tasks.append(asyncio.ensure_future(event(**kwargs), loop=self.loop))
await asyncio.gather(*tasks) await asyncio.gather(*tasks)
@ -912,26 +912,19 @@ class Game:
# Optional dynamic channels/categories # Optional dynamic channels/categories
def add_listener(self, func, name=None): def add_ww_listener(self, func, priority=0, name=None):
"""The non decorator alternative to :meth:`.listen`. """Adds a listener from the pool of listeners.
Parameters Parameters
----------- -----------
func: :ref:`coroutine <coroutine>` func: :ref:`coroutine <coroutine>`
The function to call. The function to call.
priority: Optional[:class:`int`]
Priority of the listener. Defaults to 0 (no-action)
name: Optional[:class:`str`] name: Optional[:class:`str`]
The name of the event to listen for. Defaults to ``func.__name__``. The name of the event to listen for. Defaults to ``func.__name__``.
do_sort: Optional[:class:`bool`]
Example Whether or not to sort listeners after. Skip sorting during mass appending
--------
.. code-block:: python3
async def on_ready(): pass
async def my_message(message): pass
bot.add_listener(on_ready)
bot.add_listener(my_message, 'on_message')
""" """
name = func.__name__ if name is None else name name = func.__name__ if name is None else name
@ -940,26 +933,32 @@ class Game:
raise TypeError('Listeners must be coroutines') raise TypeError('Listeners must be coroutines')
if name in self.listeners: if name in self.listeners:
self.listeners[name].append(func) if priority in self.listeners[name]:
self.listeners[name][priority].append(func)
else:
self.listeners[name][priority] = [func]
else: else:
self.listeners[name] = [func] self.listeners[name] = {priority: [func]}
def remove_listener(self, func, name=None): # self.listeners[name].sort(reverse=True)
"""Removes a listener from the pool of listeners.
Parameters
-----------
func
The function that was used as a listener to remove.
name: :class:`str`
The name of the event we want to remove. Defaults to
``func.__name__``.
"""
name = func.__name__ if name is None else name # def remove_wolf_listener(self, func, name=None):
# """Removes a listener from the pool of listeners.
if name in self.listeners: #
try: # Parameters
self.listeners[name].remove(func) # -----------
except ValueError: # func
pass # The function that was used as a listener to remove.
# name: :class:`str`
# The name of the event we want to remove. Defaults to
# ``func.__name__``.
# """
#
# name = func.__name__ if name is None else name
#
# if name in self.listeners:
# try:
# self.listeners[name].remove(func)
# except ValueError:
# pass

@ -1,7 +1,7 @@
import inspect import inspect
def wolflistener(name=None): def wolflistener(name=None, priority=0):
"""A decorator that marks a function as a listener. """A decorator that marks a function as a listener.
This is the werewolf.Game equivalent of :meth:`.Cog.listener`. This is the werewolf.Game equivalent of :meth:`.Cog.listener`.
@ -11,6 +11,22 @@ def wolflistener(name=None):
name: :class:`str` name: :class:`str`
The name of the event being listened to. If not provided, it The name of the event being listened to. If not provided, it
defaults to the function's name. defaults to the function's name.
priority: :class:`int`
The priority of the listener.
Priority guide as follows:
_at_night_start
0. No Action
1. Detain actions (Jailer/Kidnapper)
2. Group discussions and choose targets
_at_night_end
0. No Action
1. Self actions (Veteran)
2. Target switching and role blocks (bus driver, witch, escort)
3. Protection / Preempt actions (bodyguard/framer)
4. Non-disruptive actions (seer/silencer)
5. Disruptive actions (Killing)
6. Role altering actions (Cult / Mason / Shifter)
Raises Raises
-------- --------
@ -32,12 +48,12 @@ def wolflistener(name=None):
actual = actual.__func__ actual = actual.__func__
if not inspect.iscoroutinefunction(actual): if not inspect.iscoroutinefunction(actual):
raise TypeError("Listener function must be a coroutine function.") raise TypeError("Listener function must be a coroutine function.")
actual.__wolf_listener__ = True actual.__wolf_listener__ = priority
to_assign = name or actual.__name__ to_assign = name or actual.__name__
try: try:
actual.__wolf_listener_names__.append(to_assign) actual.__wolf_listener_names__.append((priority, to_assign))
except AttributeError: except AttributeError:
actual.__wolf_listener_names__ = [to_assign] actual.__wolf_listener_names__ = [(priority, to_assign)]
# we have to return `func` instead of `actual` because # we have to return `func` instead of `actual` because
# we need the type to be `staticmethod` for the metaclass # we need the type to be `staticmethod` for the metaclass
# to pick it up but the metaclass unfurls the function and # to pick it up but the metaclass unfurls the function and
@ -51,7 +67,6 @@ class WolfListenerMeta(type):
def __new__(mcs, cls, *args, **kwargs): def __new__(mcs, cls, *args, **kwargs):
name, bases = args name, bases = args
commands = {}
listeners = {} listeners = {}
need_at_msg = "Listeners must start with at_ (in method {0.__name__}.{1})" need_at_msg = "Listeners must start with at_ (in method {0.__name__}.{1})"
@ -76,10 +91,10 @@ class WolfListenerMeta(type):
listeners_as_list = [] listeners_as_list = []
for listener in listeners.values(): for listener in listeners.values():
for listener_name in listener.__wolf_listener_names__: for priority, listener_name in listener.__wolf_listener_names__:
# I use __name__ instead of just storing the value so I can inject # I use __name__ instead of just storing the value so I can inject
# the self attribute when the time comes to add them to the bot # the self attribute when the time comes to add them to the bot
listeners_as_list.append((listener_name, listener.__name__)) listeners_as_list.append((priority, listener_name, listener.__name__))
new_cls.__wolf_listeners__ = listeners_as_list new_cls.__wolf_listeners__ = listeners_as_list
return new_cls return new_cls
@ -87,5 +102,5 @@ class WolfListenerMeta(type):
class WolfListener(metaclass=WolfListenerMeta): class WolfListener(metaclass=WolfListenerMeta):
def __init__(self, game): def __init__(self, game):
for name, method_name in self.__wolf_listeners__: for priority, name, method_name in self.__wolf_listeners__:
game.add_listener(getattr(self, method_name), name) game.add_ww_listener(getattr(self, method_name), priority, name)

@ -1,6 +1,6 @@
import logging import logging
from .role import Role from werewolf.role import Role
log = logging.getLogger("red.fox_v3.werewolf.night_powers") log = logging.getLogger("red.fox_v3.werewolf.night_powers")

@ -1,6 +1,6 @@
from ..listener import wolflistener from werewolf.listener import wolflistener
from ..night_powers import pick_target from werewolf.night_powers import pick_target
from ..role import Role from werewolf.role import Role
class Seer(Role): class Seer(Role):
@ -27,17 +27,17 @@ class Seer(Role):
# self.blocked = False # self.blocked = False
# self.properties = {} # Extra data for other roles (i.e. arsonist) # self.properties = {} # Extra data for other roles (i.e. arsonist)
self.see_target = None self.see_target = None
self.action_list = [ # self.action_list = [
(self._at_game_start, 1), # (Action, Priority) # (self._at_game_start, 1), # (Action, Priority)
(self._at_day_start, 0), # (self._at_day_start, 0),
(self._at_voted, 0), # (self._at_voted, 0),
(self._at_kill, 0), # (self._at_kill, 0),
(self._at_hang, 0), # (self._at_hang, 0),
(self._at_day_end, 0), # (self._at_day_end, 0),
(self._at_night_start, 2), # (self._at_night_start, 2),
(self._at_night_end, 4), # (self._at_night_end, 4),
(self._at_visit, 0), # (self._at_visit, 0),
] # ]
async def see_alignment(self, source=None): async def see_alignment(self, source=None):
""" """
@ -60,7 +60,7 @@ class Seer(Role):
""" """
return "Villager" return "Villager"
@wolflistener("at_night_start") @wolflistener("at_night_start", priority=2)
async def _at_night_start(self, data=None): async def _at_night_start(self, data=None):
if not self.player.alive: if not self.player.alive:
return return
@ -68,7 +68,7 @@ class Seer(Role):
await self.game.generate_targets(self.player.member) await self.game.generate_targets(self.player.member)
await self.player.send_dm("**Pick a target to see tonight**") await self.player.send_dm("**Pick a target to see tonight**")
@wolflistener("at_night_end") @wolflistener("at_night_end", priority=4)
async def _at_night_end(self, data=None): async def _at_night_end(self, data=None):
if self.see_target is None: if self.see_target is None:
if self.player.alive: if self.player.alive:

@ -1,7 +1,6 @@
from ..listener import wolflistener from werewolf.listener import wolflistener
from ..role import Role from werewolf.role import Role
from werewolf.votegroups.wolfvote import WolfVote
from ..votegroups.wolfvote import WolfVote
class VanillaWerewolf(Role): class VanillaWerewolf(Role):

@ -1,4 +1,4 @@
from ..role import Role from werewolf.role import Role
class Villager(Role): class Villager(Role):

@ -0,0 +1 @@
from .wolfvote import WolfVote

@ -1,6 +1,6 @@
import random import random
from ..votegroup import VoteGroup from werewolf.votegroup import VoteGroup
class WolfVote(VoteGroup): class WolfVote(VoteGroup):

@ -6,14 +6,14 @@ from redbot.core.bot import Red
from redbot.core.commands import Cog from redbot.core.commands import Cog
from redbot.core.utils.menus import DEFAULT_CONTROLS, menu from redbot.core.utils.menus import DEFAULT_CONTROLS, menu
from .builder import ( from werewolf.builder import (
GameBuilder, GameBuilder,
role_from_alignment, role_from_alignment,
role_from_category, role_from_category,
role_from_id, role_from_id,
role_from_name, role_from_name,
) )
from .game import Game from werewolf.game import Game
log = logging.getLogger("red.fox_v3.werewolf") log = logging.getLogger("red.fox_v3.werewolf")
@ -81,8 +81,8 @@ class Werewolf(Cog):
""" """
Lists current guild settings Lists current guild settings
""" """
success, role, category, channel, log_channel = await self._get_settings(ctx) valid, role, category, channel, log_channel = await self._get_settings(ctx)
if not success: if not valid:
await ctx.send("Failed to get settings") await ctx.send("Failed to get settings")
return None return None
@ -362,13 +362,15 @@ class Werewolf(Cog):
return None return None
if guild.id not in self.games or self.games[guild.id].game_over: if guild.id not in self.games or self.games[guild.id].game_over:
await ctx.send("Starting a new game...") await ctx.send("Starting a new game...")
success, role, category, channel, log_channel = await self._get_settings(ctx) valid, role, category, channel, log_channel = await self._get_settings(ctx)
if not success: if not valid:
await ctx.send("Cannot start a new game") await ctx.send("Cannot start a new game")
return None return None
self.games[guild.id] = Game(self.bot, guild, role, category, channel, log_channel, game_code) self.games[guild.id] = Game(
self.bot, guild, role, category, channel, log_channel, game_code
)
return self.games[guild.id] return self.games[guild.id]

Loading…
Cancel
Save