Пример #1
0
class MetallicSkin(Skill):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.repeater = Repeat(self.on_cycle, args=(self.parent.parent, ))

    @classproperty
    def description(cls):
        return 'Regenerate health over time. Max 175HP.'

    @classproperty
    def max_level(cls):
        return 4

    @property
    def health(self):
        return 2 + self.level

    def on_cycle(self, player):
        if player.health < 175:
            player.health += self.health

    @events('player_death', 'player_suicide')
    def _on_player_death(self, player, **kwargs):
        self.repeater.stop()

    @events('player_spawn')
    def _on_player_spawn(self, player, **kwargs):
        if self.level == 0:
            return

        self.repeater.start(1)
Пример #2
0
class RepeatSkill(Skill):
    """A skill class which ticks repeatedly."""

    seconds_between_ticks = 1

    def __init__(self, owner, level=0):
        """Initialize the skill. Adds :attr:`_repeat` attribute.

        :param object owner:
            The owner of the skill
        :param int level:
            Initial level of the skill
        """
        super().__init__(owner, level)
        self._repeat = Repeat(self._tick)

    @Skill.level.setter
    def level(self, value):
        Skill.level.fset(value)
        if value == 0:
            self.stop_repeat()
        elif self._repeat.status == RepeatStatus.STOPPED:
            self.start_repeat()

    def start_repeat(self, *args, **kwargs):
        """Start the :attr:`tick_repeat`."""
        self._repeat.start(self.seconds_between_ticks, 0)

    def stop_repeat(self, *args, **kwargs):
        """Stop the :attr:`tick_repeat`."""
        self._repeat.stop()

    def _tick(self):
        """A method to call on every tick of :attr:`_tick_repeat`."""
        raise NotImplementedError
Пример #3
0
def dealPoison(userid, attacker, dmg, time):
    userid = int(userid)
    if userid not in poison_dict:
        poison_dict[userid] = []
    poison_repeat = Repeat(_poison_repeat, (userid, attacker, dmg))
    poison_dict[userid].append(poison_repeat)
    poison_repeat.start(time, execute_on_start=True)
Пример #4
0
def wcs_regeneration_command(command_info, player:convert_userid_to_player, value:int, duration:float, maxhealth:int, maxheal:deprecated, radius:float):
    if player is None:
        return

    repeat = Repeat(_regeneration_repeat, (player.userid, value, maxhealth, radius))
    repeat.start(duration)

    _repeats[player.userid].append(repeat)
Пример #5
0
def cluster_rockets():
    player = Player.from_userid(int(es.ServerVar('wcs_userid')))
    if player.team >= 2:
        nade_repeat = Repeat(create_nade,
                             (player, int(es.ServerVar('wcs_dmg'))))
        nade_repeat.start(0.2, int(es.ServerVar('wcs_rockets')), True)
        es.tell(
            player.userid, '#multi',
            '#green[WCS] #lightgreenYou fired #green%s Cluster Rockets!' %
            int(es.ServerVar('wcs_rockets')))
Пример #6
0
class _EmulateManager(object):
    def __init__(self):
        self._repeat = Repeat(self.tick)
        self._filter = PlayerReadyIter(['bot', 'alive'])

    def tick(self):
        chance = cfg_bot_ability_chance.get_float()

        for _, wcsplayer in self._filter:
            active_race = wcsplayer.active_race

            for skill_name in active_race.settings.config['skills']:
                if random() <= chance:
                    skill = active_race.skills[skill_name]

                    for event in ('player_ultimate', 'player_ability',
                                  'player_ability_on', 'player_ability_off'):
                        if event in skill.config['event']:
                            if skill.is_executable() is SkillReason.ALLOWED:
                                if event == 'player_ultimate':
                                    if skill._type is ModuleType.ESS_INI or skill._type is ModuleType.ESS_KEY:
                                        skill.reset_cooldown()

                                    skill.execute('player_ultimate',
                                                  define=True)
                                elif event == 'player_ability':
                                    if skill._type is ModuleType.ESS_INI or skill._type is ModuleType.ESS_KEY:
                                        skill.reset_cooldown()

                                    skill.execute('player_ability',
                                                  define=True)
                                elif event == 'player_ability_on':
                                    with FakeEvent(
                                            'player_ability_on'
                                            if skill._type is ModuleType.SP
                                            else f'{skill_name}_on',
                                            userid=wcsplayer.userid) as event:
                                        skill.execute(event.name, event)
                                elif event == 'player_ability_off':
                                    with FakeEvent(
                                            'player_ability_off'
                                            if skill._type is ModuleType.SP
                                            else f'{skill_name}_off',
                                            userid=wcsplayer.userid) as event:
                                        skill.execute(event.name, event)

                            break

    def start(self):
        if self._repeat.status != RepeatStatus.RUNNING:
            self._repeat.start(1)

    def stop(self):
        if self._repeat.status == RepeatStatus.RUNNING:
            self._repeat.stop()
Пример #7
0
class HealingWave(Skill):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.repeater = Repeat(self.on_cycle, args=(self.parent.parent, ))

    @classproperty
    def description(cls):
        return 'Heals yourself and teammates within 250 range for up to 175 health.'

    @classproperty
    def max_level(cls):
        return 6

    @property
    def health(self):
        return 4 + self.level

    @property
    def max_health(self):
        return 175

    @property
    def duration(self):
        return 5 - (self.level / 2)

    @property
    def range(self):
        return 250

    _msg_a = '{{GREEN}}Healing Wave {{PALE_GREEN}}will heal {{BLUE}}teammates {{PALE_GREEN}}for {{GREEN}}{health} {{PALE_GREEN}}every {{GREEN}}{duration} seconds.'

    def on_cycle(self, player):
        team = 'ct' if player.team == 2 else 't'
        for target in PlayerIter(is_filters='alive', not_filters=team):
            distance = player.origin.get_distance(target.origin)
            if distance < self.range and target.health < self.max_health:
                target.health += self.health

    @events('player_spawn')
    def _on_player_spawn(self, player, **kwargs):
        if self.level == 0:
            return

        self.repeater.start(self.duration)
        send_wcs_saytext_by_index(
            self._msg_a.format(health=self.health, duration=self.duration),
            player.index)

    @events('player_death')
    def _on_player_death(self, player, **kwargs):
        if self.level == 0:
            return

        self.repeater.stop()
Пример #8
0
class DMPlayer(Player):
    """Class used to interact with a specific player."""

    def __init__(self, index):
        """Store the Repeat instance for the player."""
        super().__init__(index)
        self.repeat = Repeat(self._countdown, cancel_on_level_end=True)

    def start_repeat(self):
        """Start the player's respawn countdown."""
        self.repeat.start(1, max(delay.get_int(), 1))

    def _countdown(self):
        """Send messages about impending respawn and respawns the player."""
        # Is the player alive?
        if not self.dead:

            # No need to respawn them
            return

        # Does the player's repeat have more loops remaining?
        if self.repeat.loops_remaining:

            # Message the player with the countdown
            player_dictionary[self.userid].hint_message(
                'DeathMatch:CountDown',
                seconds=self.repeat.loops_remaining,
            )

        # Are there no more loops remaining for the player?
        else:

            # Message the player that they are respawning
            player_dictionary[self.userid].hint_message(
                'DeathMatch:Respawning'
            )

            # Respawn the player
            self.spawn()

    def stop_repeat(self):
        """Stop the player's repeat."""
        self.repeat.stop()

    def is_repeat_active(self):
        """Return whether the player's repeat is running."""
        return self.repeat.status == RepeatStatus.RUNNING
Пример #9
0
class BeltOfVitality(Item):
    category = "Defensive"
    cost = 2500
    description = "If low on health you'll slowly regenerate your health back up."

    _msg_purchase = '{GREEN}Purchased {BLUE}Belt of Vitality.'

    @classmethod
    def is_available(cls, player):
        item_count = sum(isinstance(item, cls) for item in player.items)
        return player.cash >= cls.cost and not player.dead and item_count < 1

    @classproperty
    def requirement_string(cls):
        return "${}".format(cls.cost)

    @classproperty
    def requirement_sort_key(cls):
        return cls.cost

    def on_purchase(self, player):
        super().on_purchase(player)
        player.cash -= self.cost
        self.repeater = Repeat(self.on_cycle, args=(player, ))
        self.repeater.start(1)
        send_wcs_saytext_by_index(self._msg_purchase, player.index)

    def on_cycle(self, player):
        if player.health < 100:
            player.health += 5

    @events('player_death', 'player_suicide')
    def _on_player_death(self, player, **kwargs):
        self.repeater.stop()
        ## remove the item
        player.items.remove(self)
Пример #10
0
# A dictionary of all the players, uses indexes as keys
g_players = PlayerDictionary(_new_player)

# A dictionary of the heroes from warcraft.heroes.__init__.get_heroes, ordered by required level.
g_heroes = OrderedDict(
    sorted(
        ((hero.class_id, hero) for hero in warcraft.heroes.get_heroes()),
        key=lambda item: item[1].required_level)
)

# Database wrapper for accessing the Warcraft database
g_database = warcraft.database.SQLite(PLUGIN_DATA_PATH / 'warcraft.db')

# A tick repeat for saving everyone's data every 4 minutes
_data_save_repeat = Repeat(_save_all_data)
_data_save_repeat.start(240, 0)

# Translations for the Warcraft plugin
_tr = LangStrings('warcraft')
_hero_info_message = SayText2(_tr['Hero Info'])
_level_up_message = SayText2(_tr['Level Up'])
_skills_reset_message = SayText2(_tr['Skills Reset'])


# ======================================================================
# >> MENUS
# ======================================================================

main_menu = PagedMenu(title=_tr['Main Menu'])

Пример #11
0
class SpawnPowers(Skill):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.longjump = False
        self.repeater = Repeat(self.on_cycle, args=(self.parent.parent, ))
        self.state = None
        self.reduction = 0

    @classproperty
    def description(cls):
        return 'Spawn with either HP, Speed, Longjump, Reduced Gravity or Regeneration. 50-90% chance.'

    @classproperty
    def max_level(cls):
        return 8

    @property
    def chance(self):
        return 50 + (self.level * 5)

    _msg_health = "{GREEN}Spawn Powers {PALE_GREEN}granted you {GREEN}80 health."
    _msg_speed = "{GREEN}Spawn Powers {PALE_GREEN}increased your {BLUE}speed {PALE_GREEN}by {GREEN}35%."
    _msg_longjump = "{GREEN}Spawn Powers {PALE_GREEN}granted you {GREEN}longjump."
    _msg_gravity = "{GREEN}Spawn Powers {PALE_GREEN}granted you {GREEN}reduced gravity."
    _msg_regen = "{GREEN}Spawn Powers {PALE_GREEN}granted you {GREEN}regeneration."

    @property
    def min_gravity(self):
        return 0.1

    @property
    def health(self):
        return 4 + self.level

    @property
    def max_health(self):
        return 175

    @property
    def duration(self):
        return 10 - (self.level / 2)

    @property
    def range(self):
        return 250

    def on_cycle(self, player):
        player.health += self.health

    def reduce_gravity(self, player, value):
        if player.gravity < self.min_gravity:
            player.gravity = max(1 - value, self.min_gravity)
            return
        player.gravity = max(player.gravity - value, self.min_gravity)

    @events('player_pre_run_command')
    def _on_player_run_command(self, player, usercmd, **kwargs):
        if (usercmd.buttons & PlayerButtons.FORWARD
                or usercmd.buttons & PlayerButtons.BACK
                or usercmd.buttons & PlayerButtons.MOVELEFT
                or usercmd.buttons & PlayerButtons.MOVERIGHT
                or usercmd.buttons & PlayerButtons.JUMP):
            if self.state == player.move_type or self.reduction == 0:
                return

            if self.state == MoveType.LADDER:
                Delay(0.5, self.reduce_gravity, args=(player, self.reduction))
            self.state = player.move_type

    @events('player_jump')
    def _on_player_jump(self, player, **kwargs):
        if self.longjump:
            velocity = Vector()
            player.get_velocity(velocity, None)
            velocity.x *= 2
            velocity.y *= 2
            velocity.z = 10
            player.base_velocity = velocity

    @events('player_death', 'player_suicide')
    def _on_player_death(self, *args, **kwargs):
        if self.level == 0:
            return

        self.longjump = False
        self.reduction = 0
        self.repeater.stop()

    @events('player_spawn')
    def _on_player_spawn(self, player, **kwargs):
        if randint(0, 101) > self.chance or self.level == 0:
            return

        num = randint(0, 4)
        if num == 0:  ## Health
            send_wcs_saytext_by_index(self._msg_health, player.index)
            player.health += 80
        elif num == 1:  ## Speed
            send_wcs_saytext_by_index(self._msg_speed, player.index)
            player.speed += 0.35
        elif num == 2:  ## Longjump
            send_wcs_saytext_by_index(self._msg_longjump, player.index)
            self.longjump = True
        elif num == 3:  ## Gravity
            send_wcs_saytext_by_index(self._msg_gravity, player.index)
            self.reduction = 0.4
            Delay(0.5, self.reduce_gravity, args=(player, self.reduction))
        else:
            send_wcs_saytext_by_index(self._msg_regen, player.index)
            self.repeater.start(self.duration)
