Skip to content

Commit 94edf15

Browse files
authored
Restore script state on reload (#67)
Resolves #66
1 parent 5e279e4 commit 94edf15

File tree

1 file changed

+57
-31
lines changed

1 file changed

+57
-31
lines changed

rust-script/src/runtime/rust_script.rs

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,19 @@ use std::{cell::RefCell, collections::HashSet, ffi::c_void};
88

99
use godot::classes::{
1010
notify::ObjectNotification, object::ConnectFlags, ClassDb, Engine, IScriptExtension, Object,
11-
Script, ScriptExtension, ScriptLanguage, WeakRef,
11+
Script, ScriptExtension, ScriptLanguage,
1212
};
13-
use godot::global::{godot_error, godot_print, godot_warn};
13+
use godot::global::{godot_error, godot_print, godot_warn, PropertyUsageFlags};
1414
use godot::meta::{MethodInfo, PropertyInfo, ToGodot};
1515
use godot::obj::script::create_script_instance;
16-
use godot::obj::{EngineEnum, InstanceId, WithBaseField};
16+
use godot::obj::{EngineBitfield, EngineEnum, InstanceId, WithBaseField};
1717
use godot::prelude::{
1818
godot_api, Array, Base, Callable, Dictionary, GString, Gd, GodotClass, StringName, Variant,
1919
VariantArray,
2020
};
2121

2222
use crate::apply::Apply;
23+
use crate::static_script_registry::RustScriptPropertyInfo;
2324

2425
use super::rust_script_instance::GodotScriptObject;
2526
use super::{
@@ -38,11 +39,12 @@ pub(crate) struct RustScript {
3839
#[var(get = get_class_name, set = set_class_name, usage_flags = [STORAGE])]
3940
class_name: GString,
4041

42+
/// dummy property that stores the onwer ids when the extension gets reloaded by the engine.
4143
#[var( get = owner_ids, set = set_owner_ids, usage_flags = [STORAGE])]
4244
#[allow(dead_code)]
4345
owner_ids: Array<i64>,
4446

45-
owners: RefCell<Vec<Gd<WeakRef>>>,
47+
owners: RefCell<HashSet<InstanceId>>,
4648
base: Base<ScriptExtension>,
4749
}
4850

@@ -86,13 +88,7 @@ impl RustScript {
8688
fn owner_ids(&self) -> Array<i64> {
8789
let owners = self.owners.borrow();
8890

89-
let set: HashSet<_> = owners
90-
.iter()
91-
.filter_map(|item| item.get_ref().to::<Option<Gd<Object>>>())
92-
.map(|obj| obj.instance_id().to_i64())
93-
.collect();
94-
95-
set.into_iter().collect()
91+
owners.iter().map(|id| id.to_i64()).collect()
9692
}
9793

9894
#[func]
@@ -103,18 +99,10 @@ impl RustScript {
10399
}
104100

105101
if !self.owners.borrow().is_empty() {
106-
godot_warn!("over writing existing owners of rust script");
102+
godot_warn!("overwriting existing owners of rust script");
107103
}
108104

109-
*self.owners.borrow_mut() = ids
110-
.iter_shared()
111-
.map(InstanceId::from_i64)
112-
.filter_map(|id| {
113-
let result: Option<Gd<Object>> = Gd::try_from_instance_id(id).ok();
114-
result
115-
})
116-
.map(|gd_ref| godot::global::weakref(&gd_ref.to_variant()).to())
117-
.collect();
105+
*self.owners.borrow_mut() = ids.iter_shared().map(InstanceId::from_i64).collect();
118106
}
119107

120108
#[func]
@@ -140,6 +128,14 @@ impl RustScript {
140128

141129
base.call("_init", &[]);
142130
}
131+
132+
fn map_property_info_list<R>(&self, f: impl Fn(&RustScriptPropertyInfo) -> R) -> Vec<R> {
133+
let reg = SCRIPT_REGISTRY.read().expect("unable to obtain read lock");
134+
135+
reg.get(&self.str_class_name())
136+
.map(|class| class.properties().iter().map(f).collect())
137+
.unwrap_or_default()
138+
}
143139
}
144140

145141
#[godot_api]
@@ -188,9 +184,7 @@ impl IScriptExtension for RustScript {
188184
}
189185

190186
unsafe fn instance_create(&self, mut for_object: Gd<Object>) -> *mut c_void {
191-
self.owners
192-
.borrow_mut()
193-
.push(godot::global::weakref(&for_object.to_variant()).to());
187+
self.owners.borrow_mut().insert(for_object.instance_id());
194188

195189
let data = self.create_remote_instance(for_object.clone());
196190
let instance = RustScriptInstance::new(data, for_object.clone(), self.to_gd());
@@ -210,9 +204,7 @@ impl IScriptExtension for RustScript {
210204
}
211205

212206
unsafe fn placeholder_instance_create(&self, for_object: Gd<Object>) -> *mut c_void {
213-
self.owners
214-
.borrow_mut()
215-
.push(godot::global::weakref(&for_object.to_variant()).to());
207+
self.owners.borrow_mut().insert(for_object.instance_id());
216208

217209
let placeholder = RustScriptPlaceholder::new(self.to_gd());
218210

@@ -393,24 +385,58 @@ impl IScriptExtension for RustScript {
393385
}
394386

395387
// godot script reload hook
396-
fn reload(&mut self, _keep_state: bool) -> godot::global::Error {
388+
fn reload(
389+
&mut self,
390+
// before 4.4 the engine does not correctly pass the keep_state flag
391+
#[cfg_attr(before_api = "4.4", expect(unused_variables))] keep_state: bool,
392+
) -> godot::global::Error {
393+
#[cfg(before_api = "4.4")]
394+
let keep_state = true;
395+
397396
let owners = self.owners.borrow().clone();
397+
let exported_properties_list = if keep_state {
398+
self.map_property_info_list(|prop| {
399+
(prop.usage & PropertyUsageFlags::EDITOR.ord() != 0).then_some(prop.property_name)
400+
})
401+
} else {
402+
Vec::with_capacity(0)
403+
};
398404

399-
owners.iter().for_each(|owner| {
400-
let mut object: Gd<Object> = match owner.get_ref().try_to() {
405+
owners.iter().for_each(|owner_id| {
406+
let mut object: Gd<Object> = match Gd::try_from_instance_id(*owner_id) {
401407
Ok(owner) => owner,
402408
Err(err) => {
403409
godot_warn!("Failed to get script owner: {:?}", err);
404410
return;
405411
}
406412
};
407413

414+
let property_backup: Vec<_> = if keep_state {
415+
exported_properties_list
416+
.iter()
417+
.flatten()
418+
.map(|key| {
419+
let value = object.get(*key);
420+
421+
(*key, value)
422+
})
423+
.collect()
424+
} else {
425+
Vec::with_capacity(0)
426+
};
427+
408428
// clear script to destroy script instance.
409429
object.set_script(&Variant::nil());
410430

411431
self.downgrade_gd(|self_gd| {
412432
// re-assign script to create new instance.
413433
object.set_script(&self_gd.to_variant());
434+
435+
if keep_state {
436+
property_backup.into_iter().for_each(|(key, value)| {
437+
object.set(key, &value);
438+
});
439+
}
414440
})
415441
});
416442

@@ -424,7 +450,7 @@ impl IScriptExtension for RustScript {
424450
self.str_class_name()
425451
);
426452

427-
self.reload(false);
453+
self.reload(true);
428454
}
429455
}
430456

0 commit comments

Comments
 (0)