Skip to content

Commit

Permalink
Merge pull request #365 from godot-rust/feature/no-editor
Browse files Browse the repository at this point in the history
No longer run Rust classes inside Godot editor
  • Loading branch information
Bromeon authored Jul 30, 2023
2 parents d3fe5c7 + 235e0fa commit 8e56c5f
Show file tree
Hide file tree
Showing 14 changed files with 94 additions and 31 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/full-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ jobs:

- name: linux
os: ubuntu-20.04
rust-toolchain: '1.66.0'
rust-toolchain: '1.70.0'
rust-special: -msrv

steps:
Expand Down
2 changes: 1 addition & 1 deletion examples/dodge-the-creeps/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "dodge-the-creeps"
version = "0.1.0"
edition = "2021"
rust-version = "1.66"
rust-version = "1.70"
publish = false

[lib]
Expand Down
10 changes: 2 additions & 8 deletions examples/dodge-the-creeps/rust/src/player.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use godot::engine::{
AnimatedSprite2D, Area2D, Area2DVirtual, CollisionShape2D, Engine, PhysicsBody2D,
};
use godot::engine::{AnimatedSprite2D, Area2D, Area2DVirtual, CollisionShape2D, PhysicsBody2D};
use godot::prelude::*;

#[derive(GodotClass)]
Expand Down Expand Up @@ -60,11 +58,7 @@ impl Area2DVirtual for Player {
}

