-
Notifications
You must be signed in to change notification settings - Fork 208
/
bridge.rs
142 lines (116 loc) · 3.76 KB
/
bridge.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use std::collections::HashMap;
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use gdnative_bindings::{Object, Reference};
use gdnative_core::core_types::{GodotError, Variant, VariantArray};
use gdnative_core::export::user_data::{ArcData, Map};
use gdnative_core::export::{ClassBuilder, Method, NativeClass, NativeClassMethods, Varargs};
use gdnative_core::godot_site;
use gdnative_core::object::ownership::Shared;
use gdnative_core::object::{Instance, TInstance, TRef};
use crate::future::Resume;
// We need to keep our observers alive since `Object::connect` won't
static BRIDGES: OnceCell<Mutex<Pool>> = OnceCell::new();
pub(super) fn terminate() {
if let Some(pool) = BRIDGES.get() {
let mut pool = pool.lock();
std::mem::take(&mut *pool);
}
}
#[derive(Default)]
struct Pool {
busy: HashMap<i64, Entry>,
free: Vec<(i64, Instance<SignalBridge, Shared>)>,
next_id: i64,
}
impl Pool {
fn next_id(&mut self) -> i64 {
let id = self.next_id;
self.next_id += 1;
id
}
}
struct Entry {
resume: Resume<Vec<Variant>>,
// Just need to keep this alive.
_obj: Instance<SignalBridge, Shared>,
}
pub(super) struct SignalBridge {
id: i64,
}
impl NativeClass for SignalBridge {
type Base = Reference;
type UserData = ArcData<SignalBridge>;
fn class_name() -> &'static str {
// Sort of just praying that there will be no duplicates of this.
"__GDNATIVE_ASYNC_INTERNAL__SignalBridge"
}
fn register_properties(_builder: &ClassBuilder<Self>) {}
}
impl SignalBridge {
pub(crate) fn connect(
source: TRef<Object>,
signal: &str,
resume: Resume<Vec<Variant>>,
) -> Result<(), GodotError> {
let mut pool = BRIDGES.get_or_init(Mutex::default).lock();
let (id, bridge) = pool.free.pop().unwrap_or_else(|| {
let id = pool.next_id();
let bridge = Instance::emplace(SignalBridge { id }).into_shared();
(id, bridge)
});
source.connect(
signal,
bridge.base(),
"_on_signal",
VariantArray::new_shared(),
Object::CONNECT_ONESHOT,
)?;
let entry = Entry {
resume,
_obj: bridge,
};
assert!(pool.busy.insert(id, entry).is_none());
Ok(())
}
}
#[derive(Clone, Copy, Debug, Default)]
struct OnSignalFn;
impl Method<SignalBridge> for OnSignalFn {
fn call(&self, this: TInstance<'_, SignalBridge, Shared>, args: Varargs<'_>) -> Variant {
let args = args.cloned().collect();
let this_persist = this.clone().claim();
this.script()
.map(|s| {
let (resume, args) = {
let mut pool = BRIDGES.get().unwrap().lock();
match pool.busy.remove(&s.id) {
Some(entry) => {
pool.free.push((s.id, this_persist));
(entry.resume, args)
}
None => {
gdnative_core::log::warn(
Self::site().unwrap(),
"`_on_signal` should only be called once per bridge object",
);
return;
}
}
};
resume.resume(args);
})
.unwrap();
Variant::nil()
}
fn site() -> Option<gdnative_core::log::Site<'static>> {
Some(godot_site!(SignalBridge::_on_signal))
}
}
impl NativeClassMethods for SignalBridge {
fn register(builder: &ClassBuilder<Self>) {
builder
.build_method("_on_signal", OnSignalFn)
.done_stateless();
}
}