-
Notifications
You must be signed in to change notification settings - Fork 7
Home
Common Modules (most important)
-
:api
- API code (mostly interfaces, some implementations)
-
:common
- Contains the bulk of all DiscordSRV code
Platform modules
-
:platform
- Platform specific code
-
:platform:loader
(rarely need to be touched)- For jarinjar loading, explained later in this guide
Misc. modules (rarely need to be touched)
-
:common:common-api
- This contains the Slf4j implementation which needs to be included in loaders but doesn't need to be accessed by API users
-
:common:common-templates
- Contains DynamicProxies that are then used in
:common
- Contains DynamicProxies that are then used in
-
:common:common-unrelocate
- Hack to work around needing classes to be (un)relocated but only sometimes
:i18n
Packages
- Classes and interfaces are organized into packages based on what they do or what they are related to
- Examples:
- Group sync goes in
groupsync
- Message forwarding from Minecraft -> Discord goes in
messageforwarding.minecrafttodiscord
- SQL goes in
storage
- Channel locking goes in
channel
- etc.
- Group sync goes in
- Some 'exceptions':
- Configs are kept under
config.<config name>
(in each module) - Events are under
event.events
(in:api
) - Module types are under
module.type
(in:api
and:common
) - Integrations are grouped together into
integration
(in each module)
- Configs are kept under
- Examples:
To simplify development for api users and DiscordSRV itself, DiscordSRV's event bus (DiscordSRV#eventBus
) is used for listening to DiscordSRV's own events as well as JDA's events.
- The JDA event listener system is blocked in favor of DiscordSRV's own event bus
Additionally the :api
module includes a annotation processor which will cause compile time errors if the @Subscribe
annotation used by the event bus is used incorrectly
Module
is a type for easily building features and plugin integrations.
Modules...
- can request gateway intents, cache flags and member caching policies (via methods)
- are enabled and disabled when DiscordSRV reloads based on the module's
isEnabled
method's return value- triggers
Module#enable
,Module#disable
andModule#reload
- triggers
- are automatically subscribed to the event bus
Modules can be used for building plugin integrations such as permissions providers,
- The DiscordSRV class has methods to lookup modules by their type, for example:
PermissionProvider permProvider = discordSRV.getModule(PermissionProvider.class);
- Modules can specify their priorities for lookup via
Module#priority(Class)
DiscordSRV uses Configurate object serialization. Config translation is handled by taking the default config written in English and dumping the useful parts into a file that can be imported into Crowdin (:i18n
module) and then loaded back into DiscordSRV (TranslatedConfigManager
).
DiscordSRV has some useful extra's such as serialization for SendableDiscordMessage.Builder
and the following annotations,
-
@DefaultOnly
controls parts of the config that will not be added back into the config if they are removed -
@Order
for precise control over options when inheritance prevents ordering the fields as desired -
@Untranslated
to specify options and/or comments which shouldn't be translated
Configs are stored under each module in the config.<config name>
package
- Where
<config name>
is usuallymain
,connection
ormessages
- The other sub-packages are used to configure Configurate
To avoid conflicting channel names, the GameChannelLookupEvent
will be used to determine which plugin 'owns' a given channel name, when a plugin isn't provided at the time of looking up a config value.
The channels
option in the main config works like this:
- The value is equivalent to Map<String, BaseChannelConfig>
- Keys other than
default
are of typeChannelConfig
which also contains configuration for channel ids and threads - If a given key doesn't have a value for the requested option it will be looked up from the
default
key instead
To access the channels
config option:
-
DiscordSRV#channelConfig
instead ofDiscordSRV#config
-
ChannelConfigHelper#orDefault(String)
which returns aOrDefault<BaseChannelConfig>
-
OrDefault#map
/OrDefault#get
Full example:
discordSRV.channelConfig().orDefault("global").map(cfg -> cfg.discordToMinecraft).get(cfg -> cfg.enabled, false);
The value will be looked up from:
-
channels.plugin:global.discord-to-minecraft.enabled
(whereplugin
is the first to respond to GameChannelLookupEvent) channels.global.discord-to-minecraft.enabled
channels.default.discord-to-minecraft.enabled
- the second parameter of the
OrDefault#get
call (false
)
To make DiscordSRV as configurable as possible, and to avoid the pains of converting between Minecraft and Discord formatting.
Some points of how the placeholder service works,
- works by giving in a input string and 'context' which will be used by placeholders depending on their requirements
- 'context' can be more than just Minecraft Players, mainly allows using Discord users and server members as context
- includes some special party ticks such as OR on placeholders eg.
%player_display_name|player_name%
(%player_display_name% or %player_name% if the display name isn't present) - recursive placeholders out of the box
%awesomelevels_player_level_{linked_player_name}%
- hooks directly into external plugins such as PlaceholderAPI
- allows declaring placeholders directly in types, example:
public interface Player {
@Placeholder("player_name")
String username();
...
In order to deal with dependencies which use Slf4j, DiscordSRV has it's own implementation of it that is relocated to com.discordsrv.dependencies.org.slf4j
(.impl
) which redirects log messages to DiscordSRV's own logger as shown in the below illustration.
DiscordSRV's own logging happens through DiscordSRV#logger
and Module
s and other types should use the NamedLogger
proxy to specify the name of the component the log messages are for. This is the replacement for the Debug
categories in DiscordSRV1.
All log messages flow through DiscordSRVLogger
, which...
- Filters & cleans up log messages (from dependencies)
- Stores them as log files for debugging
- Keeps 3 files, rolling over when DiscordSRV is initialized
- And forwards them to the platform's logger
Illustration of the above
The main class structure, from top to bottom
-
DiscordSRVApi
(interface)- In the
:api
module - Exposes limited methods for API users (using API types)
- In the
-
DiscordSRV
(interface)- In the
:common
module - "Upgrades" some of the methods in
DiscordSRVApi
to implementation types (eg.IProfileManager
->ProfileManagerImpl
) - Exposes internal methods that aren't platform dependent
- Nearly everything is accessible from here,
logger()
,config()
,jda()
,httpClient()
,placeholderService()
etc. - The primary type that is passed to (almost) everything via dependency injection
- Avoids the nasty generics of
AbstractDiscordSRV
- Avoids the nasty generics of
- In the
-
AbstractDiscordSRV<C, CC>
(abstract class)- In the
:common
module - C and CC are the configuration types which will be specified by the PlatformDiscordSRV class
- Implements
DiscordSRV
andDiscordSRVApi
methods which don't rely on platform code - The 'true' main class of DiscordSRV
- In the
-
ServerDiscordSRV<C, CC>
andProxyDiscordSRV<C, CC>
(abstract classes)- In the
:common
module - Contains server or proxy specific code
- for example: server switch messages are only required on the proxy so registering them happens in ProxyDiscordSRV
- In the
-
PlatformDiscordSRV (
BukkitDiscordSRV
,BungeeDiscordSRV
etc.) (regular classes)- In the platform's module (
:bukkit
,:bungee
etc.) - The platform implementation
- Dependency injected into plugin hooks and platform specific implementations
- In the platform's module (
Illustration of the above:
Very similar to LuckPerms. Required to safely load dependencies at runtime on platforms that don't expose their classloader to adding new urls (Velocity & Fabric do, and this section does not apply to them)
The :platform:loader
is the file that is installed on the server and it contains the .jarinjar
(avoids shadow behavior of flattening jar
s) for the :platform
code.
The :platform:loader
contains the :api
module (including it's dependencies), as code from :platform
and dependencies loaded at runtime cannot be accessed by other plugins, as well as the bare minimum of code to launch the :platform
code.
Illustration of the above