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

Rust compilation requires Godot editor restart #1

Closed
Bromeon opened this issue Oct 3, 2022 · 9 comments
Closed

Rust compilation requires Godot editor restart #1

Bromeon opened this issue Oct 3, 2022 · 9 comments
Labels
bug status: upstream Depending on upstream fix (typically Godot)

Comments

@Bromeon
Copy link
Member

Bromeon commented Oct 3, 2022

Upstream issue: godotengine/godot#66231

On Windows, the Godot editor "locks" a DLL containing a GDExtension library and releases it only after shutdown. Native code can thus not be recompiled as long as the editor remains open. Behavior on Linux and Mac is unclear (please let me know about your experiences).

This is a considerable limitation for game developers using GDExtension. Requiring the user to reload the editor on every change not only makes a very common workflow impossible, but is also a regression from GDNative, where reloading (for non-tool classes) could be achieved while the editor was out of focus -- even if it had its bugs.

We depend on Godot to provide a mechanism that allows dynamic libraries to be recompiled. Even without a full-featured hot reloading, a bit part of the problem could be alleviated by using the recompiled dynamic library for the launched application (not in-editor).

@Bromeon Bromeon added bug status: upstream Depending on upstream fix (typically Godot) labels Oct 3, 2022
@Bromeon Bromeon pinned this issue Nov 8, 2022
@AckslD
Copy link

AckslD commented Nov 12, 2022