Пример #12
0
class _GithubManager(dict):
    def __init__(self):
        super().__init__()

        self._counter = 0
        self._repeat = Repeat(self._tick)
        self._checking_new_version = False
        self._installing_new_version = False
        self._refreshing_race_modules = False
        self._refreshing_item_modules = False
        self._refreshing_commits = False

        self._threads = []

        self['races'] = {}
        self['items'] = {}

    def _tick(self):
        if not _output.empty():
            value = _output.get_nowait()

            decrease = value[0]
            listener = value[1]

            if listener is not None:
                try:
                    listener(*value[2:])
                except:
                    except_hooks.print_exception()

            if decrease:
                self._counter -= 1

                if not self._counter:
                    self._repeat.stop()

            for thread in self._threads.copy():
                if not thread.is_alive():
                    self._threads.remove(thread)

    def _connect(self):
        if GITHUB_ACCESS_TOKEN is not None:
            return Github(GITHUB_ACCESS_TOKEN, per_page=100)

        if GITHUB_USERNAME is None or GITHUB_PASSWORD is None:
            return Github(per_page=100)

        return Github(GITHUB_USERNAME, GITHUB_PASSWORD, per_page=100)

    def _check_new_version(self):
        try:
            github = self._connect()
            repo = github.get_repo(f'{info.author.replace(" ", "")}/WCS')

            if (DATA_PATH / 'metadata.wcs_install').isfile():
                valid_version = True

                # Update the metadata file to use JSON instead of just holding the SHA value
                try:
                    with open(DATA_PATH / 'metadata.wcs_install') as inputfile:
                        sha = load(inputfile)['sha']
                except JSONDecodeError:
                    with open(DATA_PATH / 'metadata.wcs_install') as inputfile:
                        sha = inputfile.read()

                    with open(DATA_PATH / 'metadata.wcs_install',
                              'w') as outputfile:
                        dump({'sha': sha}, outputfile, indent=4)
            else:
                if (PLUGIN_PATH / 'info.ini').isfile():
                    with open(PLUGIN_PATH / 'info.ini') as inputfile:
                        for line in inputfile.read().splitlines():
                            if line.startswith('version'):
                                current_version = line.split()[2].strip()[1:-1]
                                break
                        else:
                            current_version = None
                else:
                    current_version = None

                sha = None
                commits = repo.get_commits(
                    path='addons/source-python/plugins/wcs/info.ini')

                if current_version is None:
                    sha = list(commits)[-1].sha
                else:
                    for i, commit in enumerate(commits):
                        for file_ in commit.files:
                            if file_.filename == 'addons/source-python/plugins/wcs/info.ini':
                                for line in file_.patch.splitlines():
                                    if line.startswith('+version'):
                                        old_version = line.split()[2].strip(
                                        )[1:-1]

                                        if old_version == current_version:
                                            sha = commit.sha

                                        break

                                break

                        if sha is not None:
                            break

                        # Couldn't find the correct version after 10 tries, so just set the sha code to the first ever commit
                        # This is done to avoid spending too much time here since it's not realy important
                        if i == 9:
                            sha = '8892447b0d00d65158c6ad908fe0e06289394211'
                            break
                    else:
                        # Couldn't find the correct version, so just set the sha code to the first ever commit
                        sha = '8892447b0d00d65158c6ad908fe0e06289394211'

                valid_version = current_version is not None

            commit = repo.get_commit(sha)
            response = repo.get_commits(since=commit.commit.committer.date)

            if response.totalCount > 1:
                commits = []
                pull_requests = set()

                for response in (list(response)[:-1]
                                 if valid_version else list(response)):
                    commits.append({
                        'date': response.commit.author.date,
                        'author': response.commit.author.name,
                        'messages': response.commit.message
                    })

                    pr_numbers = findall(RE_PULL_REQUEST,
                                         response.commit.message)

                    pull_requests.update(pr_numbers)

                while pull_requests:
                    pr_number = int(pull_requests.pop())

                    pr = repo.get_pull(pr_number)

                    for response in reversed(list(pr.get_commits())):
                        # TODO: For some reason 'response.commit.author.date' return the wrong value for certain commits (test: set sha in metadata.wcs_install to bff42a5d0546c95d87f053c94284cb9afa133007)
                        commits.append({
                            'date': response.commit.author.date,
                            'author': response.commit.author.name,
                            'messages': response.commit.message
                        })

                        pr_numbers = findall(RE_PULL_REQUEST,
                                             response.commit.message)

                        pull_requests.update(pr_numbers)

                # Just in case something goes wrong when retrieving the version
                try:
                    info_file = repo.get_contents(
                        'addons/source-python/plugins/wcs/info.ini')

                    for line in info_file.decoded_content.decode('utf8').split(
                            '\n'):
                        if line.startswith('version'):
                            new_version = line.split()[2].strip()[1:-1]
                            break
                    else:
                        raise ValueError("Unable to locate 'version'.")
                except:
                    except_hooks.print_exception()

                    new_version = f'Unknown (SHA: {sha})'

                _output.put((True, OnGithubNewVersionChecked.manager.notify,
                             new_version, commits))
            else:
                _output.put(
                    (True, OnGithubNewVersionChecked.manager.notify, None, []))
        except:
            _output.put((True, None))
            raise

    def _install_new_version(self):
        try:
            _output.put((False, OnGithubNewVersionUpdating.manager.notify,
                         GithubStatus.PREPARING))

            github = self._connect()
            repo = github.get_repo(f'{info.author.replace(" ", "")}/WCS')

            if (DATA_PATH / 'update_blacklist.txt').isfile():
                with open(DATA_PATH / 'update_blacklist.txt') as inputfile:
                    blacklist = inputfile.read().splitlines()
            else:
                blacklist = []

            if (DATA_PATH / 'metadata.wcs_install').isfile():
                try:
                    with open(DATA_PATH / 'metadata.wcs_install') as inputfile:
                        metadata = load(inputfile)
                except JSONDecodeError:
                    with open(DATA_PATH / 'metadata.wcs_install') as inputfile:
                        sha = inputfile.read()

                    metadata = {'sha': sha}
            else:
                metadata = {}

            _output.put((False, OnGithubNewVersionUpdating.manager.notify,
                         GithubStatus.CONNECTING))

            updated_files = set()

            most_recent_commit_sha = repo.get_branch('master').commit.sha

            if metadata.get('sha') is not None:
                _output.put(
                    (False, OnGithubNewVersionUpdating.manager.notify,
                     GithubStatus.OPTIMIZING, 0, len(updated_files), 0, 0))
                pull_requests = set()

                commit = repo.get_commit(metadata['sha'])

                commits = list(
                    repo.get_commits(since=commit.commit.committer.date))

                for i, response in enumerate(commits[:-1], 1):
                    updated_files.update([x.filename for x in response.files])

                    pr_numbers = findall(RE_PULL_REQUEST,
                                         response.commit.message)

                    pull_requests.update(pr_numbers)

                    _output.put(
                        (False, OnGithubNewVersionUpdating.manager.notify,
                         GithubStatus.OPTIMIZING, 0, len(updated_files), i,
                         len(commits) - 1))

                _output.put(
                    (False, OnGithubNewVersionUpdating.manager.notify,
                     GithubStatus.OPTIMIZING, 1, len(updated_files), 0, 0))

                while pull_requests:
                    pr_number = int(pull_requests.pop())

                    pr = repo.get_pull(pr_number)

                    commits = list(pr.get_commits())

                    for i, response in enumerate(commits, 1):
                        updated_files.update(
                            [x.filename for x in response.files])

                        pr_numbers = findall(RE_PULL_REQUEST,
                                             response.commit.message)

                        pull_requests.update(pr_numbers)

                        _output.put(
                            (False, OnGithubNewVersionUpdating.manager.notify,
                             GithubStatus.OPTIMIZING, 1, len(updated_files), i,
                             len(commits)))

                _output.put((False, OnGithubNewVersionUpdating.manager.notify,
                             GithubStatus.CONNECTING))

            with urlopen(repo.get_archive_link('zipball',
                                               'master')) as response:
                size = response.headers['Content-Length'] or None

                if size is not None:
                    size = int(size)

                _output.put((False, OnGithubNewVersionUpdating.manager.notify,
                             GithubStatus.DOWNLOADING, 0, size))

                with BytesIO() as data:
                    length = 0
                    next_update = time()

                    while True:
                        chunk = response.read(2**16)

                        if not chunk:
                            break

                        length += len(chunk)

                        data.write(chunk)

                        now = time()

                        if now >= next_update:
                            _output.put(
                                (False,
                                 OnGithubNewVersionUpdating.manager.notify,
                                 GithubStatus.DOWNLOADING, length, size))

                            next_update = now + 0.125

                    with ZipFile(data, 'r') as ref:
                        _output.put(
                            (False, OnGithubNewVersionUpdating.manager.notify,
                             GithubStatus.UNZIPPING))

                        files = ref.namelist()
                        unique_name = files[0]

                        files.remove(unique_name)

                        files_count = len(updated_files or files)

                        _output.put(
                            (False, OnGithubNewVersionUpdating.manager.notify,
                             GithubStatus.EXTRACTING, 0, files_count))

                        with TemporaryDirectory() as tmpdir:
                            next_update = time()

                            for i, member in enumerate(updated_files or files,
                                                       1):
                                now = time()

                                if now >= next_update:
                                    _output.put(
                                        (False, OnGithubNewVersionUpdating.
                                         manager.notify,
                                         GithubStatus.EXTRACTING, i,
                                         files_count))

                                    next_update = now + 0.125

                                name = member.replace(unique_name, '')

                                if name in blacklist:
                                    continue

                                ref.extract(unique_name + name, path=tmpdir)

                            _output.put(
                                (False,
                                 OnGithubNewVersionUpdating.manager.notify,
                                 GithubStatus.COPYING))

                            copy_tree(Path(tmpdir) / unique_name, GAME_PATH)

            _output.put((False, OnGithubNewVersionUpdating.manager.notify,
                         GithubStatus.FINISHING))

            metadata['sha'] = most_recent_commit_sha

            with open(DATA_PATH / 'metadata.wcs_install', 'w') as outputfile:
                dump(metadata, outputfile, indent=4)

            _output.put((True, OnGithubNewVersionInstalled.manager.notify))
        except:
            _output.put((True, None))
            raise

    def _refresh_modules(self, module_type_name, update_listener):
        github = self._connect()

        for i, repository in enumerate(GITHUB_REPOSITORIES, 1):
            repo = github.get_repo(repository)

            # Try to get the modules from the repository
            try:
                contents = repo.get_contents(module_type_name)
            # Did not find any modules
            except UnknownObjectException:
                continue

            path = MODULE_PATH / module_type_name

            # The modules from the repository
            modules = list([x.name for x in contents])

            # Are we in the last repository?
            if i == len(GITHUB_REPOSITORIES):
                # Loop through all the modules that has been added, and is not in the modules
                for name in [
                        name for name in self[module_type_name]
                        if name not in modules
                ]:
                    # Send the module to the listener since there's no more repositories that has it
                    _output.put((False, update_listener.manager.notify, name,
                                 self[module_type_name][name]))

            for name in modules:
                if module_type_name not in self or name not in self[
                        module_type_name]:
                    wcs_install_path_old = path / name / '.wcs_install'
                    wcs_install_path = path / name / 'metadata.wcs_install'

                    if wcs_install_path_old.isfile():
                        with open(wcs_install_path_old) as inputfile:
                            repository_installed = inputfile.read()

                        with open(wcs_install_path, 'w') as outputfile:
                            dump(
                                {
                                    'last_updated': wcs_install_path_old.mtime,
                                    'repository': repository_installed
                                },
                                outputfile,
                                indent=4)

                        wcs_install_path_old.remove()

                    if wcs_install_path.isfile():
                        status = GithubModuleStatus.INSTALLED

                        with open(wcs_install_path) as inputfile:
                            data = load(inputfile)

                        last_updated = data['last_updated']
                        repository_installed = data['repository']
                    else:
                        status = GithubModuleStatus.UNINSTALLED

                        last_updated = None
                        repository_installed = None

                    self[module_type_name][name] = {
                        'status': status,
                        'last_updated': last_updated,
                        'repository': repository_installed,
                        'repositories': {
                            repository: {}
                        }
                    }

                    # TODO: Move this out of here (takes over twice as long with this)
                    commits = repo.get_commits(path=module_type_name + '/' +
                                               name + '/')

                    try:
                        self[module_type_name][name]['repositories'][
                            repository]['last_modified'] = commits[
                                0].commit.committer.date.timestamp()
                    except IndexError:
                        self[module_type_name][name]['repositories'][
                            repository]['last_modified'] = None

                # Are we in the last repository?
                if i == len(GITHUB_REPOSITORIES):
                    _output.put((False, update_listener.manager.notify, name,
                                 self[module_type_name][name]))

    def _refresh_race_modules(self):
        try:
            self._refresh_modules('races', OnGithubRaceModuleUpdate)

            _output.put((True, OnGithubRaceModulesRefreshed.manager.notify,
                         self['races']))
        except:
            _output.put((True, None))
            raise

    def _refresh_item_modules(self):
        try:
            self._refresh_modules('items', OnGithubItemModuleUpdate)

            _output.put((True, OnGithubItemModulesRefreshed.manager.notify,
                         self['items']))
        except:
            _output.put((True, None))
            raise

    def _install_module(self, repository, module, name, userid):
        try:
            github = self._connect()
            repo = github.get_repo(repository)

            self._download_module(repo, f'{module}/{name}')

            metadata_path = MODULE_PATH / module / name / 'metadata.wcs_install'
            last_updated = time()

            with open(metadata_path, 'w') as outputfile:
                dump({
                    'last_updated': last_updated,
                    'repository': repository
                },
                     outputfile,
                     indent=4)

            self[module][name]['status'] = GithubModuleStatus.INSTALLED
            self[module][name]['last_updated'] = last_updated
            self[module][name]['repository'] = repository

            _output.put((True, OnGithubModuleInstalled.manager.notify,
                         repository, module, name, userid))
        except:
            _output.put((True, OnGithubModuleFailed.manager.notify, repository,
                         module, name, userid, GithubModuleStatus.INSTALLING))
            raise

    def _update_module(self, repository, module, name, userid):
        try:
            github = self._connect()
            repo = github.get_repo(repository)

            path = MODULE_PATH / module / name
            config_path = path / 'config.json'
            config_tmp_path = path / 'config.tmp.json'

            if config_path.isfile():
                config_path.rename(config_tmp_path)

            self._download_module(repo, f'{module}/{name}')

            if config_tmp_path.isfile():
                with open(config_tmp_path) as inputfile:
                    old_data = load(inputfile)

                with open(config_path) as inputfile:
                    new_data = load(inputfile)

                config_tmp_path.remove()

                if not old_data == new_data:

                    def merge(container, original, updated):
                        for key in updated:
                            if key in original:
                                if isinstance(original[key],
                                              dict) and isinstance(
                                                  updated[key], dict):
                                    container[key] = merge(
                                        OrderedDict(), original[key],
                                        updated[key])
                                else:
                                    container[key] = original[key]
                            else:
                                container[key] = updated[key]

                        return container

                    container = merge(OrderedDict(), old_data, new_data)

                    if module == 'races':
                        # Don't modify the author
                        container['author'] = new_data['author']

                    with open(config_path, 'w') as outputfile:
                        dump(container, outputfile, indent=4)

            metadata_path = MODULE_PATH / module / name / 'metadata.wcs_install'
            last_updated = time()

            with open(metadata_path) as inputfile:
                data = load(inputfile)

            data['last_updated'] = last_updated

            with open(metadata_path, 'w') as outputfile:
                dump(data, outputfile, indent=4)

            self[module][name]['status'] = GithubModuleStatus.INSTALLED
            self[module][name]['last_updated'] = last_updated

            _output.put((True, OnGithubModuleUpdated.manager.notify,
                         repository, module, name, userid))
        except:
            _output.put((True, OnGithubModuleFailed.manager.notify, repository,
                         module, name, userid, GithubModuleStatus.UPDATING))
            raise

    def _uninstall_module(self, repository, module, name, userid):
        try:
            if (MODULE_PATH_ES / module / name).isdir():
                (MODULE_PATH_ES / module / name).rmtree()

            (MODULE_PATH / module / name).rmtree()

            self[module][name]['status'] = GithubModuleStatus.UNINSTALLED
            self[module][name]['last_updated'] = None
            self[module][name]['repository'] = None

            _output.put((True, OnGithubModuleUninstalled.manager.notify,
                         repository, module, name, userid))
        except:
            _output.put(
                (True, OnGithubModuleFailed.manager.notify, repository, module,
                 name, userid, GithubModuleStatus.UNINSTALLING))
            raise

    def _download_module(self, repo, from_path):
        contents = repo.get_contents(from_path)
        name = from_path.split('/')[1]

        for content in contents:
            if content.name.endswith(f'{name}.py') or content.name.endswith(
                    f'es_{name}.txt'):
                path = MODULE_PATH_ES / content.path
            else:
                path = MODULE_PATH / content.path

            if content.type == 'dir':
                if not path.isdir():
                    path.makedirs()

                self._download_module(repo, content.path)
            else:
                if not path.parent.isdir():
                    path.parent.makedirs()

                with open(path, 'wb') as outputfile:
                    outputfile.write(
                        repo.get_contents(content.path).decoded_content)

    def _refresh_commits(self):
        try:
            github = self._connect()
            repo = github.get_repo(f'{info.author.replace(" ", "")}/WCS')

            commits = []

            for response in repo.get_commits():
                commits.append({
                    'date': response.commit.author.date,
                    'author': response.commit.author.name,
                    'messages': response.commit.message
                })

            _output.put(
                (True, OnGithubCommitsRefreshed.manager.notify, commits))
        except:
            _output.put((True, None))
            raise

    def _start_thread(self, target, name, args=None):
        if not self._counter:
            self._repeat.start(0.1)

        self._counter += 1

        thread = Thread(target=target, name=name, args=args or ())
        thread.start()

        self._threads.append(thread)

    def stop(self):
        for thread in self._threads:
            if thread.is_alive():
                thread.join()

        self._repeat.stop()

    def check_new_version(self):
        if self._checking_new_version:
            return

        self._checking_new_version = True

        self._start_thread(self._check_new_version, 'wcs.checking')

    def install_new_version(self):
        if self._installing_new_version:
            return

        self._installing_new_version = True

        self._start_thread(self._install_new_version, 'wcs.installing')

    def refresh_race_modules(self):
        if self._refreshing_race_modules:
            return

        self._refreshing_race_modules = True

        OnGithubRaceModulesRefresh.manager.notify()

        self._start_thread(self._refresh_race_modules,
                           'wcs.refresh.race.modules')

    def refresh_item_modules(self):
        if self._refreshing_item_modules:
            return

        self._refreshing_itemmodules = True

        OnGithubItemModulesRefresh.manager.notify()

        self._start_thread(self._refresh_item_modules,
                           'wcs.refresh.item.modules')

    def install_module(self, repository, module, name, userid=None):
        assert self[module][name]['status'] is GithubModuleStatus.UNINSTALLED

        self[module][name]['status'] = GithubModuleStatus.INSTALLING

        self._start_thread(self._install_module,
                           f'wcs.install.{module}.{name}',
                           (repository, module, name, userid))

    def update_module(self, module, name, userid=None):
        assert self[module][name]['status'] is GithubModuleStatus.INSTALLED

        self[module][name]['status'] = GithubModuleStatus.UPDATING

        self._start_thread(
            self._update_module, f'wcs.update.{module}.{name}',
            (self[module][name]['repository'], module, name, userid))

    def uninstall_module(self, module, name, userid=None):
        assert self[module][name]['status'] is GithubModuleStatus.INSTALLED

        self[module][name]['status'] = GithubModuleStatus.UNINSTALLING

        self._start_thread(
            self._uninstall_module, f'wcs.uninstall.{module}.{name}',
            (self[module][name]['repository'], module, name, userid))

    def refresh_commits(self):
        if self._refreshing_commits:
            return

        self._refreshing_commits = True

        OnGithubCommitsRefresh.manager.notify()

        self._start_thread(self._refresh_commits, 'wcs.refresh.commits')
