Skip to content

Diposables and PluginDataHolders #10364

@CubBossa

Description

@CubBossa

Is your feature request related to a problem?

Not really, rather general code style

Describe the solution you'd like.

When working on a Jetbrains plugin I was positively surprised about how nice the plugin system works. I came across two patterns that I would love to use in Minecraft development and wanted to throw them in as ideas here.

Disposables:
A simple interface like Closable and AutoClosable, offering a void dispose() method. The dispose method is not similar to a destructor but declares the end of usage of an object. A constructor invocation and the dispose() invocation frame the lifetime of the object unrelated to the garbage collection and referencing or anything.
In combination with a Disposer instance, Disposables can be put into relation. For example, Plugin would extend Disposable and whenever I create any lasting object within my plugin (classes like ScoreboardHandler, PlayerDataHandler, ...) I would let them implement Disposable and bind them to my plugin with

Disposer.get().register(this, myPlugin);

in the constructor.

If a plugin for whatever reasons disables, all its resources would be disposed by evaluating the relations in the Disposer instance.
A very common situation is that a plugin constructor needs to set up many different manager classes, which may want to be closed in onDisable().
If any step in onEnable() fails, onDisable() would be likely to create NullpointerExceptions and therefore have issues while disabling too.
With the disposable system, resources would be freed only if they have been claimed first and there would no need to reference them in the onDisable hook.
Another feature of it: Your own plugin could be linked to another plugin, so for example whenever Vault gets disabled, your plugin would be disposed through the linkage in the Disposer and disable itself too, if it relies on Vault. The lifespan of your plugin would be bound to its dependencies and if any fails, your plugin would not work properly and therefore disables too.

PluginDataHolder:
Another thing Jetbrains does is implement an Interface to hold keys and values, similar to the Pointered class from adventure. They implement it with File objects, AST nodes, Editors, etc.
If paper had a similar system for example for World or Entity, plugin developers could attach cache data to the player object. Whenever the player object gets cleared (or disposed) the cached data would be invalidated. Meaning, instead of requiring a Map<UUID, MyParticlePlayer> in any not so nice "ParticlePlayerHandler" class, I could attach my MyParticlePlayer to the player object directly, using a Key<MyParticlePlayer> as key. Why would I need a ParticlePlayer instance if the player is offline, so the lifespan of the player object always frames the lifespan of the ParticlePlayer. (Additionally by combining both mechanics, I could link the ParticlePlayer as Disposable to the World object it is playing in, so if the world gets deleted OR the player goes offline, the disposer takes care of the ParticlePlayer).

Jetbrains pushes this to the point where a value can be cached and listens to certain changes in its holder, like cached values on an AST node become invalidated once the AST changes or, transferred to minecraft, cached values in the entities data become invalid if its location changes for example.

I know especially the second suggestion implies big changes but I thought about it so much that I felt like I had to at least suggest it once. Plugin reloading after all became discouraged because you could run into artifacts of the previous plugin instance, like not freed singletons and stuff. Disposables are the way jetbrains handles the (de)activation of plugins at any time and only under some conditions enforces a restart. Plugins that failed to load don't needlessly occupy memory and so on.

Describe alternatives you've considered.

None, its additional features

Other

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    type: featureRequest for a new Feature.
    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions