Closed apple1417 closed 1 week ago
Garbage collection is now less of a concern with the WeakPointer
type added in 18294df4, which reduces the value of this feature down to just it'd be faster. Going to leave this up for a while to see what people think of it, but imo there's little point in adding this.
A few people have requested the ability to add a hook which only fires on a particular object.
This is already relatively simple in Python by doing something like the following:
However, this has some problems:
I think this layer is the right place to add support for it, handling it outside of Python has a several advantages, and I don't think we need to add complexity to unrealsdk for a rather niche use case, especially when other C plugins can implement the same handling relatively easily.
After discussing in the discord, the basic pattern we want is something like the following.
special_hook
(naming tbd) is some helper defined in a library, which calls through to aunrealsdk.hooks.add_special_hook
we provide. The main concept we want to keep is you add the hook using aBoundFunction
instance, rather than specifying a function name and object separately.add_special_hook
would then be implemented something like:i.e. it's just a wrapper around add hook, which does some extra checks before calling into Python. It's possible we could add some extra optimizations, like storing a set of allowed objects and comparing all at once.
At a high level this concept seems sound enough, but there are some questions when we get into the details, particularly when it comes to transient objects. To answer these we really need to see actual use cases, speculation just brings out more questions. And we need to answer these first since they may affect the interface we expose.
The basic idea for transient objects would be to have regular hooks when it's created and destroyed, and when it's created, you add a new object specific hook, when it's destroyed you remove it.
An immediate problem with this is you might not be able to get a hook before the object's destroyed - maybe best you can do is just on map load. This means we cannot rely on an object reference to tell which hook to remove - which is a problem if you want to add the same hook to multiple objects.
We can trivially do a remove all, but removing just a specific object is more tricky. We cannot rely on an object reference, so our only options are: come up with a unique hook identifier; or return a handle. Handles are kind of unergonomic, and easy to lose, so they're out. If we create a unique hook identifier, either we return it (which is just a handle again), or it will need to somehow be based on the object reference to be able to recreate it later (which we can't expect to exist when we remove it).
So we can't easily have a remove single hook function. Does this matter? Are people fine with just a remove all? Will people even want to add multiple object specific hooks to the same Python function, you can also handle that relatively simply in Python (with the same caveats as the n=1 case)? We don't know, this is all speculation, we need to see actual use cases.
Another question is how to deal with hook type. This isn't quite as hairy to solve, the helper can simply store what type it was registered with, it's more a question of ergonomics. Is it better to define a constant hook type when creating the function, or to assign it when actually adding the hook? Again we'll need to see use cases to decide.
This also has type checking implications - in
add_hook
we're able to hint that post hooks may not try block execution or override the return value. While I think it should be possible to enforce either way, we'll need different approaches based on if you define type early or late.So basically, this issue is a call to users, if this feature existed, how would you use it.