Пример #13
0
class _GithubManager(dict):
    def __init__(self):
        super().__init__()

        self._counter = 0
        self._repeat = Repeat(self._tick)
        self._checking_new_version = False
        self._installing_new_version = False
        self._refreshing_modules = False
        self._refreshing_commits = False

        self._threads = []

        self['races'] = {}
        self['items'] = {}

    def _tick(self):
        if not _output.empty():
            self._counter -= 1

            if not self._counter:
                self._repeat.stop()

            items = _output.get_nowait()

            if items is not None:
                if isinstance(items, tuple):
                    items[0](*items[1:])
                else:
                    items()

            for thread in self._threads.copy():
                if not thread.is_alive():
                    self._threads.remove(thread)

    def _connect(self):
        if GITHUB_ACCESS_TOKEN is not None:
            return Github(GITHUB_ACCESS_TOKEN, per_page=100)

        if GITHUB_USERNAME is None or GITHUB_PASSWORD is None:
            return Github(per_page=100)

        return Github(GITHUB_USERNAME, GITHUB_PASSWORD, per_page=100)

    def _check_new_version(self):
        try:
            _github = self._connect()

            _repo = _github.get_repo(f'{info.author.replace(" ", "")}/WCS')
            info_file = _repo.get_contents(
                'addons/source-python/plugins/wcs/info.ini')

            for line in info_file.decoded_content.decode('utf8').split('\n'):
                if line.startswith('version'):
                    new_version = line.split()[2].strip()[1:-1]
                    break
            else:
                raise ValueError("Unable to locate 'version'.")

            if (DATA_PATH / 'metadata.wcs_install').isfile():
                valid_version = True

                with open(DATA_PATH / 'metadata.wcs_install') as inputfile:
                    sha = inputfile.read()
            else:
                if (PLUGIN_PATH / 'info.ini').isfile():
                    with open(PLUGIN_PATH / 'info.ini') as inputfile:
                        for line in inputfile.read().splitlines():
                            if line.startswith('version'):
                                current_version = line.split()[2].strip()[1:-1]
                                break
                        else:
                            current_version = None
                else:
                    current_version = None

                sha = None
                commits = _repo.get_commits(
                    path='addons/source-python/plugins/wcs/info.ini')

                if current_version is None:
                    sha = list(commits)[-1].sha
                else:
                    for commit in commits:
                        for file_ in commit.files:
                            if file_.filename == 'addons/source-python/plugins/wcs/info.ini':
                                for line in file_.patch.splitlines():
                                    if line.startswith('+version'):
                                        old_version = line.split()[2].strip(
                                        )[1:-1]

                                        if old_version == current_version:
                                            sha = commit.sha

                                        break

                                break

                        if sha is not None:
                            break

                valid_version = current_version is not None

            commit = _repo.get_commit(sha)
            response = _repo.get_commits(since=commit.commit.committer.date)

            if response.totalCount > 1:
                commits = []

                for response in (list(response)[:-1]
                                 if valid_version else list(response)):
                    commits.append({
                        'date': response.commit.author.date,
                        'author': response.commit.author.name,
                        'messages': response.commit.message
                    })

                _output.put((OnGithubNewVersionChecked.manager.notify,
                             new_version, commits))
            else:
                _output.put(
                    (OnGithubNewVersionChecked.manager.notify, None, []))
        except:
            _output.put(None)
            raise

    def _install_new_version(self):
        try:
            _github = self._connect()

            _repo = _github.get_repo(f'{info.author.replace(" ", "")}/WCS')

            if (DATA_PATH / 'update_blacklist.txt').isfile():
                with open(DATA_PATH / 'update_blacklist.txt') as inputfile:
                    blacklist = inputfile.read().splitlines()
            else:
                blacklist = []

            with urlopen(_repo.get_archive_link('zipball',
                                                'master')) as response:
                with ZipFile(BytesIO(response.read()), 'r') as ref:
                    files = ref.namelist()
                    unique_name = files[0]

                    files.remove(unique_name)

                    with TemporaryDirectory() as tmpdir:
                        for member in files:
                            name = member.replace(unique_name, '')

                            if name in blacklist:
                                continue

                            ref.extract(member, path=tmpdir)

                        copy_tree(Path(tmpdir) / unique_name, GAME_PATH)

            commits = _repo.get_commits()
            sha = commits[0].sha

            with open(DATA_PATH / 'metadata.wcs_install', 'w') as outputfile:
                outputfile.write(sha)

            _output.put(OnGithubNewVersionInstalled.manager.notify)
        except:
            _output.put(None)
            raise

    def _refresh_modules(self):
        try:
            _github = self._connect()

            for repository in GITHUB_REPOSITORIES:
                _repo = _github.get_repo(repository)
                modules = _repo.get_contents('')

                modules_left = {}

                for module in [x.name for x in modules if x.name in self]:
                    contents = _repo.get_contents(module)
                    path = MODULE_PATH / module
                    modules_left[module] = []

                    for content in contents:
                        modules_left[module].append(content.name)

                        if module not in self or content.name not in self[
                                module]:
                            wcs_install_path = path / content.name / '.wcs_install'

                            if wcs_install_path.isfile():
                                status = GithubStatus.INSTALLED

                                last_updated = wcs_install_path.mtime

                                with open(wcs_install_path) as inputfile:
                                    repository_installed = inputfile.read()
                            else:
                                status = GithubStatus.UNINSTALLED

                                last_updated = None
                                repository_installed = None

                            self[module][content.name] = {
                                'status': status,
                                'last_updated': last_updated,
                                'repository': repository_installed,
                                'repositories': {
                                    repository: {}
                                }
                            }

                commits = _repo.get_commits()

                for commit in commits:
                    last_modified = commit.commit.committer.date.timestamp()

                    for file_ in commit.files:
                        tmp = file_.filename.split('/')
                        module = tmp[0]

                        if module in modules_left:
                            name = tmp[1]

                            if name in modules_left[module]:
                                self[module][name]['repositories'][repository][
                                    'last_modified'] = last_modified
                                modules_left[module].remove(name)

                                if not modules_left[module]:
                                    del modules_left[module]

                    if not modules_left:
                        break

            _output.put((OnGithubModulesRefreshed.manager.notify,
                         self['races'], self['items']))
        except:
            _output.put(None)
            raise

    def _install_module(self, repository, module, name, userid):
        try:
            _github = self._connect()
            _repo = _github.get_repo(repository)

            self._download_module(_repo, f'{module}/{name}')

            _path = MODULE_PATH / module / name / '.wcs_install'

            with open(_path, 'w') as outputfile:
                outputfile.write(repository)

            self[module][name]['status'] = GithubStatus.INSTALLED
            self[module][name]['last_updated'] = _path.mtime
            self[module][name]['repository'] = repository

            _output.put((OnGithubModuleInstalled.manager.notify, repository,
                         module, name, userid))
        except:
            _output.put((OnGithubModuleFailed.manager.notify, repository,
                         module, name, userid, GithubStatus.INSTALLING))
            raise

    def _update_module(self, repository, module, name, userid):
        try:
            _github = self._connect()
            _repo = _github.get_repo(repository)

            path = MODULE_PATH / module / name
            config_path = path / 'config.json'
            config_tmp_path = path / 'config.tmp.json'

            if config_path.isfile():
                config_path.rename(config_tmp_path)

            self._download_module(_repo, f'{module}/{name}')

            if config_tmp_path.isfile():
                with open(config_tmp_path) as inputfile:
                    old_data = load(inputfile)

                with open(config_path) as inputfile:
                    new_data = load(inputfile)

                config_tmp_path.remove()

                if not old_data == new_data:

                    def merge(container, original, updated):
                        for key in updated:
                            if key in original:
                                if isinstance(original[key],
                                              dict) and isinstance(
                                                  updated[key], dict):
                                    container[key] = merge(
                                        OrderedDict(), original[key],
                                        updated[key])
                                else:
                                    container[key] = original[key]
                            else:
                                container[key] = updated[key]

                        return container

                    container = merge(OrderedDict(), old_data, new_data)

                    if module == 'races':
                        # Don't modify the author
                        container['author'] = new_data['author']

                    with open(config_path, 'w') as outputfile:
                        dump(container, outputfile, indent=4)

            _path = MODULE_PATH / module / name / '.wcs_install'

            # Update the installation file, so we know it's been updated
            _path.utime(None)

            self[module][name]['status'] = GithubStatus.INSTALLED
            self[module][name]['last_updated'] = _path.mtime

            _output.put((OnGithubModuleUpdated.manager.notify, repository,
                         module, name, userid))
        except:
            _output.put((OnGithubModuleFailed.manager.notify, repository,
                         module, name, userid, GithubStatus.UPDATING))
            raise

    def _uninstall_module(self, repository, module, name, userid):
        try:
            if (MODULE_PATH_ES / module / name).isdir():
                (MODULE_PATH_ES / module / name).rmtree()

            (MODULE_PATH / module / name).rmtree()

            self[module][name]['status'] = GithubStatus.UNINSTALLED
            self[module][name]['last_updated'] = None
            self[module][name]['repository'] = None

            _output.put((OnGithubModuleUninstalled.manager.notify, repository,
                         module, name, userid))
        except:
            _output.put((OnGithubModuleFailed.manager.notify, repository,
                         module, name, userid, GithubStatus.UNINSTALLING))
            raise

    def _download_module(self, repository, from_path):
        contents = repository.get_contents(from_path)
        name = from_path.split('/')[1]

        for content in contents:
            if content.name.endswith(f'{name}.py') or content.name.endswith(
                    f'es_{name}.txt'):
                path = MODULE_PATH_ES / content.path
            else:
                path = MODULE_PATH / content.path

            if content.type == 'dir':
                if not path.isdir():
                    path.makedirs()

                self._download_module(repository, content.path)
            else:
                if not path.parent.isdir():
                    path.parent.makedirs()

                with open(path, 'wb') as outputfile:
                    outputfile.write(
                        repository.get_contents(content.path).decoded_content)

    def _refresh_commits(self):
        try:
            _github = self._connect()
            _repo = _github.get_repo(f'{info.author.replace(" ", "")}/WCS')

            commits = []

            for response in _repo.get_commits():
                commits.append({
                    'date': response.commit.author.date,
                    'author': response.commit.author.name,
                    'messages': response.commit.message
                })

            _output.put((OnGithubCommitsRefreshed.manager.notify, commits))
        except:
            _output.put(None)
            raise

    if Github is None:

        def stop(self):
            pass

        def check_new_version(self):
            pass

        def install_new_version(self):
            pass

        def refresh_modules(self):
            pass

        def install_module(self, repository, module, name, userid=None):
            pass

        def update_module(self, repository, module, name, userid=None):
            pass

        def uninstall_module(self, repository, module, name, userid=None):
            pass

        def refresh_commits(self):
            pass
    else:

        def stop(self):
            for thread in self._threads:
                if thread.is_alive():
                    thread.join()

            self._repeat.stop()

        def check_new_version(self):
            if self._checking_new_version:
                return

            self._checking_new_version = True

            if not self._counter:
                self._repeat.start(0.1)

            self._counter += 1

            thread = Thread(target=self._check_new_version,
                            name='wcs.checking')
            thread.start()

            self._threads.append(thread)

        def install_new_version(self):
            if self._installing_new_version:
                return

            self._installing_new_version = True

            if not self._counter:
                self._repeat.start(0.1)

            self._counter += 1

            thread = Thread(target=self._install_new_version,
                            name='wcs.installing')
            thread.start()

            self._threads.append(thread)

        def refresh_modules(self):
            if self._refreshing_modules:
                return

            self._refreshing_modules = True

            if not self._counter:
                self._repeat.start(0.1)

            self._counter += 1

            OnGithubModulesRefresh.manager.notify()

            thread = Thread(target=self._refresh_modules,
                            name='wcs.refresh.modules')
            thread.start()

            self._threads.append(thread)

        def install_module(self, repository, module, name, userid=None):
            assert self[module][name]['status'] is GithubStatus.UNINSTALLED

            if not self._counter:
                self._repeat.start(0.1)

            self._counter += 1

            self[module][name]['status'] = GithubStatus.INSTALLING

            thread = Thread(target=self._install_module,
                            name=f'wcs.install.{module}.{name}',
                            args=(repository, module, name, userid))
            thread.start()

            self._threads.append(thread)

        def update_module(self, module, name, userid=None):
            assert self[module][name]['status'] is GithubStatus.INSTALLED

            if not self._counter:
                self._repeat.start(0.1)

            self._counter += 1

            self[module][name]['status'] = GithubStatus.UPDATING

            thread = Thread(target=self._update_module,
                            name=f'wcs.update.{module}.{name}',
                            args=(self[module][name]['repository'], module,
                                  name, userid))
            thread.start()

            self._threads.append(thread)

        def uninstall_module(self, module, name, userid=None):
            assert self[module][name]['status'] is GithubStatus.INSTALLED

            if not self._counter:
                self._repeat.start(0.1)

            self._counter += 1

            self[module][name]['status'] = GithubStatus.UNINSTALLING

            thread = Thread(target=self._uninstall_module,
                            name=f'wcs.uninstall.{module}.{name}',
                            args=(self[module][name]['repository'], module,
                                  name, userid))
            thread.start()

            self._threads.append(thread)

        def refresh_commits(self):
            if self._refreshing_commits:
                return

            self._refreshing_commits = True

            if not self._counter:
                self._repeat.start(0.1)

            self._counter += 1

            OnGithubCommitsRefresh.manager.notify()

            thread = Thread(target=self._refresh_commits,
                            name='wcs.refresh.commits')
            thread.start()

            self._threads.append(thread)
