Skip to content

Commit 6c0b58e

Browse files
committed
add IGNORE_STRUCT sentinel
also log python errors which occur during initalization, since I may have caused some :)
1 parent dc515cd commit 6c0b58e

File tree

6 files changed

+52
-1
lines changed

6 files changed

+52
-1
lines changed

src/pyunrealsdk/dllmain.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "pyunrealsdk/pch.h"
2+
#include "pyunrealsdk/logging.h"
23
#include "pyunrealsdk/pyunrealsdk.h"
34

45
#ifdef PYUNREALSDK_INTERNAL
@@ -16,6 +17,7 @@ DWORD WINAPI startup_thread(LPVOID /*unused*/) {
1617
pyunrealsdk::init();
1718
} catch (std::exception& ex) {
1819
LOG(ERROR, "Exception occurred while initializing the python sdk: {}", ex.what());
20+
pyunrealsdk::logging::log_python_exception(ex);
1921
}
2022

2123
return 1;

src/pyunrealsdk/unreal_bindings/property_access.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
#include "pyunrealsdk/static_py_object.h"
44
#include "pyunrealsdk/unreal_bindings/uenum.h"
55
#include "pyunrealsdk/unreal_bindings/wrapped_array.h"
6+
#include "pyunrealsdk/unreal_bindings/wrapped_struct.h"
67
#include "unrealsdk/unreal/cast.h"
78
#include "unrealsdk/unreal/classes/properties/uarrayproperty.h"
9+
#include "unrealsdk/unreal/classes/properties/ustructproperty.h"
810
#include "unrealsdk/unreal/classes/uconst.h"
911
#include "unrealsdk/unreal/classes/uenum.h"
1012
#include "unrealsdk/unreal/classes/ufield.h"
@@ -140,6 +142,9 @@ py::object py_getattr(UField* field,
140142
field->Name, field->Class->Name));
141143
}
142144

145+
// The templated lambda and all the if constexprs make everything have a really high penalty
146+
// Yes it's probably a bit complex, but it's also a bit awkward trying to split it up
147+
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
143148
void py_setattr_direct(UField* field, uintptr_t base_addr, const py::object& value) {
144149
if (!field->is_instance(find_class<UProperty>())) {
145150
throw py::attribute_error(unrealsdk::fmt::format(
@@ -204,6 +209,13 @@ void py_setattr_direct(UField* field, uintptr_t base_addr, const py::object& val
204209
}
205210

206211
for (size_t i = 0; i < seq_size; i++) {
212+
// If we're setting a struct property, we might be being told to ignore it
213+
if constexpr (std::is_base_of_v<UStructProperty, T>) {
214+
if (is_ignore_struct_sentinel(value_seq[i])) {
215+
continue;
216+
}
217+
}
218+
207219
set_property<T>(prop, i, base_addr, py::cast<value_type>(value_seq[i]));
208220
}
209221
});

src/pyunrealsdk/unreal_bindings/wrapped_struct.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,23 @@ using namespace unrealsdk::unreal;
1616

1717
namespace pyunrealsdk::unreal {
1818

19+
namespace {
20+
21+
/**
22+
* @brief Gets the ignore struct sentinel.
23+
*
24+
* @return The ignore struct sentinel.
25+
*/
26+
py::object get_ignore_struct_sentinel(void) {
27+
PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> storage;
28+
return storage
29+
.call_once_and_store_result(
30+
[]() { return py::module_::import("builtins").attr("object")(); })
31+
.get_stored();
32+
}
33+
34+
} // namespace
35+
1936
WrappedStruct make_struct(
2037
std::variant<const unrealsdk::unreal::UFunction*, const unrealsdk::unreal::UScriptStruct*> type,
2138
const py::args& args,
@@ -244,6 +261,12 @@ void register_wrapped_struct(py::module_& mod) {
244261
"Returns:\n"
245262
" This struct's address.")
246263
.def_readwrite("_type", &WrappedStruct::type);
264+
265+
mod.attr("IGNORE_STRUCT") = get_ignore_struct_sentinel();
266+
}
267+
268+
bool is_ignore_struct_sentinel(const py::object& obj) {
269+
return obj.is(get_ignore_struct_sentinel());
247270
}
248271

249272
} // namespace pyunrealsdk::unreal

src/pyunrealsdk/unreal_bindings/wrapped_struct.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ void make_struct(unrealsdk::unreal::WrappedStruct& out_struct,
3939
const py::args& args,
4040
const py::kwargs& kwargs);
4141

42+
/**
43+
* @brief Checks if a python object is the ignore struct sentinel.
44+
*
45+
* @param obj The object to check.
46+
* @return True if the object is the ignore struct sentinel.
47+
*/
48+
bool is_ignore_struct_sentinel(const py::object& obj);
49+
4250
} // namespace pyunrealsdk::unreal
4351

4452
#endif

stubs/unrealsdk/unreal/__init__.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,10 @@ from ._uobject_children import (
4848
from ._weak_pointer import WeakPointer
4949
from ._wrapped_array import WrappedArray
5050
from ._wrapped_multicast_delegate import WrappedMulticastDelegate
51-
from ._wrapped_struct import WrappedStruct
51+
from ._wrapped_struct import IGNORE_STRUCT, WrappedStruct
5252

5353
__all__: tuple[str, ...] = (
54+
"IGNORE_STRUCT",
5455
"BoundFunction",
5556
"UArrayProperty",
5657
"UBlueprintGeneratedClass",

stubs/unrealsdk/unreal/_wrapped_struct.pyi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ from typing import Any
44

55
from ._uobject_children import UField, UFunction, UScriptStruct, UStruct
66

7+
# A sentinel value which can be assigned to any struct property, but which does nothing.
8+
# This is most useful when a function has a required struct arg, but you want to use the default,
9+
# zero-init, value.
10+
IGNORE_STRUCT: object
11+
712
class WrappedStruct:
813
_type: UStruct
914

0 commit comments

Comments
 (0)