On linux I can rebuild the DLL fine but some things are not updated. For example I've seen that I need to restart godot (maybe there is another way to trigger a "refresh"?) when:

  • Adding a new type of node and for it to show in the list of available node types.
  • Adding a new (#[func]) and for it to be known by godot such that I can connect a signal to it.

@setzer22
Copy link
Collaborator

I am also seeing a very similar behavior on Linux. It makes sense because:

  • Each time the game runs, the DLL is read from disk, so if there are changes, the game process will pick it up. But...
  • The DLL is read by the godot editor once at startup, and never reloaded, so whatever was there at the time of loading, is what the editor will show.

So, basically, reloading the game changes happens to work on Linux merely by accident. It was not intended in the design GDExtension. That's also why in Windows, the DLL is being locked and never freed, because the process does not expect it to ever change. It is generally a reasonable assumption to make for plugin development, but seriously hampers game development 😕

@AckslD
Copy link

AckslD commented Nov 12, 2022

Btw, to reload signals etc from the DLL, it does work to do Project -> Reload current project 👍 I guess it's effectively the same as restarting godot but a bit more convenient. Also closing to project menu and opening the project again works.

bors bot added a commit that referenced this issue Jan 22, 2023
65: PR #1/5 Astolfo feature/builtin-scalar r=Bromeon a=RealAstolfo



Co-authored-by: RealAstolfo <[email protected]>
@sadovsf
Copy link

sadovsf commented Mar 10, 2023

I can confirm similar behavior on osx as well. I can leave editor open and recompile at will as long as iam not adding new end points essentially. In that case for editor to notice them project reload must be used.

@jcdickinson
Copy link

On Windows it is usually possible to move a locked file (so long as it remains on the same disk), which would allow the build to proceed. I no longer use Windows, so I can't confirm whether this would work in this case.

If it does, you only need to move the file every time the editor starts. You could probably attempt to acquire a read lock in build.rs, or powershell, or something and move the file if that fails.

@Bromeon
Copy link
Member Author

Bromeon commented Jul 31, 2023

The thing is that all GDExtension bindings currently suffer from this issue, so I'd rather not build a Rust-specific workaround.

It would be nice if this were fixed in Godot itself. godotengine/godot-cpp#955 proposes an approach, but priorities have shifted again... I'll try to bring this up again 🙂

@Bromeon
Copy link
Member Author

Bromeon commented Aug 16, 2023

A fix for Windows DLLs has been merged in godotengine/godot#80188! Now all three main platforms allow to recompile Rust while the editor is open, which is really nice 😊

In other great news, an initial step towards hot reloading is being worked on in godotengine/godot#80284 🚀

@LeaoLuciano
Copy link
Contributor

LeaoLuciano commented Sep 23, 2023

Testing godotengine/godot#80284 and changing only compat things in gdext (https://github.com/LeaoLuciano/gdext/tree/hot-reload), it works (on linux):

hot_reloading.mp4

@Bromeon
Copy link
Member Author

Bromeon commented Sep 26, 2023

Both

have been merged to Godot master.
We can consider this very first issue and big blocker resolved! 🚀

There will likely be some follow-up questions and improvements on hot reloading, but these can be addressed separately 🙂

@Bromeon Bromeon closed this as completed Sep 26, 2023
@Bromeon Bromeon unpinned this issue Dec 28, 2023
Houtamelo added a commit to Houtamelo/dead_gds_have_rights that referenced this issue Sep 19, 2024
The style is similar to GDScript's @rpc annotation, the macro can be used as follows:

godot-rust#1 - Separate arguments:
```rust
#[rpc(any_peer, reliable)]
fn some_rpc(&mut self) {
    //..
}
```

Providing overlapping arguments generates a compile error.

Any omitted arguments are set to their default values.

godot-rust#2 - Provide an expression:
```rust
const CONFIG: RpcArgs = RpcArgs {
    mode: RpcMode::Authority,
    ..RpcArgs::default()
};

#[rpc(config = CONFIG_EXPR)]
fn some_rpc(&mut self) {
    //..
}
```

Number godot-rust#2 is useful in case you want to reuse the configuration on multiple functions.

Number godot-rust#2 is mutually exclusive with number godot-rust#1.
---

The generated macro code works as follows:
- Caches the configuration in a `ClassPlugin`.
- On `__before_ready()`, searches for the configuration in the plugin, registering them with Node::rpc_config().
Houtamelo added a commit to Houtamelo/dead_gds_have_rights that referenced this issue Sep 19, 2024
The style is similar to GDScript's @rpc annotation, the macro can be used as follows:

godot-rust#1 - Separate arguments:
```rust
#[rpc(any_peer, reliable)]
fn some_rpc(&mut self) {
    //..
}
```

Providing overlapping arguments generates a compile error.

Any omitted arguments are set to their default values.

godot-rust#2 - Provide an expression:
```rust
const CONFIG: RpcArgs = RpcArgs {
    mode: RpcMode::Authority,
    ..RpcArgs::default()
};

#[rpc(config = CONFIG_EXPR)]
fn some_rpc(&mut self) {
    //..
}
```

Number godot-rust#2 is useful in case you want to reuse the configuration on multiple functions.

Number godot-rust#2 is mutually exclusive with number godot-rust#1.
---

The generated macro code works as follows:
- Caches the configuration in a `ClassPlugin`.
- On `__before_ready()`, searches for the configuration in the plugin, registering them with Node::rpc_config().
Houtamelo added a commit to Houtamelo/dead_gds_have_rights that referenced this issue Sep 19, 2024
The style is similar to GDScript's @rpc annotation, the macro can be used as follows:

godot-rust#1 - Separate arguments:
```rust
#[rpc(any_peer, reliable)]
fn some_rpc(&mut self) {
    //..
}
```

Providing overlapping arguments generates a compile error.

Any omitted arguments are set to their default values.

godot-rust#2 - Provide an expression:
```rust
const CONFIG: RpcArgs = RpcArgs {
    mode: RpcMode::Authority,
    ..RpcArgs::default()
};

#[rpc(config = CONFIG_EXPR)]
fn some_rpc(&mut self) {
    //..
}
```

Number godot-rust#2 is useful in case you want to reuse the configuration on multiple functions.

Number godot-rust#2 is mutually exclusive with number godot-rust#1.
---

The generated macro code works as follows:
- Caches the configuration in a `ClassPlugin`.
- On `__before_ready()`, searches for the configuration in the plugin, registering them with Node::rpc_config().
Houtamelo added a commit to Houtamelo/dead_gds_have_rights that referenced this issue Sep 19, 2024
The style is similar to GDScript's @rpc annotation, the macro can be used as follows:

godot-rust#1 - Separate arguments:
```rust
#[rpc(any_peer, reliable)]
fn some_rpc(&mut self) {
    //..
}
```

Providing overlapping arguments generates a compile error.

Any omitted arguments are set to their default values.

godot-rust#2 - Provide an expression:
```rust
const CONFIG: RpcArgs = RpcArgs {
    mode: RpcMode::Authority,
    ..RpcArgs::default()
};

#[rpc(config = CONFIG_EXPR)]
fn some_rpc(&mut self) {
    //..
}
```

Number godot-rust#2 is useful in case you want to reuse the configuration on multiple functions.

Number godot-rust#2 is mutually exclusive with number godot-rust#1.
---

The generated macro code works as follows:
- Caches the configuration in a `ClassPlugin`.
- On `__before_ready()`, searches for the configuration in the plugin, registering them with Node::rpc_config().
Houtamelo added a commit to Houtamelo/dead_gds_have_rights that referenced this issue Sep 20, 2024
The style is similar to GDScript's @rpc annotation, the macro can be used as follows:

godot-rust#1 - Separate arguments:
```rust
#[rpc(any_peer, reliable)]
fn some_rpc(&mut self) {
    //..
}
```

Providing overlapping arguments generates a compile error.

Any omitted arguments are set to their default values.

godot-rust#2 - Provide an expression:
```rust
const CONFIG: RpcArgs = RpcArgs {
    mode: RpcMode::Authority,
    ..RpcArgs::default()
};

#[rpc(config = CONFIG_EXPR)]
fn some_rpc(&mut self) {
    //..
}
```

Number godot-rust#2 is useful in case you want to reuse the configuration on multiple functions.

Number godot-rust#2 is mutually exclusive with number godot-rust#1.
---

The generated macro code works as follows:
- Caches the configuration in a `ClassPlugin`.
- On `__before_ready()`, searches for the configuration in the plugin, registering them with Node::rpc_config().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug status: upstream Depending on upstream fix (typically Godot)
Projects
None yet
Development

No branches or pull requests

6 participants