Пример #14
0
class _WardManager(AutoUnload, list):
    def __init__(self):
        super().__init__()

        self._repeat = Repeat(self._tick)
        self._filter = PlayerReadyIter(is_filters='alive')

    def _unload_instance(self):
        if self._repeat.status == RepeatStatus.RUNNING:
            self._repeat.stop()

    def _tick(self):
        now = time()
        players = {
            wcsplayer: player.origin
            for player, wcsplayer in self._filter
        }

        for wcsplayer in list(players.keys()):
            if wcsplayer.player.dead:
                warn(f'Player "{wcsplayer.name}" should NOT be here')

                del players[wcsplayer]

        ignore = TraceFilterSimple(PlayerIter())

        for ward in self.copy():
            if ward._next_tick <= now:
                try:
                    ward.on_tick()
                except:
                    except_hooks.print_exception()

                    try:
                        ward.on_disappear()
                    except:
                        except_hooks.print_exception()

                    self.remove(ward)

                    continue
                else:
                    ward._next_tick = now + ward.tick_interval

                    ward.duration -= 1

                    if not ward.duration:
                        try:
                            ward.on_disappear()
                        except:
                            except_hooks.print_exception()

                        self.remove(ward)

            entities = ward.entities
            team = ward.team_target

            for wcsplayer, origin in players.items():
                if team is None or wcsplayer.player.team == team:
                    trace = GameTrace()
                    ray = Ray(ward.origin, origin)

                    engine_trace.trace_ray(ray, ContentMasks.ALL, ignore,
                                           trace)

                    if ward.has_within(origin) and not trace.did_hit_world():
                        if wcsplayer in entities:
                            if entities[wcsplayer] <= now:
                                entities[
                                    wcsplayer] = now + ward.update_interval

                                try:
                                    ward.on_update(wcsplayer)
                                except:
                                    except_hooks.print_exception()
                        else:
                            entities[wcsplayer] = now + ward.update_interval

                            try:
                                ward.on_enter(wcsplayer)
                            except:
                                except_hooks.print_exception()
                    else:
                        if wcsplayer in entities:
                            del entities[wcsplayer]

                            try:
                                ward.on_exit(wcsplayer)
                            except:
                                except_hooks.print_exception()

    def append(self, ward):
        if not self:
            self._repeat.start(0.1)

        try:
            ward.on_spawned()
        except:
            except_hooks.print_exception()

        super().append(ward)

    def remove(self, ward):
        super().remove(ward)

        try:
            ward.on_removed()
        except:
            except_hooks.print_exception()

        if not self:
            self._repeat.stop()
Пример #15
0
class _GithubManager(dict):
    def __init__(self):
        super().__init__()

        self._counter = 0
        self._repeat = Repeat(self._tick)
        self._checking_new_version = False
        self._installing_new_version = False
        self._refreshing_modules = False
        self._refreshing_commits = False

        self._threads = []

        self['races'] = {}
        self['items'] = {}

    def _tick(self):
        if not _output.empty():
            value = _output.get_nowait()

            decrease = value[0]
            listener = value[1]

            if listener is not None:
                try:
                    listener(*value[2:])
                except:
                    except_hooks.print_exception()

            if decrease:
                self._counter -= 1

                if not self._counter:
                    self._repeat.stop()

            for thread in self._threads.copy():
                if not thread.is_alive():
                    self._threads.remove(thread)

    def _connect(self):
        if GITHUB_ACCESS_TOKEN is not None:
            return Github(GITHUB_ACCESS_TOKEN, per_page=100)

        if GITHUB_USERNAME is None or GITHUB_PASSWORD is None:
            return Github(per_page=100)

        return Github(GITHUB_USERNAME, GITHUB_PASSWORD, per_page=100)

    def _check_new_version(self):
        try:
            github = self._connect()
            repo = github.get_repo(f'{info.author.replace(" ", "")}/WCS')

            if (DATA_PATH / 'metadata.wcs_install').isfile():
                valid_version = True

                # Update the metadata file to use JSON instead of just holding the SHA value
                try:
                    with open(DATA_PATH / 'metadata.wcs_install') as inputfile:
                        sha = load(inputfile)['sha']
                except JSONDecodeError:
                    with open(DATA_PATH / 'metadata.wcs_install') as inputfile:
                        sha = inputfile.read()

                    with open(DATA_PATH / 'metadata.wcs_install',
                              'w') as outputfile:
                        dump({'sha': sha}, outputfile, indent=4)
            else:
                if (PLUGIN_PATH / 'info.ini').isfile():
                    with open(PLUGIN_PATH / 'info.ini') as inputfile:
                        for line in inputfile.read().splitlines():
                            if line.startswith('version'):
                                current_version = line.split()[2].strip()[1:-1]
                                break
                        else:
                            current_version = None
                else:
                    current_version = None

                sha = None
                commits = repo.get_commits(
                    path='addons/source-python/plugins/wcs/info.ini')

                if current_version is None:
                    sha = list(commits)[-1].sha
                else:
                    for commit in commits:
                        for file_ in commit.files:
                            if file_.filename == 'addons/source-python/plugins/wcs/info.ini':
                                for line in file_.patch.splitlines():
                                    if line.startswith('+version'):
                                        old_version = line.split()[2].strip(
                                        )[1:-1]

                                        if old_version == current_version:
                                            sha = commit.sha

                                        break

                                break

                        if sha is not None:
                            break

                valid_version = current_version is not None

            commit = repo.get_commit(sha)
            response = repo.get_commits(since=commit.commit.committer.date)

            if response.totalCount > 1:
                commits = []

                for response in (list(response)[:-1]
                                 if valid_version else list(response)):
                    commits.append({
                        'date': response.commit.author.date,
                        'author': response.commit.author.name,
                        'messages': response.commit.message
                    })

                # Just in case something goes wrong when retrieving the version
                try:
                    info_file = repo.get_contents(
                        'addons/source-python/plugins/wcs/info.ini')

                    for line in info_file.decoded_content.decode('utf8').split(
                            '\n'):
                        if line.startswith('version'):
                            new_version = line.split()[2].strip()[1:-1]
                            break
                    else:
                        raise ValueError("Unable to locate 'version'.")
                except:
                    except_hooks.print_exception()

                    new_version = f'Unknown (SHA: {sha})'

                _output.put((True, OnGithubNewVersionChecked.manager.notify,
                             new_version, commits))
            else:
                _output.put(
                    (True, OnGithubNewVersionChecked.manager.notify, None, []))
        except:
            _output.put((True, None))
            raise

    def _install_new_version(self):
        try:
            _output.put((False, OnGithubNewVersionUpdating.manager.notify,
                         GithubStatus.PREPARING))

            github = self._connect()
            repo = github.get_repo(f'{info.author.replace(" ", "")}/WCS')

            if (DATA_PATH / 'update_blacklist.txt').isfile():
                with open(DATA_PATH / 'update_blacklist.txt') as inputfile:
                    blacklist = inputfile.read().splitlines()
            else:
                blacklist = []

            if (DATA_PATH / 'metadata.wcs_install').isfile():
                try:
                    with open(DATA_PATH / 'metadata.wcs_install') as inputfile:
                        metadata = load(inputfile)
                except JSONDecodeError:
                    with open(DATA_PATH / 'metadata.wcs_install') as inputfile:
                        sha = inputfile.read()

                    metadata = {'sha': sha}
            else:
                metadata = {}

            updated_files = []

            most_recent_commit_sha = repo.get_branch('master').commit.sha

            if metadata.get('sha') is not None:
                difference = repo.compare(metadata['sha'],
                                          most_recent_commit_sha)

                updated_files.extend([x.filename for x in difference.files])

            _output.put((False, OnGithubNewVersionUpdating.manager.notify,
                         GithubStatus.CONNECTING))

            with urlopen(repo.get_archive_link('zipball',
                                               'master')) as response:
                size = response.headers['Content-Length'] or None

                if size is not None:
                    size = int(size)

                _output.put((False, OnGithubNewVersionUpdating.manager.notify,
                             GithubStatus.DOWNLOADING, 0, size))

                with BytesIO() as data:
                    length = 0
                    next_update = time()

                    while True:
                        chunk = response.read(2**16)

                        if not chunk:
                            break

                        length += len(chunk)

                        data.write(chunk)

                        now = time()

                        if now >= next_update:
                            _output.put(
                                (False,
                                 OnGithubNewVersionUpdating.manager.notify,
                                 GithubStatus.DOWNLOADING, length, size))

                            next_update = now + 0.125

                    with ZipFile(data, 'r') as ref:
                        _output.put(
                            (False, OnGithubNewVersionUpdating.manager.notify,
                             GithubStatus.UNZIPPING))

                        files = ref.namelist()
                        unique_name = files[0]

                        files.remove(unique_name)

                        files_count = len(updated_files or files)

                        _output.put(
                            (False, OnGithubNewVersionUpdating.manager.notify,
                             GithubStatus.EXTRACTING, 0, files_count))

                        with TemporaryDirectory() as tmpdir:
                            next_update = time()

                            for i, member in enumerate(updated_files or files,
                                                       1):
                                now = time()

                                if now >= next_update:
                                    _output.put(
                                        (False, OnGithubNewVersionUpdating.
                                         manager.notify,
                                         GithubStatus.EXTRACTING, i,
                                         files_count))

                                    next_update = now + 0.125

                                name = member.replace(unique_name, '')

                                if name in blacklist:
                                    continue

                                ref.extract(unique_name + name, path=tmpdir)

                            _output.put(
                                (False,
                                 OnGithubNewVersionUpdating.manager.notify,
                                 GithubStatus.COPYING))

                            copy_tree(Path(tmpdir) / unique_name, GAME_PATH)

            _output.put((False, OnGithubNewVersionUpdating.manager.notify,
                         GithubStatus.FINISHING))

            metadata['sha'] = most_recent_commit_sha

            with open(DATA_PATH / 'metadata.wcs_install', 'w') as outputfile:
                dump(metadata, outputfile, indent=4)

            _output.put((True, OnGithubNewVersionInstalled.manager.notify))
        except:
            _output.put((True, None))
            raise

    def _refresh_modules(self):
        try:
            github = self._connect()

            for repository in GITHUB_REPOSITORIES:
                repo = github.get_repo(repository)
                modules = repo.get_contents('')

                modules_left = {}

                for module in [x.name for x in modules if x.name in self]:
                    contents = repo.get_contents(module)
                    path = MODULE_PATH / module
                    modules_left[module] = []

                    for content in contents:
                        modules_left[module].append(content.name)

                        if module not in self or content.name not in self[
                                module]:
                            wcs_install_path_old = path / content.name / '.wcs_install'
                            wcs_install_path = path / content.name / 'metadata.wcs_install'

                            # Update the metadata file to use JSON as well as storing last updated and repository
                            if wcs_install_path_old.isfile():
                                with open(wcs_install_path_old) as inputfile:
                                    repository_installed = inputfile.read()

                                with open(wcs_install_path, 'w') as outputfile:
                                    dump(
                                        {
                                            'last_updated':
                                            wcs_install_path_old.mtime,
                                            'repository': repository_installed
                                        },
                                        outputfile,
                                        indent=4)

                                wcs_install_path_old.remove()

                            if wcs_install_path.isfile():
                                status = GithubModuleStatus.INSTALLED

                                with open(wcs_install_path) as inputfile:
                                    data = load(inputfile)

                                last_updated = data['last_updated']
                                repository_installed = data['repository']
                            else:
                                status = GithubModuleStatus.UNINSTALLED

                                last_updated = None
                                repository_installed = None

                            self[module][content.name] = {
                                'status': status,
                                'last_updated': last_updated,
                                'repository': repository_installed,
                                'repositories': {
                                    repository: {}
                                }
                            }

                commits = repo.get_commits()

                for commit in commits:
                    last_modified = commit.commit.committer.date.timestamp()

                    for file_ in commit.files:
                        tmp = file_.filename.split('/')
                        module = tmp[0]

                        if module in modules_left:
                            name = tmp[1]

                            if name in modules_left[module]:
                                self[module][name]['repositories'][repository][
                                    'last_modified'] = last_modified
                                modules_left[module].remove(name)

                                if not modules_left[module]:
                                    del modules_left[module]

                    if not modules_left:
                        break

            _output.put((True, OnGithubModulesRefreshed.manager.notify,
                         self['races'], self['items']))
        except:
            _output.put((True, None))
            raise

    def _install_module(self, repository, module, name, userid):
        try:
            github = self._connect()
            repo = github.get_repo(repository)

            self._download_module(repo, f'{module}/{name}')

            metadata_path = MODULE_PATH / module / name / 'metadata.wcs_install'
            last_updated = time()

            with open(metadata_path, 'w') as outputfile:
                dump({
                    'last_updated': last_updated,
                    'repository': repository
                },
                     outputfile,
                     indent=4)

            self[module][name]['status'] = GithubModuleStatus.INSTALLED
            self[module][name]['last_updated'] = last_updated
            self[module][name]['repository'] = repository

            _output.put((True, OnGithubModuleInstalled.manager.notify,
                         repository, module, name, userid))
        except:
            _output.put((True, OnGithubModuleFailed.manager.notify, repository,
                         module, name, userid, GithubModuleStatus.INSTALLING))
            raise

    def _update_module(self, repository, module, name, userid):
        try:
            github = self._connect()
            repo = github.get_repo(repository)

            path = MODULE_PATH / module / name
            config_path = path / 'config.json'
            config_tmp_path = path / 'config.tmp.json'

            if config_path.isfile():
                config_path.rename(config_tmp_path)

            self._download_module(repo, f'{module}/{name}')

            if config_tmp_path.isfile():
                with open(config_tmp_path) as inputfile:
                    old_data = load(inputfile)

                with open(config_path) as inputfile:
                    new_data = load(inputfile)

                config_tmp_path.remove()

                if not old_data == new_data:

                    def merge(container, original, updated):
                        for key in updated:
                            if key in original:
                                if isinstance(original[key],
                                              dict) and isinstance(
                                                  updated[key], dict):
                                    container[key] = merge(
                                        OrderedDict(), original[key],
                                        updated[key])
                                else:
                                    container[key] = original[key]
                            else:
                                container[key] = updated[key]

                        return container

                    container = merge(OrderedDict(), old_data, new_data)

                    if module == 'races':
                        # Don't modify the author
                        container['author'] = new_data['author']

                    with open(config_path, 'w') as outputfile:
                        dump(container, outputfile, indent=4)

            metadata_path = MODULE_PATH / module / name / 'metadata.wcs_install'
            last_updated = time()

            with open(metadata_path) as inputfile:
                data = load(inputfile)

            data['last_updated'] = last_updated

            with open(metadata_path, 'w') as outputfile:
                dump(data, outputfile, indent=4)

            self[module][name]['status'] = GithubModuleStatus.INSTALLED
            self[module][name]['last_updated'] = last_updated

            _output.put((True, OnGithubModuleUpdated.manager.notify,
                         repository, module, name, userid))
        except:
            _output.put((True, OnGithubModuleFailed.manager.notify, repository,
                         module, name, userid, GithubModuleStatus.UPDATING))
            raise

    def _uninstall_module(self, repository, module, name, userid):
        try:
            if (MODULE_PATH_ES / module / name).isdir():
                (MODULE_PATH_ES / module / name).rmtree()

            (MODULE_PATH / module / name).rmtree()

            self[module][name]['status'] = GithubModuleStatus.UNINSTALLED
            self[module][name]['last_updated'] = None
            self[module][name]['repository'] = None

            _output.put((True, OnGithubModuleUninstalled.manager.notify,
                         repository, module, name, userid))
        except:
            _output.put(
                (True, OnGithubModuleFailed.manager.notify, repository, module,
                 name, userid, GithubModuleStatus.UNINSTALLING))
            raise

    def _download_module(self, repo, from_path):
        contents = repo.get_contents(from_path)
        name = from_path.split('/')[1]

        for content in contents:
            if content.name.endswith(f'{name}.py') or content.name.endswith(
                    f'es_{name}.txt'):
                path = MODULE_PATH_ES / content.path
            else:
                path = MODULE_PATH / content.path

            if content.type == 'dir':
                if not path.isdir():
                    path.makedirs()

                self._download_module(repo, content.path)
            else:
                if not path.parent.isdir():
                    path.parent.makedirs()

                with open(path, 'wb') as outputfile:
                    outputfile.write(
                        repo.get_contents(content.path).decoded_content)

    def _refresh_commits(self):
        try:
            github = self._connect()
            repo = github.get_repo(f'{info.author.replace(" ", "")}/WCS')

            commits = []

            for response in repo.get_commits():
                commits.append({
                    'date': response.commit.author.date,
                    'author': response.commit.author.name,
                    'messages': response.commit.message
                })

            _output.put(
                (True, OnGithubCommitsRefreshed.manager.notify, commits))
        except:
            _output.put((True, None))
            raise

    def _start_thread(self, target, name, args=None):
        if not self._counter:
            self._repeat.start(0.1)

        self._counter += 1

        thread = Thread(target=target, name=name, args=args or ())
        thread.start()

        self._threads.append(thread)

    def stop(self):
        for thread in self._threads:
            if thread.is_alive():
                thread.join()

        self._repeat.stop()

    def check_new_version(self):
        if self._checking_new_version:
            return

        self._checking_new_version = True

        self._start_thread(self._check_new_version, 'wcs.checking')

    def install_new_version(self):
        if self._installing_new_version:
            return

        self._installing_new_version = True

        self._start_thread(self._install_new_version, 'wcs.installing')

    def refresh_modules(self):
        if self._refreshing_modules:
            return

        self._refreshing_modules = True

        OnGithubModulesRefresh.manager.notify()

        self._start_thread(self._refresh_modules, 'wcs.refresh.modules')

    def install_module(self, repository, module, name, userid=None):
        assert self[module][name]['status'] is GithubModuleStatus.UNINSTALLED

        self[module][name]['status'] = GithubModuleStatus.INSTALLING

        self._start_thread(self._install_module,
                           f'wcs.install.{module}.{name}',
                           (repository, module, name, userid))

    def update_module(self, module, name, userid=None):
        assert self[module][name]['status'] is GithubModuleStatus.INSTALLED

        self[module][name]['status'] = GithubModuleStatus.UPDATING

        self._start_thread(
            self._update_module, f'wcs.update.{module}.{name}',
            (self[module][name]['repository'], module, name, userid))

    def uninstall_module(self, module, name, userid=None):
        assert self[module][name]['status'] is GithubModuleStatus.INSTALLED

        self[module][name]['status'] = GithubModuleStatus.UNINSTALLING

        self._start_thread(
            self._uninstall_module, f'wcs.uninstall.{module}.{name}',
            (self[module][name]['repository'], module, name, userid))

    def refresh_commits(self):
        if self._refreshing_commits:
            return

        self._refreshing_commits = True

        OnGithubCommitsRefresh.manager.notify()

        self._start_thread(self._refresh_commits, 'wcs.refresh.commits')
