def end_scene_callback() -> None: global last_color, color_scheme if last_color != color_scheme: last_color = color_scheme if last_color == "Dark": pyd_imgui.style_colors_dark() elif last_color == "Light": pyd_imgui.style_colors_light() elif last_color == "Classic": pyd_imgui.style_colors_classic() # noinspection PyBroadException try: for func in _on_end_scene.copy( ): # a copy should fix 'RuntimeError: Set changed size during iteration' func() except Exception: # call new_frame to override the current draw stack pyd_imgui.new_frame() # instead, show the error message pyd_imgui.begin("An Exception occured!", flags=pyd_imgui.WINDOW_FLAGS_ALWAYS_AUTO_RESIZE) pyd_imgui.text("[PyImgui] An exception was raised in drawthread!") pyd_imgui.text( "Please fix the error before continuing using this mod!") pyd_imgui.text("Full traceback:") pyd_imgui.separator() pyd_imgui.text_unformatted(traceback.format_exc()) pyd_imgui.end() pyd_imgui.end_frame() unrealsdk.Log( f"[PyImgui] An exception was raised in drawthread! Game might crash now." ) unrealsdk.Log(traceback.format_exc())
def handle_load_package(args: argparse.Namespace) -> None: upks = fnmatch.filter(all_upks, args.package) if len(upks) <= 0: unrealsdk.Log(f"Could not find package '{args.package}'!") elif len(upks) > 10: unrealsdk.Log(f"'{args.package}' matches more than 10 packages!") else: for package in upks: unrealsdk.LoadPackage(package)
def SaveAllModSettings() -> None: """ Saves the options, keybinds, and enabled state of all loaded mods, where applicable. """ for mod in ModObjects.Mods: try: SaveModSettings(mod) except Exception: unrealsdk.Log(f"Unable to save settings for '{mod.Name}'") tb = traceback.format_exc().split('\n') unrealsdk.Log(f" {tb[-4].strip()}") unrealsdk.Log(f" {tb[-3].strip()}") unrealsdk.Log(f" {tb[-2].strip()}")
def HandleStdout(self, msg: str) -> None: msg_dict: Dict[str, Any] title: str try: msg_dict = json.loads(msg) title = msg_dict["data"]["redemption"]["reward"]["title"].lower().strip() except (json.JSONDecodeError, KeyError) as ex: unrealsdk.Log("Error parsing message:") unrealsdk.Log(ex) return for trig in self.Triggers: if title == trig.Trigger.lower() and trig.IsEnabled: trig.Effect.OnRedeem(msg_dict)
def end_scene_callback() -> None: pyd_imgui.new_frame() # noinspection PyBroadException try: for func in _on_end_scene: func() except Exception: unrealsdk.Log( f"[PyImgui] An exception was raised in drawthread! Game might crash now." ) unrealsdk.Log(traceback.format_exc()) pyd_imgui.end_frame() pyd_imgui.render()
def handle_keep_alive(args: argparse.Namespace) -> None: match = re_obj_name.match(args.object) if match is None: unrealsdk.Log(f"Unable to parse object name {args.object}") return klass = match.group("class") or "Object" name = match.group("fullname") obj = unrealsdk.FindObject(klass, name) if obj is None: unrealsdk.Log(f"Unable to find object {args.object}") return unrealsdk.KeepAlive(obj)
def Enable(self) -> None: def WillowClientDisableLoadingMovie(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: for clas, handler in self.CLASS_HANDLER_MAP.items(): for obj in unrealsdk.FindAll(clas): if obj not in self.MenuObjects: handler(obj) return True unrealsdk.RegisterHook( "WillowGame.WillowPlayerController.WillowClientDisableLoadingMovie", self.Name, WillowClientDisableLoadingMovie) # If you're re-enabling then we can exit right here, the rest of this is non-reversible if len(self.MenuObjects) > 0: return # Objects we load here will still be alive for all the FindAll commands, we don't need to # parse them yet for package, objects in self.FORCE_LOAD.items(): unrealsdk.LoadPackage(package) for obj_name in objects: obj = unrealsdk.FindObject(obj_name[0], obj_name[1]) if obj is None: unrealsdk.Log( f"[{self.Name}] Unable to find object '{obj_name[1]}'") unrealsdk.KeepAlive(obj) # Do our inital parse over everything, saving what we can access for clas, handler in self.CLASS_HANDLER_MAP.items(): for obj in unrealsdk.FindAll(clas): self.MenuObjects.add(obj) handler(obj)
def _OnTick(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: now = datetime.now() for time, callbk_list in _Callbacks.items(): if time < now: for callbk in callbk_list: try: callbk() except Exception: unrealsdk.Log("[AsyncUtil] Exception thrown:") for line in traceback.format_exc().split("\n"): unrealsdk.Log(line) if time != datetime.min: del _Callbacks[time] else: break return True
def HandlePartListCollection(self, obj: unrealsdk.UObject) -> None: if "WillowGame.Default__" in str(obj): return item_type = str(obj).split(" ")[0] all_parts: Set[str] = set() if item_type == "ItemPartListCollectionDefinition": all_parts = { "AlphaPartData", "BetaPartData", "GammaPartData", "DeltaPartData", "EpsilonPartData", "ZetaPartData", "EtaPartData", "ThetaPartData", "MaterialPartData" } elif item_type == "WeaponPartListCollectionDefinition": all_parts = { "BodyPartData", "GripPartData", "BarrelPartData", "SightPartData", "StockPartData", "ElementalPartData", "Accessory1PartData", "Accessory2PartData", "MaterialPartData" } else: unrealsdk.Log( f"[{self.Name}] Unexpected class '{item_type}' on '{obj.PathName(obj)}'" ) return level_indexes = set() for part in all_parts: weighted = getattr(obj, part).WeightedParts if weighted is not None: for weighted_part in weighted: if weighted_part.MaxGameStageIndex: level_indexes.add(weighted_part.MaxGameStageIndex) self.FixCAID(obj, level_indexes)
def Close(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: PC = caller.Outer.GetOwningPlayerController() inv_manager = PC.GetPawnInventoryManager() save_name: str is_bank: bool if caller == inv_manager.TheBank: save_name = PC.SaveGameName is_bank = True elif caller == inv_manager.TheStash: save_name = STASH_NAME is_bank = False else: unrealsdk.Log("[SanitySaver] Could not identify opened container!") return True new_save = SaveManager(save_name, is_bank) existing_save = SaveManager(save_name, is_bank) existing_save.load() for chest_data in caller.TheChest: item = chest_data.Inventory if item is None or item.DefinitionData is None: continue new_save.add_item(item.DefinitionData, chest_data.InventoryClass.Name == "WillowWeapon", existing_save) new_save.write() return True
def DisplayRecentDamageForPlayer(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: PC = unrealsdk.GetEngine().GamePlayers[0].Actor if params.PC != PC: return True damage = params.DamageEventData.TotalDamageForDamageType if damage < 10**self.MinDamageSlider.CurrentValue: return True actor = params.DamageEventData.DamagedActor name = actor.PathName(actor) if actor.AIClass is not None and actor.AIClass.DefaultDisplayName is not None: name = actor.AIClass.DefaultDisplayName if actor.BalanceDefinitionState.BalanceDefinition is not None: ptNum = PC.GetCurrentPlaythrough() + 1 for pt in actor.BalanceDefinitionState.BalanceDefinition.PlayThroughs: if pt.PlayThrough > ptNum: continue if pt.DisplayName is None or len(pt.DisplayName) == 0: continue name = pt.DisplayName unrealsdk.Log( f"Dealt {damage} damage to level {actor.GetExpLevel()} {name}") return True
def _print_message(self, message: str, file: Optional[IO[str]] = None) -> None: if message and file is None: unrealsdk.Log(message) else: super()._print_message(message, file)
def command(self, caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: args: List[str] = params.Command.split() cmd: str = args[0].strip().lower() if cmd == "exec": binaries = self.FILE_PATH while os.path.basename(binaries).lower() != "binaries": binaries = os.path.abspath(os.path.join(binaries, "..")) exec_file = os.path.join( binaries, args[1].lstrip("/").lstrip("\\")) # this is case-sensitive if not os.path.isfile(exec_file): # we could not find the file return True with open(exec_file) as fp: for line in fp: # type: str if line.lower().startswith("set") \ and unrealsdk.FindObject("MaterialInstanceConstant", line.split()[1]) is not None: try: _exec_skins(line) except Exception as e: unrealsdk.Log(e) elif cmd == "set": if len(args) >= 4: if unrealsdk.FindObject("MaterialInstanceConstant", args[1]) is not None: _exec_skins(params.Command) return True
def on_start_load(self, movie_name: str) -> None: if movie_name != "Loading.bik": # loading main menu, all pawns already got cleaned up # for some weird reason some WillowAiPawns don't get destroyed, resulting in endless loading screens for pawn in self.spawned_pawns: if pawn: unrealsdk.Log(bl2tools.get_obj_path_name(pawn)) pawn.Destroyed() self.spawned_pawns.clear() # all pawns are dead
def projectile_body_composition_helper(template_obj: unrealsdk.UObject, new_obj: unrealsdk.UObject) -> None: for i, data_union in enumerate(template_obj.BodyComposition.Attachments): if data_union.Data.ComponentData.Component: template = data_union.Data.ComponentData.Component new_comp = unrealsdk.ConstructObject(Class=template.Class, Outer=new_obj, Name=f"{template.Name}_{i}", Template=template) unrealsdk.Log(bl2tools.get_obj_path_name(new_comp)) unrealsdk.KeepAlive(new_comp) if new_comp: new_comp.ObjectFlags.B |= 4 try: unrealsdk.Log(new_obj.BodyComposition.Attachments) new_obj.BodyComposition.Attachments[i].Data.ComponentData.Component = new_comp unrealsdk.Log("works") except Exception as e: unrealsdk.Log(f"Exception: {repr(e)}") unrealsdk.Log(e)
def PrintWarning(msg: str) -> None: """ Prints a warning containing the provided message. Will only happen once per message. Args: msg: The message to print. """ if msg not in _printed_deprecation_warnings: _printed_deprecation_warnings.add(msg) unrealsdk.Log(f"[Warning] {msg}")
def InjectSkills(self, caller: UObject, function: UFunction, params: FStruct) -> bool: if not self.Seed: self.Seed = random.randrange(sys.maxsize) unrealsdk.Log("Randomizing with seed '{}'".format(self.Seed)) self.RNG = random.Random(self.Seed) self.RecordSeed() else: self.RNG = random.Random(self.Seed) self.RandomizeTree(params.SkillTreeDef) return True
def Execute(self) -> None: for mod in unrealsdk.Mods: if mod.Name == self.ModName: if mod.Status == "Enabled": unrealsdk.Log(f"[{self.Name}] Mod '{self.ModName}' is already enabled!") break if "Enable" not in mod.SettingsInputs.values(): unrealsdk.Log(f"[{self.Name}] Mod '{self.ModName}' does not currently support an enable input!") break try: mod.SettingsInputPressed("Enable") except Exception: unrealsdk.Log(f"[{self.Name}] Mod '{self.ModName}' caused an exception while trying to enable:") for line in traceback.format_exc(): unrealsdk.Log(line) break else: unrealsdk.Log(f"[{self.Name}] Unable to find mod '{self.ModName}'!") self.OnFinishExecution()
def handle_clone(args: argparse.Namespace) -> None: src_match = re_obj_name.match(args.base) if src_match is None: unrealsdk.Log(f"Unable to parse object name {args.base}") return src_class = src_match.group("class") or "Object" src_fullname = src_match.group("fullname") src_object = unrealsdk.FindObject(src_class, src_fullname) if src_object is None: unrealsdk.Log(f"Unable to find object {args.base}") return dst_match = re_obj_name.match(args.clone) if dst_match is None: unrealsdk.Log(f"Unable to parse object name {args.clone}") return dst_class = dst_match.group("class") or "Object" if dst_class != "Object" and dst_class != src_class: unrealsdk.Log( f"Cannot clone object of class {src_class} as class {dst_class}") return dst_outer = dst_match.group("outer") dst_outer_object = None if dst_outer: dst_outer_object = unrealsdk.FindObject("Object", dst_outer) if dst_outer_object is None: unrealsdk.Log(f"Unable to find outer object {dst_outer}") return dst_name = dst_match.group("name") cloned = unrealsdk.ConstructObject(Class=src_object.Class, Outer=dst_outer_object, Name=dst_name, Template=src_object) if cloned is not None: unrealsdk.KeepAlive(cloned)
def cached_obj_find(klass: str, name: str) -> unrealsdk.UObject: if name is None or name == "None": return None if name in obj_cache: return obj_cache[name] obj = unrealsdk.FindObject(klass, name) # Warn about missing objects but still cache them if obj is None: unrealsdk.Log(f"[SanitySaver] Couldn't find {klass}'{name}'") obj_cache[name] = obj return obj
def check_willow_engine_ini() -> bool: ini_path = os.path.join(os.path.expanduser("~"), "Documents", "my games", "Borderlands 2", "WillowGame", "Config") try: with open(os.path.join(ini_path, "WillowEngine.ini"), "r") as f: for line in f: if "bforcenomovies=true" in line.lower(): return False return True except Exception as e: unrealsdk.Log(e) return True
def Execute(self) -> None: for mod in unrealsdk.Mods: if mod.Name == self.ModName: if self.Input in mod.SettingsInputs: try: mod.SettingsInputPressed( mod.SettingsInputs[self.Input]) except Exception: unrealsdk.Log( f"[{self.Name}] Mod '{self.ModName}' caused an exception while inputing '{self.Input}':" ) for line in traceback.format_exc(): unrealsdk.Log(line) else: unrealsdk.Log( f"[{self.Name}] Mod '{self.ModName}' does not currently support the input '{self.Input}'!" ) break else: unrealsdk.Log( f"[{self.Name}] Unable to find mod '{self.ModName}'!") self.OnFinishExecution()
def _try_handle_command(cmd: str) -> bool: """ Takes in a command string `cmd` and tries to handle it. Returns: True if the command string corosponded to one of our custom commands, or False otherwise. """ name, _, args = cmd.partition(" ") name = name.lower() if name == "exec": try: parsed_args = _exec_parser.parse_args(shlex.split(args)) file_path = path.abspath( path.join(path.dirname(sys.executable), "..", parsed_args.file)) if not path.isfile(file_path): return False with open(file_path) as file: firstline = file.readline().strip() file.seek(0) # Handle it as a blcm file if we can if firstline.startswith("<BLCMM"): _handle_blcmm_file(file) return False # Otherwise treat each line as it's own command for line in file: _try_handle_command(line) except (_ParsingFailedError, SystemExit): pass return False if name not in _parser_callback_map: return False parser, callback, splitter = _parser_callback_map[name] try: callback(parser.parse_args(splitter(args))) except _ParsingFailedError as ex: ex.log() except SystemExit: pass except Exception: unrealsdk.Log(traceback.format_exc()) return True
def _client_message(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: message = params.S message_type = params.Type if message_type is None or not message_type.startswith("unrealsdk."): return True if message_type == "unrealsdk.__serverack__": if len(_message_queue) > 0 and _message_queue[0].ID == message: _dequeue_message() return False message_components = message.split(":", 1) if len(message_components) != 2: return False message_id = message_components[0] methods = _client_message_types.get(message_type) if methods is not None and len(methods) > 0: sample_method = next(iter(methods)) cls = type(sample_method.__self__) # type: ignore arguments = None try: arguments = cls.NetworkDeserialize(message_components[1]) except Exception: unrealsdk.Log( f"Unable to deserialize arguments for '{message_type}'") tb = traceback.format_exc().split('\n') unrealsdk.Log(f" {tb[-4].strip()}") unrealsdk.Log(f" {tb[-3].strip()}") unrealsdk.Log(f" {tb[-2].strip()}") if arguments is not None: for method in methods: try: method(*arguments["args"], **arguments["kwargs"]) except Exception: unrealsdk.Log( f"Unable to call remotely requested {method}.") tb = traceback.format_exc().split('\n') unrealsdk.Log(f" {tb[-4].strip()}") unrealsdk.Log(f" {tb[-3].strip()}") unrealsdk.Log(f" {tb[-2].strip()}") caller.ServerSpeech(message_id, 0, "unrealsdk.__clientack__") return False
def Hide(self) -> None: """ Hides the text input box, without running any callbacks. Displays a warning but does nothing if the text input box is not currently being displayed. """ if self._TrainingBox is None: unrealsdk.Log( "[UserFeedback] Warning: tried to hide a text input box that was already closed" ) return unrealsdk.RemoveHook("WillowGame.WillowGFxTrainingDialogBox.HandleInputKey", "CustomTextInputBox") self._TrainingBox.Close() self._TrainingBox = None
def open_map(self, levelname): """ Loads the specified map name """ pc = unrealsdk.GetEngine().GamePlayers[0].Actor maplist = dumperdata.level_pkgs[self.game][levelname] maplist_len = len(maplist) for idx, pkgname in enumerate(maplist): unrealsdk.Log('Preparing map change for {}: {}, {}'.format( pkgname, idx == 0, idx == (maplist_len - 1), )) pc.ClientPrepareMapChange(pkgname, idx == 0, idx == (maplist_len - 1)) #pc.ClientPrepareMapChange(levelname, True, True) pc.ClientCommitMapChange() self.setNextDelay(self.map_change_delay)
def ShowButton(self, button: OptionBoxButton) -> None: """ This has been deprecated since version 1.3. Use the optional argument on `Show()` instead. Displays the option box with the provided button selected. Args: button: The button you want to be selected Raises: ValueError: If the provided button is not currently in one of the stored pages. This may happen if you forgot to call Update(). """ if not self._DeprecationWarning_ShowButton: unrealsdk.Log( "[UserFeedback] Use of `OptionBox.ShowButton()` is deprecated!" ) self._DeprecationWarning_ShowButton = True self.Show(button)
def Hide(self) -> None: """ Hides the chat box, without running any callbacks. Displays a warning but does nothing if the chat box is not currently being displayed. """ if not self.IsShowing(): unrealsdk.Log( "[UserFeedback] Warning: tried to hide a chat box that was already closed" ) return unrealsdk.RemoveHook( "WillowGame.TextChatGFxMovie.AddChatMessageInternal", "ChatBoxInput") unrealsdk.RemoveHook("WillowGame.TextChatGFxMovie.HandleTextChatInput", "ChatBoxInput") self._Chat.StopTextChatInternal() self._Chat.Close()
def load_map(self, name: str, curr_map: str) -> None: """ Load a custom map from a given .json file. :param name: The name of the .json map file. :return: """ if os.path.isfile(os.path.join(self.path, "Maps", f"{name}.json")): with open(os.path.join(self.path, "Maps", f"{name}.json")) as fp: map_dict = json.load(fp) else: unrealsdk.Log( f"Map {os.path.join(self.path, 'Maps', f'{name}.json')} does not exist!" ) return load_this = map_dict.get(curr_map, None) if not load_this: # No Map data for currently loaded map found! return placeablehelper.load_map( load_this) # Finally, load the actual map from data dict
def Hide(self) -> None: unrealsdk.RemoveHook("WillowGame.WillowGFxDialogBox.Accepted", "CustomOptionBox") unrealsdk.RemoveHook("WillowGame.WillowGFxDialogBox.Cancelled", "CustomOptionBox") unrealsdk.RemoveHook("WillowGame.WillowGFxDialogBox.HandleInputKey", "CustomOptionBox") # If it's already closed just give a warning if self._OptionBox is None: unrealsdk.Log( "[UserFeedback] Warning: tried to hide a option box page that was already closed" ) return # This convinces it to instantly GC itself somehow. It'd get collected next cycle if we # ignored it anyway, but might as well get rid of it now. self._OptionBox.Cancelled(0) self._OptionBox.Close() self._OptionBox = None