PocketMine-MP
PocketMine-MP copied to clipboard
Added ApiMap
Introduction
This provides a type-based approach for plugins to expose APIs to each other.
For example, PocketMine can provide a BanList interface with a default implementation (using the current banned-players.txt etc), then plugins can set it to their own implementation.
This class also handles conflict conditions.
For details, please check the documentation in the code.
Relevant issues
Resolves #3087
Changes
API changes
Added the ApiMap class.
Its methods are directly re-exported in Server.
Behavioural changes
None yet.
Backwards compatibility
This is a pure API addition with no BC issues.
Follow-up
Improve error messages.
Tests
See ApiMapTest.
Is there a reason to not just add a getApiMap() method to Server() instead of adding a bunch of proxy methods?
I also have my doubts that a centralized approach for this is necessary. Some plugins already have their own ways to do this with different behaviour.
I also have my doubts that a centralized approach for this is necessary. Some plugins already have their own ways to do this with different behaviour.
This is a move away from singleton approach. I am hoping to provide this as the basis for better DI pattern that can replace stuff like EconomyAPI::getInstance() with $server->getApi(EconomyApi::class).
I think centralization is necessary since I plan to implement this with stuff like PlayerGrayList (whitelist + ban list) in the future.
Is there a reason to not just add a
getApiMap()method toServer()instead of adding a bunch of proxy methods?
Just for convenience. For now I don't see any actual reason to expose the ApiMap object directly.
I'm also not sure what the point of "default" APIs is. Allowing there to be a difference just allows plugins to fight with each other, which seems to defeat the objective.
The default API is the implementation provided by the interface declarer, such as by PocketMine itself, explicitly intended for overriding. I don't think there is a problem since there is only one interface declarer.
Suggested addition:
public function onFinalize(string $class, Closure $closure) {
$this->onStartupHook[] = function() {
$instance = $this->getApi($class);
if($instance) $closure($instance);
}
}
This allows decoupling plugin dependencies by only running functions like "register xxx handler" on certain APIs after they are all available. In this case, $class does not have to be loaded at the time of calling onFinalize, and the closure is only executed if the API is available.
Finalization is possible if #2825 is accepted.
Side note (previously discussed on discord): I would also like to use ApiMap to replace MetadataStore, so that plugins do not have to handle sessions themselves. E.g. an economy plugin:
public function onLogin(PlayerLoginEvent $event) {
$event->getPlayer()->getApiMap()->provideApi(EconomyData::class, EconomyData::loadForPlayer($event->getPlayer());
}
It is also worth considering whether we should rename ApiMap to TypeMap or MetadataMap for more generic applications in the future; it's unreasonable to call plugin player sessions an "API" when it's supposed to be internal.
I just noticed bukkit has something very similar: https://bukkit.org/threads/services-api-intro.26998/