Пример #16
0
class _GithubManager(dict):
    def __init__(self):
        super().__init__()

        self._counter = 0
        self._repeat = Repeat(self._tick)
        self._downloading = False
        self._refreshing = False

        self._threads = []

        self['races'] = {}
        self['items'] = {}

    def _tick(self):
        if not _output.empty():
            self._counter -= 1

            if not self._counter:
                self._repeat.stop()

            items = _output.get_nowait()

            if items is not None:
                if isinstance(items, tuple):
                    items[0](*items[1:])
                else:
                    items()

    def _connect(self):
        if GITHUB_ACCESS_TOKEN is not None:
            return Github(GITHUB_ACCESS_TOKEN)

        if GITHUB_USERNAME is None or GITHUB_PASSWORD is None:
            return Github()

        return Github(GITHUB_USERNAME, GITHUB_PASSWORD)

    def _download_update(self):
        try:
            _github = self._connect()

            _repo = _github.get_repo(f'{info.author.replace(" ", "")}/WCS')
            info_file = _repo.get_contents(
                'addons/source-python/plugins/wcs/info.ini')

            for line in info_file.decoded_content.decode('utf8').split('\n'):
                if line.startswith('version'):
                    new_version = line.split()[2].strip()[1:-1]
                    break
            else:
                raise ValueError("Unable to locate 'version'.")

            new_version_epoch = new_version.split('-')
            new_version_epoch = mktime(
                strptime(new_version_epoch[0], r'%Y.%m.%d')) + int(
                    new_version_epoch[1])

            local_version_epoch = info.version.split('-')
            local_version_epoch = mktime(
                strptime(local_version_epoch[0], r'%Y.%m.%d')) + int(
                    local_version_epoch[1])

            if new_version_epoch > local_version_epoch:
                if (DATA_PATH / 'update_blacklist.txt').isfile():
                    with open(DATA_PATH / 'update_blacklist.txt') as f:
                        blacklist = f.read().splitlines()
                else:
                    blacklist = []

                with urlopen(_repo.get_archive_link('zipball',
                                                    'master')) as response:
                    with ZipFile(BytesIO(response.read()), 'r') as ref:
                        files = ref.namelist()
                        unique_name = files[0]

                        files.remove(unique_name)

                        with TemporaryDirectory() as tmpdir:
                            for member in files:
                                name = member.replace(unique_name, '')

                                if name in blacklist:
                                    continue

                                ref.extract(member, path=tmpdir)

                            copy_tree(Path(tmpdir) / unique_name, GAME_PATH)

                _output.put((OnDownloadComplete.manager.notify, new_version))
            else:
                _output.put((OnDownloadComplete.manager.notify, None))
        except:
            _output.put(None)
            raise

    def _refresh(self):
        try:
            _github = self._connect()

            for repository in GITHUB_REPOSITORIES:
                _repo = _github.get_repo(repository)
                modules = _repo.get_contents('')

                modules_left = {}

                for module in [x.name for x in modules if x.name in self]:
                    contents = _repo.get_contents(module)
                    path = MODULE_PATH / module
                    modules_left[module] = []

                    for content in contents:
                        modules_left[module].append(content.name)

                        if module not in self or content.name not in self[
                                module]:
                            wcs_install_path = path / content.name / '.wcs_install'

                            if wcs_install_path.isfile():
                                status = GithubStatus.INSTALLED

                                last_updated = wcs_install_path.mtime

                                with open(wcs_install_path) as inputfile:
                                    repository_installed = inputfile.read()
                            else:
                                status = GithubStatus.UNINSTALLED

                                last_updated = None
                                repository_installed = None

                            self[module][content.name] = {
                                'status': status,
                                'last_updated': last_updated,
                                'repository': repository_installed,
                                'repositories': {
                                    repository: {}
                                }
                            }

                commits = _repo.get_commits()

                for commit in commits:
                    last_modified = commit.commit.committer.date.timestamp()

                    for file_ in commit.files:
                        tmp = file_.filename.split('/')
                        module = tmp[0]

                        if module in modules_left:
                            name = tmp[1]

                            if name in modules_left[module]:
                                self[module][name]['repositories'][repository][
                                    'last_modified'] = last_modified
                                modules_left[module].remove(name)

                                if not modules_left[module]:
                                    del modules_left[module]

                    if not modules_left:
                        break

            _output.put((OnGithubRefreshed.manager.notify, self['races'],
                         self['items']))
        except:
            _output.put(None)
            raise

    def _install(self, repository, module, name, userid):
        try:
            _github = self._connect()
            _repo = _github.get_repo(repository)

            self._download(_repo, f'{module}/{name}')

            _path = MODULE_PATH / module / name / '.wcs_install'

            with open(_path, 'w') as outputfile:
                outputfile.write(repository)

            self[module][name]['status'] = GithubStatus.INSTALLED
            self[module][name]['last_updated'] = _path.mtime
            self[module][name]['repository'] = repository

            _output.put((OnGithubInstalled.manager.notify, repository, module,
                         name, userid))
        except:
            _output.put((OnGithubFailed.manager.notify, repository, module,
                         name, userid, GithubStatus.INSTALLING))
            raise

    def _update(self, repository, module, name, userid):
        try:
            _github = self._connect()
            _repo = _github.get_repo(repository)

            path = MODULE_PATH / module / name
            config_path = path / 'config.json'
            config_tmp_path = path / 'config.tmp.json'

            if config_path.isfile():
                config_path.rename(config_tmp_path)

            self._download(_repo, f'{module}/{name}')

            if config_tmp_path.isfile():
                with open(config_tmp_path) as inputfile:
                    old_data = load(inputfile)

                with open(config_path) as inputfile:
                    new_data = load(inputfile)

                config_tmp_path.remove()

                if not old_data == new_data:
                    modified = False

                    # TODO: Make this prettier...
                    if module == 'races':
                        for key in old_data:
                            if key == 'skills':
                                if 'skills' in new_data:
                                    for skill_name in old_data['skills']:
                                        if skill_name in new_data['skills']:
                                            for key, value in old_data[
                                                    'skills'][
                                                        skill_name].items():
                                                if key == 'variables':
                                                    for variable, value in old_data[
                                                            'skills'][skill_name][
                                                                'variables'].items(
                                                                ):
                                                        new_value = new_data[
                                                            'skills'][skill_name][
                                                                'variables'].get(
                                                                    variable)

                                                        if new_value is not None and not value == new_value:
                                                            new_data['skills'][
                                                                skill_name][
                                                                    'variables'][
                                                                        variable] = value

                                                            modified = True
                                                else:
                                                    new_value = new_data[
                                                        'skills'][
                                                            skill_name].get(
                                                                key)

                                                    if new_value is not None and not value == new_value:
                                                        new_data['skills'][
                                                            skill_name][
                                                                key] = value

                                                        modified = True
                            # Don't modify the author
                            elif not key == 'author':
                                value = old_data[key]

                                if not value == new_data.get(key):
                                    new_data[key] = value

                                    modified = True
                    elif module == 'items':
                        for key, value in old_data.items():
                            if not value == new_data.get(key):
                                new_data[key] = value

                                modified = True
                    else:
                        raise ValueError(f'Invalid module name: "{module}"')

                    if modified:
                        with open(config_path, 'w') as outputfile:
                            dump(new_data, outputfile, indent=4)

            _path = MODULE_PATH / module / name / '.wcs_install'

            # Update the installation file, so we know it's been updated
            _path.utime(None)

            self[module][name]['status'] = GithubStatus.INSTALLED
            self[module][name]['last_updated'] = _path.mtime

            _output.put((OnGithubUpdated.manager.notify, repository, module,
                         name, userid))
        except:
            _output.put((OnGithubFailed.manager.notify, repository, module,
                         name, userid, GithubStatus.UPDATING))
            raise

    def _uninstall(self, repository, module, name, userid):
        try:
            if (MODULE_PATH_ES / module / name).isdir():
                (MODULE_PATH_ES / module / name).rmtree()

            (MODULE_PATH / module / name).rmtree()

            self[module][name]['status'] = GithubStatus.UNINSTALLED
            self[module][name]['last_updated'] = None
            self[module][name]['repository'] = None

            _output.put((OnGithubUninstalled.manager.notify, repository,
                         module, name, userid))
        except:
            _output.put((OnGithubFailed.manager.notify, repository, module,
                         name, userid, GithubStatus.UNINSTALLING))
            raise

    def _download(self, repository, from_path):
        contents = repository.get_contents(from_path)
        name = from_path.split('/')[1]

        for content in contents:
            if content.name.endswith(f'{name}.py') or content.name.endswith(
                    f'es_{name}.txt'):
                path = MODULE_PATH_ES / content.path
            else:
                path = MODULE_PATH / content.path

            if content.type == 'dir':
                if not path.isdir():
                    path.makedirs()

                self._download(repository, content.path)
            else:
                if not path.parent.isdir():
                    path.parent.makedirs()

                with open(path, 'wb') as outputfile:
                    outputfile.write(
                        repository.get_contents(content.path).decoded_content)

    if Github is None:

        def download_update(self):
            pass

        def refresh(self):
            pass

        def stop(self):
            pass

        def install(self, repository, module, name, userid=None):
            pass

        def update(self, repository, module, name, userid=None):
            pass

        def uninstall(self, repository, module, name, userid=None):
            pass
    else:

        def download_update(self):
            if self._downloading:
                return

            self._downloading = True

            if not self._counter:
                self._repeat.start(0.1)

            self._counter += 1

            OnDownloadBegin.manager.notify()

            thread = Thread(target=self._download_update, name='wcs.download')
            thread.start()

            self._threads.append(thread)

        def refresh(self):
            if self._refreshing:
                return

            self._refreshing = True

            if not self._counter:
                self._repeat.start(0.1)

            self._counter += 1

            OnGithubRefresh.manager.notify()

            thread = Thread(target=self._refresh, name='wcs.refresh')
            thread.start()

            self._threads.append(thread)

        def stop(self):
            for thread in self._threads:
                if thread.is_alive():
                    thread.join()

            self._repeat.stop()

        def install(self, repository, module, name, userid=None):
            assert self[module][name]['status'] is GithubStatus.UNINSTALLED

            if not self._counter:
                self._repeat.start(0.1)

            self._counter += 1

            self[module][name]['status'] = GithubStatus.INSTALLING

            thread = Thread(target=self._install,
                            name=f'wcs.install.{module}.{name}',
                            args=(repository, module, name, userid))
            thread.start()

            self._threads.append(thread)

        def update(self, module, name, userid=None):
            assert self[module][name]['status'] is GithubStatus.INSTALLED

            if not self._counter:
                self._repeat.start(0.1)

            self._counter += 1

            self[module][name]['status'] = GithubStatus.UPDATING

            thread = Thread(target=self._update,
                            name=f'wcs.update.{module}.{name}',
                            args=(self[module][name]['repository'], module,
                                  name, userid))
            thread.start()

            self._threads.append(thread)

        def uninstall(self, module, name, userid=None):
            assert self[module][name]['status'] is GithubStatus.INSTALLED

            if not self._counter:
                self._repeat.start(0.1)

            self._counter += 1

            self[module][name]['status'] = GithubStatus.UNINSTALLING

            thread = Thread(target=self._uninstall,
                            name=f'wcs.uninstall.{module}.{name}',
                            args=(self[module][name]['repository'], module,
                                  name, userid))
            thread.start()

            self._threads.append(thread)
Пример #17
0
class Blizzard(Skill):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.cooldowns = CooldownDict()
        self._player = self.parent.parent
        self._repeater = None
        self._players_hit = set()

        if not ice_sound.is_precached:
            ice_sound.precache()

    @classproperty
    def description(cls):
        return 'Create a blizzard at an enemy. Slows and damages enemies who walk through it.'

    @classproperty
    def max_level(cls):
        return 4

    _model = Model('sprites/blueflare1.vmt', True)
    _msg_a = '{{PALE_GREEN}}You created a {{BLUE}}Blizzard {{PALE_GREEN}}under {{GREEN}}{name}{{PALE_GREEN}}.'

    @property
    def range(self):
        return 400 + (40 * self.level)

    @property
    def damage(self):
        return 1 + self.level

    @property
    def slow(self):
        return 1 - (0.04 * self.level)

    @property
    def weapon_index(self):
        if not hasattr(self, '_player') or not self._player:
            return None

        for index in self._player.weapon_indexes():
            return index
        else:
            return None

    @events('player_death', 'round_end')
    def _on_round_end(self, player, **kwargs):
        if self._repeater:
            self._repeater.stop()

    @events('player_spawn', 'player_upgrade_skill')
    def _on_player_spawn(self, player, **kwargs):
        self._repeater = None

    def _repeat(self):
        for player in player_dict.values():
            if player.origin.get_distance(self._center) < (self.range / 2) and player.team != self._player.team and not player.playerinfo.is_dead():
                if player not in self._players_hit:
                    player.take_damage(self.damage, attacker_index=self._player.index, weapon_index=self.weapon_index, skip_hooks=True)
                    if not player.is_slowed:
                        speed = player.speed
                        player.speed *= self.slow
                        self._players_hit.add(player)
                        Delay(1, setattr, args=(player, 'speed', speed))
                    Delay(1, self._players_hit.discard, args=(player, ))
                    ice_sound.origin = player.origin
                    ice_sound.play()

    @events('player_pre_attack')
    def _on_player_pre_attack(self, attacker, victim, **kwargs):
        if randint(1, 100) > 20 or self.cooldowns['blizzard'] > 0 or self.level == 0:
            return

        self._center = victim.origin
        self._player = attacker
        self._players_hit.clear()
        self._repeater = Repeat(self._repeat)
        self._repeater.start(0.1)

        self._effect = TempEntity('BeamRingPoint', center=self._center, start_radius=self.range,
            end_radius=self.range+1, model_index=self._model.index, halo_index=self._model.index,
            life_time=7, amplitude=10, red=200, green=200, blue=255, alpha=245, flags=0,
            start_width=10, end_width=10)
        self._effect.create()

        self._stack = Entity.create('env_smokestack')

        self._stack.teleport(self._center, QAngle(0, 180, 0), None)
        self._stack.base_spread = self.range / 2
        self._stack.spread_speed = 10
        self._stack.start_size = 2
        self._stack.end_size = 1
        self._stack.jet_length = 100
        self._stack.angles = QAngle(0, 0, 0)
        self._stack.rate = 600
        self._stack.speed = 100
        self._stack.twist = 180
        self._stack.render_mode = RenderMode.TRANS_COLOR
        self._stack.render_amt = 100
        self._stack.render_color = Color(200, 200, 255)
        self._stack.add_output('SmokeMaterial particle/rain.vmt')
        self._stack.turn_on()

        self._stack2 = Entity.create('env_smokestack')

        self._stack2.teleport(self._center, None, QAngle(0, 180, 0))
        self._stack2.base_spread = self.range / 4
        self._stack2.spread_speed = self.range / 2
        self._stack2.start_size = 2
        self._stack2.end_size = 1
        self._stack2.jet_length = 100
        self._stack2.angles = QAngle(0, 180, 0)
        self._stack2.rate = 600
        self._stack2.speed = 100
        self._stack2.twist = 120
        self._stack2.render_mode = RenderMode.TRANS_COLOR
        self._stack2.render_amt = 100
        self._stack2.render_color = Color(200, 200, 255)
        self._stack2.add_output('SmokeMaterial particle/rain.vmt')
        self._stack2.turn_on()

        send_wcs_saytext_by_index(self._msg_a.format(name=victim.name), attacker.index)

        self._stack.delay(7, self._stack.turn_off)
        self._stack2.delay(7, self._stack2.turn_off)
        Delay(7, self._repeater.stop)

        self.cooldowns['blizzard'] = 10
