-
-
Notifications
You must be signed in to change notification settings - Fork 21.5k
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
Fix first assignment to _min/max_value
in Curve
bypassing min < max
check
#78931
Conversation
Can't we just do set_limits like...
And not have extra variables for this? |
Tangentially related: #72250 |
@MewPurPur your logic is correct, but the actual issue isn't so much how to avoid the check, but instead knowing when to avoid it! The current implementation uses extra variables to disable checks the first time a variable is assigned to, which is often (but not always, as in this bug) during deserialization. This PR's solution is exclusively used during de/serialization, so it addresses the when to use the checks perfectly. If we do move towards having curves with arbitrary domains (the other linked PR), it also scales really well to adding |
Actually looks to be the exact same issue. I'll see if I can't replicate this solution there as well. Probably worth standardizing one way or another. The downside of this approach is that somehow GDScript recognizes the properties & related methods, which perhaps it shouldn't, since they are meant to be |
I think this should be solved, somehow, with a new property hint. That hint would take care of not setting values in a wrong order or not setting them one by one. It's like having linked properties in a sense. The current implementation technically breaks compatibility for serialized data. |
Thanks for looking at this! :) It does break compatibility (it's forward-compatible but not backwards-compatible), but not in a way that affects the curve data. These limits are used exclusively for drawing the curve in the inspector, so it's not necessarily a huge deal. Users could manually change domain values to be able to see the whole curve. We could also update this PR to enforce the domain (when not found) to include min and max points in the curve. I do think it'd be lovely if the core had a way to handle mutually-dependent properties though! I'm not immediately seeing a solution with property hints though. A load order hint wouldn't work (e.g. always load Is there a way to bypass setters when loading, instead immediately assigning to a property? That could be the way to go, perhaps. |
The hint could possibly act as a delay for setting values until after all of the values are parsed, then calling a special setter to set them together. As an example, at least. |
Bypassing setters might be hard, because What might work instead is if private:
virtual void _before_load() {}
virtual void _after_load() {} These are called by Resources with complex interdependencies between their properties can then do whatever they like in these callbacks, e.g. set a flag to temporarily suppress validity checking in setters. This particular case doesn't even need a flag though. In |
My initial thought was to create a new macro called I feel like I would prefer this to a more stateful solution like the one you suggested (which can introduce threading issues, though I don't think that would happen in this particular case), even though what you suggest is perhaps less intrusive? |
What kind of threading issues are you thinking of? No other references exist to the resource yet, since it's newly being loaded. Adding new "properties of properties" is more heavyweight, because every property in ClassDB will need to have fields for it, even if they are rarely used. I'm not sure it's an actual problem, but the benefit needs to be higher to outweigh the cost. Also, wouldn't you want to validate the properties after they've all been loaded? You'd need some kind of "loading done" callback for that anyway. Could someone from the core dev team maybe comment? Maybe there are other cases in the engine that could make use of this API and help inform its design? |
Maybe we can use In this way, when setting multiple properties (deserialization), all linked properties will be reset according to the setting priority. |
Some updates on this! I love documenting stuff so bear with me or skip to TL;DR :) I looked into the approach suggested by @ttencate with pre-load and post-load callbacks. A post-load hook
TL;DR all other suggested approaches have pitfalls that would require remembering to call specific functions at specific places in the codebase, which is a sure way to create bugs or inconsistent behavior. I'm currently looking into creating a loader specific to |
Been thinking about this too. There are only two resource loaders in the engine, right? And I don't think writing a custom loader in a module or GDExtension is all that common. So option 2 is not so bad imo. Not perfect, but very simple and non-invasive. We could wrap the creation of the resource in a helper function on the abstract |
So unfortunately there are 18 subclasses of So I don't believe we want the load hooks to exist at the I've been digging into I'm going to try to make a subclass of Resource called |
I hope that only |
d43ea05
to
a9036ab
Compare
Rebased. I do think this is the only sensible solution, and it's on the path towards getting #67857 revived, which I'm interested in doing. There's a deeper discussion of this in #88224, but all other alternatives seem to require nontrivial changes to
|
Closed as this is included in #67857. |
Currently, the very first time one assigns to either
_min_value
or_max_value
inCurve
, it bypasses themin < max
condition:This PR implements "bulk load" for curve limits by adding a "fake" property called
_limits
, which loads an array containing all the values. These are assigned to the proper variables without checking formin < value
, as it should be during deserialization. Themin < max
condition is still ensured by theset
methods for themin/max
values, so that we always serializemin < max
values.This solution was taken from #29959 (comment), and extracted from #67857 as a first step towards reviving it. It also sets the stage for other limits to be added (such as the domain ones) without having to replicate the current, buggy solution.
Long-winded explanation of the problem to serve as reference in the future:
The reason for the current behavior is that the
min < max
check is disabled the first time each property is written to. The idea is that this happens during deserialization, so that the checks don't conflict with the default values of 0 and 1. Suppose serialized values aremin=5, max=10
. When deserializingmin
first, and trying to set it, the condition thatmin < max(default)
is5 < 1
, which fails.However, if the
Curve
has default min/max values, those properties aren't serialized in the first place, and, therefore no first time assignment happens deserialization. This makes it so the first time the user attempts to assign to these limits, the checks are not guaranteed.Downsides of approach
GDScript recognizes the properties tagged as
PROPERTY_USAGE_INTERNAL
& related methods, which perhaps it shouldn't. In this case, it is unintended since these properties are "fake" and mean to be used just for de/serialization. On the other hand, there might be some GDScript code out there that makes use of internal properties. I can imagine some addons, through GDExtension or GDScript, would do it. See #25651 for discussion on whetherPROPERTY_USAGE_INTERNAL
properties should be exposed to GDScript.Warning: This contains a minor compatibility break in that
_min/max_value
are no longer serialized. However, they are still deserialized because the resource loaders don't check that the property they are reading must have thePROPERTY_USAGE_STORAGE
flag.