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)
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
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)
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)
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')))
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()
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()
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
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)
# 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'])
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)
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')
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)
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()
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')
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)
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
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)
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')
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()
def load(): global log_players_loop log_players_loop = Repeat(log_players) log_players_loop.start(5, execute_on_start=True)
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." %
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))
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
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')
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)