Пример #18
0
def dealTimed(userid, attacker, dmg, time):
    if userid not in timed_dict:
        timed_dict[userid] = []
    timed_repeat = Repeat(_timed_repeat, (userid, attacker, dmg))
    timed_dict[userid].append(timed_repeat)
    timed_repeat.start(time, execute_on_start=True)
Пример #19
0
class _WarmupManager(object):
    """Class used to provide warmup functionality."""

    def __init__(self):
        """Store the base attributes."""
        self.repeat = Repeat(self.countdown, cancel_on_level_end=True)
        self.extensions = 0
        self.warmup_time = 0
        self.weapon = None
        self.weapon_cycle = None

    def set_warmup_weapon(self):
        """Set the warmup weapon(s)."""
        # Get the warmup weapon(s)
        current = warmup_weapon.get_string()

        # Is the value a specific weapon?
        if current in _possible_weapons:

            # Set the weapon cycle to include just the weapon
            self.weapon_cycle = cycle([current])
            return

        # Are all weapons supposed to be used at random?
        if current == 'random':

            # Set the weapon cycle to a randomized list of all weapons
            weapons = list(_possible_weapons)
            shuffle(weapons)
            self.weapon_cycle = cycle(weapons)
            return

        # Is the value a list of weapons?
        if ',' in current:

            # Store the weapons from the given list to the weapon cycle
            weapons = [
                weapon for weapon in current.split(
                    ','
                ) if weapon in _possible_weapons
            ]
            if weapons:
                self.weapon_cycle = cycle(weapons)
                return

        # Store the weapon cycle as the first weapon in the active weapon order
        self.weapon_cycle = cycle([weapon_order_manager.active[1].weapon])

    def start_warmup(self):
        """Start warmup round."""
        if GunGameStatus.MATCH is GunGameMatchStatus.WARMUP:
            return
        self.extensions = 0
        self.warmup_time = warmup_time.get_int()

        # Was an invalid value given?
        if self.warmup_time <= 0:
            warn(
                '"gg_warmup_time" is set to an invalid number.'
                '  Skipping warmup round.'
            )
            self.end_warmup()
            return

        if self.weapon_cycle is None:
            self.set_warmup_weapon()

        # Get the warmup weapon
        self.get_current_warmup_weapon()

        # Set the match status
        GunGameStatus.MATCH = GunGameMatchStatus.WARMUP

        # Start the warmup repeat
        self.repeat.start(1, self.warmup_time)

        listeners.load()

    @staticmethod
    def end_warmup():
        """End warmup and start the match."""
        listeners.unload()
        from ..listeners import start_match
        GunGameStatus.MATCH = GunGameMatchStatus.INACTIVE
        start_match(ending_warmup=True)

    def get_current_warmup_weapon(self):
        """Return the next weapon in the warmup cycle."""
        self.weapon = next(self.weapon_cycle)

    def countdown(self):
        """Determine what to do once a second during warmup."""
        # Get the remaining time for warmup
        remaining = self.repeat.loops_remaining

        # Is there no more time for the warmup?
        if not remaining:

            # End the warmup round
            self.end_warmup()
            return

        # Has the player limit been reached?
        if len(list(_human_no_spec)) > min_players.get_int():

            # Get what to do when the player limit is reached
            current = players_reached.get_int()

            # Should warmup end?
            if (
                current == 2 or
                (self.extensions and current == 1)
            ):

                message_manager.center_message(
                    message='Warmup:Reduce',
                )

                # Cause warmup to end in 1 second
                self.repeat.reduce(self.repeat.loops_remaining - 1)
                return

        # Is there just one second remaining in warmup?
        if remaining == 1:

            # Should warmup be extended?
            if self.extensions < max_extensions.get_int():

                message_manager.center_message(
                    message='Warmup:Extend',
                )

                # Extend the warmup round
                self.extensions += 1
                self.repeat.extend(self.warmup_time)
                return

        message_manager.center_message(
            message='Warmup:Countdown',
            seconds=remaining,
        )

        if remaining <= 5:
            sound_manager.play_sound('count_down')
Пример #20
0
class SerpentWard(Skill):
    _model = Model('sprites/blueflare1.vmt', True)
    _model2 = Model('sprites/lgtning.vmt', True)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.cooldowns = CooldownDict()
        self._player = self.parent.parent
        self._players_hit = set()
        self._repeater = Repeat(self._repeat, args=(self._player, ))
        self._wards = list()

        self.outer_ring = TempEntity('BeamRingPoint',
                                     model_index=self._model.index,
                                     halo_index=self._model.index,
                                     amplitude=10,
                                     red=250,
                                     green=200,
                                     blue=100,
                                     alpha=245,
                                     fade_length=100,
                                     start_width=10,
                                     end_width=10,
                                     speed=3)
        self.beam = TempEntity('BeamPoints',
                               alpha=255,
                               red=0,
                               green=0,
                               blue=255,
                               model_index=self._model2.index,
                               start_width=7,
                               end_width=7,
                               frame_rate=255,
                               halo_index=self._model2.index)

    @classproperty
    def description(cls):
        return 'Place 1-2 serpent wards, dealing 5-10 damage per second to enemies within their range. Ability.'

    @classproperty
    def max_level(cls):
        return 6

    _msg_a = '{PALE_GREEN}You created a {BLUE}Serpent Ward {PALE_GREEN}at your {GREEN}location{PALE_GREEN}.'
    _msg_f = '{PALE_GREEN}You {DULL_RED}cannot {PALE_GREEN}create more {BLUE}Serpent Wards{PALE_GREEN}.'

    @property
    def range(self):
        return 250 + (20 * self.level)

    @property
    def damage(self):
        return 8 + self.level

    @property
    def duration(self):
        return 12 + (self.level / 2)

    @property
    def weapon_index(self):
        if not hasattr(self, '_player') or not self._player:
            return None

        for index in self._player.weapon_indexes():
            return index
        else:
            return None

    def _draw_ward(self, origin):
        start_point = origin.copy()
        start_point.z += 4
        self.outer_ring.create(center=start_point,
                               start_radius=self.range,
                               end_radius=self.range + 10,
                               life_time=1)
        end_point = origin.copy()
        end_point.z += 150
        self.beam.create(start_point=start_point,
                         end_point=end_point,
                         life_time=1)

    def _delete_ward(self, origin):
        try:
            self._wards.remove(origin)
        except:
            pass

    def _repeat(self, player):
        for ward_origin in self._wards:
            end_point = ward_origin.copy()
            end_point.z += 150
            self._draw_ward(ward_origin)
            for target in player_dict.values():
                if target.origin.get_distance(ward_origin) < (
                        self.range /
                        2) and target.team != player.team and not target.dead:
                    if target not in self._players_hit:
                        self._players_hit.add(target)
                        target_point = target.origin.copy()
                        target_point.z += 40
                        target.take_damage(self.damage,
                                           attacker_index=player.index,
                                           weapon_index=self.weapon_index)
                        self.beam.create(start_point=end_point,
                                         end_point=target_point,
                                         life_time=0.2,
                                         start_width=5,
                                         end_width=1)
                        Delay(1, self._players_hit.discard, args=(target, ))

    @clientcommands('ability')
    def _on_ability(self, player, **kwargs):
        if self.level == 0:
            return

        if len(self._wards) > 1:
            send_wcs_saytext_by_index(self._msg_f, player.index)
            return

        location = player.origin.copy()
        self._wards.append(location)
        self._players_hit.clear()
        self._repeater.start(0.2)

        send_wcs_saytext_by_index(self._msg_a, player.index)

        Delay(self.duration, self._delete_ward, args=(location, ))

    @events('player_death', 'round_end')
    def _on_player_death(self, **kwargs):
        if self.level == 0:
            return

        self._repeater.stop()
        self._wards.clear()
Пример #21
0
def load():
    global log_players_loop
    log_players_loop = Repeat(log_players)
    log_players_loop.start(5, execute_on_start=True)
Пример #22
0
joined = set()


def exptick():
    for player in PlayerIter():
        steamid = player.steamid
        if steamid == 'BOT':
            steamid = 'BOT_' + player.name.upper()
        if tickoxp.get_int():
            db[steamid]["exp"] += int(tickoxp)
        db[steamid]["left"] = time()


repeat = Repeat(exptick)
repeat.start(timer.get_int())


