More listener
This commit is contained in:
		
							parent
							
								
									28bf2a73e1
								
							
						
					
					
						commit
						1723dc381d
					
				
							
								
								
									
										183
									
								
								werewolf/game.py
									
									
									
									
									
								
							
							
						
						
									
										183
									
								
								werewolf/game.py
									
									
									
									
									
								
							@ -1,4 +1,5 @@
 | 
			
		||||
import asyncio
 | 
			
		||||
import inspect
 | 
			
		||||
import logging
 | 
			
		||||
import random
 | 
			
		||||
from collections import deque
 | 
			
		||||
@ -36,9 +37,7 @@ class Game:
 | 
			
		||||
        "**Morning has arrived on day {}..**",
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    night_messages = [
 | 
			
		||||
        "**Dawn falls on day {}..****"
 | 
			
		||||
    ]
 | 
			
		||||
    night_messages = ["**Dawn falls on day {}..****"]
 | 
			
		||||
 | 
			
		||||
    day_vote_count = 3
 | 
			
		||||
 | 
			
		||||
@ -87,6 +86,7 @@ class Game:
 | 
			
		||||
        self.loop = asyncio.get_event_loop()
 | 
			
		||||
 | 
			
		||||
        self.action_queue = deque()
 | 
			
		||||
        self.listeners = {}
 | 
			
		||||
 | 
			
		||||
    # def __del__(self):
 | 
			
		||||
    #     """
 | 
			
		||||
@ -265,9 +265,7 @@ class Game:
 | 
			
		||||
    ############START Notify structure############
 | 
			
		||||
    async def _cycle(self):
 | 
			
		||||
        """
 | 
			
		||||
        Each event calls the next event
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        Each event enqueues the next event
 | 
			
		||||
 | 
			
		||||
        _at_day_start()
 | 
			
		||||
            _at_voted()
 | 
			
		||||
@ -296,7 +294,7 @@ class Game:
 | 
			
		||||
            embed=discord.Embed(title="Game is starting, please wait for setup to complete")
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        await self._notify(0)
 | 
			
		||||
        await self._notify("at_game_start")
 | 
			
		||||
 | 
			
		||||
    async def _at_day_start(self):  # ID 1
 | 
			
		||||
        if self.game_over:
 | 
			
		||||
@ -318,7 +316,7 @@ class Game:
 | 
			
		||||
        await self.generate_targets(self.village_channel)
 | 
			
		||||
 | 
			
		||||
        await self.day_perms(self.village_channel)
 | 
			
		||||
        await self._notify(1)
 | 
			
		||||
        await self._notify("at_day_start")
 | 
			
		||||
 | 
			
		||||
        await self._check_game_over()
 | 
			
		||||
        if self.game_over:
 | 
			
		||||
@ -346,7 +344,7 @@ class Game:
 | 
			
		||||
        if self.game_over:
 | 
			
		||||
            return
 | 
			
		||||
        data = {"player": target}
 | 
			
		||||
        await self._notify(2, data)
 | 
			
		||||
        await self._notify("at_voted", player=target)
 | 
			
		||||
 | 
			
		||||
        self.ongoing_vote = True
 | 
			
		||||
 | 
			
		||||
@ -357,9 +355,7 @@ class Game:
 | 
			
		||||
            "**{} will be put to trial and has 30 seconds to defend themselves**".format(
 | 
			
		||||
                target.mention
 | 
			
		||||
            ),
 | 
			
		||||
            allowed_mentions=discord.AllowedMentions(
 | 
			
		||||
                everyone=False, users=[target]
 | 
			
		||||
            )
 | 
			
		||||
            allowed_mentions=discord.AllowedMentions(everyone=False, users=[target]),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        await asyncio.sleep(30)
 | 
			
		||||
@ -371,9 +367,7 @@ class Game:
 | 
			
		||||
            "👍 to save, 👎 to lynch\n"
 | 
			
		||||
            "*Majority rules, no-lynch on ties, "
 | 
			
		||||
            "vote both or neither to abstain, 15 seconds to vote*".format(target.mention),
 | 
			
		||||
            allowed_mentions=discord.AllowedMentions(
 | 
			
		||||
                everyone=False, users=[target]
 | 
			
		||||
            )
 | 
			
		||||
            allowed_mentions=discord.AllowedMentions(everyone=False, users=[target]),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        await message.add_reaction("👍")
 | 
			
		||||
@ -422,13 +416,13 @@ class Game:
 | 
			
		||||
        if self.game_over:
 | 
			
		||||
            return
 | 
			
		||||
        data = {"player": target}
 | 
			
		||||
        await self._notify(3, data)
 | 
			
		||||
        await self._notify("at_kill", player=target)
 | 
			
		||||
 | 
			
		||||
    async def _at_hang(self, target):  # ID 4
 | 
			
		||||
        if self.game_over:
 | 
			
		||||
            return
 | 
			
		||||
        data = {"player": target}
 | 
			
		||||
        await self._notify(4, data)
 | 
			
		||||
        await self._notify("at_hang", player=target)
 | 
			
		||||
 | 
			
		||||
    async def _at_day_end(self):  # ID 5
 | 
			
		||||
        await self._check_game_over()
 | 
			
		||||
@ -447,14 +441,14 @@ class Game:
 | 
			
		||||
            embed=discord.Embed(title="**The sun sets on the village...**")
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        await self._notify(5)
 | 
			
		||||
        await self._notify("at_day_end")
 | 
			
		||||
        await asyncio.sleep(5)
 | 
			
		||||
        self.action_queue.append(self._at_night_start())
 | 
			
		||||
 | 
			
		||||
    async def _at_night_start(self):  # ID 6
 | 
			
		||||
        if self.game_over:
 | 
			
		||||
            return
 | 
			
		||||
        await self._notify(6)
 | 
			
		||||
        await self._notify("at_night_start")
 | 
			
		||||
 | 
			
		||||
        await asyncio.sleep(12)  # 2 minutes FixMe to 120 later
 | 
			
		||||
        await self.village_channel.send(
 | 
			
		||||
@ -471,7 +465,7 @@ class Game:
 | 
			
		||||
    async def _at_night_end(self):  # ID 7
 | 
			
		||||
        if self.game_over:
 | 
			
		||||
            return
 | 
			
		||||
        await self._notify(7)
 | 
			
		||||
        await self._notify("at_night_end")
 | 
			
		||||
 | 
			
		||||
        await asyncio.sleep(10)
 | 
			
		||||
        self.action_queue.append(self._at_day_start())
 | 
			
		||||
@ -480,25 +474,30 @@ class Game:
 | 
			
		||||
        if self.game_over:
 | 
			
		||||
            return
 | 
			
		||||
        data = {"target": target, "source": source}
 | 
			
		||||
        await self._notify(8, data)
 | 
			
		||||
        await self._notify("at_visit", target=target, source=source)
 | 
			
		||||
 | 
			
		||||
    async def _notify(self, event, data=None):
 | 
			
		||||
    async def _notify(self, event, **kwargs):
 | 
			
		||||
        for i in range(1, 7):  # action guide 1-6 (0 is no action)
 | 
			
		||||
            tasks = []
 | 
			
		||||
            for event in self.listeners.get(event, []):
 | 
			
		||||
                tasks.append(asyncio.ensure_future(event(**kwargs), loop=self.loop))
 | 
			
		||||
            await asyncio.gather(*tasks)
 | 
			
		||||
 | 
			
		||||
            # self.bot.dispatch(f"red.fox.werewolf.{event}", data=data, priority=i)
 | 
			
		||||
            # self.bot.extra_events
 | 
			
		||||
            tasks = []
 | 
			
		||||
            # Role priorities
 | 
			
		||||
            role_order = [role for role in self.roles if role.action_list[event][1] == i]
 | 
			
		||||
            for role in role_order:
 | 
			
		||||
                tasks.append(asyncio.ensure_future(role.on_event(event, data), loop=self.loop))
 | 
			
		||||
            # VoteGroup priorities
 | 
			
		||||
            vote_order = [vg for vg in self.vote_groups.values() if vg.action_list[event][1] == i]
 | 
			
		||||
            for vote_group in vote_order:
 | 
			
		||||
                tasks.append(
 | 
			
		||||
                    asyncio.ensure_future(vote_group.on_event(event, data), loop=self.loop)
 | 
			
		||||
                )
 | 
			
		||||
            if tasks:
 | 
			
		||||
                await asyncio.gather(*tasks)
 | 
			
		||||
            # tasks = []
 | 
			
		||||
            # # Role priorities
 | 
			
		||||
            # role_order = [role for role in self.roles if role.action_list[event][1] == i]
 | 
			
		||||
            # for role in role_order:
 | 
			
		||||
            #     tasks.append(asyncio.ensure_future(role.on_event(event, data), loop=self.loop))
 | 
			
		||||
            # # VoteGroup priorities
 | 
			
		||||
            # vote_order = [vg for vg in self.vote_groups.values() if vg.action_list[event][1] == i]
 | 
			
		||||
            # for vote_group in vote_order:
 | 
			
		||||
            #     tasks.append(
 | 
			
		||||
            #         asyncio.ensure_future(vote_group.on_event(event, data), loop=self.loop)
 | 
			
		||||
            #     )
 | 
			
		||||
            # if tasks:
 | 
			
		||||
            #     await asyncio.gather(*tasks)
 | 
			
		||||
            # Run same-priority task simultaneously
 | 
			
		||||
 | 
			
		||||
    ############END Notify structure############
 | 
			
		||||
@ -911,3 +910,117 @@ class Game:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        # Optional dynamic channels/categories
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def wolflistener(cls, name=None):
 | 
			
		||||
        """A decorator that marks a function as a listener.
 | 
			
		||||
 | 
			
		||||
        This is the cog equivalent of :meth:`.Bot.listen`.
 | 
			
		||||
 | 
			
		||||
        Parameters
 | 
			
		||||
        ------------
 | 
			
		||||
        name: :class:`str`
 | 
			
		||||
            The name of the event being listened to. If not provided, it
 | 
			
		||||
            defaults to the function's name.
 | 
			
		||||
 | 
			
		||||
        Raises
 | 
			
		||||
        --------
 | 
			
		||||
        TypeError
 | 
			
		||||
            The function is not a coroutine function or a string was not passed as
 | 
			
		||||
            the name.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        if name is not None and not isinstance(name, str):
 | 
			
		||||
            raise TypeError(
 | 
			
		||||
                "Cog.listener expected str but received {0.__class__.__name__!r} instead.".format(
 | 
			
		||||
                    name
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        def decorator(func):
 | 
			
		||||
            actual = func
 | 
			
		||||
            if isinstance(actual, staticmethod):
 | 
			
		||||
                actual = actual.__func__
 | 
			
		||||
            if not inspect.iscoroutinefunction(actual):
 | 
			
		||||
                raise TypeError("Listener function must be a coroutine function.")
 | 
			
		||||
            actual.__werewolf_listener__ = True
 | 
			
		||||
            to_assign = name or actual.__name__
 | 
			
		||||
            try:
 | 
			
		||||
                actual.__cog_listener_names__.append(to_assign)
 | 
			
		||||
            except AttributeError:
 | 
			
		||||
                actual.__cog_listener_names__ = [to_assign]
 | 
			
		||||
            # we have to return `func` instead of `actual` because
 | 
			
		||||
            # we need the type to be `staticmethod` for the metaclass
 | 
			
		||||
            # to pick it up but the metaclass unfurls the function and
 | 
			
		||||
            # thus the assignments need to be on the actual function
 | 
			
		||||
            return func
 | 
			
		||||
 | 
			
		||||
        return decorator
 | 
			
		||||
 | 
			
		||||
    def wolflisten(self, name=None):
 | 
			
		||||
        """A decorator that registers another function as an external
 | 
			
		||||
        event listener. Basically this allows you to listen to multiple
 | 
			
		||||
        events from different places e.g. such as :func:`.on_ready`
 | 
			
		||||
 | 
			
		||||
        The functions being listened to must be a :ref:`coroutine <coroutine>`.
 | 
			
		||||
 | 
			
		||||
        Example
 | 
			
		||||
        --------
 | 
			
		||||
 | 
			
		||||
        .. code-block:: python3
 | 
			
		||||
 | 
			
		||||
            @bot.listen()
 | 
			
		||||
            async def on_message(message):
 | 
			
		||||
                print('one')
 | 
			
		||||
 | 
			
		||||
            # in some other file...
 | 
			
		||||
 | 
			
		||||
            @bot.listen('on_message')
 | 
			
		||||
            async def my_message(message):
 | 
			
		||||
                print('two')
 | 
			
		||||
 | 
			
		||||
        Would print one and two in an unspecified order.
 | 
			
		||||
 | 
			
		||||
        Raises
 | 
			
		||||
        -------
 | 
			
		||||
        TypeError
 | 
			
		||||
            The function being listened to is not a coroutine.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        def decorator(func):
 | 
			
		||||
            self.add_wolflistener(func, name)
 | 
			
		||||
            return func
 | 
			
		||||
 | 
			
		||||
        return decorator
 | 
			
		||||
 | 
			
		||||
    def add_wolflistener(self, func, name=None):
 | 
			
		||||
        """The non decorator alternative to :meth:`.listen`.
 | 
			
		||||
 | 
			
		||||
        Parameters
 | 
			
		||||
        -----------
 | 
			
		||||
        func: :ref:`coroutine <coroutine>`
 | 
			
		||||
            The function to call.
 | 
			
		||||
        name: Optional[:class:`str`]
 | 
			
		||||
            The name of the event to listen for. Defaults to ``func.__name__``.
 | 
			
		||||
 | 
			
		||||
        Example
 | 
			
		||||
        --------
 | 
			
		||||
 | 
			
		||||
        .. 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
 | 
			
		||||
 | 
			
		||||
        if not asyncio.iscoroutinefunction(func):
 | 
			
		||||
            raise TypeError('Listeners must be coroutines')
 | 
			
		||||
 | 
			
		||||
        if name in self.listeners:
 | 
			
		||||
            self.listeners[name].append(func)
 | 
			
		||||
        else:
 | 
			
		||||
            self.listeners[name] = [func]
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,8 @@
 | 
			
		||||
import inspect
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from werewolf import Werewolf
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger("red.fox_v3.werewolf.role")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -64,27 +67,27 @@ class Role:
 | 
			
		||||
        self.blocked = False
 | 
			
		||||
        self.properties = {}  # Extra data for other roles (i.e. arsonist)
 | 
			
		||||
 | 
			
		||||
        self.action_list = [
 | 
			
		||||
            (self._at_game_start, 1),  # (Action, Priority)
 | 
			
		||||
            (self._at_day_start, 0),
 | 
			
		||||
            (self._at_voted, 0),
 | 
			
		||||
            (self._at_kill, 0),
 | 
			
		||||
            (self._at_hang, 0),
 | 
			
		||||
            (self._at_day_end, 0),
 | 
			
		||||
            (self._at_night_start, 0),
 | 
			
		||||
            (self._at_night_end, 0),
 | 
			
		||||
            (self._at_visit, 0),
 | 
			
		||||
        ]
 | 
			
		||||
        # self.action_list = [
 | 
			
		||||
        #     (self._at_game_start, 1),  # (Action, Priority)
 | 
			
		||||
        #     (self._at_day_start, 0),
 | 
			
		||||
        #     (self._at_voted, 0),
 | 
			
		||||
        #     (self._at_kill, 0),
 | 
			
		||||
        #     (self._at_hang, 0),
 | 
			
		||||
        #     (self._at_day_end, 0),
 | 
			
		||||
        #     (self._at_night_start, 0),
 | 
			
		||||
        #     (self._at_night_end, 0),
 | 
			
		||||
        #     (self._at_visit, 0),
 | 
			
		||||
        # ]
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return self.__class__.__name__
 | 
			
		||||
 | 
			
		||||
    async def on_event(self, event, data):
 | 
			
		||||
        """
 | 
			
		||||
        See Game class for event guide
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        await self.action_list[event][0](data)
 | 
			
		||||
    # async def on_event(self, event, data):
 | 
			
		||||
    #     """
 | 
			
		||||
    #     See Game class for event guide
 | 
			
		||||
    #     """
 | 
			
		||||
    #
 | 
			
		||||
    #     await self.action_list[event][0](data)
 | 
			
		||||
 | 
			
		||||
    async def assign_player(self, player):
 | 
			
		||||
        """
 | 
			
		||||
@ -124,35 +127,36 @@ class Role:
 | 
			
		||||
        """
 | 
			
		||||
        return "Default"
 | 
			
		||||
 | 
			
		||||
    @wolflistener("at_game_start")
 | 
			
		||||
    async def _at_game_start(self, data=None):
 | 
			
		||||
        if self.channel_id:
 | 
			
		||||
            await self.game.register_channel(self.channel_id, self)
 | 
			
		||||
 | 
			
		||||
        await self.player.send_dm(self.game_start_message)  # Maybe embeds eventually
 | 
			
		||||
 | 
			
		||||
    async def _at_day_start(self, data=None):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    async def _at_voted(self, data=None):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    async def _at_kill(self, data=None):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    async def _at_hang(self, data=None):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    async def _at_day_end(self, data=None):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    async def _at_night_start(self, data=None):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    async def _at_night_end(self, data=None):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    async def _at_visit(self, data=None):
 | 
			
		||||
        pass
 | 
			
		||||
    # async def _at_day_start(self, data=None):
 | 
			
		||||
    #     pass
 | 
			
		||||
    #
 | 
			
		||||
    # async def _at_voted(self, data=None):
 | 
			
		||||
    #     pass
 | 
			
		||||
    #
 | 
			
		||||
    # async def _at_kill(self, data=None):
 | 
			
		||||
    #     pass
 | 
			
		||||
    #
 | 
			
		||||
    # async def _at_hang(self, data=None):
 | 
			
		||||
    #     pass
 | 
			
		||||
    #
 | 
			
		||||
    # async def _at_day_end(self, data=None):
 | 
			
		||||
    #     pass
 | 
			
		||||
    #
 | 
			
		||||
    # async def _at_night_start(self, data=None):
 | 
			
		||||
    #     pass
 | 
			
		||||
    #
 | 
			
		||||
    # async def _at_night_end(self, data=None):
 | 
			
		||||
    #     pass
 | 
			
		||||
    #
 | 
			
		||||
    # async def _at_visit(self, data=None):
 | 
			
		||||
    #     pass
 | 
			
		||||
 | 
			
		||||
    async def kill(self, source):
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user