4.0.0-RC6 - Blizzard #1082
byRoadrunner
announced in
Releases
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Update 4.0.0-RC6 - Blizzard
We are pleased to announce the sixth release candidate for CloudNet 4.0. This release mainly focuses on the migration from static instance usages to dependency injection - internally as well as for external api consumers. Please see the dedicated information section for dependency injection below for further info. We urge all users to install the update, as we will no longer provide support for RC5. Users who want to switch from 3.4.X to 4.0 can find instructions in the release information for RC1.
Cheers!
(Please remember, CloudNet is provided as-is - we are not responsible for data loss or corruption. You are encouraged to back up your files before any updates!)
Changelog
🐛 Fixes
PlayerExecutor#connect
method rather than the best matching serviceperms_group_
✨ Improvements
CommandPreProcess
andCommandPostProcess
eventColouredLogFormatter
toColoredLogFormatter
andConsoleColor.toColouredString
toConsoleColor.toColoredString
to keep the spelling of color consistent🔗 Links
Dependency Injection
❓ Why DI
There are a few reasons why we deided to go for dependency injection. The top three include:
💡 What to note when using DI
What is DI?
Dependency injection is a design pattern that allows objects to be constructed without needing to know how their dependencies are created. A dependency is an object that another object relies on in order to function properly. In traditional software development, objects are responsible for creating and managing their own dependencies. However, with dependency injection, the responsibility of creating and managing dependencies is shifted to a separate component, known as an injector.
The injector is responsible for providing the necessary dependencies to objects at runtime. This is done by injecting the dependencies directly into the objects that need them, rather than having the objects create or manage the dependencies themselves. By doing so, the objects become less tightly coupled and more flexible, making it easier to change or replace dependencies without affecting the rest of the application.
Constructor injection is the most common form of dependency injection, where dependencies are passed in through the constructor of a class. The class is defined with a constructor that takes the dependencies as arguments, and the injector is responsible for providing these dependencies when the class is instantiated. The target constructor to inject is detected by using either of:
@Inject
(dev.derklaro.aerogel.Inject
orjakarta.inject.Inject
).When the injector constructed the type completely member injection is executed. Member injection will be done on
@Inject
(Note that final fields might get ignored from injection)@Inject
will be executed. These methods are allowed to take arguments.@PostConstruct
will be executed. These methods are not allowed to take arguments.The injection order of injectable methods and post construct listeners can be constrolled with the
@Order
annotation.A quickstart guide for dependency injection
@Inject
annotation markes constructors and classes that should be injected and the@Singleton
annotation lets aerogel know that only a single instance of the annotated type should be constructed.⛅ How did we implement it in CloudNet
Because of the little flexibility and many outside influences we have due to the supported Minecraft server/proxy implementation, we had to implement DI to be as flexible as possible.
For this reason, some decisions had to be made that would not have been a problem in a normal standalone application:
🔌 Support for Plugins
Support for plugins can be achieved with the platform inject api. This generates classes during compile time based on annotations added to classes (a list of these annotations is below). It is important that the dependency for the annotation processor is only available at compile time and not at runtime:
The supported annotations
@PlatformPlugin
: This annotates the main class of the plugin. Due to the abstraction of CloudNet extended/implemented, the annotated class no longer implements an implementation-specific class (such as JavaPlugin) but always implements the interface PlatformEntrypoint. The following settings can be made via the annotation:platform
: The name of the platform for which the annotated class is the entry point. Supported are currently:bukkit
,bungeecord
,fabric
,minestom
,nukkit
,sponge
,velocity
&waterdog
. Note that for the platform a plugin info file will be generated as well (for example theplugin.yml
for bukkit). A template file can be created in the resources directory with the name of the output file (see below for options) suffixed with.template
. All configurations made in the template file will be copied to the final file and will not get overridden.name
: The name of the plugin. CloudNet automatically takes care that the name complies with the platform's policies (and adjusts the name if necessary) and also generates a plugin ID based on the name (if necessary).version
: The version of the plugin.pluginFileNames
: Sets the output file name for the plugin. By default, the file names used by CloudNet are used, which can be customized during copying to the target service (e.g. plugin.bungeecord.yml). In the array you can specify the names of the files where the plugin info for the platform should be written. Folders are also supported, these are simply specified via slash (e.g.myFolder/plugin.yml
).api
: An optional api version that is required for the plugin to run. This setting can be interpreted differently or even ignored based on the target platform.description
: An optional description of the plugin.homepage
: An optional url to the homepage of the plugin.authors
: An optional array of the plugin authors.providesScan
: An optional set of patterns which should be considered when searching for@ProvidesFor
annotations. By default the same package in which the plugin main class is located in will be used. Each entry in the given array can be in the formts:<packageName>
: In this case the given name will be taken and all classes and sub-packages in the given package will be scanned<type>:<packageName>
: The name is used depending on the given type. The type can either be:r
,regexp
,pattern
: the given package name is parsed as a pattern.p
,plain
: the given package name is used in a literal way and all classes in the given and sub packages will be scanned.g
,glob
: the given package name is parsed as a simplified glob. The only supported glob chars are:*
,?
,.
and\
.commands
: An optional array of commands that is provided by the plugin. Depending on the platform the information can be used in different ways or even be igored.dependencies
: An optional array of plugin-level dependencies. The given information may not be used based on the plugin.externalDependencies
: An optional array of external dependencies that are required for the plugin to run. Based on the platform the information might be ignored.@ProvidesFor
: marks an implementation for the given type(s) which should only be present on the specified platform. The following settings can be made via the annotation:platform
: The platform on which the binding should be present.types
: The types to which the annotated class should get bound (Note that there will be no check if the type is actually assignable to the annotated type).bindGenericSupertypes
: If enabled the directly extended superclass and implemented interfaces will be scanned if they contain one of the types given via thetypes
setting which is generic. In that case the generic version will be bound based on the extending declaration (for example if in the type arrayMap.class
is specified and the class implementsMap<String, String>
then the map implementation with type parameters will be bound to the annotated type as well).bindWildcardTypeOfParameterizedTypes
: If enabled all generic, parameterized types will be bound fully wildcarded as well (for example if the class implementsMap<String, Map<String, String>>
the typeMap<?, ?>
will be bound to the type as well).@ConstructionListener
: The annotation can be added to the main class of a plugin (the same class as annotated with@PlatformPlugin
) and specifies a single class will should get called when the plugin instance is created by the platform. Note that the target class must declare a constructor which takes the platform plugin data (which can vary based on the platform) as it's only argument. Only the constructor in the target class is called, all other steps are left to the constructor to do.Annotations to use with
@PlatformPlugin
The following annotations are used in the PlatformPlugin annotation and describe various configuration options for the info generation for each platform. Depending on the target platform, some of the information provided to the annotation might get dropped, or the complete annotation is ignored if unused:
@Command
: Allows to specify a command which is provided by the plugin.@Dependency
: Allows to specify a dependency on another plugin.@ExternalDependency
: Allows to specify an external dependency which is required for the plugin in order to run.@Repository
: Sets the repository of the external dependency. Some platform are only allowing dependencies to get downloaded from maven central, in that case the information provided to the annotation is ignored.An example plugin class might look like this:
Which types are bound for which platform by default?
The
InjectionLayer
for the plugin can be injected by using the layer type and requesting an instance calledplugin
.Bukkit
Plugin Type
Plugin
,PluginBase
andJavaPlugin
Platform Types
Server
BukkitScheduler
PluginManager
ServicesManager
ScoreboardManager
Bungeecord
Plugin Type
Plugin
Platform Types
ProxyServer
ProxyConfig
TaskScheduler
PluginManager
Fabric
Fabric has no bindings by default as there is nothing to bind.
Minestom
Plugin Type
Extension
Platform Types
ComponentLogger
TagManager
TeamManager
BiomeManager
BlockManager
RecipeManager
BossBarManager
CommandManager
PacketProcessor
InstanceManager
ExceptionManager
ExtensionManager
BenchmarkManager
SchedulerManager
ConnectionManager
GlobalEventHandler
AdvancementManager
DimensionTypeManager
PacketListenerManager
Nukkit
Plugin Type
Plugin
andPluginBase
Platform Types
Server
CommandMap
ServerScheduler
PluginManager
ServiceManager
CraftingManager
ResourcePackManager
Sponge
Plugin Type
PluginContainer
Platform Types
Scheduler
(two variants: one namedsync
(for the sync scheduler) and one nameasync
(for the async scheduler))Game
Platform
SqlManager
DataManager
EventManager
ConfigManager
PluginManager
ChannelManager
BuilderProvider
FactoryProvider
MetricsConfigManager
ServiceProvider.GameScoped
MapStorage
UserManager
WorldManager
Server
RecipeManager
TeleportHelper
CommandManager
PackRepository
ResourceManager
CauseStackManager
GameProfileManager
GameProfileProvider
ServiceProvider
ServiceProvider.ServerScoped
Velocity
Plugin Type
PluginContainer
Object
namedplugin
which is the plugin main class instancePlatform Types
ProxyServer
Scheduler
EventManager
PluginManager
CommandManager
ChannelRegistrar
Waterdog
Plugin Type
Plugin
Platform Types
ProxyServer
MainLogger
CommandMap
PackManager
LangConfig
EventManager
PlayerManager
ServerInfoMap
PluginManager
WaterdogScheduler
ConfigurationManager
🔌 Support for Modules
The CloudNet module system (whether on the wrapper or the node) has full support for dependency injection. The module's main class can have dependencies injected into the constructor, and all module tasks have the ability to inject dependencies. No further configuration is required.
An example module task might be:
Commands
The node command system allows injection into command processing methods as well. This does not include suggestion processors or parameter parsers.
🔌 Support for Event Listeners
Event Listeners are allowed to take parameters via injection as well. The main catch here is, that the first parameter of an event listener method still needs to be the event which the method wants to get notified about. All other parameters are looked up when the listener should be invoked. An example listener that takes arguments:
This discussion was created from the release 4.0.0-RC6 - Blizzard.
Beta Was this translation helpful? Give feedback.
All reactions