-
Notifications
You must be signed in to change notification settings - Fork 54
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
Use After Free on Node Reference issues #416
Comments
I have been using something like this to handle node references: import SwiftGodot
class NodeReference<T: Node> {
private var _ref: Node?
private var _variant: Variant?
var inner: Node? {
get {
if _ref == nil {
return nil
}
if GD.isInstanceValid(instance: _variant!) {
return _ref
}
printerr("Referenced node was no longer valid, clearing")
_ref = nil
return nil
}
set {
printd("Setting node reference to: \(newValue)")
_ref = newValue
}
}
init(node: Node) {
inner = node
_variant = Variant(node)
}
} But i think this is not ideal, and would be good if something could be done inside SwiftGodot so this is taken care of for the user if at all possible |
Thanks for sharing the details of this issue, let me explore a couple of options. The range is:
|
…ect is still alive, helps for issue #416
I have added an |
Thanks, i'll give that a test! I also just want to show this, mainly to have a record of it somewhere, but i was able to confirm that node references being kept around after the node has been freed can suddenly change to different nodes entirely, if a new node is allocated in memory at the same place as the old node. Here is console output of the contents of an array of Decal nodes I was using to recycle old bullet hole decals, as well as their addresses in memory:
Then after changing scene, some of these decals are freed, and new nodes are allocated. These new nodes happened to be allocated in the same place as the decals, resulting in unexpected behaviour of random nodes being contained in the array:
|
The new printd("Ref was valid: \(_ref?.isValid)")
printd("Variant was valid: \(GD.isInstanceValid(instance: _variant!))") result:
Seems there are times when the variant thinks its not valid but the Wrapped thinks its still valid |
A potential solution which could be quite elegant, but im not sure will be possible, is if we can extend and override the behaviour of optional: import SwiftGodot
public extension Optional where Wrapped: Node {
static func == (lhs: Node?, rhs: _OptionalNilComparisonType) -> Bool {
if lhs?.isValid == false {
// this will make 'node == nil' return true when the node reference is invalid
return true
}
// im not 100% sure how to handle default case so this is definitely incorrect!
return false
}
} while this will work for While i love the // This would work fine!
if node != nil {
node!.doThing()
}
// But this would still try to call doThing even if node.isValid is false! Bad!
node?.doThing() |
Looks like operators |
In SwiftGodot, it is easy to end up in a situation where you can attempt to use a node reference after it has been freed.
It is also possible to create this situation in gdscript, however the key difference is that in GDScript, doing so will not crash the entire game, but will just log an error and continue. I believe it would be in everyones best interest if we could have this behaviour in SwiftGodot.
It is possible to detect this situation using
GD.isInstanceValid()
on a Variant created from the node reference:note, that it is important to to create this variant at a time where you are 100% sure that the node reference is valid. if you create the variant at time of testing, you can end up with some unexpected behaviour:
The text was updated successfully, but these errors were encountered: