Skip to content

Commit

Permalink
godot-rust#434 Hot reload: implement unregistering of classes
Browse files Browse the repository at this point in the history
  • Loading branch information
kkolyan committed Oct 1, 2023
1 parent 9775bf1 commit 182c7b0
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 6 deletions.
4 changes: 2 additions & 2 deletions godot-core/src/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ fn gdext_on_level_init(level: InitLevel) {
}

/// Tasks needed to be done by gdext internally upon unloading an initialization level. Called after user code.
fn gdext_on_level_deinit(_level: InitLevel) {
// No logic at the moment.
fn gdext_on_level_deinit(level: InitLevel) {
crate::unregister_classes(level);
}

// ----------------------------------------------------------------------------------------------------------------------------------------------
Expand Down
50 changes: 46 additions & 4 deletions godot-core/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#![allow(dead_code)] // FIXME

use crate::init::InitLevel;
use crate::log;
use crate::{godot_print, log};
use crate::obj::*;
use crate::private::as_storage;
use crate::storage::InstanceStorage;
Expand All @@ -21,6 +21,10 @@ use crate::out;
use std::any::Any;
use std::collections::HashMap;
use std::{fmt, ptr};
use std::sync::{Mutex, MutexGuard, TryLockError};


static LOADED_CLASSES: Mutex<Option<HashMap<InitLevel, Vec<ClassName>>>> = Mutex::new(None);

// TODO(bromeon): some information coming from the proc-macro API is deferred through PluginComponent, while others is directly
// translated to code. Consider moving more code to the PluginComponent, which allows for more dynamic registration and will
Expand Down Expand Up @@ -239,22 +243,59 @@ pub fn auto_register_classes(init_level: InitLevel) {
.or_insert_with(|| default_registration_info(name));

fill_class_info(elem.component.clone(), class_info);
// class_info.godot_params.recreate_instance_func = Some(callbacks::recreate::<T>)
});

//out!("Class-map: {map:#?}");
let mut loaded_classes = get_loaded_classes_with_mutex();
let loaded_classes = loaded_classes.get_or_insert_with(Default::default);

for info in map.into_values() {
out!(
"Register class: {} at level `{init_level:?}`",
info.class_name
);
loaded_classes
.entry(init_level)
.or_default()
.push(info.class_name);
register_class_raw(info);

godot_print!("Class {} loaded",info.class_name);
}

out!("All classes for level `{init_level:?}` auto-registered.");
}

pub fn unregister_classes(level: InitLevel) {
let mut loaded_classes = get_loaded_classes_with_mutex();
let loaded_classes = loaded_classes.get_or_insert_with(Default::default);
let loaded_classes = loaded_classes.remove(&level).unwrap_or_default();
out!("Unregistering classes of level {:?}...", level);
for class_name in loaded_classes.iter().rev() {
out!(
"Unregister class: {} at level `{init_level:?}`",
info.class_name
);
unsafe {
#[allow(clippy::let_unit_value)]
let _: () = interface_fn!(classdb_unregister_extension_class)(
sys::get_library(),
class_name.string_sys(),
);
}
godot_print!("Class {class_name} unloaded");
}
}

fn get_loaded_classes_with_mutex() -> MutexGuard<'static, Option<HashMap<InitLevel, Vec<ClassName>>>> {
match LOADED_CLASSES.try_lock() {
Ok(it) => it,
Err(err) => match err {
TryLockError::Poisoned(_err) => panic!("CLASSES poisoned. seems like class registration or deregistration panicked."),
TryLockError::WouldBlock => panic!("unexpected concurrent access detected to CLASSES"),
},
}
}

/// Populate `c` with all the relevant data from `component` (depending on component type).
fn fill_class_info(component: PluginComponent, c: &mut ClassRegistrationInfo) {
// out!("| reg (before): {c:?}");
Expand Down Expand Up @@ -304,6 +345,7 @@ fn fill_class_info(component: PluginComponent, c: &mut ClassRegistrationInfo) {
c.is_editor_plugin = true;
}
}
// c.godot_params.unwrap().
// out!("| reg (after): {c:?}");
// out!();
}
Expand Down Expand Up @@ -631,6 +673,6 @@ fn default_creation_info() -> sys::GDExtensionClassCreationInfo2 {
call_virtual_with_data_func: None,
get_rid_func: None,
class_userdata: ptr::null_mut(),
is_exposed: false as u8,
is_exposed: true as u8,
}
}
6 changes: 6 additions & 0 deletions godot-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ license = "MPL-2.0"
keywords = ["gamedev", "godot", "engine", "derive", "macro"]
categories = ["game-engines", "graphics"]

[features]
custom-godot = ["godot-bindings/custom-godot"]

[lib]
proc-macro = true

Expand All @@ -20,3 +23,6 @@ proc-macro2 = "1.0.63"
quote = "1.0.29"

venial = "0.5"

[build-dependencies]
godot-bindings = { path = "../godot-bindings" } # emit_godot_version_cfg
9 changes: 9 additions & 0 deletions godot-macros/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

fn main() {
godot_bindings::emit_godot_version_cfg();
}

0 comments on commit 182c7b0

Please sign in to comment.