On namespaces, addon dependencies, and common functionality #6802
Replies: 2 comments
-
This kind of limitation sounds difficult to enforce but also quite arbitrary. If we introduce any intentional limitation, it would probably be better to limit the maximum depth of the dependency tree to 1 (or 2 for entire projects distributed on the asset library). This means an add-on can depend on another add-on, but you wouldn't be able to have an add-on that depends on an add-on which in turn depends on another add-on. That said, I think this kind of limitation would mostly be about limiting the engine's implementation complexity. People are already wary about npm-style one-liner dependencies nowdays, and it's not something I see quite as often as before in modern programming languages like Rust (or even JavaScript/TypeScript with Deno – also because Deno comes with a larger standard library). |
Beta Was this translation helpful? Give feedback.
-
It's an issue I have also run into recently. I am writing two addons in gdscript and one is much easier to use when the other is installed. I am being as careful as I can trying to make them independent, but I can't develop the first without the second. I am wondering how to release the addon because it will say "For better functionality, please also install XYZ". I get that Godot is not Python/etc. and it's only as lean as it is because it's cutting a lot of, frankly, quality-of-life functionality. I don't know how to solve the paradox: do we add nice-to-haves (and get bigger) or do we leave the issues to the end-dev and let them reproduce code all over the place? |
Beta Was this translation helpful? Give feedback.
-
I'm aware of the risk of allowing addons to have dependencies. And I don't just mean a matter of trust, but instead that dependencies can grow hard to manage. An scenario I won't rather not have is having two addons that depend on a third addon, but different versions of that third addon.
However, I wish I could author addons that depend on other of my addons. That is because they need similar functionality that I'd wish I didn't duplicate.
So, deploying my addons on my projects became an issue. Since I didn't want to duplicate code, instead of them working out of the box, they would fail telling me I need another addon. Which is particularly annoying when the addon is a plugin.
That let me to rethink their design, accept some duplication, and specialize the code when possible. After all, the duplicated code had to be general, and that introduced some unnecessary complexity in the code. Plus, you cannot beat the usability of simply copying a folder.
However, there is duplication. So there are classes that exists in different addons, but I don't want conflicts between them. Currently their names are prefixed with the name of the addon. This has let me to a desire for namespaces. I'll be refactoring to use
preload
so I can leave the classes anonymous.This all, of course, would not be a thing, if this duplicated functionality was part of Godot core. In fact, I believe the back and forth between not duplicating code and duplicating means that what is left would actually be useful.
Here are some of the duplicated things:
func update_array(current:Array, updated:Array, added:Array = [], removed:Array = []) -> void
This is a method that takes an array
current
that will be modified to math the contents of the arrayupdated
, and theadded
andremoved
arrays will be populated according to the changes made tocurrent
. The order of the elements is not guaranteed.I use this method in two places:
Promise
This is a class with
completed
,succeeded
, andfailed
signals. It has anstatus
property that can take three values:Running
,Succeeded
, orFailed
, once it changed it cannot be changed back. Changing thestatus
it will trigger the appropriate signal, but deferred. The class isRefCounted
and will increase its reference count by one when created, and decrease it back when the status changed.I use this class in two places:
Promise
instances that allows to yield on their completion. These can fail if the audio stream is invalid.Promise
instances. These can fail is the path of the character is obstructed.(I have more planned uses for
Promise
, but this is it so far)get_damp_factor(time:float, delta:float)
This is for frame independent damping. It takes the total time it should take to converge (within a hard coded epsilon), and the elapsed time since the last iteration.
I use this method in three places:
InterpolatedCamera
, except it is frame rate independent and I has predictable convergence time.RemoteTransform
, it is the opposite in that my code pulls a transform instead of pushing it. And it is frame rate independent and I has predictable convergence time.Some
Quaternion
methods:quaternion_between_vectors
: BasicallyQuaternion(axis, from.signed_angle_to(to, axis))
, but can pick the axis ifZERO
is provided (which is basicallycross
, but we also need to handle when that yieldsZERO
), and the resulting quaternion represents the rotation the short way around.quaternion_from_basis
: Basicallyget_rotation_quaternion
, and the resultingQuaternion
represents the rotation the short way around.quaternion_scale_rotation
: Converts theQuaternion
to axis-angle, scales the rotation, wraps it, and converts back.build_transform
: Builds aTransform3D
from a positionVector3
, aQuaternion
rotation, and a scaleVector3
.build_basis
: Builds aBasis
from aQuaternion
rotation, and a scaleVector3
.quaternion_twist
: Computes the twist of aQuaternion
around an axis.quaternion_swing
: Computes the complement of the twist (see swing twist decomposition).alignment_quaternion
: Takes twoBasis
and aVector3
and returns the rotation that needs to be applied to the firstBasis
to make theVector3
on bothBasis
match.All of these methods check for infinite vectors, nans, and check if a vector is approximately zero before normalizing it or projecting on it. The fallback is easy:
IDENTITY
. Furthermore, they will never result in an infiniteQuaternion
,Basis
, orTranform3D
. I have been incorporating all these checks because - as you would surely know by now - division by zero, unexpected results when normalizing vectors that are almost zero, and some bug with physics sometimes giving nan positions and velocities. I also wrote them aware that aBasis
can be zero. Which - by the way - can happens if you are interpolating the scale to a value with different signs.By the way, I suppose
get_axis
is correct for whatever you are doing with animations or whatever it is. But I ended up not using it because it gave me unexpected results.I use these methods in a three places:
InterpolatedCamera
. Although it does not use very listed method.RemoteTransform
. This in particular has multiple modes for handling the rotation.I also have methods that handle vectors and have similar checks (against nans, infinity, and also against zero when normalizing or projecting), but I have been able to keep these local, which is why I'm not including them here.
I also want a couple methods to work with
Node
s:find_parent
.find_node
.These should be able to handle custom classes. As a result of avoiding duplication, my code for this ended up specializing for the class they search for. The purpose of these methods is to ease connecting references to other
Node
s intool
scripts (which are usingNodePath
s until the fix forNode
exports is released) so that I need to do less clicking. In particular when the thing I need to connect is inside of another scene, these can save me the trouble of enabling editable children.To recapitulate:
preload
instead of usingclass_name
, and avoid polluting the global namespace that way.Thus, I have nothing. But, still... Would people be interested in any of this? Would it be a good idea to write a proposal for some of these in particular? I don't know.
Beta Was this translation helpful? Give feedback.
All reactions