def player_menu_select(menu, index, choice):
    userid = choice.value
    player = Player.from_userid(userid)

    steamid = player.steamid
    if steamid == 'BOT':
        steamid = 'BOT_' + player.name.upper()
    check(steamid)
    exp = int(db[steamid]["exp"])
    if player.index == index:
        wcs.wcs.tell(
            Player(index).userid,
            "\x04[WCS]\x05 You currently have %s rested experience." %
Пример #23
0
class Warmup_handle:
    def __init__(self):
        self.state_warmup = True
        self.handle_flag = False
        self.checking = Repeat(self.players_quantity)
        self.players_to_start_sdm = settings.PAYERS_TO_START
        self.player_weapon = "weapon_glock"
        self.bot_weapon = "weapon_knife"
        self.single_player_weapon_restrict = WeaponRestrictionHandler()

    def players_quantity(self):
        sdm_logger.info(f"connected players: {len(PlayerIter('human'))}")
        # TODO: prevent connecting more than we want (check on_player_connect) and provide reconnect
        if len(PlayerIter('human')) >= self.players_to_start_sdm:# Server maximum clients for round
            authorized_clients = list()
            for player in PlayerIter('human'):
                sdm_logger.debug(f'Checking player {player.index} whose nickname is {player.name}')
                backend_approved = sdm_api.request_server_loads(player, hostname=ConVar('hostname').get_string())
                if backend_approved:
                    authorized_clients.append(player.index)
            if len(authorized_clients) >= self.players_to_start_sdm and self.state_warmup:
                sdm_logger.debug('Starting game preparation')
                self.stop()
                sdm_logger.debug(f'Preparing game for player {authorized_clients[0]}')
                self.game_prepare(authorized_clients[0])
            else:
                sdm_logger.debug(f'Number of clients is {len(authorized_clients)} To start we need {self.players_to_start_sdm} and state of warmup is {self.state_warmup}')

    def game_prepare(self, authorized_client):
        sdm_api.request_get_game(hostname=ConVar('hostname').get_string())
        player_freeze()
        message_show("Начнется через", 5, x=-1, y=0.15) #without
        if self.players_to_start_sdm > 1:
            sdm_logger.warning('Multiplayer requested. Assigning single player mode.')
        Delay(6, self.game_start, (authorized_client, sdm_single, sdm_api))
        self.state_warmup = False
        sdm_logger.debug('==============sdm_warmup ended=============')

    def game_start(self, player_index: int, game_mode, api):
        if api.input_json['weapon_for_user']:
            self.player_weapon = api.input_json['weapon_for_user']
            sdm_logger.debug(f'Current player from backend {self.player_weapon}')
        else:
            sdm_logger.debug(f'No weapon for player from backend Leaving {self.player_weapon}')
        if api.input_json['weapon_for_bot'] and api.input_json['weapon_for_bot'] != "weapon_knife":
            self.bot_weapon = api.input_json['weapon_for_bot']
            sdm_logger.debug(f'Current bot from backend {self.bot_weapon}')
        else:
            sdm_logger.debug(f'No weapon for bot from backend Leaving {self.bot_weapon}')
        game_mode.single_start = True
        ConVar('mp_roundtime').set_int(api.input_json["game_time"] // 60)  # in minutes
        ConVar('mp_round_restart_delay').set_int(14)
        ConVar('mp_teammates_are_enemies').set_int(0)
        ConVar('bot_difficulty').set_int(api.input_json.get("difficulty", 1))
        sdm_logger.info(f"Bot difficulty is set to {ConVar('bot_difficulty').get_int()}")
        queue_server_command('bot_kick')
        queue_server_command('mp_warmup_end')
        if not self.state_warmup:
            self.spawn_enemies(player_index, api)

            #sdm_logger.debug(f'Restriction of {bot_team[player.team]} team from {self.player_weapon} is done')
        else:
            sdm_logger.error('Warmup was not finished. Skipping game start.')

    def start(self):
        if not self.handle_flag:
            self.handle_flag = True
            self.checking.start(2)

    def stop(self):
        self.handle_flag = False
        self.checking.stop()

    def spawn_enemies(self, player_index, api):
        player = Player(player_index)
        bot_team = {3: "t", 2: "ct"}  # 3 means player is ct, 2 means player is t
        bot_count = api.input_json["bots_count"] if api.input_json["bots_count"] else 8
        play_sound = False
        if api.input_json["models_for_bot"] == 'zombie':
            play_sound = True
        Delay(1.0, bot_spawn, (bot_count, bot_team[player.team], play_sound))
Пример #24
0
class BloodFury(Skill):
    laser = Model('sprites/lgtning.vmt', True)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.can_crit = False
        self.counter = 0
        self.repeater = None
        self.beam = TempEntity('BeamPoints',
                               alpha=255,
                               red=0,
                               green=255,
                               blue=0,
                               life_time=1.0,
                               model_index=self.laser.index,
                               start_width=3,
                               end_width=3,
                               frame_rate=255,
                               halo_index=self.laser.index)

    @classproperty
    def description(cls):
        return 'You are able to hit vital points, causing major damage.'

    @classproperty
    def max_level(cls):
        return 8

    _msg_a = '{{GREEN}}Critical strike {{PALE_GREEN}}on {{RED}}{name} {{PALE_GREEN}}caused {{DULL_RED}}vital damage!'

    @events('player_spawn', 'skill_level_up')
    def _on_spawn_start_repeat(self, player, **kwargs):
        self.can_crit = True
        self.counter = 0

        self.repeater = Repeat(self._on_game_tick,
                               kwargs={},
                               cancel_on_level_end=True)
        self.repeater.start(0.1)

    @events('player_death')
    def _on_death_stop_repeat(self, player, **kwargs):
        if self.repeater:
            self.repeater.stop()

    @events('player_pre_attack')
    def _on_player_pre_attack(self, attacker, victim, info, **kwargs):
        if self.level == 0:
            return

        if self.can_crit:
            info.damage *= 1 + 0.2 * self.level
            send_wcs_saytext_by_index(self._msg_a.format(name=victim.name),
                                      attacker.index)

            weapon = attacker.active_weapon
            if weapon and weapon.weapon_name.split(
                    "_")[-1] not in weapon_manager.projectiles:
                start_location = weapon.origin.copy()
                start_location.z += 40
                end_location = attacker.get_view_coordinates()

                self.beam.create(start_point=start_location,
                                 end_point=end_location)

            self.can_crit = False
            self.counter = 0

    def _on_game_tick(self):
        self.counter += 1
        if self.counter == 256 - (self.level * 2):
            self.can_crit = True
Пример #25
0
class _WarmupManager(object):
    """Class used to provide warmup functionality."""

    def __init__(self):
        """Store the base attributes."""
        self.repeat = Repeat(self._countdown)
        self.extensions = 0
        self.warmup_time = 0
        self.weapon = None
        self.weapon_cycle = None

    def set_warmup_weapon(self):
        """Set the warmup weapon(s)."""
        # Get the warmup weapon(s)
        current = warmup_weapon.get_string()

        # Is the value a specific weapon?
        if current in _possible_weapons:

            # Set the weapon cycle to include just the weapon
            self._weapon_cycle = cycle([current])
            return

        # Are all weapons supposed to be used at random?
        if current == 'random':

            # Set the weapon cycle to a randomized list of all weapons
            weapons = list(_possible_weapons)
            shuffle(weapons)
            self._weapon_cycle = cycle(weapons)
            return

        # Is the value a list of weapons?
        if ',' in current:

            # Store the weapons from the given list to the weapon cycle
            weapons = [
                weapon for weapon in current.split(
                    ','
                ) if weapon in _possible_weapons
            ]
            if len(weapons):
                self._weapon_cycle = cycle(weapons)
                return

        # Store the weapon cycle as the first weapon in the active weapon order
        self._weapon_cycle = cycle([weapon_order_manager.active[1].weapon])

    def start_warmup(self):
        """Start warmup round."""
        # Reset the extensions used
        self._extensions = 0

        # Get the amount of time for warmup
        self._warmup_time = warmup_time.get_int()

        # Was an invalid value given?
        if self._warmup_time <= 0:
            warn(
                '"gg_warmup_time" is set to an invalid number.'
                '  Skipping warmup round.'
            )
            self.end_warmup()
            return

        # Get the configuration to call on warmup start
        current = start_config.get_string()

        # Is a configuration file supposed to be called?
        if current:

            # Call the start configuration
            engine_server.server_command(
                'exec {config};'.format(
                    config=current,
                )
            )

        # Get the warmup weapon
        self._find_warmup_weapon()

        # Set the match status
        GunGameStatus.MATCH = GunGameMatchStatus.WARMUP

        # TODO: Give warmup weapon

        # Start the warmup repeat
        self.repeat.start(1, self._warmup_time)

    @staticmethod
    def end_warmup():
        """End warmup and start the match."""
        # TODO: Call start match
        # Get the configuration to call on warmup end
        current = end_config.get_string()

        # Is a configuration file supposed to be called?
        if current:

            # Call the end configuration
            engine_server.server_command(
                'exec {config};'.format(
                    config=current,
                )
            )

    def _find_warmup_weapon(self):
        """Return the next weapon in the warmup cycle."""
        self._weapon = next(self.weapon_cycle)

    def _countdown(self):
        """Determine what to do once a second during warmup."""
        # Get the remaining time for warmup
        remaining = self.repeat.loops_remaining

        # Is there no more time for the warmup?
        if not remaining:

            # End the warmup round
            self.end_warmup()
            return

        # Has the player limit been reached?
        if len(list(_human_no_spec)) > min_players.get_int():

            # Get what to do when the player limit is reached
            current = players_reached.get_int()

            # Should warmup end?
            if (
                current == 2 or
                (self.extensions and current == 1)
            ):

                message_manager.center_message(
                    message='Warmup:Reduce',
                )

                # Cause warmup to end in 1 second
                self.repeat.reduce(self.repeat.loops_remaining - 1)
                return

        # Is there just one second remaining in warmup?
        if remaining == 1:

            # Should warmup be extended?
            if self.extensions < max_extensions.get_int():

                message_manager.center_message(
                    message='Warmup:Extend',
                )

                # Extend the warmup round
                self._extensions += 1
                self.repeat.extend(self._warmup_time)
                return

        message_manager.center_message(
            message='Warmup:Countdown',
            seconds=remaining,
        )

        if remaining <= 5:
            sound_manager.play_sound('countdown')
Пример #26
0
class _GithubManager(dict):
    def __init__(self):
        super().__init__()

        self._counter = 0
        self._repeat = Repeat(self._tick)
        self._refreshing = False

        self._threads = []

        self['races'] = {}
        self['items'] = {}

    def _tick(self):
        if not _output.empty():
            self._counter -= 1

            if not self._counter:
                self._repeat.stop()

            items = _output.get_nowait()

            if items is not None:
                if isinstance(items, tuple):
                    items[0](*items[1:])
                else:
                    items()

    def _refresh(self):
        try:
            _github = Github(GITHUB_ACCESS_TOKEN)
            _repo = _github.get_repo(GITHUB_REPOSITORIES[0])
            modules = _repo.get_contents('')

            modules_left = {}

            for module in [x.name for x in modules if x.name in self]:
                contents = _repo.get_contents(module)
                path = MODULE_PATH / module
                modules_left[module] = []

                for content in contents:
                    modules_left[module].append(content.name)

                    wcs_install_path = path / content.name / '.wcs_install'

                    if wcs_install_path.isfile():
                        status = GithubStatus.INSTALLED

                        last_updated = wcs_install_path.mtime
                    else:
                        status = GithubStatus.UNINSTALLED

                        last_updated = None

                    self[module][content.name] = {
                        'status': status,
                        'last_updated': last_updated
                    }

            commits = _repo.get_commits()

            for commit in commits:
                last_modified = commit.commit.committer.date.timestamp()

                for file_ in commit.files:
                    tmp = file_.filename.split('/')
                    module = tmp[0]

                    if module in modules_left:
                        name = tmp[1]

                        if name in modules_left[module]:
                            self[module][name]['last_modified'] = last_modified
                            modules_left[module].remove(name)

                            if not modules_left[module]:
                                del modules_left[module]

                if not modules_left:
                    break

            _output.put((OnGithubRefreshed.manager.notify, self['races'],
                         self['items']))
        except:
            _output.put(None)
            raise

    def _install(self, module, name, userid):
        try:
            _github = Github(GITHUB_ACCESS_TOKEN)
            _repo = _github.get_repo(GITHUB_REPOSITORIES[0])

            self._download(_repo, f'{module}/{name}')

            with open(MODULE_PATH / module / name / '.wcs_install',
                      'w') as outputfile:
                outputfile.write('')

            self[module][name]['status'] = GithubStatus.INSTALLED

            _output.put(
                (OnGithubInstalled.manager.notify, module, name, userid))
        except:
            _output.put((OnGithubFailed.manager.notify, module, name, userid,
                         GithubStatus.INSTALLING))
            raise

    def _update(self, module, name, userid):
        try:
            _github = Github(GITHUB_ACCESS_TOKEN)
            _repo = _github.get_repo(GITHUB_REPOSITORIES[0])

            path = MODULE_PATH / module / name
            config_path = path / 'config.json'
            config_tmp_path = path / 'config.tmp.json'

            if config_path.isfile():
                config_path.rename(config_tmp_path)

            self._download(_repo, f'{module}/{name}')

            if config_tmp_path.isfile():
                with open(config_tmp_path) as inputfile:
                    old_data = load(inputfile)

                with open(config_path) as inputfile:
                    new_data = load(inputfile)

                config_tmp_path.remove()

                if not old_data == new_data:
                    modified = False

                    # TODO: Make this prettier...
                    if module == 'races':
                        for key in old_data:
                            if key == 'skills':
                                if 'skills' in new_data:
                                    for skill_name in old_data['skills']:
                                        if skill_name in new_data['skills']:
                                            for key, value in old_data[
                                                    'skills'][
                                                        skill_name].items():
                                                if key == 'variables':
                                                    for variable, value in old_data[
                                                            'skills'][skill_name][
                                                                'variables'].items(
                                                                ):
                                                        new_value = new_data[
                                                            'skills'][skill_name][
                                                                'variables'].get(
                                                                    variable)

                                                        if new_value is not None and not value == new_value:
                                                            new_data['skills'][
                                                                skill_name][
                                                                    'variables'][
                                                                        variable] = value

                                                            modified = True
                                                else:
                                                    new_value = new_data[
                                                        'skills'][
                                                            skill_name].get(
                                                                key)

                                                    if new_value is not None and not value == new_value:
                                                        new_data['skills'][
                                                            skill_name][
                                                                key] = value

                                                        modified = True
                            # Don't modify the author
                            elif not key == 'author':
                                value = old_data[key]

                                if not value == new_data.get(key):
                                    new_data[key] = value

                                    modified = True
                    elif module == 'items':
                        for key, value in old_data.items():
                            if not value == new_data.get(key):
                                new_data[key] = value

                                modified = True
                    else:
                        raise ValueError(f'Invalid module name: "{module}"')

                    if modified:
                        with open(config_path, 'w') as outputfile:
                            dump(new_data, outputfile, indent=4)

            self[module][name]['status'] = GithubStatus.INSTALLED

            _output.put((OnGithubUpdated.manager.notify, module, name, userid))
        except:
            _output.put((OnGithubFailed.manager.notify, module, name, userid,
                         GithubStatus.UPDATING))
            raise

    def _uninstall(self, module, name, userid):
        try:
            if (MODULE_PATH_ES / module / name).isdir():
                (MODULE_PATH_ES / module / name).rmtree()

            (MODULE_PATH / module / name).rmtree()

            self[module][name]['status'] = GithubStatus.UNINSTALLED

            _output.put(
                (OnGithubUninstalled.manager.notify, module, name, userid))
        except:
            _output.put((OnGithubFailed.manager.notify, module, name, userid,
                         GithubStatus.UNINSTALLING))
            raise

    def _download(self, repository, from_path):
        contents = repository.get_contents(from_path)
        name = from_path.split('/')[1]

        for content in contents:
            if content.name.endswith(f'{name}.py') or content.name.endswith(
                    f'es_{name}.txt'):
                path = MODULE_PATH_ES / content.path
            else:
                path = MODULE_PATH / content.path

            if content.type == 'dir':
                if not path.isdir():
                    path.makedirs()

                self._download(repository, content.path)
            else:
                if not path.parent.isdir():
                    path.parent.makedirs()

                with open(path, 'wb') as outputfile:
                    outputfile.write(
                        repository.get_contents(content.path).decoded_content)

    def refresh(self):
        if self._refreshing:
            return

        self._refreshing = True

        if not self._counter:
            self._repeat.start(0.1)

        self._counter += 1

        OnGithubRefresh.manager.notify()

        thread = Thread(target=self._refresh)
        thread.start()

        self._threads.append(thread)

    def stop(self):
        for thread in self._threads:
            if thread.is_alive():
                thread.join()

        self._repeat.stop()

    def install(self, module, name, userid=None):
        assert self[module][name]['status'] is GithubStatus.UNINSTALLED

        if not self._counter:
            self._repeat.start(0.1)

        self._counter += 1

        self[module][name]['status'] = GithubStatus.INSTALLING

        thread = Thread(target=self._install, args=(module, name, userid))
        thread.start()

        self._threads.append(thread)

    def update(self, module, name, userid=None):
        assert self[module][name]['status'] is GithubStatus.INSTALLED

        if not self._counter:
            self._repeat.start(0.1)

        self._counter += 1

        self[module][name]['status'] = GithubStatus.UPDATING

        thread = Thread(target=self._update, args=(module, name, userid))
        thread.start()

        self._threads.append(thread)

    def uninstall(self, module, name, userid=None):
        assert self[module][name]['status'] is GithubStatus.INSTALLED

        if not self._counter:
            self._repeat.start(0.1)

        self._counter += 1

        self[module][name]['status'] = GithubStatus.UNINSTALLING

        thread = Thread(target=self._uninstall, args=(module, name, userid))
        thread.start()

        self._threads.append(thread)