fn process(&mut self, delta: f64) {
// Don't process if running in editor. This part should be removed when
// issue is resolved: https://github.com/godot-rust/gdext/issues/70
if Engine::singleton().is_editor_hint() {
return;
}
println!("process");

let mut animated_sprite = self
.base
Expand Down
2 changes: 1 addition & 1 deletion godot-bindings/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "godot-bindings"
version = "0.1.0"
edition = "2021"
rust-version = "1.66"
rust-version = "1.70"
license = "MPL-2.0"
keywords = ["gamedev", "godot", "engine", "ffi", "sys"]
categories = ["game-engines", "graphics"]
Expand Down
2 changes: 1 addition & 1 deletion godot-codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "godot-codegen"
version = "0.1.0"
edition = "2021"
rust-version = "1.66"
rust-version = "1.70"
license = "MPL-2.0"
keywords = ["gamedev", "godot", "engine", "codegen"]
categories = ["game-engines", "graphics"]
Expand Down
2 changes: 1 addition & 1 deletion godot-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "godot-core"
version = "0.1.0"
edition = "2021"
rust-version = "1.66"
rust-version = "1.70"
license = "MPL-2.0"
keywords = ["gamedev", "godot", "engine", "2d", "3d"] # possibly: "ffi"
categories = ["game-engines", "graphics"]
Expand Down
64 changes: 53 additions & 11 deletions godot-core/src/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
*/

use godot_ffi as sys;
use std::collections::btree_map::BTreeMap;
use sys::out;

use std::cell;
use std::collections::BTreeMap;

#[doc(hidden)]
// TODO consider body safe despite unsafe function, and explicitly mark unsafe {} locations
Expand All @@ -15,7 +18,17 @@ pub unsafe fn __gdext_load_library<E: ExtensionLibrary>(
init: *mut sys::GDExtensionInitialization,
) -> sys::GDExtensionBool {
let init_code = || {
sys::initialize(interface_or_get_proc_address, library);
let virtuals_active_in_editor = match E::editor_run_behavior() {
EditorRunBehavior::Full => true,
EditorRunBehavior::NoVirtuals => false,
};

let config = sys::GdextConfig {
virtuals_active_in_editor,
is_editor: cell::OnceCell::new(),
};

sys::initialize(interface_or_get_proc_address, library, config);

let mut handle = InitHandle::new();

Expand All @@ -41,11 +54,6 @@ pub unsafe fn __gdext_load_library<E: ExtensionLibrary>(
is_success.unwrap_or(0)
}

#[doc(hidden)]
pub fn __gdext_default_init(handle: &mut InitHandle) {
handle.register_layer(InitLevel::Scene, DefaultLayer);
}

unsafe extern "C" fn ffi_initialize_layer(
_userdata: *mut std::ffi::c_void,
init_level: sys::GDExtensionInitializationLevel,
Expand Down Expand Up @@ -97,8 +105,8 @@ pub static mut INIT_HANDLE: Option<InitHandle> = None;
///
/// ```
/// # use godot::init::*;
///
/// // This is just a type tag without any functionality
/// // This is just a type tag without any functionality.
/// // Its name is irrelevant.
/// struct MyExtension;
///
/// #[gdextension]
Expand All @@ -113,14 +121,42 @@ pub static mut INIT_HANDLE: Option<InitHandle> = None;
/// responsible to uphold them: namely in GDScript code or other GDExtension bindings loaded by the engine.
/// Violating this may cause undefined behavior, even when invoking _safe_ functions.
///
/// [gdextension]: crate::init::gdextension
/// [safety]: https://godot-rust.github.io/book/gdextension/safety.html
/// [gdextension]: attr.gdextension.html
/// [safety]: https://godot-rust.github.io/book/gdext/advanced/safety.html
// FIXME intra-doc link
pub unsafe trait ExtensionLibrary {
fn load_library(handle: &mut InitHandle) -> bool {
handle.register_layer(InitLevel::Scene, DefaultLayer);
true
}

/// Determines if and how an extension's code is run in the editor.
fn editor_run_behavior() -> EditorRunBehavior {
EditorRunBehavior::NoVirtuals
}
}

/// Determines if and how an extension's code is run in the editor.
///
/// By default, Godot 4 runs all virtual lifecycle callbacks (`_ready`, `_process`, `_physics_process`, ...)
/// for extensions. This behavior is different from Godot 3, where extension classes needed to be explicitly
/// marked as "tool".
///
/// In many cases, users write extension code with the intention to run in games, not inside the editor.
/// This is why the default behavior in gdext deviates from Godot: lifecycle callbacks are disabled inside the
/// editor. It is however possible to restore the original behavior with this enum.
///
/// See also [`ExtensionLibrary::editor_run_behavior()`].
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub enum EditorRunBehavior {
/// Runs the extension with full functionality in editor.
Full,

/// Does not invoke any Godot virtual functions.
///
/// Classes are still registered, and calls from GDScript to Rust are still possible.
NoVirtuals,
}

pub trait ExtensionLayer: 'static {
Expand Down Expand Up @@ -179,13 +215,19 @@ impl InitHandle {
// f();
// }
if let Some(layer) = self.layers.get_mut(&level) {
out!("init: initialize level {level:?}...");
layer.initialize()
} else {
out!("init: skip init of level {level:?}.");
}
}

pub fn run_deinit_function(&mut self, level: InitLevel) {
if let Some(layer) = self.layers.get_mut(&level) {
out!("init: deinitialize level {level:?}...");
layer.deinitialize()
} else {
out!("init: skip deinit of level {level:?}.");
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion godot-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "godot-ffi"
version = "0.1.0"
edition = "2021"
rust-version = "1.66"
rust-version = "1.70"
license = "MPL-2.0"
keywords = ["gamedev", "godot", "engine", "ffi"]
categories = ["game-engines", "graphics"]
Expand Down
24 changes: 22 additions & 2 deletions godot-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ mod plugins;
mod toolbox;

use compat::BindingCompat;
use std::cell;
use std::ffi::CStr;

// See https://github.com/dtolnay/paste/issues/69#issuecomment-962418430
Expand All @@ -52,12 +53,18 @@ struct GodotBinding {
library: GDExtensionClassLibraryPtr,
method_table: GlobalMethodTable,
runtime_metadata: GdextRuntimeMetadata,
config: GdextConfig,
}

struct GdextRuntimeMetadata {
godot_version: GDExtensionGodotVersion,
}

pub struct GdextConfig {
pub virtuals_active_in_editor: bool,
pub is_editor: cell::OnceCell<bool>,
}

/// Late-init globals
// Note: static mut is _very_ dangerous. Here a bit less so, since modification happens only once (during init) and no
// &mut references are handed out (except for registry, see below). Overall, UnsafeCell/RefCell + Sync might be a safer abstraction.
Expand All @@ -71,7 +78,11 @@ static mut BINDING: Option<GodotBinding> = None;
/// - The `library` pointer must be the pointer given by Godot at initialisation.
/// - This function must not be called from multiple threads.
/// - This function must be called before any use of [`get_library`].
pub unsafe fn initialize(compat: InitCompat, library: GDExtensionClassLibraryPtr) {
pub unsafe fn initialize(
compat: InitCompat,
library: GDExtensionClassLibraryPtr,
config: GdextConfig,
) {
out!("Initialize gdext...");

out!(
Expand Down Expand Up @@ -100,6 +111,7 @@ pub unsafe fn initialize(compat: InitCompat, library: GDExtensionClassLibraryPtr
method_table,
library,
runtime_metadata,
config,
});
out!("Assigned binding.");

Expand Down Expand Up @@ -144,12 +156,20 @@ pub unsafe fn method_table() -> &'static GlobalMethodTable {

/// # Safety
///
/// Must be accessed from the main thread.
/// Must be accessed from the main thread, and the interface must have been initialized.
#[inline(always)]
pub(crate) unsafe fn runtime_metadata() -> &'static GdextRuntimeMetadata {
&BINDING.as_ref().unwrap().runtime_metadata
}

/// # Safety
///
/// Must be accessed from the main thread, and the interface must have been initialized.
#[inline]
pub unsafe fn config() -> &'static GdextConfig {
&BINDING.as_ref().unwrap().config
}

// ----------------------------------------------------------------------------------------------------------------------------------------------
// Macros to access low-level function bindings

Expand Down
2 changes: 1 addition & 1 deletion godot-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "godot-macros"
version = "0.1.0"
edition = "2021"
rust-version = "1.66"
rust-version = "1.70"
license = "MPL-2.0"
keywords = ["gamedev", "godot", "engine", "derive", "macro"]
categories = ["game-engines", "graphics"]
Expand Down
7 changes: 7 additions & 0 deletions godot-macros/src/godot_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,13 @@ fn transform_trait_impl(original_impl: Impl) -> Result<TokenStream, Error> {
fn __virtual_call(name: &str) -> ::godot::sys::GDExtensionClassCallVirtual {
//println!("virtual_call: {}.{}", std::any::type_name::<Self>(), name);

let __config = unsafe { ::godot::sys::config() };

if !__config.virtuals_active_in_editor
&& *__config.is_editor.get_or_init(|| ::godot::engine::Engine::singleton().is_editor_hint()) {
return None;
}

match name {
#(
#virtual_method_names => #virtual_method_callbacks,
Expand Down
2 changes: 1 addition & 1 deletion godot-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ pub fn itest(meta: TokenStream, input: TokenStream) -> TokenStream {

/// Proc-macro attribute to be used in combination with the [`ExtensionLibrary`] trait.
///
/// [`ExtensionLibrary`]: crate::init::ExtensionLibrary
/// [`ExtensionLibrary`]: trait.ExtensionLibrary.html
// FIXME intra-doc link
#[proc_macro_attribute]
pub fn gdextension(meta: TokenStream, input: TokenStream) -> TokenStream {
Expand Down
2 changes: 1 addition & 1 deletion godot/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "godot"
version = "0.1.0"
edition = "2021"
rust-version = "1.66"
rust-version = "1.70"
license = "MPL-2.0"
keywords = ["gamedev", "godot", "engine", "2d", "3d"] # possibly: "ffi"
categories = ["game-engines", "graphics"]
Expand Down
2 changes: 1 addition & 1 deletion itest/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "itest"
version = "0.1.0"
edition = "2021"
rust-version = "1.66"
rust-version = "1.70"
publish = false

[lib]
Expand Down

0 comments on commit 8e56c5f

Please sign in to comment.