Skip to content

Commit 9046203

Browse files
author
Roberto De Ioris
committed
added timer support
1 parent 0a76510 commit 9046203

6 files changed

Lines changed: 171 additions & 18 deletions

File tree

‎Source/UnrealEnginePython/Private/UEPyModule.cpp‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "UEPyAttaching.h"
1616
#include "UEPySkeletal.h"
1717
#include "UEPyTraceAndSweep.h"
18+
#include "UEPyTimer.h"
1819
#include "UEPyPhysics.h"
1920
#include "UEPyAudio.h"
2021
#if WITH_EDITOR
@@ -263,6 +264,8 @@ static PyMethodDef ue_PyUObject_methods[] = {
263264
// Skeletal
264265
{ "get_anim_instance", (PyCFunction)py_ue_get_anim_instance, METH_VARARGS, "" },
265266

267+
// Timer
268+
{ "set_timer", (PyCFunction)py_ue_set_timer, METH_VARARGS, "" },
266269

267270
{ "add_function", (PyCFunction)py_ue_add_function, METH_VARARGS, "" },
268271
{ NULL } /* Sentinel */
@@ -336,6 +339,8 @@ void unreal_engine_init_py_module() {
336339
Py_INCREF(&ue_PyUObjectType);
337340
PyModule_AddObject(new_unreal_engine_module, "UObject", (PyObject *)&ue_PyUObjectType);
338341

342+
ue_python_init_ftimerhandle(new_unreal_engine_module);
343+
339344
// Collision channels
340345
PyDict_SetItemString(unreal_engine_dict, "COLLISION_CHANNEL_CAMERA", PyLong_FromLong(ECollisionChannel::ECC_Camera));
341346
PyDict_SetItemString(unreal_engine_dict, "COLLISION_CHANNEL_DESTRUCTIBLE", PyLong_FromLong(ECollisionChannel::ECC_Destructible));

‎Source/UnrealEnginePython/Private/UEPyObject.cpp‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,8 @@ PyObject *py_ue_add_function(ue_PyUObject * self, PyObject * args) {
303303
u_class->Children = function;
304304
u_class->AddFunctionToFunctionMap(function);
305305

306-
307-
308306
Py_INCREF(Py_None);
309307
return Py_None;
310308
}
311309

310+
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#include "UnrealEnginePythonPrivatePCH.h"
2+
3+
static PyObject *py_ue_ftimerhandle_clear(ue_PyFTimerHandle *self, PyObject * args) {
4+
self->world->GetTimerManager().ClearTimer(self->thandle);
5+
Py_INCREF(Py_None);
6+
return Py_None;
7+
}
8+
9+
static PyObject *py_ue_ftimerhandle_pause(ue_PyFTimerHandle *self, PyObject * args) {
10+
Py_INCREF(Py_None);
11+
return Py_None;
12+
}
13+
14+
static PyObject *py_ue_ftimerhandle_unpause(ue_PyFTimerHandle *self, PyObject * args) {
15+
Py_INCREF(Py_None);
16+
return Py_None;
17+
}
18+
19+
20+
static PyMethodDef ue_PyFTimerHandle_methods[] = {
21+
22+
// Transform
23+
24+
{ "clear", (PyCFunction)py_ue_ftimerhandle_clear, METH_VARARGS, "" },
25+
{ "pause", (PyCFunction)py_ue_ftimerhandle_pause, METH_VARARGS, "" },
26+
{ "unpause", (PyCFunction)py_ue_ftimerhandle_unpause, METH_VARARGS, "" },
27+
{ NULL } /* Sentinel */
28+
};
29+
30+
// destructor
31+
static void ue_pyftimerhandle_dealloc(ue_PyFTimerHandle *self) {
32+
Py_DECREF(self->py_callable);
33+
Py_TYPE(self)->tp_free((PyObject *)self);
34+
}
35+
36+
static PyTypeObject ue_PyFTimerHandleType = {
37+
PyVarObject_HEAD_INIT(NULL, 0)
38+
"unreal_engine.FTimerHandle", /* tp_name */
39+
sizeof(ue_PyFTimerHandle), /* tp_basicsize */
40+
0, /* tp_itemsize */
41+
(destructor)ue_pyftimerhandle_dealloc, /* tp_dealloc */
42+
0, /* tp_print */
43+
0, /* tp_getattr */
44+
0, /* tp_setattr */
45+
0, /* tp_reserved */
46+
0, /* tp_repr */
47+
0, /* tp_as_number */
48+
0, /* tp_as_sequence */
49+
0, /* tp_as_mapping */
50+
0, /* tp_hash */
51+
0, /* tp_call */
52+
0, /* tp_str */
53+
0, /* tp_getattro */
54+
0, /* tp_setattro */
55+
0, /* tp_as_buffer */
56+
Py_TPFLAGS_DEFAULT, /* tp_flags */
57+
"Unreal Engine FTimerHandle", /* tp_doc */
58+
0, /* tp_traverse */
59+
0, /* tp_clear */
60+
0, /* tp_richcompare */
61+
0, /* tp_weaklistoffset */
62+
0, /* tp_iter */
63+
0, /* tp_iternext */
64+
ue_PyFTimerHandle_methods, /* tp_methods */
65+
};
66+
67+
void ue_python_init_ftimerhandle(PyObject *ue_module) {
68+
ue_PyFTimerHandleType.tp_new = PyType_GenericNew;
69+
if (PyType_Ready(&ue_PyFTimerHandleType) < 0)
70+
return;
71+
72+
Py_INCREF(&ue_PyFTimerHandleType);
73+
PyModule_AddObject(ue_module, "FTimerHandle", (PyObject *)&ue_PyFTimerHandleType);
74+
}
75+
76+
PyObject *py_ue_set_timer(ue_PyUObject *self, PyObject * args) {
77+
78+
ue_py_check(self);
79+
80+
float rate;
81+
PyObject *py_callable;
82+
PyObject *py_loop = nullptr;
83+
float first_delay = -1;
84+
if (!PyArg_ParseTuple(args, "fO|Of:simple_move_to_location", &rate, &py_callable, &py_loop, &first_delay)) {
85+
return NULL;
86+
}
87+
88+
if (!PyCallable_Check(py_callable))
89+
return PyErr_Format(PyExc_Exception, "object is not a callable");
90+
91+
bool loop = false;
92+
if (py_loop && PyObject_IsTrue(py_loop))
93+
loop = true;
94+
95+
UWorld *world = ue_get_uworld(self);
96+
if (!world)
97+
return PyErr_Format(PyExc_Exception, "unable to retrieve UWorld from uobject");
98+
99+
FTimerDelegate timer_delegate;
100+
UPythonDelegate *py_delegate = NewObject<UPythonDelegate>();
101+
py_delegate->SetPyCallable(py_callable);
102+
// fake UFUNCTION for bypassing checks
103+
timer_delegate.BindUFunction(py_delegate, FName("PyFakeCallable"));
104+
//py_delegate->AddToRoot();
105+
106+
FTimerHandle thandle;
107+
world->GetTimerManager().SetTimer(thandle, timer_delegate, rate, loop, first_delay);
108+
109+
ue_PyFTimerHandle *ret = (ue_PyFTimerHandle *)PyObject_New(ue_PyFTimerHandle, &ue_PyFTimerHandleType);
110+
if (!ret) {
111+
world->GetTimerManager().ClearTimer(thandle);
112+
return PyErr_Format(PyExc_Exception, "unable to allocate FTimerHandle python object");
113+
}
114+
ret->thandle = thandle;
115+
ret->py_callable = py_callable;
116+
// will be decref'ed on clear
117+
Py_INCREF(ret->py_callable);
118+
ret->world = world;
119+
120+
return (PyObject *)ret;
121+
}
122+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#pragma once
2+
3+
4+
5+
#include "UnrealEnginePython.h"
6+
7+
typedef struct {
8+
PyObject_HEAD
9+
/* Type-specific fields go here. */
10+
FTimerHandle thandle;
11+
PyObject *py_callable;
12+
UWorld *world;
13+
} ue_PyFTimerHandle;
14+
15+
PyObject *py_ue_set_timer(ue_PyUObject *, PyObject *);
16+
17+
void ue_python_init_ftimerhandle(PyObject *);

‎Source/UnrealEnginePython/Public/PythonDelegate.cpp‎

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#include "UnrealEnginePythonPrivatePCH.h"
22
#include "PythonDelegate.h"
33

4-
4+
UPythonDelegate::UPythonDelegate() {
5+
py_callable = nullptr;
6+
signature_set = false;
7+
}
58

69
void UPythonDelegate::SetPyCallable(PyObject *callable)
710
{
@@ -12,6 +15,7 @@ void UPythonDelegate::SetPyCallable(PyObject *callable)
1215
void UPythonDelegate::SetSignature(UFunction *original_signature)
1316
{
1417
signature = original_signature;
18+
signature_set = true;
1519
}
1620

1721
void UPythonDelegate::ProcessEvent(UFunction *function, void *Parms)
@@ -20,24 +24,28 @@ void UPythonDelegate::ProcessEvent(UFunction *function, void *Parms)
2024
if (!py_callable)
2125
return;
2226

23-
PyObject *py_args = PyTuple_New(signature->NumParms);
24-
Py_ssize_t argn = 0;
27+
PyObject *py_args = nullptr;
2528

26-
TFieldIterator<UProperty> PArgs(signature);
27-
for (; PArgs && argn < signature->NumParms && ((PArgs->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm); ++PArgs) {
28-
UProperty *prop = *PArgs;
29-
PyObject *arg = ue_py_convert_property(prop, (uint8 *)Parms);
30-
if (!arg) {
31-
unreal_engine_py_log_error();
32-
Py_DECREF(py_args);
33-
return;
29+
if (signature_set) {
30+
py_args = PyTuple_New(signature->NumParms);
31+
Py_ssize_t argn = 0;
32+
33+
TFieldIterator<UProperty> PArgs(signature);
34+
for (; PArgs && argn < signature->NumParms && ((PArgs->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm); ++PArgs) {
35+
UProperty *prop = *PArgs;
36+
PyObject *arg = ue_py_convert_property(prop, (uint8 *)Parms);
37+
if (!arg) {
38+
unreal_engine_py_log_error();
39+
Py_DECREF(py_args);
40+
return;
41+
}
42+
PyTuple_SetItem(py_args, argn, arg);
43+
argn++;
3444
}
35-
PyTuple_SetItem(py_args, argn, arg);
36-
argn++;
3745
}
3846

3947
PyObject *ret = PyObject_CallObject(py_callable, py_args);
40-
Py_DECREF(py_args);
48+
Py_XDECREF(py_args);
4149
if (!ret) {
4250
unreal_engine_py_log_error();
4351
return;
@@ -65,9 +73,9 @@ void UPythonDelegate::PyInputAxisHandler(float value)
6573
if (!ret) {
6674
unreal_engine_py_log_error();
6775
return;
68-
}
76+
}
6977
Py_DECREF(ret);
70-
}
78+
}
7179

7280
UPythonDelegate::~UPythonDelegate()
7381
{

‎Source/UnrealEnginePython/Public/PythonDelegate.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class UPythonDelegate : public UObject
99
GENERATED_BODY()
1010

1111
public:
12+
UPythonDelegate();
1213
~UPythonDelegate();
1314
virtual void ProcessEvent(UFunction *function, void *Parms) override;
1415
void SetPyCallable(PyObject *callable);
@@ -19,6 +20,7 @@ class UPythonDelegate : public UObject
1920

2021
private:
2122
UFunction *signature;
23+
bool signature_set;
2224

2325
UFUNCTION()
2426
void PyFakeCallable();

0 commit comments

Comments
 (0)