Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More reliable macOS hot-reloading test, workaround missing GDExtension methods #657

Merged
merged 5 commits into from
Apr 6, 2024
Merged
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
2 changes: 1 addition & 1 deletion .github/composite/godot-install/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ runs:

# - name: "Check cache for installed Godot version"
# id: "cache-godot"
# uses: actions/cache@v3
# uses: actions/cache@v4
# with:
# path: ${{ runner.temp }}/godot_bin
# key: ${{ inputs.artifact-name }}-v${{ inputs.godot-ver }}
Expand Down
2 changes: 1 addition & 1 deletion .github/composite/llvm/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ runs:
id: cache-llvm
# Note: conditionals not yet supported; see https://github.com/actions/runner/issues/834
# if: inputs.llvm == 'true'
uses: actions/cache@v3
uses: actions/cache@v4
with:
# path: |
# C:/Program Files/LLVM
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/full-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ jobs:
godot-binary: ${{ matrix.godot-binary }}
godot-args: ${{ matrix.godot-args }} # currently unused
godot-prebuilt-patch: ${{ matrix.godot-prebuilt-patch }}
rust-extra-args: ${{ matrix.rust-extra-args }} --features godot/codegen-full
rust-extra-args: ${{ matrix.rust-extra-args }}
rust-toolchain: ${{ matrix.rust-toolchain || 'stable' }}
rust-env-rustflags: ${{ matrix.rust-env-rustflags }}
rust-target: ${{ matrix.rust-target }}
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ Cargo.lock

# Needed to run projects without having to open the editor first.
!**/.godot/extension_list.cfg
!**/.godot/global_script_class_cache.cfg

# This project: input JSONs and code generation
gen
Expand Down

This file was deleted.

This file was deleted.

120 changes: 120 additions & 0 deletions examples/hot-reload/godot/test/ReloadOrchestrator.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Copyright (c) godot-rust; Bromeon and contributors.
# 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/.

# See https://docs.godotengine.org/en/stable/tutorials/editor/command_line_tutorial.html#running-a-script.

class_name ReloadOrchestrator
extends SceneTree

const TIMEOUT_S = 30
const TO_GODOT_PORT = 1337
const FROM_GODOT_PORT = 1338

var run_main_loop: bool = false
var elapsed: float = 0.0
var udp := PacketPeerUDP.new()
var args: Array # must be Array for match, not PackedStringArray.

func _initialize():
args = OS.get_cmdline_user_args()
print("[GD Orch] Start ", args)

var ok := true
match args:
["await"]:
ok = receive_udp()
run_main_loop = true

["replace"]:
print("[GD Orch] Replace source code...")
ok = replace_line("../../rust/src/lib.rs")

["notify"]:
print("[GD Orch] Notify Godot about change...")
ok = send_udp()

_:
fail("Invalid command-line args")
ok = false

if not ok:
quit(1)
return


func _finalize():
udp.close()
print("[GD Orch] Stop ", args)

func _process(delta: float) -> bool:
if not run_main_loop:
return true

elapsed += delta
if elapsed > TIMEOUT_S:
fail(str("Timed out waiting for Godot (", TIMEOUT_S, " seconds)."))
return true

if udp.get_available_packet_count() == 0:
return false

var packet = udp.get_packet().get_string_from_ascii()
print("[GD Orch] Received UDP packet [", packet.length(), "]: ", packet)
return true


func replace_line(file_path: String) -> bool:
var file = FileAccess.open(file_path, FileAccess.READ)
if file == null:
return false

var lines = []
while not file.eof_reached():
lines.append(file.get_line())
file.close()

var replaced = 0
file = FileAccess.open(file_path, FileAccess.WRITE)
for line: String in lines:
if line.strip_edges() == 'fn get_number(&self) -> i64 { 100 }':
file.store_line(line.replace("100", "777"))
replaced += 1
else:
file.store_line(line)
file.close()

if replaced == 0:
fail("Line not found in file.")
return false
else:
return true


func receive_udp() -> bool:
if udp.bind(FROM_GODOT_PORT) != OK:
fail("Failed to bind UDP")
return false

print("[GD Orch] Waiting for Godot to be ready (UDP)...")
return true


func send_udp() -> bool:
var out_udp = PacketPeerUDP.new()
if out_udp.set_dest_address("127.0.0.1", TO_GODOT_PORT) != OK:
fail("Failed to set destination address")
return false

if out_udp.put_packet("reload".to_utf8_buffer()) != OK:
fail("Failed to send packet")
return false

print("[GD Orch] Packet sent successfully")
return true


func fail(s: String) -> void:
print("::error::[GD Orch] ", s) # GitHub Action syntax
quit(1)
20 changes: 8 additions & 12 deletions examples/hot-reload/godot/test/ReloadTest.gd
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ var extension_name: String


func _ready() -> void:
print("[GDScript] Start...")
print("[GD Editor] Start...")

var r = Reloadable.new()
var num = r.get_number()
r.free()

print("[GDScript] Sanity check: initial number is ", num)
print("[GD Editor] Sanity check: initial number is ", num)

var extensions = GDExtensionManager.get_loaded_extensions()
if extensions.size() == 1:
Expand All @@ -31,17 +31,14 @@ func _ready() -> void:
return

udp.bind(1337)
print("[GDScript] ReloadTest ready to receive...")
print("[GD Editor] ReloadTest ready to receive...")

send_udp()


func send_udp():
# Attempt to bind the UDP socket to any available port for sending.
# You can specify a port number instead of 0 if you need to bind to a specific port.
var out_udp = PacketPeerUDP.new()

# Set the destination address and port for the message
if out_udp.set_dest_address("127.0.0.1", 1338) != OK:
fail("Failed to set destination address")
return
Expand All @@ -50,12 +47,12 @@ func send_udp():
fail("Failed to send packet")
return

print("[GDScript] Packet sent successfully")
print("[GD Editor] Packet sent successfully")
out_udp.close()


func _exit_tree() -> void:
print("[GDScript] ReloadTest exit.")
print("[GD Editor] ReloadTest exit.")
udp.close()


Expand All @@ -64,7 +61,7 @@ func _process(delta: float) -> void:
return

var packet = udp.get_packet().get_string_from_ascii()
print("[GDScript] Received UDP packet [", packet.length(), "]: ", packet)
print("[GD Editor] Received UDP packet [", packet.length(), "]: ", packet)

if not _hot_reload():
return
Expand All @@ -74,15 +71,14 @@ func _process(delta: float) -> void:
r.free()

if num == 777:
print("[GDScript] Successful hot-reload! Exit...")
print("[GD Editor] Successful hot-reload! Exit...")
get_tree().quit(0)
else:
fail(str("Number was not updated correctly (is ", num, ")"))
return


func _hot_reload():
# TODO sometimes fails because .so is not found
var status = GDExtensionManager.reload_extension(extension_name)
if status != OK:
fail(str("Failed to reload extension: ", status))
Expand All @@ -92,7 +88,7 @@ func _hot_reload():


func fail(s: String) -> void:
print("::error::[GDScript] ", s) # GitHub Action syntax
print("::error::[GD Editor] ", s) # GitHub Action syntax
get_tree().quit(1)


84 changes: 0 additions & 84 deletions examples/hot-reload/godot/test/orchestrate.py

This file was deleted.

24 changes: 14 additions & 10 deletions examples/hot-reload/godot/test/run-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,22 @@
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

# Restore un-reloaded files on exit (for local testing).
cleanedUp=0 # avoid recursion if cleanup fails
cleanup() {
echo "[Bash] Cleanup..."
git checkout --quiet ../../rust/src/lib.rs ../rust.gdextension ../MainScene.tscn
if [[ $cleanedUp -eq 0 ]]; then
cleanedUp=1
echo "[Bash] Cleanup..."
git checkout --quiet ../../rust/src/lib.rs ../rust.gdextension ../MainScene.tscn || true # ignore errors here
fi
}

set -euo pipefail
trap cleanup EXIT

echo "[Bash] Start hot-reload integration test..."
echo "[Bash] Start hot-reload integration test..."

# Restore un-reloaded file (for local testing).
git checkout --quiet ../../rust/src/lib.rs ../rust.gdextension
git checkout --quiet ../../rust/src/lib.rs ../rust.gdextension

# Set up editor file which has scene open, so @tool script loads at startup. Also copy scene file that holds a script.
mkdir -p ../.godot/editor
Expand All @@ -33,20 +37,20 @@ sleep 0.5

$GODOT4_BIN -e --headless --path .. &
pid=$!
echo "[Bash] Wait for Godot ready (PID $pid)..."
echo "[Bash] Wait for Godot ready (PID $pid)..."

python orchestrate.py await
python orchestrate.py replace
$GODOT4_BIN --headless --no-header --script ReloadOrchestrator.gd -- await
$GODOT4_BIN --headless --no-header --script ReloadOrchestrator.gd -- replace

# Compile updated Rust source.
cargo build -p hot-reload $cargoArgs

python orchestrate.py notify
$GODOT4_BIN --headless --no-header --script ReloadOrchestrator.gd -- notify

echo "[Bash] Wait for Godot exit..."
echo "[Bash] Wait for Godot exit..."
wait $pid
status=$?
echo "[Bash] Godot (PID $pid) has completed with status $status."
echo "[Bash] Godot (PID $pid) has completed with status $status."



4 changes: 2 additions & 2 deletions examples/hot-reload/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ struct HotReload;
#[gdextension]
unsafe impl ExtensionLibrary for HotReload {
fn on_level_init(_level: InitLevel) {
println!("[Rust] Init level {:?}", _level);
println!("[Rust] Init level {:?}", _level);
}

fn on_level_deinit(_level: InitLevel) {
println!("[Rust] Deinit level {:?}", _level);
println!("[Rust] Deinit level {:?}", _level);
}
}

Expand Down
Loading
Loading