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

Some NOTIFICATION_* constants collide by sharing the same integer value #75839

Closed
Bromeon opened this issue Apr 8, 2023 · 2 comments
Closed
Milestone

Comments

@Bromeon
Copy link
Contributor

Bromeon commented Apr 8, 2023

Godot version

4.0.2-stable

System information

N/A

Issue description

Godot uses NOTIFICATION_* constants across different classes to identify different types of lifecycle callbacks. Since they are defined in different classes, they are not enums, but resolve to int in GDScript. At the moment, some of these constants share the same value, although they mean different things.

Examples I've found (not necessarily exhaustive):

30:   CanvasItem.NOTIFICATION_DRAW               Node.NOTIFICATION_NODE_RECACHE_REQUESTED      Window.NOTIFICATION_VISIBILITY_CHANGED
32:   CanvasItem.NOTIFICATION_ENTER_CANVAS       Window.NOTIFICATION_THEME_CHANGED
41:   Control.NOTIFICATION_MOUSE_ENTER           Node3D.NOTIFICATION_ENTER_WORLD
42:   Control.NOTIFICATION_MOUSE_EXIT            Node3D.NOTIFICATION_EXIT_WORLD
43:   Control.NOTIFICATION_FOCUS_ENTER           Node3D.NOTIFICATION_VISIBILITY_CHANGED
44:   Control.NOTIFICATION_FOCUS_EXIT            Node3D.NOTIFICATION_LOCAL_TRANSFORM_CHANGED
50:   Container.NOTIFICATION_PRE_SORT_CHILDREN   Skeleton3D.NOTIFICATION_UPDATE_SKELETON

It can be argued that this may not be an issue as long as those collisions happen in different inheritance branches (i.e. never directly between a base and derived class).

However, the first example above even violates that: both CanvasItem and Window inherit from Node, so there are simultaneously two different notification constants of the same value in scope.

30:   
  CanvasItem.NOTIFICATION_DRAW
  Node.NOTIFICATION_NODE_RECACHE_REQUESTED 
  Window.NOTIFICATION_VISIBILITY_CHANGED

This can be problematic in the virtual function Object::_notification(int what), which can be overridden by the user. There is no way to distinguish such notifications at the moment.


The open question is whether only this specific bug should be fixed, or collisions should be avoided in general. Given a 32-bit-integer, it's probably reasonable to never have overlapping values; yet backwards-compatibility (mostly with GDExtension) might be a factor in the decision.

Steps to reproduce

See above.

Minimal reproduction project

N/A

bors bot added a commit to godot-rust/gdext that referenced this issue Apr 19, 2023
223: Object notification API, `engine` intra-doc links r=Bromeon a=Bromeon

So far, there was no way to intercept the Godot `_notification` API, and also issuing notifications was very cumbersome, as the constants were spread across several classes.

This PR introduces multiple new APIs:

1. `{Class}Notification` enum, which contains all notifications relevant for a given class.
    * For example, `Node3DNotification` accumulates all `NOTIFICATION_*` constants across `Node3D`, `Node` and `Object` classes.
    * In GDScript, this is less of an issue because there is true inheritance, so one can type `Node3D.NOTIF...` and auto-complete will list all candidates. In Rust, constants are not inherited.
    * There is currently a workaround for bug [godot#75839](godotengine/godot#75839): in two cases, two enumerators cannot be distinguished as they share the same integer value. We address this by using a single enumerator to represent both (not ideal, but there's nothing else we can do).

2. Virtual method `{Class}Virtual::on_notification(what: {NearestClass}Notification)`.
    * This is the equivalent to GDScript's `_notification` (with underscore), but more meaningfully named. It can be used to intercept low-level notifications for the given class.
    *  The parameter type is either the notification enum for that class; or if the class itself does not have any notification constants, the nearest base class that _does_. This avoids proliferation of enum types with the exact same interface. It's possibly breaking when Godot adds such constants in new classes, but it helps reduce the API surface if not _every single class_ needs its own enum (since there are relatively few classes working with notifications).

3. The method `{Class}::issue_notification(what: {NearestClass}Notification)`.
    * This is the counterpart of `on_notification` to _send_ notifications. 
    * In GDScript, this is named `notification` (no underscore).

---

Apart from that, there are smaller changes:
* `Display` is now implemented on all `Gd<T>` types, by using the Godot string representation. `T`'s `Display` impl is ignored.
* Several generated symbols in `godot::engine` are now slighly documented and contain intra-doc links to navigate easily:
   * Class <-> virtual trait
   * Class <-> notification enum
   * Class <-> sidecar module
   * Class -> Godot online docs
   * Modules `global`, `utilities` and `notify`

Co-authored-by: Jan Haller <bromeon@gmail.com>
@YuriSizov YuriSizov added this to the 4.3 milestone Oct 19, 2023
@Bromeon
Copy link
Contributor Author

Bromeon commented Apr 3, 2024

NOTIFICATION_NODE_RECACHE_REQUESTED was removed, fixing the worst conflict.

The other duplications are less problematic, as they appear in disjoint inheritance branches. Is the decision to keep them as-is?

@akien-mga
Copy link
Member

I think so. For 5.0 we should look into a system that avoids such overlaps, but for now it would be worth it IMO to break compat in those key notifications if there's no actual problem with reusing the same number in separate inheritance chains.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants