Skip to content

Commit 8b59da6

Browse files
authored
Merge pull request #24 from apple1417/master
even more legacy compat fixes
2 parents 661abb8 + a195d12 commit 8b59da6

File tree

4 files changed

+68
-32
lines changed

4 files changed

+68
-32
lines changed

changelog.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@
77

88
### Legacy Compat v1.2
99
- Fixed that some more legacy mods would not auto-enable properly.
10-
- Added more fixups for previously unreported issues in Arcania, BL2Fix, and Reign of Giants.
10+
- Added more fixups for previously unreported issues in Arcania, Better Damage Feedback, BL2Fix,
11+
Exodus, Reign of Giants, and Reward Reroller.
1112

1213
### [Mods Base v1.7](https://github.com/bl-sdk/mods_base/blob/master/Readme.md#v17)
14+
> - The "Update Available" notification should now immediately go away upon updating, instead of
15+
> waiting a day for the next check.
16+
>
1317
> - Changed the functions the keybind implementation should overwrite from `KeybindType.enable` to
1418
> `KeybindType._enable` (+ same for disable). These functions don't need to set `is_enabled`.
1519

src/legacy_compat/meta_path_finder.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,14 @@ def find_spec( # noqa: D102
152152

153153
# Imported by the init script, accept both `sdk_mods` folder in the release and the
154154
# `src` folder for if developing in this repo
155+
155156
# BL2Fix does some redundant random.seed() setting. Py 3.11 removed support for setting
156157
# the seed from an arbitrary type, so just completely get rid of the calls.
157158
case (("src" | "sdk_mods"), "Mods.BL2Fix"):
158159
return spec_with_replacements(
159160
fullname,
160161
path,
161162
target,
162-
# Redundant, removed support for arbitrary types in py 3.11
163163
(rb"random\.seed\(datetime\.datetime\.now\(\)\)", b""),
164164
)
165165

@@ -173,5 +173,20 @@ def find_spec( # noqa: D102
173173
(rb'@ModMenu\.Hook\("Engine\.GameInfo\.PostCommitMapChange"\)', b""),
174174
)
175175

176+
# This is a use case the new SDK kind of broke. Reward Rreroller passed the
177+
# `MissionStatusPlayerData:ObjectivesProgress` field to `ShouldGrantAlternateReward`.
178+
# While they're both arrays of ints called `ObjectivesProgress`, since they're different
179+
# properties they're no longer compatible. Turn it into a list to make a copy.
180+
case (("src" | "sdk_mods"), "Mods.RewardReroller"):
181+
return spec_with_replacements(
182+
fullname,
183+
path,
184+
target,
185+
(
186+
rb"mission\.ShouldGrantAlternateReward\(progress\)",
187+
b"mission.ShouldGrantAlternateReward(list(progress))",
188+
),
189+
)
190+
176191
case _, _:
177192
return None

src/legacy_compat/unrealsdk/__init__.py

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
UObject,
4242
UObjectProperty,
4343
UProperty,
44-
UScriptStruct,
4544
UStrProperty,
4645
UStruct,
4746
UStructProperty,
@@ -138,8 +137,11 @@ def ConstructObject(
138137
Error: None = None, # noqa: ARG001
139138
InstanceGraph: None = None, # noqa: ARG001
140139
bAssumeTemplateIsArchetype: int = 0, # noqa: ARG001
141-
) -> UObject:
142-
return construct_object(Class, Outer, Name, SetFlags, Template)
140+
) -> UObject | None:
141+
try:
142+
return construct_object(Class, Outer, Name, SetFlags, Template)
143+
except RuntimeError:
144+
return None
143145

144146

145147
type _SDKHook = Callable[[UObject, UFunction, FStruct], bool | None]
@@ -295,28 +297,11 @@ def KeepAlive(obj: UObject, /) -> None:
295297
_default_func_call = BoundFunction.__call__
296298

297299

298-
def _create_struct_from_tuples(struct: UScriptStruct, value: tuple[Any, ...]) -> WrappedStruct:
299-
"""
300-
Recursively creates a wrapped struct from it's tuple equivalent.
301-
302-
Args:
303-
struct: The struct type to create:
304-
value: The tuple to create it with.
305-
Returns:
306-
The new struct.
307-
"""
308-
return WrappedStruct(
309-
struct,
310-
*(
311-
_create_struct_from_tuples(prop.Struct, inner_val) # pyright: ignore[reportUnknownArgumentType]
312-
if isinstance(prop, UStructProperty) and isinstance(inner_val, tuple)
313-
else inner_val
314-
for prop, inner_val in zip(struct._properties(), value, strict=False)
315-
),
316-
)
317-
318-
319-
def _convert_struct_tuple_if_required(prop: UProperty, value: Any) -> Any:
300+
def _convert_struct_tuple_if_required(
301+
prop: UProperty,
302+
value: Any,
303+
_ignore_array_dim: bool = False,
304+
) -> Any:
320305
"""
321306
Convert any tuple-based structs in the given value into Wrapped Structs.
322307
@@ -327,16 +312,29 @@ def _convert_struct_tuple_if_required(prop: UProperty, value: Any) -> Any:
327312
The possibly converted value.
328313
"""
329314

315+
# If it's a fixed array of structs, need to convert each inner value
316+
if not _ignore_array_dim and prop.ArrayDim > 1 and isinstance(prop, UStructProperty):
317+
return tuple(
318+
_convert_struct_tuple_if_required(prop, inner_val, _ignore_array_dim=True)
319+
for inner_val in value # type: ignore
320+
)
321+
330322
# If it's a struct being set as a tuple directly
331323
if isinstance(prop, UStructProperty) and isinstance(value, tuple):
332-
return _create_struct_from_tuples(prop.Struct, value) # pyright: ignore[reportUnknownArgumentType]
324+
return WrappedStruct(
325+
prop.Struct,
326+
*(
327+
_convert_struct_tuple_if_required(inner_prop, inner_val)
328+
for inner_prop, inner_val in zip(prop.Struct._properties(), value, strict=False) # type: ignore
329+
),
330+
)
333331

334332
# If it's an array of structs, need to convert each value
335333
if isinstance(prop, UArrayProperty) and isinstance(prop.Inner, UStructProperty):
336334
seq_value: Sequence[Any] = value
337335

338336
return tuple(
339-
_create_struct_from_tuples(prop.Inner.Struct, inner_val) # pyright: ignore[reportUnknownArgumentType]
337+
_convert_struct_tuple_if_required(prop.Inner, inner_val)
340338
if isinstance(inner_val, tuple)
341339
else inner_val
342340
for inner_val in seq_value
@@ -432,6 +430,9 @@ def _uobject_setattr(self: UObject, name: str, value: Any) -> None:
432430

433431
@wraps(UObject.__repr__)
434432
def _uobject_repr(self: UObject) -> str:
433+
if self is None or self.Class is None: # type: ignore
434+
return "(null)"
435+
435436
current = self
436437
output = f"{self.Name}"
437438
while current := current.Outer:
@@ -614,10 +615,24 @@ def _ustructproperty_get_struct(self: UStructProperty) -> UStruct:
614615

615616

616617
@staticmethod
617-
def uobject_path_name(obj: UObject, /) -> str:
618+
def _uobject_path_name(obj: UObject, /) -> str:
618619
return obj._path_name()
619620

620621

622+
def _wrapped_struct_structType_getter(self: WrappedStruct) -> UStruct:
623+
return self._type
624+
625+
626+
def _wrapped_struct_structType_setter(self: WrappedStruct, val: UStruct) -> None:
627+
self._type = val
628+
629+
630+
_wrapped_struct_structType = property( # noqa: N816
631+
_wrapped_struct_structType_getter,
632+
_wrapped_struct_structType_setter,
633+
)
634+
635+
621636
@contextmanager
622637
def _unreal_method_compat_handler() -> Iterator[None]:
623638
UObject.__getattr__ = _uobject_getattr
@@ -634,8 +649,9 @@ def _unreal_method_compat_handler() -> Iterator[None]:
634649

635650
UObject.FindObjectsContaining = _uobject_find_objects_containing # type: ignore
636651
UStructProperty.GetStruct = _ustructproperty_get_struct # type: ignore
637-
UObject.PathName = uobject_path_name # type: ignore
652+
UObject.PathName = _uobject_path_name # type: ignore
638653
UObject.GetFullName = _uobject_repr # type: ignore
654+
WrappedStruct.structType = _wrapped_struct_structType # type: ignore
639655

640656
try:
641657
yield
@@ -656,6 +672,7 @@ def _unreal_method_compat_handler() -> Iterator[None]:
656672
del UStructProperty.GetStruct # type: ignore
657673
del UObject.PathName # type: ignore
658674
del UObject.GetFullName # type: ignore
675+
del WrappedStruct.structType # type: ignore
659676

660677

661678
compat_handlers.append(_unreal_method_compat_handler)

src/mods_base

0 commit comments

Comments
 (0)