Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions godot-core/src/meta/class_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,17 @@ impl ClassIdCache {
}

fn clear(&mut self) {
self.entries.clear();
// There are two types of hot reload:
// - dylib reload (dylib mtime newer): the old dylib is unloaded and the new one is loaded
// - .gdextension reload (.gdextension mtime newer): the existing dylib is re-initialized without unloading
//
// .gdextension reload keeps existing ClassIds alive with each id being an index into entries.
// To account for this, we preserve the backing data for existing entries, but drop the cached Godot
// StringNames and the TypeId lookup so they can be rebuilt.
for entry in &mut self.entries {
entry.godot_str = OnceCell::new();
}

self.type_to_index.clear();
self.string_to_index.clear();
}
}
10 changes: 9 additions & 1 deletion godot-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,15 @@ pub unsafe fn initialize(
/// # Safety
/// See [`initialize`].
pub unsafe fn deinitialize() {
deinitialize_binding()
deinitialize_binding();

// Clear the main thread ID to allow re-initialization during hot reload.
#[cfg(not(wasm_nothreads))]
{
if MAIN_THREAD_ID.is_initialized() {
MAIN_THREAD_ID.clear();
}
}
}

fn print_preamble(version: GDExtensionGodotVersion) {
Expand Down
31 changes: 31 additions & 0 deletions itest/hot-reload/godot/run-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ cargo build -p hot-reload $cargoArgs
# Wait briefly so artifacts are present on file system.
sleep 0.5

# ----------------------------------------------------------------
# Test Case 1: Update Rust source and compile to trigger reload.
# ----------------------------------------------------------------

echo "[Bash] Scenario 1: Reload after updating Rust source..."
$GODOT4_BIN -e --headless --path $rel &
godotPid=$!
echo "[Bash] Wait for Godot ready (PID $godotPid)..."
Expand All @@ -70,6 +75,32 @@ echo "[Bash] Wait for Godot exit..."
wait $godotPid
status=$?
echo "[Bash] Godot (PID $godotPid) has completed with status $status."
if [[ $status -ne 0 ]]; then
exit $status
fi

# ----------------------------------------------------------------
# Test Case 2: Touch the .gdextension file to trigger reload.
# ----------------------------------------------------------------
godotPid=0

echo "[Bash] Scenario 2: Reload after touching rust.gdextension..."
$GODOT4_BIN -e --headless --path $rel &
godotPid=$!
echo "[Bash] Wait for Godot ready (PID $godotPid)..."

$GODOT4_BIN --headless --no-header --script ReloadOrchestrator.gd -- await

# update timestamp to trigger reload
touch "$rel/rust.gdextension"

$GODOT4_BIN --headless --no-header --script ReloadOrchestrator.gd -- notify

echo "[Bash] Wait for Godot exit (dirty scenario)..."
wait $godotPid
status=$?
echo "[Bash] Godot (PID $godotPid) has completed with status $status."
if [[ $status -ne 0 ]]; then
exit $status
fi
godotPid=0
Loading