Refactored API, added kotlin extensions

This commit is contained in:
Auxilor
2022-02-02 11:22:19 +00:00
parent 7ff3eeef06
commit 25e8cc0837
175 changed files with 72 additions and 18 deletions

View File

@@ -0,0 +1,144 @@
package com.willfp.eco.core;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
/**
* Wrapper class for ProtocolLib packets.
*/
public abstract class AbstractPacketAdapter extends PacketAdapter {
/**
* The packet type to listen for.
*/
private final PacketType type;
/**
* Whether the packet adapter should be registered after the server has loaded.
* <p>
* Useful for monitor priority adapters that <b>must</b> be ran last.
*/
private final boolean postLoad;
/**
* Create a new packet adapter for a specified plugin and type.
*
* @param plugin The plugin that ProtocolLib should mark as the owner.
* @param type The {@link PacketType} to listen for.
* @param priority The priority at which the adapter should be ran on packet send/receive.
* @param postLoad If the packet adapter should be registered after the server has loaded.
*/
protected AbstractPacketAdapter(@NotNull final EcoPlugin plugin,
@NotNull final PacketType type,
@NotNull final ListenerPriority priority,
final boolean postLoad) {
super(plugin, priority, Collections.singletonList(type));
this.type = type;
this.postLoad = postLoad;
}
/**
* Create a new packet adapter for a specified plugin and type.
*
* @param plugin The plugin that ProtocolLib should mark as the owner.
* @param type The {@link PacketType} to listen for.
* @param postLoad If the packet adapter should be registered after the server has loaded.
*/
protected AbstractPacketAdapter(@NotNull final EcoPlugin plugin,
@NotNull final PacketType type,
final boolean postLoad) {
this(plugin, type, ListenerPriority.NORMAL, postLoad);
}
/**
* The code that should be executed once the packet has been received.
*
* @param packet The packet.
* @param player The player.
* @param event The event.
*/
public void onReceive(@NotNull final PacketContainer packet,
@NotNull final Player player,
@NotNull final PacketEvent event) {
// Empty rather than abstract as implementations don't need both
}
/**
* THe code that should be executed once the packet has been sent.
*
* @param packet The packet.
* @param player The player.
* @param event The event.
*/
public void onSend(@NotNull final PacketContainer packet,
@NotNull final Player player,
@NotNull final PacketEvent event) {
// Empty rather than abstract as implementations don't need both
}
/**
* Boilerplate to assert that the packet is of the specified type.
*
* @param event The ProtocolLib event.
*/
@Override
public final void onPacketReceiving(final PacketEvent event) {
if (event.getPacket() == null) {
return;
}
if (!event.getPacket().getType().equals(type)) {
return;
}
onReceive(event.getPacket(), event.getPlayer(), event);
}
/**
* Boilerplate to assert that the packet is of the specified type.
*
* @param event The ProtocolLib event.
*/
@Override
public final void onPacketSending(final PacketEvent event) {
if (event.getPacket() == null) {
return;
}
if (!event.getPacket().getType().equals(type)) {
return;
}
onSend(event.getPacket(), event.getPlayer(), event);
}
@Override
public final EcoPlugin getPlugin() {
return (EcoPlugin) super.getPlugin();
}
/**
* Register the packet adapter with ProtocolLib.
*/
public final void register() {
if (!ProtocolLibrary.getProtocolManager().getPacketListeners().contains(this)) {
ProtocolLibrary.getProtocolManager().addPacketListener(this);
}
}
/**
* Get if the packet adapter should be loaded last.
*
* @return If post load.
*/
public boolean isPostLoad() {
return this.postLoad;
}
}

View File

@@ -0,0 +1,82 @@
package com.willfp.eco.core;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Holds the instance of the eco handler for bridging between the frontend
* and backend.
*
* @see Eco#getHandler()
* @see Handler
*/
@ApiStatus.Internal
public final class Eco {
/**
* Instance of eco handler.
*/
@ApiStatus.Internal
private static Handler handler;
/**
* Set the handler.
*
* @param handler The handler.
*/
@ApiStatus.Internal
public static void setHandler(@NotNull final Handler handler) {
Validate.isTrue(Eco.handler == null, "Already initialized!");
Eco.handler = handler;
}
/**
* Get the instance of the eco handler; the bridge between the api frontend
* and the implementation backend.
* <p>
* <strong>Do not use the handler in your plugins!</strong> It can and will contain
* breaking changes between minor versions and even patches, and you will create
* compatibility issues by using the handler. All parts of the handler have been abstracted
* into logically named API components that you can use.
* <p>
* Prior to version 6.12.0, the handler was considered as an API component, but it has
* since been moved into an internal component, and in 6.17.0, the first breaking change
* was introduced to {@link com.willfp.eco.core.config.wrapper.ConfigFactory}. This means
* that any usages of the handler can now cause problems in your plugins.
*
* @return The handler.
*/
@ApiStatus.Internal
public static Handler getHandler() {
return handler;
}
/**
* Eco Handler components are internals, so if a class is marked as a handler component,
* then it should be treated the same as if it was marked with {@link ApiStatus.Internal}.
* <p>
* If a class is marked with {@link HandlerComponent}, <strong>Do not reference it in
* your code!</strong> It can and will contain breaking changes between minor versions and
* even patches, and you will create compatibility issues by using them.
* <p>
* Handler components should also be marked with {@link ApiStatus.Internal} in order to
* cause compiler / IDE warnings.
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.TYPE})
public @interface HandlerComponent {
}
private Eco() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -0,0 +1,880 @@
package com.willfp.eco.core;
import com.willfp.eco.core.command.impl.PluginCommand;
import com.willfp.eco.core.config.base.ConfigYml;
import com.willfp.eco.core.config.base.LangYml;
import com.willfp.eco.core.config.updating.ConfigHandler;
import com.willfp.eco.core.display.Display;
import com.willfp.eco.core.display.DisplayModule;
import com.willfp.eco.core.events.EventManager;
import com.willfp.eco.core.extensions.Extension;
import com.willfp.eco.core.extensions.ExtensionLoader;
import com.willfp.eco.core.factory.MetadataValueFactory;
import com.willfp.eco.core.factory.NamespacedKeyFactory;
import com.willfp.eco.core.factory.RunnableFactory;
import com.willfp.eco.core.integrations.IntegrationLoader;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import com.willfp.eco.core.proxy.ProxyFactory;
import com.willfp.eco.core.scheduling.Scheduler;
import com.willfp.eco.core.web.UpdateChecker;
import org.apache.commons.lang.Validate;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* EcoPlugin is the base plugin class for eco-based plugins.
* <p>
* It functions as a replacement for {@link JavaPlugin}.
* <p>
* EcoPlugin is a lot more powerful than {@link JavaPlugin} and
* contains many methods to reduce boilerplate code and reduce
* plugin complexity.
* <p>
* It is recommended to view the source code for this class to
* gain a better understanding of how it works.
* <p>
* <b>IMPORTANT: When reloading a plugin, all runnables / tasks will
* be cancelled.</b>
*/
@SuppressWarnings("unused")
public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
/**
* The polymart resource ID of the plugin.
*/
private final int resourceId;
/**
* The bStats resource ID of the plugin.
*/
private final int bStatsId;
/**
* The package where proxy implementations are.
*/
private final String proxyPackage;
/**
* The color of the plugin, used in messages.
*/
private final String color;
/**
* Loaded integrations.
*/
private final Set<String> loadedIntegrations = new HashSet<>();
/**
* The internal plugin scheduler.
*/
private final Scheduler scheduler;
/**
* The internal plugin Event Manager.
*/
private final EventManager eventManager;
/**
* Config.yml.
*/
private final ConfigYml configYml;
/**
* Lang.yml.
*/
private final LangYml langYml;
/**
* The internal factory to produce {@link org.bukkit.NamespacedKey}s.
*/
private final NamespacedKeyFactory namespacedKeyFactory;
/**
* The internal factory to produce {@link org.bukkit.metadata.FixedMetadataValue}s.
*/
private final MetadataValueFactory metadataValueFactory;
/**
* The internal factory to produce {@link com.willfp.eco.core.scheduling.RunnableTask}s.
*/
private final RunnableFactory runnableFactory;
/**
* The loader for all plugin extensions.
*
* @see com.willfp.eco.core.extensions.Extension
*/
private final ExtensionLoader extensionLoader;
/**
* The handler class for updatable classes.
*/
private final ConfigHandler configHandler;
/**
* The display module for the plugin.
*/
private DisplayModule displayModule;
/**
* The logger for the plugin.
*/
private final Logger logger;
/**
* If the server is running an outdated version of the plugin.
*/
private boolean outdated = false;
/**
* If the plugin supports extensions.
*/
private final boolean supportingExtensions;
/**
* The proxy factory.
*/
@Nullable
private final ProxyFactory proxyFactory;
/**
* Create a new plugin without a specified color, proxy support, polymart, or bStats.
*/
protected EcoPlugin() {
this("&f");
}
/**
* Create a new plugin without proxy support, polymart, or bStats.
*
* @param color The color.
*/
protected EcoPlugin(@NotNull final String color) {
this("", color);
}
/**
* Create a new plugin unlinked to polymart and bStats.
*
* @param proxyPackage The package where proxy implementations are stored.
* @param color The color of the plugin (used in messages, using standard formatting)
*/
protected EcoPlugin(@NotNull final String proxyPackage,
@NotNull final String color) {
this(0, 0, proxyPackage, color);
}
/**
* Create a new plugin without proxy or extension support.
*
* @param resourceId The polymart resource ID for the plugin.
* @param bStatsId The bStats resource ID for the plugin.
* @param color The color of the plugin (used in messages, using standard formatting)
*/
protected EcoPlugin(final int resourceId,
final int bStatsId,
@NotNull final String color) {
this(resourceId, bStatsId, "", color);
}
/**
* Create a new plugin without proxy support.
*
* @param resourceId The polymart resource ID for the plugin.
* @param bStatsId The bStats resource ID for the plugin.
* @param color The color of the plugin (used in messages, using standard formatting)
* @param supportingExtensions If the plugin supports extensions.
*/
protected EcoPlugin(final int resourceId,
final int bStatsId,
@NotNull final String color,
final boolean supportingExtensions) {
this(resourceId, bStatsId, "", color, supportingExtensions);
}
/**
* Create a new plugin without extension support.
*
* @param resourceId The polymart resource ID for the plugin.
* @param bStatsId The bStats resource ID for the plugin.
* @param proxyPackage The package where proxy implementations are stored.
* @param color The color of the plugin (used in messages, using standard formatting)
*/
protected EcoPlugin(final int resourceId,
final int bStatsId,
@NotNull final String proxyPackage,
@NotNull final String color) {
this(resourceId, bStatsId, proxyPackage, color, false);
}
/**
* Create a new plugin.
*
* @param resourceId The polymart resource ID for the plugin.
* @param bStatsId The bStats resource ID for the plugin.
* @param proxyPackage The package where proxy implementations are stored.
* @param color The color of the plugin (used in messages, using standard formatting)
* @param supportingExtensions If the plugin supports extensions.
*/
protected EcoPlugin(final int resourceId,
final int bStatsId,
@NotNull final String proxyPackage,
@NotNull final String color,
final boolean supportingExtensions) {
/*
The handler must be initialized before any plugin's constructors
are called, as the constructors call Eco#getHandler().
To fix this, EcoSpigotPlugin an abstract class and the 'actual'
plugin class is EcoHandler - that way I can create the handler
before any plugins are loaded while still having a separation between
the plugin class and the handler class (for code clarity).
I don't really like the fact that the handler class *is* the
spigot plugin, but it is what it is.
There is probably a better way of doing it - maybe with
some sort of HandlerCreator interface in order to still have
a standalone handler class, but then there would be an interface
left in the API that doesn't really help anything.
The other alternative would be do use reflection to get a 'createHandler'
method that only exists in EcoSpigotPlugin - but that feels really dirty
and I'd rather only use reflection where necessary.
*/
if (Eco.getHandler() == null && this instanceof Handler) {
/*
This code is only ever called by EcoSpigotPlugin (EcoHandler)
as it's the first plugin to load and it is a handler.
Any other plugins will never call this code as the handler
will have already been initialized.
*/
Eco.setHandler((Handler) this);
}
assert Eco.getHandler() != null;
this.resourceId = resourceId;
this.bStatsId = bStatsId;
this.proxyPackage = proxyPackage;
this.color = color;
this.supportingExtensions = supportingExtensions;
this.scheduler = Eco.getHandler().createScheduler(this);
this.eventManager = Eco.getHandler().createEventManager(this);
this.namespacedKeyFactory = Eco.getHandler().createNamespacedKeyFactory(this);
this.metadataValueFactory = Eco.getHandler().createMetadataValueFactory(this);
this.runnableFactory = Eco.getHandler().createRunnableFactory(this);
this.extensionLoader = Eco.getHandler().createExtensionLoader(this);
this.configHandler = Eco.getHandler().createConfigHandler(this);
this.logger = Eco.getHandler().createLogger(this);
this.proxyFactory = this.proxyPackage.equalsIgnoreCase("") ? null : Eco.getHandler().createProxyFactory(this);
this.langYml = this.createLangYml();
this.configYml = this.createConfigYml();
Eco.getHandler().addNewPlugin(this);
/*
The minimum eco version check was moved here because it's very common
to add a lot of code in the constructor of plugins; meaning that the plugin
can throw errors without it being obvious to the user that the reason is
because they have an outdated version of eco installed.
*/
DefaultArtifactVersion runningVersion = new DefaultArtifactVersion(Eco.getHandler().getEcoPlugin().getDescription().getVersion());
DefaultArtifactVersion requiredVersion = new DefaultArtifactVersion(this.getMinimumEcoVersion());
if (!(runningVersion.compareTo(requiredVersion) > 0 || runningVersion.equals(requiredVersion))) {
this.getLogger().severe("You are running an outdated version of eco!");
this.getLogger().severe("You must be on at least" + this.getMinimumEcoVersion());
this.getLogger().severe("Download the newest version here:");
this.getLogger().severe("https://polymart.org/download/773/recent/JSpprMspkuyecf5y1wQ2Jn8OoLQSQ_IW");
Bukkit.getPluginManager().disablePlugin(this);
}
}
/**
* Default code to be executed on plugin enable.
*/
@Override
public final void onEnable() {
super.onEnable();
this.getLogger().info("");
this.getLogger().info("Loading " + this.getColor() + this.getName());
if (this.getResourceId() != 0) {
new UpdateChecker(this).getVersion(version -> {
DefaultArtifactVersion currentVersion = new DefaultArtifactVersion(this.getDescription().getVersion());
DefaultArtifactVersion mostRecentVersion = new DefaultArtifactVersion(version);
if (!(currentVersion.compareTo(mostRecentVersion) > 0 || currentVersion.equals(mostRecentVersion))) {
this.outdated = true;
this.getLogger().warning("&c" + this.getName() + " is out of date! (Version " + this.getDescription().getVersion() + ")");
this.getLogger().warning("&cThe newest version is &f" + version);
this.getLogger().warning("&cDownload the new version!");
}
});
}
if (this.getBStatsId() != 0) {
Eco.getHandler().registerBStats(this);
}
Set<String> enabledPlugins = Arrays.stream(Bukkit.getPluginManager().getPlugins())
.map(Plugin::getName)
.map(String::toLowerCase)
.collect(Collectors.toSet());
if (enabledPlugins.contains("PlaceholderAPI".toLowerCase())) {
this.loadedIntegrations.add("PlaceholderAPI");
PlaceholderManager.addIntegration(Eco.getHandler().createPAPIIntegration(this));
}
this.loadIntegrationLoaders().forEach((integrationLoader -> {
if (enabledPlugins.contains(integrationLoader.getPluginName().toLowerCase())) {
this.loadedIntegrations.add(integrationLoader.getPluginName());
integrationLoader.load();
}
}));
this.getLogger().info("Loaded integrations: " + String.join(", ", this.getLoadedIntegrations()));
Prerequisite.update();
this.loadPacketAdapters().forEach(abstractPacketAdapter -> {
if (!abstractPacketAdapter.isPostLoad()) {
abstractPacketAdapter.register();
}
});
this.loadListeners().forEach(listener -> this.getEventManager().registerListener(listener));
this.loadPluginCommands().forEach(PluginCommand::register);
this.getScheduler().runLater(this::afterLoad, 1);
if (this.isSupportingExtensions()) {
this.getExtensionLoader().loadExtensions();
if (this.getExtensionLoader().getLoadedExtensions().isEmpty()) {
this.getLogger().info("&cNo extensions found");
} else {
this.getLogger().info("Extensions Loaded:");
this.getExtensionLoader().getLoadedExtensions().forEach(extension -> this.getLogger().info("- " + extension.getName() + " v" + extension.getVersion()));
}
}
this.handleEnable();
this.getLogger().info("");
}
/**
* Default code to be executed on plugin disable.
*/
@Override
public final void onDisable() {
super.onDisable();
this.getEventManager().unregisterAllListeners();
this.getScheduler().cancelAll();
this.handleDisable();
if (this.isSupportingExtensions()) {
this.getExtensionLoader().unloadExtensions();
}
this.getLogger().info("Cleaning up...");
Eco.getHandler().getCleaner().clean(this);
}
/**
* Default code to be executed on plugin load.
*/
@Override
public final void onLoad() {
super.onLoad();
this.handleLoad();
}
/**
* Default code to be executed after the server is up.
*/
public final void afterLoad() {
this.displayModule = createDisplayModule();
if (this.getDisplayModule() != null) {
Display.registerDisplayModule(this.getDisplayModule());
}
this.loadPacketAdapters().forEach(abstractPacketAdapter -> {
if (abstractPacketAdapter.isPostLoad()) {
abstractPacketAdapter.register();
}
});
if (!Prerequisite.HAS_PAPER.isMet()) {
this.getLogger().severe("");
this.getLogger().severe("----------------------------");
this.getLogger().severe("");
this.getLogger().severe("You don't seem to be running paper!");
this.getLogger().severe("Paper is strongly recommended for all servers,");
this.getLogger().severe("and some things may not function properly without it");
this.getLogger().severe("Download Paper from &fhttps://papermc.io");
this.getLogger().severe("");
this.getLogger().severe("----------------------------");
this.getLogger().severe("");
}
this.handleAfterLoad();
this.reload();
for (Extension extension : this.getExtensionLoader().getLoadedExtensions()) {
extension.handleAfterLoad();
}
this.getLogger().info("Loaded " + this.color + this.getName());
}
/**
* Reload the plugin.
*/
public final void reload() {
this.getConfigHandler().updateConfigs();
this.getConfigHandler().callUpdate();
this.getConfigHandler().callUpdate(); // Call twice to fix issues
this.getScheduler().cancelAll();
this.handleReload();
for (Extension extension : this.extensionLoader.getLoadedExtensions()) {
extension.handleReload();
}
}
/**
* Reload the plugin and return the time taken to reload.
*
* @return The time.
*/
public final long reloadWithTime() {
long startTime = System.currentTimeMillis();
this.reload();
return System.currentTimeMillis() - startTime;
}
/**
* The plugin-specific code to be executed on enable.
* <p>
* Override when needed.
*/
protected void handleEnable() {
}
/**
* The plugin-specific code to be executed on disable.
* <p>
* Override when needed.
*/
protected void handleDisable() {
}
/**
* The plugin-specific code to be executed on load.
* <p>
* This is executed before enabling.
* <p>
* Override when needed.
*/
protected void handleLoad() {
}
/**
* The plugin-specific code to be executed on reload.
* <p>
* Override when needed.
*/
protected void handleReload() {
}
/**
* The plugin-specific code to be executed after the server is up.
* <p>
* Override when needed.
*/
protected void handleAfterLoad() {
}
/**
* The plugin-specific integrations to be tested and loaded.
*
* @return A list of integrations.
*/
protected List<IntegrationLoader> loadIntegrationLoaders() {
return new ArrayList<>();
}
/**
* The commands to be registered.
*
* @return A list of commands.
*/
protected List<PluginCommand> loadPluginCommands() {
return new ArrayList<>();
}
/**
* ProtocolLib packet adapters to be registered.
* <p>
* If the plugin does not require ProtocolLib this can be left empty.
*
* @return A list of packet adapters.
*/
protected List<AbstractPacketAdapter> loadPacketAdapters() {
return new ArrayList<>();
}
/**
* All listeners to be registered.
*
* @return A list of all listeners.
*/
protected abstract List<Listener> loadListeners();
/**
* Useful for custom LangYml implementations.
* <p>
* Override if needed.
*
* @return lang.yml.
*/
protected LangYml createLangYml() {
return new LangYml(this);
}
/**
* Useful for custom ConfigYml implementations.
* <p>
* Override if needed.
*
* @return config.yml.
*/
protected ConfigYml createConfigYml() {
return new ConfigYml(this);
}
/**
* Create the display module for the plugin.
*
* @return The display module, or null.
*/
@Nullable
protected DisplayModule createDisplayModule() {
Validate.isTrue(
this.getDisplayModule() == null,
"Display module exists!"
);
return null;
}
/**
* Get the minimum version of eco to use the plugin.
*
* @return The version.
*/
public String getMinimumEcoVersion() {
return "6.0.0";
}
/**
* Get the plugin's logger.
*
* @return The logger.
*/
@NotNull
@Override
public Logger getLogger() {
return logger;
}
/**
* Get a proxy.
*
* @param proxyClass The proxy class.
* @param <T> The proxy type.
* @return The proxy.
*/
public final <T> T getProxy(@NotNull final Class<T> proxyClass) {
Validate.notNull(proxyFactory, "Plugin does not support proxy!");
return proxyFactory.getProxy(proxyClass);
}
/**
* Get unwrapped config.
* Does not use eco config system, don't use.
*
* @return The bukkit config.
* @deprecated Use getConfigYml() instead.
*/
@NotNull
@Override
@Deprecated
public final FileConfiguration getConfig() {
this.getLogger().warning("Call to default config method in eco plugin!");
return Objects.requireNonNull(this.getConfigYml().getBukkitHandle());
}
/**
* Does not use eco config system, don't use.
*
* @deprecated Use eco config system.
*/
@Override
@Deprecated
public final void saveConfig() {
this.getLogger().warning("Call to default config method in eco plugin!");
super.saveConfig();
}
/**
* Does not use eco config system, don't use.
*
* @deprecated Use eco config system.
*/
@Override
@Deprecated
public final void saveDefaultConfig() {
this.getLogger().warning("Call to default config method in eco plugin!");
super.saveDefaultConfig();
}
/**
* Does not use eco config system, don't use.
*
* @deprecated Use eco config system.
*/
@Override
@Deprecated
public final void reloadConfig() {
this.getLogger().warning("Call to default config method in eco plugin!");
super.reloadConfig();
}
/**
* Get an EcoPlugin by name.
*
* @param pluginName The name.
* @return The plugin.
*/
public static EcoPlugin getPlugin(@NotNull final String pluginName) {
return Eco.getHandler().getPluginByName(pluginName);
}
/**
* Get all EcoPlugin names.
*
* @return The set of names.
*/
public static Set<String> getPluginNames() {
return new HashSet<>(Eco.getHandler().getLoadedPlugins());
}
/**
* Get the polymart resource ID.
*
* @return The resource ID.
*/
public int getResourceId() {
return this.resourceId;
}
/**
* Get the bStats ID.
*
* @return The ID.
*/
public int getBStatsId() {
return this.bStatsId;
}
/**
* Get the proxy package.
*
* @return The package where proxies are contained.
*/
public String getProxyPackage() {
return this.proxyPackage;
}
/**
* Get the plugin color (uses legacy formatting).
*
* @return The color.
*/
public String getColor() {
return this.color;
}
/**
* Get all loaded integration names.
*
* @return The integrations.
*/
public Set<String> getLoadedIntegrations() {
return this.loadedIntegrations;
}
/**
* Get the scheduler.
*
* @return The scheduler.
*/
public Scheduler getScheduler() {
return this.scheduler;
}
/**
* Get the event manager.
*
* @return The event manager.
*/
public EventManager getEventManager() {
return this.eventManager;
}
/**
* Get config.yml.
*
* @return config.yml.
*/
public ConfigYml getConfigYml() {
return this.configYml;
}
/**
* Get lang.yml.
*
* @return lang.yml.
*/
public LangYml getLangYml() {
return this.langYml;
}
/**
* Get the NamespacedKey factory.
*
* @return The factory.
*/
public NamespacedKeyFactory getNamespacedKeyFactory() {
return this.namespacedKeyFactory;
}
/**
* Get the metadata value factory.
*
* @return The factory.
*/
public MetadataValueFactory getMetadataValueFactory() {
return this.metadataValueFactory;
}
/**
* Get the runnable factory.
*
* @return The runnable factory.
*/
public RunnableFactory getRunnableFactory() {
return this.runnableFactory;
}
/**
* Get the extension loader.
*
* @return The extension loader.
*/
public ExtensionLoader getExtensionLoader() {
return this.extensionLoader;
}
/**
* Get the config handler.
*
* @return The config handler.
*/
public ConfigHandler getConfigHandler() {
return this.configHandler;
}
/**
* Get the plugin's display module.
*
* @return The display module.
*/
@Nullable
public DisplayModule getDisplayModule() {
return this.displayModule;
}
/**
* Get if the plugin is outdated.
*
* @return If outdated.
*/
public boolean isOutdated() {
return this.outdated;
}
/**
* Get if the plugin supports extensions.
*
* @return If extensions are supported.
*/
public boolean isSupportingExtensions() {
return this.supportingExtensions;
}
/**
* Get the proxy factory.
*
* @return The proxy factory.
*/
@Nullable
public ProxyFactory getProxyFactory() {
return this.proxyFactory;
}
}

View File

@@ -0,0 +1,262 @@
package com.willfp.eco.core;
import com.willfp.eco.core.config.updating.ConfigHandler;
import com.willfp.eco.core.config.wrapper.ConfigFactory;
import com.willfp.eco.core.data.ProfileHandler;
import com.willfp.eco.core.data.keys.KeyRegistry;
import com.willfp.eco.core.drops.DropQueueFactory;
import com.willfp.eco.core.events.EventManager;
import com.willfp.eco.core.extensions.ExtensionLoader;
import com.willfp.eco.core.factory.MetadataValueFactory;
import com.willfp.eco.core.factory.NamespacedKeyFactory;
import com.willfp.eco.core.factory.RunnableFactory;
import com.willfp.eco.core.fast.FastItemStack;
import com.willfp.eco.core.gui.GUIFactory;
import com.willfp.eco.core.integrations.placeholder.PlaceholderIntegration;
import com.willfp.eco.core.proxy.Cleaner;
import com.willfp.eco.core.proxy.ProxyFactory;
import com.willfp.eco.core.requirement.RequirementFactory;
import com.willfp.eco.core.scheduling.Scheduler;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.logging.Logger;
/**
* @see Eco#getHandler()
*/
@ApiStatus.Internal
public interface Handler {
/**
* Create a scheduler.
*
* @param plugin The plugin.
* @return The scheduler.
*/
@NotNull
Scheduler createScheduler(@NotNull EcoPlugin plugin);
/**
* Create an event manager.
*
* @param plugin The plugin.
* @return The event manager.
*/
@NotNull
EventManager createEventManager(@NotNull EcoPlugin plugin);
/**
* Create a NamespacedKey factory.
*
* @param plugin The plugin.
* @return The factory.
*/
@NotNull
NamespacedKeyFactory createNamespacedKeyFactory(@NotNull EcoPlugin plugin);
/**
* Create a MetadataValue factory.
*
* @param plugin The plugin.
* @return The factory.
*/
@NotNull
MetadataValueFactory createMetadataValueFactory(@NotNull EcoPlugin plugin);
/**
* Create a Runnable factory.
*
* @param plugin The plugin.
* @return The factory.
*/
@NotNull
RunnableFactory createRunnableFactory(@NotNull EcoPlugin plugin);
/**
* Create an ExtensionLoader.
*
* @param plugin The plugin.
* @return The factory.
*/
@NotNull
ExtensionLoader createExtensionLoader(@NotNull EcoPlugin plugin);
/**
* Create a config handler.
*
* @param plugin The plugin.
* @return The handler.
*/
@NotNull
ConfigHandler createConfigHandler(@NotNull EcoPlugin plugin);
/**
* Create a logger.
*
* @param plugin The plugin.
* @return The logger.
*/
@NotNull
Logger createLogger(@NotNull EcoPlugin plugin);
/**
* Create a PAPI integration.
*
* @param plugin The plugin.
* @return The integration.
*/
@NotNull
PlaceholderIntegration createPAPIIntegration(@NotNull EcoPlugin plugin);
/**
* Create a proxy factory.
*
* @param plugin The plugin.
* @return The factory.
*/
@NotNull
ProxyFactory createProxyFactory(@NotNull EcoPlugin plugin);
/**
* Get eco Spigot plugin.
*
* @return The plugin.
*/
@NotNull
EcoPlugin getEcoPlugin();
/**
* Get config factory.
*
* @return The factory.
*/
@NotNull
ConfigFactory getConfigFactory();
/**
* Get drop queue factory.
*
* @return The factory.
*/
@NotNull
DropQueueFactory getDropQueueFactory();
/**
* Get GUI factory.
*
* @return The factory.
*/
@NotNull
GUIFactory getGUIFactory();
/**
* Get cleaner.
*
* @return The cleaner.
*/
@NotNull
Cleaner getCleaner();
/**
* Add new plugin.
*
* @param plugin The plugin.
*/
void addNewPlugin(@NotNull EcoPlugin plugin);
/**
* Get plugin by name.
*
* @param name The name.
* @return The plugin.
*/
@Nullable
EcoPlugin getPluginByName(@NotNull String name);
/**
* Get all loaded eco plugins.
*
* @return A list of plugin names in lowercase.
*/
@NotNull
List<String> getLoadedPlugins();
/**
* Create a FastItemStack.
*
* @param itemStack The base ItemStack.
* @return The FastItemStack.
*/
@NotNull
FastItemStack createFastItemStack(@NotNull ItemStack itemStack);
/**
* Register bStats metrics.
*
* @param plugin The plugin.
*/
void registerBStats(@NotNull EcoPlugin plugin);
/**
* Get the requirement factory.
*
* @return The factory.
*/
@NotNull
RequirementFactory getRequirementFactory();
/**
* Get Adventure audiences.
*
* @return The audiences.
*/
@Nullable
BukkitAudiences getAdventure();
/**
* Get the key registry.
*
* @return The registry.
*/
@NotNull
KeyRegistry getKeyRegistry();
/**
* Get the PlayerProfile handler.
*
* @return The handler.
*/
@NotNull
ProfileHandler getProfileHandler();
/**
* Create dummy entity - never spawned, exists purely in code.
*
* @param location The location.
* @return The entity.
*/
@NotNull
Entity createDummyEntity(@NotNull Location location);
/**
* Create a {@link NamespacedKey} quickly
* <p>
* Bypasses the constructor, allowing for the creation of invalid keys,
* therefore this is considered unsafe and should only be called after
* the key has been confirmed to be valid.
*
* @param namespace The namespace.
* @param key The key.
* @return The key.
*/
@NotNull
NamespacedKey createNamespacedKey(@NotNull String namespace,
@NotNull String key);
}

View File

@@ -0,0 +1,42 @@
package com.willfp.eco.core;
import org.jetbrains.annotations.NotNull;
/**
* Quick DI class to manage passing eco plugins.
* <p>
* Basically just a quick bit of laziness if you can't be bothered to add a private field
* and a protected getter, don't use this in kotlin as you can just specify
* {@code
* private val plugin: EcoPlugin
* }
* in the constructor.
*
* @param <T> The eco plugin type.
*/
public abstract class PluginDependent<T extends EcoPlugin> {
/**
* The {@link EcoPlugin} that is stored.
*/
@NotNull
private final T plugin;
/**
* Pass an {@link EcoPlugin} in order to interface with it.
*
* @param plugin The plugin to manage.
*/
protected PluginDependent(@NotNull final T plugin) {
this.plugin = plugin;
}
/**
* Get the plugin.
*
* @return The plugin.
*/
@NotNull
protected T getPlugin() {
return this.plugin;
}
}

View File

@@ -0,0 +1,37 @@
package com.willfp.eco.core;
import com.willfp.eco.core.config.updating.ConfigHandler;
import java.io.File;
import java.util.logging.Logger;
/**
* Represents any class that acts like a plugin, for example {@link EcoPlugin}
* or {@link com.willfp.eco.core.extensions.Extension}. This exists to create
* things such as extension base configs rather than needing to pass an instance
* of the owning plugin.
*/
public interface PluginLike {
/**
* Get the data folder of the object.
* <p>
* Returns the plugin data folder for a plugin, or the extension's parent plugin's folder
*
* @return The data folder.
*/
File getDataFolder();
/**
* Get the handler class for updatable classes.
*
* @return The config handler.
*/
ConfigHandler getConfigHandler();
/**
* Get the logger.
*
* @return The logger.
*/
Logger getLogger();
}

View File

@@ -0,0 +1,151 @@
package com.willfp.eco.core;
import com.willfp.eco.core.integrations.economy.EconomyManager;
import com.willfp.eco.core.proxy.ProxyConstants;
import com.willfp.eco.util.ClassUtils;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
/**
* A prerequisite is a requirement for something.
* <p>
* For example, you can require the server to have paper or be a specific version,
* or have some other dependency.
*/
public class Prerequisite {
/**
* All existing prerequisites are registered on creation.
*/
private static final List<Prerequisite> VALUES = new ArrayList<>();
/**
* Requires the server to be running an implementation of paper.
*/
public static final Prerequisite HAS_PAPER = new Prerequisite(
() -> ClassUtils.exists("com.destroystokyo.paper.event.player.PlayerElytraBoostEvent"),
"Requires server to be running paper (or a fork)"
);
/**
* Requires the server to have vault installed.
*
* @deprecated Use {@link EconomyManager#hasRegistrations()} instead.
*/
@Deprecated
public static final Prerequisite HAS_VAULT = new Prerequisite(
() -> ClassUtils.exists("net.milkbowl.vault.economy.Economy"),
"Requires server to have vault"
);
/**
* Requires the server to be running 1.18.
*/
public static final Prerequisite HAS_1_18 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("18"),
"Requires server to be running 1.18+"
);
/**
* Requires the server to be running 1.17.
*/
public static final Prerequisite HAS_1_17 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("17") || HAS_1_18.isMet(),
"Requires server to be running 1.17+"
);
/**
* Requires the server to be running an implementation of BungeeCord.
*/
public static final Prerequisite HAS_BUNGEECORD = new Prerequisite(
() -> ClassUtils.exists("net.md_5.bungee.api.event.ServerConnectedEvent"),
"Requires server to be running BungeeCord (or a fork)"
);
/**
* Requires the server to be running an implementation of Velocity.
*/
public static final Prerequisite HAS_VELOCITY = new Prerequisite(
() -> ClassUtils.exists("com.velocitypowered.api.event.player.ServerConnectedEvent"),
"Requires server to be running Velocity (or a fork)"
);
/**
* If the necessary prerequisite condition has been met.
*/
private boolean isMet;
/**
* Retrieve if the necessary prerequisite condition is met.
*/
private final Supplier<Boolean> isMetSupplier;
/**
* The description of the requirements of the prerequisite.
*/
private final String description;
/**
* Create a prerequisite.
*
* @param isMetSupplier An {@link Supplier<Boolean>} that returns if the prerequisite is met
* @param description The description of the prerequisite, shown to the user if it isn't
*/
public Prerequisite(@NotNull final Supplier<Boolean> isMetSupplier,
@NotNull final String description) {
this.isMetSupplier = isMetSupplier;
this.isMet = isMetSupplier.get();
this.description = description;
VALUES.add(this);
}
/**
* Refresh the condition set in the supplier, updates {@link this#isMet}.
*/
private void refresh() {
this.isMet = this.isMetSupplier.get();
}
/**
* Update all prerequisites' {@link Prerequisite#isMet}.
*/
public static void update() {
VALUES.forEach(Prerequisite::refresh);
}
/**
* Check if all prerequisites in array are met.
*
* @param prerequisites A primitive array of prerequisites to check.
* @return If all the prerequisites are met.
*/
public static boolean areMet(@NotNull final Prerequisite[] prerequisites) {
update();
return Arrays.stream(prerequisites).allMatch(Prerequisite::isMet);
}
static {
update();
}
/**
* Get if the prerequisite is met.
*
* @return If the condition is met.
*/
public boolean isMet() {
return this.isMet;
}
/**
* Get the description.
*
* @return The description.
*/
public String getDescription() {
return this.description;
}
}

View File

@@ -0,0 +1,108 @@
package com.willfp.eco.core.command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* Interface for all command implementations.
*/
public interface CommandBase {
/**
* Get command name.
*
* @return The name.
*/
String getName();
/**
* Get command permission.
*
* @return The permission.
*/
String getPermission();
/**
* If only players can execute the command.
*
* @return If true.
*/
boolean isPlayersOnly();
/**
* Add a subcommand to the command.
*
* @param command The subcommand.
* @return The parent command.
*/
CommandBase addSubcommand(@NotNull CommandBase command);
/**
* Handle command execution.
* <p>
* Marked as default void with no implementation for backwards compatibility.
*
* @param sender The sender.
* @param args The args.
*/
default void onExecute(@NotNull CommandSender sender,
@NotNull List<String> args) {
// Do nothing.
}
/**
* Handle tab completion.
* <p>
* Marked as default void with no implementation for backwards compatibility.
*
* @param sender The sender.
* @param args The args.
* @return The results.
*/
default List<String> tabComplete(@NotNull CommandSender sender,
@NotNull List<String> args) {
return new ArrayList<>();
}
/**
* Get the handler.
*
* @return The handler.
* @see CommandHandler
* @deprecated Use {@link CommandBase#onExecute(CommandSender, List)} instead.
*/
@Deprecated
CommandHandler getHandler();
/**
* Set the handler.
*
* @param handler The handler.
* @see CommandHandler
* @deprecated Handlers have been deprecated.
*/
@Deprecated
void setHandler(@NotNull CommandHandler handler);
/**
* Get the tab completer.
*
* @return The tab completer.
* @see TabCompleteHandler
* @deprecated Use {@link CommandBase#tabComplete(CommandSender, List)} instead.
*/
@Deprecated
TabCompleteHandler getTabCompleter();
/**
* Set the tab completer.
*
* @param handler The handler.
* @see TabCompleteHandler
* @deprecated Handlers have been deprecated.
*/
@Deprecated
void setTabCompleter(@NotNull TabCompleteHandler handler);
}

View File

@@ -0,0 +1,29 @@
package com.willfp.eco.core.command;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* A command handler handles the actual code for a command.
* <p>
* The replacement for {@link org.bukkit.command.CommandExecutor#onCommand(CommandSender, Command, String, String[])}
*
* @see CommandBase
* @deprecated Handlers have been deprecated. This legacy system will eventually be removed,
* update to use the new system: {@link CommandBase#onExecute(CommandSender, List)}.
*/
@FunctionalInterface
@Deprecated(since = "6.17.0")
public interface CommandHandler {
/**
* The code to be called on execution.
*
* @param sender The sender.
* @param args The arguments.
*/
void onExecute(@NotNull CommandSender sender,
@NotNull List<String> args);
}

View File

@@ -0,0 +1,30 @@
package com.willfp.eco.core.command;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* A Tab Complete handler handles the actual tab-completion code.
* <p>
* The replacement for {@link org.bukkit.command.TabCompleter#onTabComplete(CommandSender, Command, String, String[])}
*
* @see CommandBase
* @deprecated Handlers have been deprecated. This legacy system will eventually be removed,
* update to use the new system: {@link CommandBase#tabComplete(CommandSender, List)}
*/
@FunctionalInterface
@Deprecated(since = "6.17.0")
public interface TabCompleteHandler {
/**
* Handle Tab Completion.
*
* @param sender The sender.
* @param args The arguments.
* @return The tab completion results.
*/
List<String> tabComplete(@NotNull CommandSender sender,
@NotNull List<String> args);
}

View File

@@ -0,0 +1,268 @@
package com.willfp.eco.core.command.impl;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginDependent;
import com.willfp.eco.core.command.CommandBase;
import com.willfp.eco.core.command.CommandHandler;
import com.willfp.eco.core.command.TabCompleteHandler;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* Abstract class for commands that can be handled.
* <p>
* Handled commands have a method to pass in raw input from bukkit commands
* in order to execute the command-specific code. It's essentially an internal
* layer, hence why it's a package-private class.
*/
@SuppressWarnings({"DeprecatedIsStillUsed"})
abstract class HandledCommand extends PluginDependent<EcoPlugin> implements CommandBase {
/**
* The name of the command.
*/
private final String name;
/**
* The permission required to execute the command.
* <p>
* Written out as a string for flexibility with subclasses.
*/
private final String permission;
/**
* Should the command only be allowed to be executed by players?
* <p>
* In other worlds, only allowed to be executed by console.
*/
private final boolean playersOnly;
/**
* The actual code to be executed in the command.
*/
@Deprecated
@Nullable
private CommandHandler handler = null;
/**
* The tab completion code to be executed in the command.
*/
@Deprecated
@Nullable
private TabCompleteHandler tabCompleter = null;
/**
* All subcommands for the command.
*/
private final List<CommandBase> subcommands;
/**
* Create a new command.
* <p>
* The name cannot be the same as an existing command as this will conflict.
*
* @param plugin Instance of a plugin.
* @param name The name used in execution.
* @param permission The permission required to execute the command.
* @param playersOnly If only players should be able to execute this command.
*/
HandledCommand(@NotNull final EcoPlugin plugin,
@NotNull final String name,
@NotNull final String permission,
final boolean playersOnly) {
super(plugin);
this.name = name;
this.permission = permission;
this.playersOnly = playersOnly;
this.subcommands = new ArrayList<>();
}
/**
* Add a subcommand to the command.
*
* @param subcommand The subcommand.
* @return The parent command.
*/
@Override
public final CommandBase addSubcommand(@NotNull final CommandBase subcommand) {
subcommands.add(subcommand);
return this;
}
/**
* Handle the command.
*
* @param sender The sender.
* @param args The arguments.
*/
protected final void handle(@NotNull final CommandSender sender,
@NotNull final String[] args) {
if (!canExecute(sender, this, this.getPlugin())) {
return;
}
if (args.length > 0) {
for (CommandBase subcommand : this.getSubcommands()) {
if (subcommand.getName().equalsIgnoreCase(args[0])) {
if (!canExecute(sender, subcommand, this.getPlugin())) {
return;
}
((HandledCommand) subcommand).handle(sender, Arrays.copyOfRange(args, 1, args.length));
return;
}
}
}
if (this.getHandler() != null) {
this.getHandler().onExecute(sender, Arrays.asList(args));
} else {
this.onExecute(sender, Arrays.asList(args));
}
}
/**
* Handle the tab completion.
*
* @param sender The sender.
* @param args The arguments.
* @return The tab completion results.
*/
protected final List<String> handleTabCompletion(@NotNull final CommandSender sender,
@NotNull final String[] args) {
if (!sender.hasPermission(this.getPermission())) {
return null;
}
if (args.length == 1) {
List<String> completions = new ArrayList<>();
StringUtil.copyPartialMatches(
args[0],
this.getSubcommands().stream().map(CommandBase::getName).collect(Collectors.toList()),
completions
);
Collections.sort(completions);
if (!completions.isEmpty()) {
return completions;
}
}
if (args.length >= 2) {
HandledCommand command = null;
for (CommandBase subcommand : this.getSubcommands()) {
if (args[0].equalsIgnoreCase(subcommand.getName())) {
command = (HandledCommand) subcommand;
}
}
if (command != null) {
return command.handleTabCompletion(sender, Arrays.copyOfRange(args, 1, args.length));
}
}
if (this.getTabCompleter() != null) {
return this.getTabCompleter().tabComplete(sender, Arrays.asList(args));
} else {
return this.tabComplete(sender, Arrays.asList(args));
}
}
/**
* If a sender can execute the command.
*
* @param sender The sender.
* @param command The command.
* @param plugin The plugin.
* @return If the sender can execute.
*/
public static boolean canExecute(@NotNull final CommandSender sender,
@NotNull final CommandBase command,
@NotNull final EcoPlugin plugin) {
if (command.isPlayersOnly() && !(sender instanceof Player)) {
sender.sendMessage(plugin.getLangYml().getMessage("not-player"));
return false;
}
if (!sender.hasPermission(command.getPermission()) && sender instanceof Player) {
sender.sendMessage(plugin.getLangYml().getNoPermission());
return false;
}
return true;
}
/**
* Get the command name.
*
* @return The name.
*/
public String getName() {
return this.name;
}
/**
* Get the permission required to execute the command.
*
* @return The permission.
*/
public String getPermission() {
return this.permission;
}
/**
* Get if the command can only be executed by players.
*
* @return If players only.
*/
public boolean isPlayersOnly() {
return this.playersOnly;
}
/**
* Get the subcommands of the command.
*
* @return The subcommands.
*/
public List<CommandBase> getSubcommands() {
return this.subcommands;
}
@Deprecated
@Override
public @Nullable CommandHandler getHandler() {
return this.handler;
}
@Deprecated
@Override
public @Nullable TabCompleteHandler getTabCompleter() {
return this.tabCompleter;
}
@Deprecated
@Override
public void setHandler(@Nullable final CommandHandler handler) {
this.handler = handler;
}
@Deprecated
@Override
public void setTabCompleter(@Nullable final TabCompleteHandler tabCompleter) {
this.tabCompleter = tabCompleter;
}
}

View File

@@ -0,0 +1,96 @@
package com.willfp.eco.core.command.impl;
import com.willfp.eco.core.EcoPlugin;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* PluginCommands are the class to be used instead of CommandExecutor,
* they function as the base command, e.g. {@code /ecoenchants} would be a base command, with each
* subsequent argument functioning as subcommands.
* <p>
* The command will not be registered until register() is called.
* <p>
* The name cannot be the same as an existing command as this will conflict.
*/
public abstract class PluginCommand extends HandledCommand implements CommandExecutor, TabCompleter {
/**
* Create a new command.
*
* @param plugin The plugin.
* @param name The name used in execution.
* @param permission The permission required to execute the command.
* @param playersOnly If only players should be able to execute this command.
*/
protected PluginCommand(@NotNull final EcoPlugin plugin,
@NotNull final String name,
@NotNull final String permission,
final boolean playersOnly) {
super(plugin, name, permission, playersOnly);
}
/**
* Registers the command with the server,
* <p>
* Requires the command name to exist, defined in plugin.yml.
*/
public final void register() {
org.bukkit.command.PluginCommand command = Bukkit.getPluginCommand(this.getName());
assert command != null;
command.setExecutor(this);
command.setTabCompleter(this);
}
/**
* Internal implementation used to clean up boilerplate.
* Used for parity with {@link CommandExecutor#onCommand(CommandSender, Command, String, String[])}.
*
* @param sender The executor of the command.
* @param command The bukkit command.
* @param label The name of the executed command.
* @param args The arguments of the command (anything after the physical command name)
* @return If the command was processed by the linked {@link EcoPlugin}
*/
@Override
public final boolean onCommand(@NotNull final CommandSender sender,
@NotNull final Command command,
@NotNull final String label,
@NotNull final String[] args) {
if (!command.getName().equalsIgnoreCase(this.getName())) {
return false;
}
this.handle(sender, args);
return true;
}
/**
* Internal implementation used to clean up boilerplate.
* Used for parity with {@link TabCompleter#onTabComplete(CommandSender, Command, String, String[])}.
*
* @param sender The executor of the command.
* @param command The bukkit command.
* @param label The name of the executed command.
* @param args The arguments of the command (anything after the physical command name).
* @return The list of tab-completions.
*/
@Override
public @Nullable List<String> onTabComplete(@NotNull final CommandSender sender,
@NotNull final Command command,
@NotNull final String label,
@NotNull final String[] args) {
if (!command.getName().equalsIgnoreCase(this.getName())) {
return null;
}
return this.handleTabCompletion(sender, args);
}
}

View File

@@ -0,0 +1,38 @@
package com.willfp.eco.core.command.impl;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.command.CommandBase;
import org.jetbrains.annotations.NotNull;
/**
* Subcommands can be added to PluginCommands or to other Subcommands.
*/
public abstract class Subcommand extends HandledCommand {
/**
* Create subcommand.
*
* @param plugin The plugin.
* @param name The subcommand name.
* @param permission The subcommand permission.
* @param playersOnly If the subcommand only works on players.
*/
protected Subcommand(@NotNull final EcoPlugin plugin,
@NotNull final String name,
@NotNull final String permission,
final boolean playersOnly) {
super(plugin, name, permission, playersOnly);
}
/**
* Create subcommand.
*
* @param plugin The plugin.
* @param name The name of the subcommand.
* @param parent The parent command.
*/
protected Subcommand(@NotNull final EcoPlugin plugin,
@NotNull final String name,
@NotNull final CommandBase parent) {
super(plugin, name, parent.getPermission(), parent.isPlayersOnly());
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.core.config;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.wrapper.LoadableConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Config implementation for configs present in the plugin's base directory (eg config.yml, lang.yml).
* <p>
* Automatically updates.
*/
public abstract class BaseConfig extends LoadableConfigWrapper {
/**
* Create new Base Config.
*
* @param plugin The plugin or extension.
* @param configName The config name (excluding extension).
* @param removeUnused If unused sections should be removed.
* @param type The config type.
*/
protected BaseConfig(@NotNull final String configName,
@NotNull final PluginLike plugin,
final boolean removeUnused,
@NotNull final ConfigType type) {
super(Eco.getHandler().getConfigFactory().createUpdatableConfig(
configName,
plugin,
"",
plugin.getClass(),
removeUnused,
type
));
}
}

View File

@@ -0,0 +1,16 @@
package com.willfp.eco.core.config;
/**
* Config types, classified by file extension.
*/
public enum ConfigType {
/**
* .json config.
*/
JSON,
/**
* .yml config.
*/
YAML
}

View File

@@ -0,0 +1,44 @@
package com.willfp.eco.core.config;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.wrapper.LoadableConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Config implementation for configs present in one of two places:
* <ul>
* <li>Plugin base directory (eg config.yml, lang.json)</li>
* <li>Other extension's configs</li>
* </ul>
* <p>
* Automatically updates.
*/
public abstract class ExtendableConfig extends LoadableConfigWrapper {
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @param subDirectoryPath The subdirectory path.
* @param type The config type.
* @param source The class that owns the resource.
*/
protected ExtendableConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final PluginLike plugin,
@NotNull final Class<?> source,
@NotNull final String subDirectoryPath,
@NotNull final ConfigType type,
@NotNull final String... updateBlacklist) {
super(Eco.getHandler().getConfigFactory().createUpdatableConfig(
configName,
plugin,
subDirectoryPath,
source,
removeUnused,
type,
updateBlacklist
));
}
}

View File

@@ -0,0 +1,32 @@
package com.willfp.eco.core.config;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.wrapper.LoadableConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Non-updatable yaml config that exists within a plugin jar.
*/
public abstract class StaticBaseConfig extends LoadableConfigWrapper {
/**
* Config implementation for configs present in the plugin's base directory (eg config.yml, lang.yml).
* <p>
* Does not automatically update.
*
* @param configName The name of the config
* @param plugin The plugin.
* @param type The config type.
*/
protected StaticBaseConfig(@NotNull final String configName,
@NotNull final PluginLike plugin,
@NotNull final ConfigType type) {
super(Eco.getHandler().getConfigFactory().createLoadableConfig(
configName,
plugin,
"",
plugin.getClass(),
type
));
}
}

View File

@@ -0,0 +1,48 @@
package com.willfp.eco.core.config;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.wrapper.ConfigWrapper;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
/**
* Config that exists purely in the code, not linked to any file.
* <p>
* Use for inline configs to move data around or to add subsections to other configs.
*/
public class TransientConfig extends ConfigWrapper<Config> {
/**
* @param config The YamlConfiguration handle.
*/
public TransientConfig(@NotNull final YamlConfiguration config) {
super(Eco.getHandler().getConfigFactory().createConfig(config));
}
/**
* Create a new empty transient config.
*
* @param values The values.
*/
public TransientConfig(@NotNull final Map<String, Object> values) {
super(Eco.getHandler().getConfigFactory().createConfig(values));
}
/**
* Create a new empty transient config.
*/
public TransientConfig() {
super(Eco.getHandler().getConfigFactory().createConfig("", ConfigType.YAML));
}
/**
* @param contents The contents of the config.
* @param type The config type.
*/
public TransientConfig(@NotNull final String contents,
@NotNull final ConfigType type) {
super(Eco.getHandler().getConfigFactory().createConfig(contents, type));
}
}

View File

@@ -0,0 +1,55 @@
package com.willfp.eco.core.config.base;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.config.BaseConfig;
import com.willfp.eco.core.config.ConfigType;
import org.jetbrains.annotations.NotNull;
/**
* Default plugin config.yml.
*/
public class ConfigYml extends BaseConfig {
/**
* Config.yml.
*
* @param plugin The plugin.
*/
public ConfigYml(@NotNull final EcoPlugin plugin) {
super("config", plugin, true, ConfigType.YAML);
}
/**
* Config.yml.
*
* @param plugin The plugin.
* @param removeUnused Remove unused.
*/
public ConfigYml(@NotNull final EcoPlugin plugin,
final boolean removeUnused) {
super("config", plugin, removeUnused, ConfigType.YAML);
}
/**
* Config.yml.
*
* @param plugin The plugin.
* @param name The config name.
*/
public ConfigYml(@NotNull final EcoPlugin plugin,
@NotNull final String name) {
super(name, plugin, true, ConfigType.YAML);
}
/**
* Config.yml.
*
* @param plugin The plugin.
* @param name The config name.
* @param removeUnused Remove unused.
*/
public ConfigYml(@NotNull final EcoPlugin plugin,
@NotNull final String name,
final boolean removeUnused) {
super(name, plugin, removeUnused, ConfigType.YAML);
}
}

View File

@@ -0,0 +1,61 @@
package com.willfp.eco.core.config.base;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.config.BaseConfig;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.util.StringUtils;
import org.jetbrains.annotations.NotNull;
/**
* Default plugin lang.yml.
*/
public class LangYml extends BaseConfig {
/**
* Lang.yml.
*
* @param plugin The plugin.
*/
public LangYml(@NotNull final EcoPlugin plugin) {
super("lang", plugin, false, ConfigType.YAML);
}
/**
* Get the prefix for messages in chat.
*
* @return The prefix.
*/
public String getPrefix() {
return this.getFormattedString("messages.prefix");
}
/**
* Get the no permission message.
*
* @return The message.
*/
public String getNoPermission() {
return getPrefix() + this.getFormattedString("messages.no-permission");
}
/**
* Get a chat message.
*
* @param message The key of the message.
* @return The message with a prefix appended.
*/
public String getMessage(@NotNull final String message) {
return getMessage(message, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a chat message.
*
* @param message The key of the message.
* @param option The format options.
* @return The message with a prefix appended.
*/
public String getMessage(@NotNull final String message,
@NotNull final StringUtils.FormatOption option) {
return getPrefix() + this.getFormattedString("messages." + message, option);
}
}

View File

@@ -0,0 +1,632 @@
package com.willfp.eco.core.config.interfaces;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.TransientConfig;
import com.willfp.eco.util.NumberUtils;
import com.willfp.eco.util.StringUtils;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* All configs implement this interface.
* <p>
* Contains all methods that must exist in yaml and json configurations.
*/
@SuppressWarnings("unused")
public interface Config extends Cloneable {
/**
* Clears cache.
*/
void clearCache();
/**
* Convert the config into readable text.
*
* @return The plaintext.
*/
String toPlaintext();
/**
* Get if the config contains a key.
*
* @param path The key to check.
* @return If contained.
*/
boolean has(@NotNull String path);
/**
* Get config keys.
*
* @param deep If keys from subsections should be fetched too.
* @return A list of keys.
*/
@NotNull
List<String> getKeys(boolean deep);
/**
* Get an object from config.
* Default implementations call {@link org.bukkit.configuration.file.YamlConfiguration#get(String)}.
*
* @param path The path.
* @return The object.
*/
@Nullable
Object get(@NotNull String path);
/**
* Set an object in config.
* Default implementations call {@link org.bukkit.configuration.file.YamlConfiguration#set(String, Object)}
*
* @param path The path.
* @param object The object.
*/
void set(@NotNull String path,
@Nullable Object object);
/**
* Get subsection from config.
*
* @param path The key to check.
* @return The subsection. Returns an empty section if not found.
*/
@NotNull
default Config getSubsection(@NotNull String path) {
return Objects.requireNonNullElse(getSubsectionOrNull(path), new TransientConfig());
}
/**
* Get subsection from config.
*
* @param path The key to check.
* @return The subsection, or null if not found.
*/
@Nullable
Config getSubsectionOrNull(@NotNull String path);
/**
* Get an integer from config.
*
* @param path The key to fetch the value from.
* @return The found value, or 0 if not found.
*/
default int getInt(@NotNull String path) {
return Objects.requireNonNullElse(getIntOrNull(path), 0);
}
/**
* Get an integer from config with a specified default (not found) value.
*
* @param path The key to fetch the value from.
* @param def The value to default to if not found.
* @return The found value, or the default.
*/
default int getInt(@NotNull String path,
int def) {
return Objects.requireNonNullElse(getIntOrNull(path), def);
}
/**
* Get a decimal value via a mathematical expression.
*
* @param path The key to fetch the value from.
* @return The computed value, or 0 if not found or invalid.
*/
default int getIntFromExpression(@NotNull String path) {
return getIntFromExpression(path, null);
}
/**
* Get a decimal value via a mathematical expression.
*
* @param path The key to fetch the value from.
* @param player The player to evaluate placeholders with respect to.
* @return The computed value, or 0 if not found or invalid.
*/
default int getIntFromExpression(@NotNull String path,
@Nullable Player player) {
return Double.valueOf(getDoubleFromExpression(path, player)).intValue();
}
/**
* Get an integer from config.
*
* @param path The key to fetch the value from.
* @return The found value, or null if not found.
*/
@Nullable
Integer getIntOrNull(@NotNull String path);
/**
* Get a list of integers from config.
*
* @param path The key to fetch the value from.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
*/
@NotNull
default List<Integer> getInts(@NotNull String path) {
return Objects.requireNonNullElse(getIntsOrNull(path), new ArrayList<>());
}
/**
* Get a list of integers from config.
*
* @param path The key to fetch the value from.
* @return The found value, or null if not found.
*/
@Nullable
List<Integer> getIntsOrNull(@NotNull String path);
/**
* Get a boolean from config.
*
* @param path The key to fetch the value from.
* @return The found value, or false if not found.
*/
default boolean getBool(@NotNull String path) {
return Objects.requireNonNullElse(getBoolOrNull(path), false);
}
/**
* Get a boolean from config.
*
* @param path The key to fetch the value from.
* @return The found value, or null if not found.
*/
@Nullable
Boolean getBoolOrNull(@NotNull String path);
/**
* Get a list of booleans from config.
*
* @param path The key to fetch the value from.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
*/
@NotNull
default List<Boolean> getBools(@NotNull String path) {
return Objects.requireNonNullElse(getBoolsOrNull(path), new ArrayList<>());
}
/**
* Get a list of booleans from config.
*
* @param path The key to fetch the value from.
* @return The found value, or null if not found.
*/
@Nullable
List<Boolean> getBoolsOrNull(@NotNull String path);
/**
* Get a formatted string from config.
*
* @param path The key to fetch the value from.
* @return The found value, or an empty string if not found.
*/
@NotNull
default String getFormattedString(@NotNull String path) {
return getString(path, true, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a formatted string from config.
*
* @param path The key to fetch the value from.
* @param option The format option.
* @return The found value, or an empty string if not found.
*/
@NotNull
default String getFormattedString(@NotNull String path,
@NotNull StringUtils.FormatOption option) {
return getString(path, true, option);
}
/**
* Get a string from config.
* <p>
* Not formatted.
*
* @param path The key to fetch the value from.
* @return The found value, or an empty string if not found.
*/
@NotNull
default String getString(@NotNull String path) {
return getString(path, false);
}
/**
* Get a string from config.
*
* @param path The key to fetch the value from.
* @param format If the string should be formatted.
* @return The found value, or an empty string if not found.
* @deprecated Since 6.18.0, {@link Config#getString(String)} is not formatted by default.
*/
@Deprecated(since = "6.18.0")
default String getString(@NotNull String path,
boolean format) {
return this.getString(path, format, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a string from config.
*
* @param path The key to fetch the value from.
* @param option The format option.
* @return The found value, or an empty string if not found.
* @deprecated Use {@link Config#getFormattedString(String, StringUtils.FormatOption)} instead.
*/
@NotNull
@Deprecated
default String getString(@NotNull String path,
@NotNull final StringUtils.FormatOption option) {
return this.getString(path, true, option);
}
/**
* Get a string from config.
*
* @param path The key to fetch the value from.
* @param format If the string should be formatted.
* @param option The format option.
* @return The found value, or an empty string if not found.
*/
@NotNull
default String getString(@NotNull String path,
boolean format,
@NotNull StringUtils.FormatOption option) {
return Objects.requireNonNullElse(getStringOrNull(path, format, option), "");
}
/**
* Get a formatted string from config.
*
* @param path The key to fetch the value from.
* @return The found value, or an empty string if not found.
*/
@Nullable
default String getFormattedStringOrNull(@NotNull String path) {
return getStringOrNull(path, true, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a formatted string from config.
*
* @param path The key to fetch the value from.
* @param option The format option.
* @return The found value, or an empty string if not found.
*/
@Nullable
default String getFormattedStringOrNull(@NotNull String path,
@NotNull StringUtils.FormatOption option) {
return getStringOrNull(path, true, option);
}
/**
* Get a string from config.
* <p>
* Formatted by default.
*
* @param path The key to fetch the value from.
* @return The found value, or null if not found.
*/
@Nullable
default String getStringOrNull(@NotNull String path) {
return getStringOrNull(path, false, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a string from config.
*
* @param path The key to fetch the value from.
* @param format If the string should be formatted.
* @return The found value, or null if not found.
* @deprecated Since 6.18.0, {@link Config#getString(String)} is not formatted by default.
*/
@Nullable
@Deprecated(since = "6.18.0")
default String getStringOrNull(@NotNull String path,
boolean format) {
return this.getStringOrNull(path, format, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a string from config.
*
* @param path The key to fetch the value from.
* @param option The format option.
* @return The found value, or null if not found.
* @deprecated Use {@link Config#getFormattedString(String, StringUtils.FormatOption)} instead.
*/
@Nullable
@Deprecated
default String getStringOrNull(@NotNull String path,
@NotNull StringUtils.FormatOption option) {
return this.getStringOrNull(path, true, option);
}
/**
* Get a string from config.
*
* @param path The key to fetch the value from.
* @param format If the string should be formatted.
* @param option The format option. If format is false, this will be ignored.
* @return The found value, or null if not found.
*/
@Nullable
String getStringOrNull(@NotNull String path,
boolean format,
@NotNull StringUtils.FormatOption option);
/**
* Get a list of strings from config.
* <p>
* Formatted by default.
*
* @param path The key to fetch the value from.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
*/
@NotNull
default List<String> getFormattedStrings(@NotNull String path) {
return getStrings(path, true, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a list of strings from config.
* <p>
* Formatted by default.
*
* @param path The key to fetch the value from.
* @param option The format option.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
*/
@NotNull
default List<String> getFormattedStrings(@NotNull String path,
@NotNull StringUtils.FormatOption option) {
return getStrings(path, true, option);
}
/**
* Get a list of strings from config.
* <p>
* Not formatted.
*
* @param path The key to fetch the value from.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
*/
@NotNull
default List<String> getStrings(@NotNull String path) {
return getStrings(path, false, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a list of strings from config.
*
* @param path The key to fetch the value from.
* @param format If the strings should be formatted.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
* @deprecated Since 6.18.0, {@link Config#getString(String)} is not formatted by default.
*/
@NotNull
@Deprecated(since = "6.18.0")
default List<String> getStrings(@NotNull String path,
boolean format) {
return this.getStrings(path, format, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a list of strings from config.
*
* @param path The key to fetch the value from.
* @param option The format option.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
* @deprecated Use {@link Config#getFormattedStrings(String, StringUtils.FormatOption)} instead.
*/
@NotNull
@Deprecated
default List<String> getStrings(@NotNull String path,
@NotNull StringUtils.FormatOption option) {
return getStrings(path, false, option);
}
/**
* Get a list of strings from config.
*
* @param path The key to fetch the value from.
* @param format If the strings should be formatted.
* @param option The option.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
*/
@NotNull
default List<String> getStrings(@NotNull String path,
boolean format,
@NotNull StringUtils.FormatOption option) {
return Objects.requireNonNullElse(getStringsOrNull(path, format, option), new ArrayList<>());
}
/**
* Get a list of strings from config.
* <p>
* Formatted.
*
* @param path The key to fetch the value from.
* @return The found value, or null if not found.
*/
@Nullable
default List<String> getFormattedStringsOrNull(@NotNull String path) {
return getStringsOrNull(path, true, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a list of strings from config.
* <p>
* Formatted.
*
* @param path The key to fetch the value from.
* @param option The format option.
* @return The found value, or null if not found.
*/
@Nullable
default List<String> getFormattedStringsOrNull(@NotNull String path,
@NotNull StringUtils.FormatOption option) {
return getStringsOrNull(path, true, option);
}
/**
* Get a list of strings from config.
* <p>
* Not formatted.
* <p>
* This will be changed in newer versions to <b>not</b> format by default.
*
* @param path The key to fetch the value from.
* @return The found value, or null if not found.
*/
@Nullable
default List<String> getStringsOrNull(@NotNull String path) {
return getStringsOrNull(path, false, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a list of strings from config.
*
* @param path The key to fetch the value from.
* @param format If the strings should be formatted.
* @return The found value, or null if not found.
* @deprecated Since 6.18.0, {@link Config#getString(String)} is not formatted by default.
*/
@Nullable
@Deprecated(since = "6.18.0")
default List<String> getStringsOrNull(@NotNull String path,
boolean format) {
return getStringsOrNull(path, format, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a list of strings from config.
*
* @param path The key to fetch the value from.
* @param option The format option.
* @return The found value, or null if not found.
* @deprecated Use {@link Config#getFormattedStringsOrNull(String, StringUtils.FormatOption)} instead.
*/
@Nullable
@Deprecated
default List<String> getStringsOrNull(@NotNull String path,
@NotNull StringUtils.FormatOption option) {
return getStringsOrNull(path, false, option);
}
/**
* Get a list of strings from config.
*
* @param path The key to fetch the value from.
* @param format If the strings should be formatted.
* @param option The format option.
* @return The found value, or null if not found.
*/
@Nullable
List<String> getStringsOrNull(@NotNull String path,
boolean format,
@NotNull StringUtils.FormatOption option);
/**
* Get a decimal from config.
*
* @param path The key to fetch the value from.
* @return The found value, or 0 if not found.
*/
default double getDouble(@NotNull String path) {
return Objects.requireNonNullElse(getDoubleOrNull(path), 0.0);
}
/**
* Get a decimal value via a mathematical expression.
*
* @param path The key to fetch the value from.
* @return The computed value, or 0 if not found or invalid.
*/
default double getDoubleFromExpression(@NotNull String path) {
return getDoubleFromExpression(path, null);
}
/**
* Get a decimal value via a mathematical expression.
*
* @param path The key to fetch the value from.
* @param player The player to evaluate placeholders with respect to.
* @return The computed value, or 0 if not found or invalid.
*/
default double getDoubleFromExpression(@NotNull String path,
@Nullable Player player) {
return NumberUtils.evaluateExpression(this.getString(path), player);
}
/**
* Get a decimal from config.
*
* @param path The key to fetch the value from.
* @return The found value, or null if not found.
*/
@Nullable
Double getDoubleOrNull(@NotNull String path);
/**
* Get a list of decimals from config.
*
* @param path The key to fetch the value from.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
*/
@NotNull
default List<Double> getDoubles(@NotNull String path) {
return Objects.requireNonNullElse(getDoublesOrNull(path), new ArrayList<>());
}
/**
* Get a list of decimals from config.
*
* @param path The key to fetch the value from.
* @return The found value, or null if not found.
*/
@Nullable
List<Double> getDoublesOrNull(@NotNull String path);
/**
* Get a list of subsections from config.
*
* @param path The key to fetch the value from.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
*/
@NotNull
default List<? extends Config> getSubsections(@NotNull String path) {
return Objects.requireNonNullElse(getSubsectionsOrNull(path), new ArrayList<>());
}
/**
* Get a list of subsections from config.
*
* @param path The key to fetch the value from.
* @return The found value, or null if not found.
*/
@Nullable
List<? extends Config> getSubsectionsOrNull(@NotNull String path);
/**
* Get config type.
*
* @return The type.
*/
@NotNull
ConfigType getType();
/**
* Clone the config.
*
* @return The clone.
*/
Config clone();
}

View File

@@ -0,0 +1,63 @@
package com.willfp.eco.core.config.interfaces;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* JSON config.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
@SuppressWarnings("DeprecatedIsStillUsed")
public interface JSONConfig extends Config {
/**
* Get a list of subsections from config.
*
* @param path The key to fetch the value from.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
*/
@NotNull
default List<JSONConfig> getSubsections(@NotNull String path) {
return Objects.requireNonNullElse(getSubsectionsOrNull(path), new ArrayList<>());
}
/**
* Get a list of subsections from config.
*
* @param path The key to fetch the value from.
* @return The found value, or null if not found.
*/
@Nullable
List<JSONConfig> getSubsectionsOrNull(@NotNull String path);
/**
* Get subsection from config.
*
* @param path The key to check.
* @return The subsection. Throws NPE if not found.
*/
@Override
@NotNull
JSONConfig getSubsection(@NotNull String path);
/**
* Get subsection from config.
*
* @param path The key to check.
* @return The subsection, or null if not found.
*/
@Override
@Nullable
JSONConfig getSubsectionOrNull(@NotNull String path);
@Override
JSONConfig clone();
}

View File

@@ -0,0 +1,60 @@
package com.willfp.eco.core.config.interfaces;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
/**
* Interface for configs that physically exist as files in plugins.
*/
public interface LoadableConfig extends Config {
/**
* Create the file.
*/
void createFile();
/**
* Get resource path as relative to base directory.
*
* @return The resource path.
*/
String getResourcePath();
/**
* Save the config.
*
* @throws IOException If error in saving.
*/
void save() throws IOException;
/**
* Get the config file.
*
* @return The file.
*/
File getConfigFile();
/**
* Get the config name (including extension).
*
* @return The name.
*/
String getName();
/**
* Get bukkit {@link YamlConfiguration}.
* <p>
* This method is not recommended unless absolutely required as it
* only returns true if the type of config is {@link com.willfp.eco.core.config.ConfigType#YAML},
* and if the handle is an {@link YamlConfiguration} specifically. This depends on the internals
* and the implementation, and so may cause problems - it exists mostly for parity with
* {@link JavaPlugin#getConfig()}.
*
* @return The config, or null if config is not yaml-based.
*/
@Nullable
YamlConfiguration getBukkitHandle();
}

View File

@@ -0,0 +1,21 @@
package com.willfp.eco.core.config.interfaces;
import org.bukkit.configuration.file.YamlConfiguration;
/**
* Interface for configs that wrap an {@link YamlConfiguration}.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
@SuppressWarnings("DeprecatedIsStillUsed")
public interface WrappedYamlConfiguration {
/**
* Get the ConfigurationSection handle.
*
* @return The handle.
*/
YamlConfiguration getBukkitHandle();
}

View File

@@ -0,0 +1,90 @@
package com.willfp.eco.core.config.json;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.interfaces.JSONConfig;
import com.willfp.eco.core.config.json.wrapper.LoadableJSONConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Config implementation for configs present in the plugin's base directory (eg config.json).
* <p>
* Automatically updates.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class JSONBaseConfig extends LoadableJSONConfigWrapper {
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
*/
protected JSONBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final PluginLike plugin,
@NotNull final String... updateBlacklist) {
super(
(JSONConfig)
Eco.getHandler().getConfigFactory().createUpdatableConfig(
configName,
plugin,
"",
plugin.getClass(),
removeUnused,
ConfigType.JSON,
updateBlacklist
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
*/
protected JSONBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final PluginLike plugin) {
super(
(JSONConfig)
Eco.getHandler().getConfigFactory().createUpdatableConfig(
configName,
plugin,
"",
plugin.getClass(),
removeUnused,
ConfigType.JSON
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
*/
protected JSONBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final String... updateBlacklist) {
this(configName, removeUnused, (PluginLike) plugin, updateBlacklist);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
*/
protected JSONBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin) {
this(configName, removeUnused, (PluginLike) plugin);
}
}

View File

@@ -0,0 +1,70 @@
package com.willfp.eco.core.config.json;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.interfaces.JSONConfig;
import com.willfp.eco.core.config.json.wrapper.LoadableJSONConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Config implementation for configs present in one of two places:
* <ul>
* <li>Plugin base directory (eg config.yml, lang.yml)</li>
* <li>Other extension's configs</li>
* </ul>
* <p>
* Automatically updates.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class JSONExtendableConfig extends LoadableJSONConfigWrapper {
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
*/
protected JSONExtendableConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final PluginLike plugin,
@NotNull final Class<?> source,
@NotNull final String subDirectoryPath,
@NotNull final String... updateBlacklist) {
super(
(JSONConfig)
Eco.getHandler().getConfigFactory().createUpdatableConfig(
configName,
plugin,
subDirectoryPath,
source,
removeUnused,
ConfigType.JSON,
updateBlacklist
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
*/
protected JSONExtendableConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final Class<?> source,
@NotNull final String subDirectoryPath,
@NotNull final String... updateBlacklist) {
this(configName, removeUnused, (PluginLike) plugin, source, subDirectoryPath, updateBlacklist);
}
}

View File

@@ -0,0 +1,45 @@
package com.willfp.eco.core.config.json;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.interfaces.JSONConfig;
import com.willfp.eco.core.config.json.wrapper.LoadableJSONConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Non-updatable JSON config that exists within a plugin jar.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class JSONStaticBaseConfig extends LoadableJSONConfigWrapper {
/**
* Config implementation for configs present in the plugin's base directory (eg config.json, lang.json).
* <p>
* Does not automatically update.
*
* @param configName The name of the config
* @param plugin The plugin.
*/
protected JSONStaticBaseConfig(@NotNull final String configName,
@NotNull final PluginLike plugin) {
super((JSONConfig) Eco.getHandler().getConfigFactory().createLoadableConfig(configName, plugin, "", plugin.getClass(), ConfigType.JSON));
}
/**
* Config implementation for configs present in the plugin's base directory (eg config.json, lang.json).
* <p>
* Does not automatically update.
*
* @param configName The name of the config
* @param plugin The plugin.
*/
protected JSONStaticBaseConfig(@NotNull final String configName,
@NotNull final EcoPlugin plugin) {
this(configName, (PluginLike) plugin);
}
}

View File

@@ -0,0 +1,37 @@
package com.willfp.eco.core.config.json;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.config.interfaces.JSONConfig;
import com.willfp.eco.core.config.json.wrapper.JSONConfigWrapper;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
/**
* Raw JSON config with a map of values at its core.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public class JSONTransientConfig extends JSONConfigWrapper {
/**
* Config implementation for passing maps.
* <p>
* Does not automatically update.
*
* @param values The map of values.
*/
public JSONTransientConfig(@NotNull final Map<String, Object> values) {
super((JSONConfig) Eco.getHandler().getConfigFactory().createConfig(values));
}
/**
* Empty JSON config.
*/
public JSONTransientConfig() {
super((JSONConfig) Eco.getHandler().getConfigFactory().createConfig(new HashMap<>()));
}
}

View File

@@ -0,0 +1,53 @@
package com.willfp.eco.core.config.json.wrapper;
import com.willfp.eco.core.config.interfaces.JSONConfig;
import com.willfp.eco.core.config.wrapper.ConfigWrapper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* Wrapper to handle the backend JSON config implementations.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class JSONConfigWrapper extends ConfigWrapper<JSONConfig> implements JSONConfig {
/**
* Create a config wrapper.
*
* @param handle The handle.
*/
protected JSONConfigWrapper(@NotNull final JSONConfig handle) {
super(handle);
}
@Override
public @NotNull List<JSONConfig> getSubsections(@NotNull final String path) {
return this.getHandle().getSubsections(path);
}
@Override
public @Nullable List<JSONConfig> getSubsectionsOrNull(@NotNull final String path) {
return this.getHandle().getSubsectionsOrNull(path);
}
@Override
public @NotNull JSONConfig getSubsection(@NotNull final String path) {
return this.getHandle().getSubsection(path);
}
@Override
public @Nullable JSONConfig getSubsectionOrNull(@NotNull final String path) {
return this.getHandle().getSubsectionOrNull(path);
}
@Override
public JSONConfig clone() {
return this.getHandle().clone();
}
}

View File

@@ -0,0 +1,63 @@
package com.willfp.eco.core.config.json.wrapper;
import com.willfp.eco.core.config.interfaces.JSONConfig;
import com.willfp.eco.core.config.interfaces.LoadableConfig;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
/**
* Wrapper to handle the backend loadable JSON config implementations.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class LoadableJSONConfigWrapper extends JSONConfigWrapper implements LoadableConfig {
/**
* Create a config wrapper.
*
* @param handle The handle.
*/
protected LoadableJSONConfigWrapper(@NotNull final JSONConfig handle) {
super(handle);
Validate.isTrue(handle instanceof LoadableConfig, "Wrapped config must be loadable!");
}
@Override
public void createFile() {
((LoadableConfig) this.getHandle()).createFile();
}
@Override
public String getResourcePath() {
return ((LoadableConfig) this.getHandle()).getResourcePath();
}
@Override
public void save() throws IOException {
((LoadableConfig) this.getHandle()).save();
}
@Override
public File getConfigFile() {
return ((LoadableConfig) this.getHandle()).getConfigFile();
}
@Override
public String getName() {
return ((LoadableConfig) this.getHandle()).getName();
}
@Override
public @Nullable YamlConfiguration getBukkitHandle() {
return null;
}
}

View File

@@ -0,0 +1,33 @@
package com.willfp.eco.core.config.updating;
import com.willfp.eco.core.config.interfaces.LoadableConfig;
import org.jetbrains.annotations.NotNull;
/**
* Every {@link com.willfp.eco.core.PluginLike} has a config handler.
* <p>
* Handles updating and saving configs.
*/
public interface ConfigHandler {
/**
* Invoke all update methods.
*/
void callUpdate();
/**
* Save all configs.
*/
void saveAllConfigs();
/**
* Update all updatable configs.
*/
void updateConfigs();
/**
* Add new config to be saved.
*
* @param config The config.
*/
void addConfig(@NotNull LoadableConfig config);
}

View File

@@ -0,0 +1,43 @@
package com.willfp.eco.core.config.updating;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to put on update methods.
* <p>
* All update methods must be public and static,
* and can accept an EcoPlugin as a parameter.
* <p>
* As such, there are only 2 valid update methods:
* <p>
* The first:
* <pre>{@code
* @ConfigUpdater
* public static void update() {
* // Update code
* }
* }</pre>
* <p>
* The second:
* <pre>{@code
* public static void update(EcoPlugin plugin) {
* // Update code
* }
* }</pre>
* <p>
* If using kotlin, you have to annotate the method with {@code @JvmStatic}
* in order to prevent null pointer exceptions.
* <p>
* Config update methods in all classes in a plugin jar will be called
* on reload.
* <p>
* By having a plugin as a parameter, you shouldn't really need getInstance()
* calls in your code.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ConfigUpdater {
}

View File

@@ -0,0 +1,81 @@
package com.willfp.eco.core.config.wrapper;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.interfaces.LoadableConfig;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
/**
* Internal component to create backend config implementations.
*/
@ApiStatus.Internal
@Eco.HandlerComponent
public interface ConfigFactory {
/**
* Updatable config.
*
* @param configName The name of the config
* @param plugin The plugin.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param type The config type.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @return The config implementation.
*/
LoadableConfig createUpdatableConfig(@NotNull String configName,
@NotNull PluginLike plugin,
@NotNull String subDirectoryPath,
@NotNull Class<?> source,
boolean removeUnused,
@NotNull ConfigType type,
@NotNull String... updateBlacklist);
/**
* Loadable config.
*
* @param configName The name of the config
* @param plugin The plugin.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
* @param type The config type.
* @return The config implementation.
*/
LoadableConfig createLoadableConfig(@NotNull String configName,
@NotNull PluginLike plugin,
@NotNull String subDirectoryPath,
@NotNull Class<?> source,
@NotNull ConfigType type);
/**
* Create config.
*
* @param config The handle.
* @return The config implementation.
*/
Config createConfig(@NotNull YamlConfiguration config);
/**
* Create config.
*
* @param values The values.
* @return The config implementation.
*/
Config createConfig(@NotNull Map<String, Object> values);
/**
* Create config.
*
* @param contents The file contents.
* @param type The type.
* @return The config implementation.
*/
Config createConfig(@NotNull String contents,
@NotNull ConfigType type);
}

View File

@@ -0,0 +1,145 @@
package com.willfp.eco.core.config.wrapper;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.util.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* Configs from eco have an internal implementation,
* which is the handle.
* <p>
* This class handles them.
*
* @param <T> The type of the handle.
*/
@SuppressWarnings("MethodDoesntCallSuperMethod")
public abstract class ConfigWrapper<T extends Config> implements Config {
/**
* Configs from eco have an internal implementation,
* which is the handle.
* <p>
* The handle should only ever be used if you want to
* do something <i>interesting</i> config-wise with some
* internals.
* <p>
* In general use, though, the handle isn't necessary.
*/
private final T handle;
/**
* Create a config wrapper.
*
* @param handle The config that is being wrapped.
*/
protected ConfigWrapper(@NotNull final T handle) {
this.handle = handle;
}
@Override
public void clearCache() {
handle.clearCache();
}
@Override
public String toPlaintext() {
return handle.toPlaintext();
}
@Override
public boolean has(@NotNull final String path) {
return handle.has(path);
}
@Override
public @NotNull List<String> getKeys(final boolean deep) {
return handle.getKeys(deep);
}
@Override
public @Nullable Object get(@NotNull final String path) {
return handle.get(path);
}
@Override
public void set(@NotNull final String path,
@Nullable final Object object) {
handle.set(path, object);
}
@Override
public @Nullable Config getSubsectionOrNull(@NotNull final String path) {
return handle.getSubsectionOrNull(path);
}
@Override
public @Nullable Integer getIntOrNull(@NotNull final String path) {
return handle.getIntOrNull(path);
}
@Override
public @Nullable List<Integer> getIntsOrNull(@NotNull final String path) {
return handle.getIntsOrNull(path);
}
@Override
public @Nullable Boolean getBoolOrNull(@NotNull final String path) {
return handle.getBoolOrNull(path);
}
@Override
public @Nullable List<Boolean> getBoolsOrNull(@NotNull final String path) {
return handle.getBoolsOrNull(path);
}
@Override
public @Nullable String getStringOrNull(@NotNull final String path,
final boolean format,
@NotNull final StringUtils.FormatOption option) {
return handle.getStringOrNull(path, format, option);
}
@Override
public @Nullable List<String> getStringsOrNull(@NotNull final String path,
final boolean format,
@NotNull final StringUtils.FormatOption option) {
return handle.getStringsOrNull(path, format, option);
}
@Override
public @Nullable Double getDoubleOrNull(@NotNull final String path) {
return handle.getDoubleOrNull(path);
}
@Override
public @Nullable List<Double> getDoublesOrNull(@NotNull final String path) {
return handle.getDoublesOrNull(path);
}
@Override
public @Nullable List<? extends Config> getSubsectionsOrNull(@NotNull final String path) {
return handle.getSubsectionsOrNull(path);
}
@Override
public Config clone() {
return handle.clone();
}
@Override
public @NotNull ConfigType getType() {
return handle.getType();
}
/**
* Get the handle.
*
* @return The handle.
*/
public T getHandle() {
return this.handle;
}
}

View File

@@ -0,0 +1,53 @@
package com.willfp.eco.core.config.wrapper;
import com.willfp.eco.core.config.interfaces.LoadableConfig;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
/**
* Wrapper to handle the backend loadable yaml config implementations.
*/
public abstract class LoadableConfigWrapper extends ConfigWrapper<LoadableConfig> implements LoadableConfig {
/**
* Create a config wrapper.
*
* @param handle The handle.
*/
protected LoadableConfigWrapper(@NotNull final LoadableConfig handle) {
super(handle);
}
@Override
public void createFile() {
this.getHandle().createFile();
}
@Override
public String getResourcePath() {
return this.getHandle().getResourcePath();
}
@Override
public void save() throws IOException {
this.getHandle().save();
}
@Override
public File getConfigFile() {
return this.getHandle().getConfigFile();
}
@Override
public String getName() {
return this.getHandle().getName();
}
@Override
public @Nullable YamlConfiguration getBukkitHandle() {
return this.getHandle().getBukkitHandle();
}
}

View File

@@ -0,0 +1,87 @@
package com.willfp.eco.core.config.yaml;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.yaml.wrapper.LoadableYamlConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Config implementation for configs present in the plugin's base directory (eg config.yml, lang.yml).
* <p>
* Automatically updates.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class YamlBaseConfig extends LoadableYamlConfigWrapper {
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
*/
protected YamlBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final PluginLike plugin,
@NotNull final String... updateBlacklist) {
super(
Eco.getHandler().getConfigFactory().createUpdatableConfig(
configName,
plugin,
"",
plugin.getClass(),
removeUnused,
ConfigType.YAML,
updateBlacklist
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
*/
protected YamlBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final PluginLike plugin) {
super(
Eco.getHandler().getConfigFactory().createUpdatableConfig(
configName,
plugin,
"",
plugin.getClass(),
removeUnused,
ConfigType.YAML
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
*/
protected YamlBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final String... updateBlacklist) {
this(configName, removeUnused, (PluginLike) plugin, updateBlacklist);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
*/
protected YamlBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin) {
this(configName, removeUnused, (PluginLike) plugin);
}
}

View File

@@ -0,0 +1,68 @@
package com.willfp.eco.core.config.yaml;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.yaml.wrapper.LoadableYamlConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Config implementation for configs present in one of two places:
* <ul>
* <li>Plugin base directory (eg config.yml, lang.yml)</li>
* <li>Other extension's configs</li>
* </ul>
* <p>
* Automatically updates.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class YamlExtendableConfig extends LoadableYamlConfigWrapper {
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
*/
protected YamlExtendableConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final PluginLike plugin,
@NotNull final Class<?> source,
@NotNull final String subDirectoryPath,
@NotNull final String... updateBlacklist) {
super(
Eco.getHandler().getConfigFactory().createUpdatableConfig(
configName,
plugin,
subDirectoryPath,
source,
removeUnused,
ConfigType.YAML,
updateBlacklist
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
*/
protected YamlExtendableConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final Class<?> source,
@NotNull final String subDirectoryPath,
@NotNull final String... updateBlacklist) {
this(configName, removeUnused, (PluginLike) plugin, source, subDirectoryPath, updateBlacklist);
}
}

View File

@@ -0,0 +1,44 @@
package com.willfp.eco.core.config.yaml;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.yaml.wrapper.LoadableYamlConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Non-updatable yaml config that exists within a plugin jar.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class YamlStaticBaseConfig extends LoadableYamlConfigWrapper {
/**
* Config implementation for configs present in the plugin's base directory (eg config.yml, lang.yml).
* <p>
* Does not automatically update.
*
* @param configName The name of the config
* @param plugin The plugin.
*/
protected YamlStaticBaseConfig(@NotNull final String configName,
@NotNull final PluginLike plugin) {
super(Eco.getHandler().getConfigFactory().createLoadableConfig(configName, plugin, "", plugin.getClass(), ConfigType.YAML));
}
/**
* Config implementation for configs present in the plugin's base directory (eg config.yml, lang.yml).
* <p>
* Does not automatically update.
*
* @param configName The name of the config
* @param plugin The plugin.
*/
protected YamlStaticBaseConfig(@NotNull final String configName,
@NotNull final EcoPlugin plugin) {
this(configName, (PluginLike) plugin);
}
}

View File

@@ -0,0 +1,42 @@
package com.willfp.eco.core.config.yaml;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.yaml.wrapper.YamlConfigWrapper;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import java.io.StringReader;
/**
* Config implementation for passing YamlConfigurations.
* <p>
* Does not automatically update.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public class YamlTransientConfig extends YamlConfigWrapper {
/**
* @param config The YamlConfiguration handle.
*/
public YamlTransientConfig(@NotNull final YamlConfiguration config) {
super(Eco.getHandler().getConfigFactory().createConfig(config));
}
/**
* @param contents The contents of the config.
*/
public YamlTransientConfig(@NotNull final String contents) {
super(Eco.getHandler().getConfigFactory().createConfig(contents, ConfigType.YAML));
}
/**
* Create a new empty transient config.
*/
public YamlTransientConfig() {
super(Eco.getHandler().getConfigFactory().createConfig(YamlConfiguration.loadConfiguration(new StringReader(""))));
}
}

View File

@@ -0,0 +1,63 @@
package com.willfp.eco.core.config.yaml.wrapper;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.interfaces.LoadableConfig;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
/**
* Wrapper to handle the backend loadable yaml config implementations.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class LoadableYamlConfigWrapper extends YamlConfigWrapper implements LoadableConfig {
/**
* Create a config wrapper.
*
* @param handle The handle.
*/
protected LoadableYamlConfigWrapper(@NotNull final Config handle) {
super(handle);
Validate.isTrue(handle instanceof LoadableConfig, "Wrapped config must be loadable!");
}
@Override
public void createFile() {
((LoadableConfig) this.getHandle()).createFile();
}
@Override
public String getResourcePath() {
return ((LoadableConfig) this.getHandle()).getResourcePath();
}
@Override
public void save() throws IOException {
((LoadableConfig) this.getHandle()).save();
}
@Override
public File getConfigFile() {
return ((LoadableConfig) this.getHandle()).getConfigFile();
}
@Override
public String getName() {
return ((LoadableConfig) this.getHandle()).getName();
}
@Override
public @Nullable YamlConfiguration getBukkitHandle() {
return ((LoadableConfig) this.getHandle()).getBukkitHandle();
}
}

View File

@@ -0,0 +1,31 @@
package com.willfp.eco.core.config.yaml.wrapper;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.interfaces.WrappedYamlConfiguration;
import com.willfp.eco.core.config.wrapper.ConfigWrapper;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
/**
* Wrapper to handle the backend yaml config implementations.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class YamlConfigWrapper extends ConfigWrapper<Config> implements WrappedYamlConfiguration {
/**
* Create a config wrapper.
*
* @param handle The handle.
*/
protected YamlConfigWrapper(@NotNull final Config handle) {
super(handle);
}
@Override
public YamlConfiguration getBukkitHandle() {
return ((WrappedYamlConfiguration) this.getHandle()).getBukkitHandle();
}
}

View File

@@ -0,0 +1,36 @@
package com.willfp.eco.core.data;
import com.willfp.eco.core.Eco;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* Persistent data storage interface for players.
* <p>
* Profiles save automatically, so there is no need to save after changes.
*/
public interface PlayerProfile extends Profile {
/**
* Load a player profile.
*
* @param player The player.
* @return The profile.
*/
@NotNull
static PlayerProfile load(@NotNull final OfflinePlayer player) {
return load(player.getUniqueId());
}
/**
* Load a player profile.
*
* @param uuid The player's UUID.
* @return The profile.
*/
@NotNull
static PlayerProfile load(@NotNull final UUID uuid) {
return Eco.getHandler().getProfileHandler().load(uuid);
}
}

View File

@@ -0,0 +1,30 @@
package com.willfp.eco.core.data;
import com.willfp.eco.core.data.keys.PersistentDataKey;
import org.jetbrains.annotations.NotNull;
/**
* Persistent data storage interface.
* <p>
* Profiles save automatically, so there is no need to save after changes.
*/
public interface Profile {
/**
* Write a key to persistent data.
*
* @param key The key.
* @param value The value.
* @param <T> The type of the key.
*/
<T> void write(@NotNull PersistentDataKey<T> key,
@NotNull T value);
/**
* Read a key from persistent data.
*
* @param key The key.
* @param <T> The type of the key.
* @return The value, or the default value if not found.
*/
<T> @NotNull T read(@NotNull PersistentDataKey<T> key);
}

View File

@@ -0,0 +1,89 @@
package com.willfp.eco.core.data;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.data.keys.PersistentDataKey;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
import java.util.UUID;
/**
* API to handle profiles.
*/
@ApiStatus.Internal
@Eco.HandlerComponent
public interface ProfileHandler {
/**
* Load a player profile.
*
* @param uuid The UUID.
* @return The profile.
*/
PlayerProfile load(@NotNull UUID uuid);
/**
* Load the server profile.
*
* @return The profile.
*/
ServerProfile loadServerProfile();
/**
* Unload a player profile from memory.
* <p>
* This will not save the profile first.
*
* @param uuid The uuid.
*/
void unloadPlayer(@NotNull UUID uuid);
/**
* Save a player profile.
* <p>
* Can run async if using MySQL.
*
* @param uuid The uuid.
* @deprecated Saving changes is faster and should be used. Saving a player manually is not recommended.
*/
@Deprecated
default void savePlayer(@NotNull UUID uuid) {
this.saveKeysFor(uuid, PersistentDataKey.values());
}
/**
* Save keys for a player.
* <p>
* Can run async if using MySQL.
*
* @param uuid The uuid.
* @param keys The keys.
*/
void saveKeysFor(@NotNull UUID uuid,
@NotNull Set<PersistentDataKey<?>> keys);
/**
* Save all player data.
*
* @param async If the saving should be done asynchronously.
* @deprecated async is now handled automatically depending on implementation.
*/
@Deprecated
default void saveAll(boolean async) {
saveAll();
}
/**
* Save all player data.
* <p>
* Can run async if using MySQL.
*/
void saveAll();
/**
* Commit all changes to the file.
* <p>
* Does nothing if using MySQL.
*/
void save();
}

View File

@@ -0,0 +1,21 @@
package com.willfp.eco.core.data;
import com.willfp.eco.core.Eco;
import org.jetbrains.annotations.NotNull;
/**
* Persistent data storage interface for servers.
* <p>
* Profiles save automatically, so there is no need to save after changes.
*/
public interface ServerProfile extends Profile {
/**
* Load the server profile.
*
* @return The profile.
*/
@NotNull
static ServerProfile load() {
return Eco.getHandler().getProfileHandler().loadServerProfile();
}
}

View File

@@ -0,0 +1,24 @@
package com.willfp.eco.core.data.keys;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
/**
* API to register persistent data keys.
*/
public interface KeyRegistry {
/**
* Register a persistent data key to be stored.
*
* @param key The key.
*/
void registerKey(@NotNull PersistentDataKey<?> key);
/**
* Get all registered keys.
*
* @return The keys.
*/
Set<PersistentDataKey<?>> getRegisteredKeys();
}

View File

@@ -0,0 +1,91 @@
package com.willfp.eco.core.data.keys;
import com.willfp.eco.core.Eco;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
/**
* A persistent data key is a key with a type that can be stored about an offline player.
*
* @param <T> The type of the data.
*/
public class PersistentDataKey<T> {
/**
* The key of the persistent data value.
*/
private final NamespacedKey key;
/**
* The default value for the key.
*/
private final T defaultValue;
/**
* The persistent data key type.
*/
private final PersistentDataKeyType<T> type;
/**
* Create a new Persistent Data Key.
*
* @param key The key.
* @param type The data type.
* @param defaultValue The default value.
*/
public PersistentDataKey(@NotNull final NamespacedKey key,
@NotNull final PersistentDataKeyType<T> type,
@NotNull final T defaultValue) {
this.key = key;
this.defaultValue = defaultValue;
this.type = type;
Eco.getHandler().getKeyRegistry().registerKey(this);
}
@Override
public String toString() {
return "PersistentDataKey{"
+ "key=" + key
+ ", defaultValue=" + defaultValue
+ ", type=" + type
+ '}';
}
/**
* Get the key.
*
* @return The key.
*/
public NamespacedKey getKey() {
return this.key;
}
/**
* Get the default value.
*
* @return The default value.
*/
public T getDefaultValue() {
return this.defaultValue;
}
/**
* Get the data key type.
*
* @return The key type.
*/
public PersistentDataKeyType<T> getType() {
return this.type;
}
/**
* Get all persistent data keys.
*
* @return The keys.
*/
public static Set<PersistentDataKey<?>> values() {
return Eco.getHandler().getKeyRegistry().getRegisteredKeys();
}
}

View File

@@ -0,0 +1,108 @@
package com.willfp.eco.core.data.keys;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* All storable data key types.
*
* @param <T> The type.
*/
public final class PersistentDataKeyType<T> {
/**
* The registered key types.
*/
private static final List<PersistentDataKeyType<?>> VALUES = new ArrayList<>();
/**
* String.
*/
public static final PersistentDataKeyType<String> STRING = new PersistentDataKeyType<>(String.class, "STRING");
/**
* Boolean.
*/
public static final PersistentDataKeyType<Boolean> BOOLEAN = new PersistentDataKeyType<>(Boolean.class, "BOOLEAN");
/**
* Int.
*/
public static final PersistentDataKeyType<Integer> INT = new PersistentDataKeyType<>(Integer.class, "INT");
/**
* Double.
*/
public static final PersistentDataKeyType<Double> DOUBLE = new PersistentDataKeyType<>(Double.class, "DOUBLE");
/**
* The class of the type.
*/
private final Class<T> typeClass;
/**
* The name of the key type.
*/
private final String name;
/**
* Get the class of the type.
*
* @return The class.
*/
public Class<T> getTypeClass() {
return typeClass;
}
/**
* Get the name of the key type.
*
* @return The name.
*/
public String name() {
return name;
}
/**
* Create new PersistentDataKeyType.
*
* @param typeClass The type class.
* @param name The name.
*/
private PersistentDataKeyType(@NotNull final Class<T> typeClass,
@NotNull final String name) {
VALUES.add(this);
this.typeClass = typeClass;
this.name = name;
}
/**
* Get all registered {@link PersistentDataKeyType}s.
*
* @return The registered types.
*/
@NotNull
public static PersistentDataKeyType<?>[] values() {
return VALUES.toArray(new PersistentDataKeyType[0]);
}
/**
* Get a key type from a name.
*
* @param name The name.
* @return The type, or null if not found.
*/
@Nullable
public static PersistentDataKeyType<?> valueOf(@NotNull final String name) {
for (PersistentDataKeyType<?> type : VALUES) {
if (type.name.equalsIgnoreCase(name)) {
return type;
}
}
return null;
}
}

View File

@@ -0,0 +1,181 @@
package com.willfp.eco.core.display;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Utility class to manage client-side item display.
*/
public final class Display {
/**
* The prefix for client-side lore lines.
*/
public static final String PREFIX = "§z";
/**
* The display handler.
*/
private static DisplayHandler handler = null;
/**
* Display on ItemStacks.
*
* @param itemStack The item.
* @return The ItemStack.
*/
public static ItemStack display(@NotNull final ItemStack itemStack) {
return display(itemStack, null);
}
/**
* Display on ItemStacks.
*
* @param itemStack The item.
* @param player The player.
* @return The ItemStack.
*/
public static ItemStack display(@NotNull final ItemStack itemStack,
@Nullable final Player player) {
return handler.display(itemStack, player);
}
/**
* Display on ItemStacks and then finalize.
*
* @param itemStack The item.
* @return The ItemStack.
*/
public static ItemStack displayAndFinalize(@NotNull final ItemStack itemStack) {
return finalize(display(itemStack, null));
}
/**
* Display on ItemStacks and then finalize.
*
* @param itemStack The item.
* @param player The player.
* @return The ItemStack.
*/
public static ItemStack displayAndFinalize(@NotNull final ItemStack itemStack,
@Nullable final Player player) {
return finalize(display(itemStack, player));
}
/**
* Revert on ItemStacks.
*
* @param itemStack The item.
* @return The ItemStack.
*/
public static ItemStack revert(@NotNull final ItemStack itemStack) {
return handler.revert(itemStack);
}
/**
* Finalize an ItemStacks.
*
* @param itemStack The item.
* @return The ItemStack.
*/
public static ItemStack finalize(@NotNull final ItemStack itemStack) {
return handler.finalize(itemStack);
}
/**
* Unfinalize an ItemStacks.
*
* @param itemStack The item.
* @return The ItemStack.
*/
public static ItemStack unfinalize(@NotNull final ItemStack itemStack) {
return handler.unfinalize(itemStack);
}
/**
* If an item is finalized.
*
* @param itemStack The item.
* @return If finalized.
*/
public static boolean isFinalized(@NotNull final ItemStack itemStack) {
return handler.isFinalized(itemStack);
}
/**
* Register a new display module.
*
* @param module The module.
*/
public static void registerDisplayModule(@NotNull final DisplayModule module) {
handler.registerDisplayModule(module);
}
/**
* Initialize the display system.
*
* @param handler The handler.
*/
@ApiStatus.Internal
public static void init(@NotNull final DisplayHandler handler) {
if (Display.handler != null) {
throw new IllegalArgumentException("Already Initialized!");
}
Display.handler = handler;
}
/**
* Extremely janky method - also internal, so don't use it. <b>This method is
* NOT part of the API and may be removed at any time!</b>
* <p>
* This calls a display module with the specified parameters, now
* you might ask why I need a static java method when the DisplayHandler
* implementation could just call it itself? Well, kotlin doesn't really
* like dealing with vararg ambiguity, and so while kotlin can't figure out
* what is and isn't a vararg when I call display with a player, java can.
* <p>
* Because of this, I need to have this part of the code in java.
*
* <b>Don't call this method as part of your plugins!</b>
* <p>
* No, seriously - don't. This skips a bunch of checks and you'll almost
* definitely break something.
*
* @param module The display module.
* @param itemStack The ItemStack.
* @param player The player.
* @param args The args.
*/
@ApiStatus.Internal
public static void callDisplayModule(@NotNull final DisplayModule module,
@NotNull final ItemStack itemStack,
@Nullable final Player player,
@NotNull final Object... args) {
module.display(itemStack, args);
if (player != null) {
module.display(itemStack, player, args);
}
}
/**
* Set the display handler.
* <p>
* Internal API component, you will cause bugs if you create your own handler.
*
* @param handler The handler.
*/
@ApiStatus.Internal
public static void setHandler(@NotNull final DisplayHandler handler) {
if (Display.handler != null) {
throw new IllegalStateException("Display already initialized!");
}
Display.handler = handler;
}
private Display() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -0,0 +1,64 @@
package com.willfp.eco.core.display;
import com.willfp.eco.core.Eco;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Interface for display implementations.
*/
@ApiStatus.Internal
@Eco.HandlerComponent
public interface DisplayHandler {
/**
* Register display module.
*
* @param module The module.
*/
void registerDisplayModule(@NotNull DisplayModule module);
/**
* Display on ItemStacks.
*
* @param itemStack The item.
* @param player The player.
* @return The ItemStack.
*/
ItemStack display(@NotNull ItemStack itemStack,
@Nullable Player player);
/**
* Revert on ItemStacks.
*
* @param itemStack The item.
* @return The ItemStack.
*/
ItemStack revert(@NotNull ItemStack itemStack);
/**
* Finalize an ItemStacks.
*
* @param itemStack The item.
* @return The ItemStack.
*/
ItemStack finalize(@NotNull ItemStack itemStack);
/**
* Unfinalize an ItemStacks.
*
* @param itemStack The item.
* @return The ItemStack.
*/
ItemStack unfinalize(@NotNull ItemStack itemStack);
/**
* If an item is finalized.
*
* @param itemStack The item.
* @return If finalized.
*/
boolean isFinalized(@NotNull ItemStack itemStack);
}

View File

@@ -0,0 +1,91 @@
package com.willfp.eco.core.display;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginDependent;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Class for all plugin-specific client-side item display modules.
*/
public abstract class DisplayModule extends PluginDependent<EcoPlugin> {
/**
* The priority of the module.
*/
private final DisplayPriority priority;
/**
* Create a new display module.
*
* @param plugin The plugin that the display is for.
* @param priority The priority of the module.
*/
protected DisplayModule(@NotNull final EcoPlugin plugin,
@NotNull final DisplayPriority priority) {
super(plugin);
this.priority = priority;
}
/**
* Display an item.
*
* @param itemStack The item.
* @param args Optional args for display.
*/
public void display(@NotNull final ItemStack itemStack,
@NotNull final Object... args) {
// Technically optional.
}
/**
* Display an item.
*
* @param itemStack The item.
* @param player The player.
* @param args Optional args for display.
*/
public void display(@NotNull final ItemStack itemStack,
@Nullable final Player player,
@NotNull final Object... args) {
// Technically optional.
}
/**
* Revert an item.
*
* @param itemStack The item.
*/
public void revert(@NotNull final ItemStack itemStack) {
// Technically optional.
}
/**
* Create varargs to pass back to ItemStack after reverting, but before display.
*
* @param itemStack The itemStack.
* @return The plugin-specific varargs.
*/
public Object[] generateVarArgs(@NotNull final ItemStack itemStack) {
return new Object[0];
}
/**
* Get name of plugin.
*
* @return The plugin name.
*/
public final String getPluginName() {
return super.getPlugin().getName();
}
/**
* Get the display priority.
*
* @return The priority.
*/
public DisplayPriority getPriority() {
return this.priority;
}
}

View File

@@ -0,0 +1,26 @@
package com.willfp.eco.core.display;
/**
* The priority (order) of display modules.
*/
public enum DisplayPriority {
/**
* Ran first.
*/
LOWEST,
/**
* Ran second.
*/
LOW,
/**
* Ran third.
*/
HIGH,
/**
* Ran last.
*/
HIGHEST
}

View File

@@ -0,0 +1,93 @@
package com.willfp.eco.core.drops;
import com.willfp.eco.core.Eco;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* A {@link DropQueue} is a set of drops linked to player.
* <p>
* All drops should be passed through a drop queue for telekinesis integration.
* <p>
* It functions essentially as a builder class, and runs very quickly.
*
* @see com.willfp.eco.util.TelekinesisUtils
*/
public class DropQueue {
/**
* The internally used {@link DropQueue}.
*/
private final InternalDropQueue handle;
/**
* @param player The player.
*/
public DropQueue(@NotNull final Player player) {
handle = Eco.getHandler().getDropQueueFactory().create(player);
}
/**
* Add item to queue.
*
* @param item The item to add.
* @return The DropQueue.
*/
public DropQueue addItem(@NotNull final ItemStack item) {
handle.addItem(item);
return this;
}
/**
* Add multiple items to queue.
*
* @param itemStacks The items to add.
* @return The DropQueue.
*/
public DropQueue addItems(@NotNull final Collection<ItemStack> itemStacks) {
handle.addItems(itemStacks);
return this;
}
/**
* Add xp to queue.
*
* @param amount The amount to add.
* @return The DropQueue.
*/
public DropQueue addXP(final int amount) {
handle.addXP(amount);
return this;
}
/**
* Set location of the origin of the drops.
*
* @param location The location.
* @return The DropQueue.
*/
public DropQueue setLocation(@NotNull final Location location) {
handle.setLocation(location);
return this;
}
/**
* Force the queue to act as if player is telekinetic.
*
* @return The DropQueue.
*/
public DropQueue forceTelekinesis() {
handle.forceTelekinesis();
return this;
}
/**
* Push the queue.
*/
public void push() {
handle.push();
}
}

View File

@@ -0,0 +1,21 @@
package com.willfp.eco.core.drops;
import com.willfp.eco.core.Eco;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Internal component to create backend DropQueue implementations.
*/
@ApiStatus.Internal
@Eco.HandlerComponent
public interface DropQueueFactory {
/**
* Create a DropQueue.
*
* @param player The player.
* @return The Queue.
*/
InternalDropQueue create(@NotNull Player player);
}

View File

@@ -0,0 +1,60 @@
package com.willfp.eco.core.drops;
import com.willfp.eco.core.Eco;
import org.bukkit.Location;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* Internal interface for backend DropQueue implementations.
*/
@ApiStatus.Internal
@Eco.HandlerComponent
public interface InternalDropQueue {
/**
* Add item to queue.
*
* @param item The item to add.
* @return The DropQueue.
*/
InternalDropQueue addItem(@NotNull ItemStack item);
/**
* Add multiple items to queue.
*
* @param itemStacks The items to add.
* @return The DropQueue.
*/
InternalDropQueue addItems(@NotNull Collection<ItemStack> itemStacks);
/**
* Add xp to queue.
*
* @param amount The amount to add.
* @return The DropQueue.
*/
InternalDropQueue addXP(int amount);
/**
* Set location of the origin of the drops.
*
* @param location The location.
* @return The DropQueue.
*/
InternalDropQueue setLocation(@NotNull Location location);
/**
* Force the queue to act as if player is telekinetic.
*
* @return The DropQueue.
*/
InternalDropQueue forceTelekinesis();
/**
* Push the queue.
*/
void push();
}

View File

@@ -0,0 +1,84 @@
package com.willfp.eco.core.entities;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* A custom entity has 3 components.
*
* <ul>
* <li>The key to identify it</li>
* <li>The test to check if any entity is this custom entity</li>
* <li>The supplier to spawn the custom {@link org.bukkit.entity.Entity}</li>
* </ul>
*/
public class CustomEntity implements TestableEntity {
/**
* The key.
*/
private final NamespacedKey key;
/**
* The test for Entities to pass.
*/
private final Predicate<@NotNull Entity> test;
/**
* The provider to spawn the entity.
*/
private final Function<Location, Entity> provider;
/**
* Create a new custom entity.
*
* @param key The entity key.
* @param test The test.
* @param provider The provider to spawn the entity.
*/
public CustomEntity(@NotNull final NamespacedKey key,
@NotNull final Predicate<@NotNull Entity> test,
@NotNull final Function<Location, Entity> provider) {
this.key = key;
this.test = test;
this.provider = provider;
}
@Override
public boolean matches(@Nullable final Entity entity) {
if (entity == null) {
return false;
}
return test.test(entity);
}
@Override
public Entity spawn(@NotNull final Location location) {
Validate.notNull(location.getWorld());
return provider.apply(location);
}
/**
* Register the entity.
*/
public void register() {
Entities.registerCustomEntity(this.getKey(), this);
}
/**
* Get the key.
*
* @return The key.
*/
public NamespacedKey getKey() {
return this.key;
}
}

View File

@@ -0,0 +1,239 @@
package com.willfp.eco.core.entities;
import com.willfp.eco.core.entities.args.EntityArgParseResult;
import com.willfp.eco.core.entities.args.EntityArgParser;
import com.willfp.eco.core.entities.impl.EmptyTestableEntity;
import com.willfp.eco.core.entities.impl.ModifiedTestableEntity;
import com.willfp.eco.core.entities.impl.SimpleTestableEntity;
import com.willfp.eco.util.NamespacedKeyUtils;
import com.willfp.eco.util.StringUtils;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
* Class to manage all custom and vanilla entities.
*/
public final class Entities {
/**
* All entities.
*/
private static final Map<NamespacedKey, TestableEntity> REGISTRY = new ConcurrentHashMap<>();
/**
* All entity parsers.
*/
private static final List<EntityArgParser> ARG_PARSERS = new ArrayList<>();
/**
* Register a new custom item.
*
* @param key The key of the item.
* @param item The item.
*/
public static void registerCustomEntity(@NotNull final NamespacedKey key,
@NotNull final TestableEntity item) {
REGISTRY.put(key, item);
}
/**
* Register a new arg parser.
*
* @param parser The parser.
*/
public static void registerArgParser(@NotNull final EntityArgParser parser) {
ARG_PARSERS.add(parser);
}
/**
* Remove an entity.
*
* @param key The key of the entity.
*/
public static void removeCustomEntity(@NotNull final NamespacedKey key) {
REGISTRY.remove(key);
}
/**
* This is the backbone of the entire eco entity system.
* <p>
* You can look up a TestableEntity for any type or custom entity,
* and it will return it with any modifiers passed as parameters.
* <p>
* If you want to get an Entity instance from this, then just call
* {@link TestableEntity#spawn(Location)}.
* <p>
* The advantages of the testable entity system are that there is the inbuilt
* {@link TestableEntity#matches(Entity)} - this allows to check if any entity
* is that testable entity; which may sound negligible, but actually it allows for
* much more power and flexibility. For example, you can have an entity with an
* extra metadata tag, extra lore lines, different display name - and it
* will still work as long as the test passes.
*
* @param key The lookup string.
* @return The testable entity, or an empty testable entity if not found.
*/
@NotNull
public static TestableEntity lookup(@NotNull final String key) {
if (key.contains("?")) {
String[] options = key.split("\\?");
for (String option : options) {
TestableEntity lookup = lookup(option);
if (!(lookup instanceof EmptyTestableEntity)) {
return lookup;
}
}
return new EmptyTestableEntity();
}
String[] args = StringUtils.parseTokens(key);
if (args.length == 0) {
return new EmptyTestableEntity();
}
TestableEntity entity;
String[] split = args[0].toLowerCase().split(":");
if (split.length == 1) {
EntityType type;
try {
type = EntityType.valueOf(args[0].toUpperCase());
} catch (IllegalArgumentException e) {
return new EmptyTestableEntity();
}
entity = new SimpleTestableEntity(type);
} else {
String namespace = split[0];
String keyID = split[1];
NamespacedKey namespacedKey = NamespacedKeyUtils.create(namespace, keyID);
TestableEntity part = REGISTRY.get(namespacedKey);
if (part == null) {
return new EmptyTestableEntity();
}
entity = part;
}
String[] modifierArgs = Arrays.copyOfRange(args, 1, args.length);
List<EntityArgParseResult> parseResults = new ArrayList<>();
for (EntityArgParser argParser : ARG_PARSERS) {
EntityArgParseResult result = argParser.parseArguments(modifierArgs);
if (result != null) {
parseResults.add(result);
}
}
Function<Location, Entity> spawner = entity::spawn;
if (!parseResults.isEmpty()) {
entity = new ModifiedTestableEntity(
entity,
test -> {
for (EntityArgParseResult parseResult : parseResults) {
if (!parseResult.test().test(test)) {
return false;
}
}
return true;
},
location -> {
Entity spawned = spawner.apply(location);
for (EntityArgParseResult parseResult : parseResults) {
parseResult.modifier().accept(spawned);
}
return spawned;
}
);
}
return entity;
}
/**
* Get a Testable Entity from an ItemStack.
* <p>
* Will search for registered entity first. If there are no matches in the registry,
* then it will return a {@link com.willfp.eco.core.entities.impl.SimpleTestableEntity} matching the entity type.
* <p>
* If the entity is not custom and has unknown type, this will return null.
*
* @param entity The Entity.
* @return The found Testable Entity.
*/
@Nullable
public static TestableEntity getEntity(@Nullable final Entity entity) {
if (entity == null) {
return null;
}
TestableEntity customEntity = getEntity(entity);
if (customEntity != null) {
return customEntity;
}
for (TestableEntity known : REGISTRY.values()) {
if (known.matches(entity)) {
return known;
}
}
if (entity.getType() == EntityType.UNKNOWN) {
return null;
}
return new SimpleTestableEntity(entity.getType());
}
/**
* Get if entity is a custom entity.
*
* @param entity The entity to check.
* @return If is custom.
*/
public static boolean isCustomEntity(@NotNull final Entity entity) {
for (TestableEntity testable : REGISTRY.values()) {
if (testable.matches(entity)) {
return true;
}
}
return false;
}
/**
* Get all registered custom items.
*
* @return A set of all items.
*/
public static Set<TestableEntity> getCustomEntities() {
return new HashSet<>(REGISTRY.values());
}
private Entities() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -0,0 +1,27 @@
package com.willfp.eco.core.entities;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* An item with a test to see if any item is that item.
*/
public interface TestableEntity {
/**
* If an Entity matches the test.
*
* @param entity The entity to test.
* @return If the entity matches.
*/
boolean matches(@Nullable Entity entity);
/**
* Spawn the entity.
*
* @param location The location.
* @return The entity.
*/
Entity spawn(@NotNull Location location);
}

View File

@@ -0,0 +1,33 @@
package com.willfp.eco.core.entities.args;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* @param test The test for the entity.
* @param modifier The modifier to apply to the entity.
* @see EntityArgParser
*/
public record EntityArgParseResult(@NotNull Predicate<Entity> test,
@NotNull Consumer<Entity> modifier) {
/**
* Kotlin destructuring support.
*
* @return The test.
*/
public Predicate<Entity> component1() {
return test;
}
/**
* Kotlin destructuring support.
*
* @return The modifier.
*/
public Consumer<Entity> component2() {
return modifier;
}
}

View File

@@ -0,0 +1,20 @@
package com.willfp.eco.core.entities.args;
import com.willfp.eco.core.entities.TestableEntity;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* An argument parser should generate the predicate as well
* as modify the Entity for {@link TestableEntity#spawn(Location)}.
*/
public interface EntityArgParser {
/**
* Parse the arguments.
*
* @param args The arguments.
* @return The predicate test to apply to the modified entity.
*/
@Nullable EntityArgParseResult parseArguments(@NotNull String[] args);
}

View File

@@ -0,0 +1,33 @@
package com.willfp.eco.core.entities.impl;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.entities.TestableEntity;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Empty entity.
*/
public class EmptyTestableEntity implements TestableEntity {
/**
* Create a new empty testable entity.
*/
public EmptyTestableEntity() {
}
@Override
public boolean matches(@Nullable final Entity entity) {
return false;
}
@Override
public Entity spawn(@NotNull final Location location) {
Validate.notNull(location.getWorld());
return Eco.getHandler().createDummyEntity(location);
}
}

View File

@@ -0,0 +1,69 @@
package com.willfp.eco.core.entities.impl;
import com.willfp.eco.core.entities.TestableEntity;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* Existing testable entity with an extra filter.
*
* @see com.willfp.eco.core.entities.CustomEntity
*/
public class ModifiedTestableEntity implements TestableEntity {
/**
* The item.
*/
private final TestableEntity handle;
/**
* The amount.
*/
private final Predicate<Entity> test;
/**
* The provider to spawn the entity.
*/
private final Function<Location, Entity> provider;
/**
* Create a new modified testable entity.
*
* @param entity The base entity.
* @param test The test.
* @param provider The provider to spawn the entity.
*/
public ModifiedTestableEntity(@NotNull final TestableEntity entity,
@NotNull final Predicate<@NotNull Entity> test,
@NotNull final Function<Location, Entity> provider) {
this.handle = entity;
this.test = test;
this.provider = provider;
}
@Override
public boolean matches(@Nullable final Entity entity) {
return entity != null && handle.matches(entity) && test.test(entity);
}
@Override
public Entity spawn(@NotNull final Location location) {
Validate.notNull(location.getWorld());
return provider.apply(location);
}
/**
* Get the handle.
*
* @return The handle.
*/
public TestableEntity getHandle() {
return this.handle;
}
}

View File

@@ -0,0 +1,53 @@
package com.willfp.eco.core.entities.impl;
import com.willfp.eco.core.entities.TestableEntity;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Default vanilla entities.
*/
public class SimpleTestableEntity implements TestableEntity {
/**
* The entity type.
*/
private final EntityType type;
/**
* Create a new simple testable entity.
*
* @param type The entity type.
*/
public SimpleTestableEntity(@NotNull final EntityType type) {
this.type = type;
Validate.notNull(type.getEntityClass(), "Entity cannot be of unknown type!");
}
@Override
public boolean matches(@Nullable final Entity entity) {
return entity != null && entity.getType() == type;
}
@Override
public Entity spawn(@NotNull final Location location) {
Validate.notNull(location.getWorld());
assert type.getEntityClass() != null;
return location.getWorld().spawn(location, type.getEntityClass());
}
/**
* Get the type.
*
* @return The type.
*/
public EntityType getType() {
return this.type;
}
}

View File

@@ -0,0 +1,84 @@
package com.willfp.eco.core.events;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* The armor change event <b>does</b> contain information about the event.
* <p>
* Unlike {@link ArmorEquipEvent}, it is called the next tick and contains previous and current armor contents.
*/
public class ArmorChangeEvent extends PlayerEvent {
/**
* Bukkit parity.
*/
private static final HandlerList HANDLERS = new HandlerList();
/**
* The armor contents before. 0 is helmet, 3 is boots.
*/
private final List<ItemStack> before;
/**
* The armor contents after. 0 is helmet, 3 is boots.
*/
private final List<ItemStack> after;
/**
* Create a new ArmorChangeEvent.
*
* @param player The player.
* @param before The armor contents before.
* @param after The armor contents after.
*/
public ArmorChangeEvent(@NotNull final Player player,
@NotNull final List<ItemStack> before,
@NotNull final List<ItemStack> after) {
super(player);
this.before = before;
this.after = after;
}
/**
* Gets a list of handlers handling this event.
*
* @return A list of handlers handling this event.
*/
@Override
@NotNull
public HandlerList getHandlers() {
return HANDLERS;
}
/**
* Bukkit parity.
*
* @return The handler list.
*/
public static HandlerList getHandlerList() {
return HANDLERS;
}
/**
* Get the contents before the change.
*
* @return The contents.
*/
public List<ItemStack> getBefore() {
return this.before;
}
/**
* Get the current contents.
*
* @return The contents.
*/
public List<ItemStack> getAfter() {
return this.after;
}
}

View File

@@ -0,0 +1,54 @@
package com.willfp.eco.core.events;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
/**
* The armor equip event <b>does not contain information about the event.</b>
* <p>
* It is purely a trigger called whenever a player changes armor, you have to run
* your own checks.
* <p>
* The event is called before the player's inventory actually updates,
* so you can check a tick later to see the new contents.
*
* @see ArmorChangeEvent
*/
public class ArmorEquipEvent extends PlayerEvent {
/**
* Bukkit parity.
*/
private static final HandlerList HANDLERS = new HandlerList();
/**
* Create a new ArmorEquipEvent.
*
* @param player The player.
*/
public ArmorEquipEvent(@NotNull final Player player) {
super(player);
}
/**
* Gets a list of handlers handling this event.
*
* @return A list of handlers handling this event.
*/
@Override
@NotNull
public HandlerList getHandlers() {
return HANDLERS;
}
/**
* Bukkit parity.
*
* @return The handler list.
*/
public static HandlerList getHandlerList() {
return HANDLERS;
}
}

View File

@@ -0,0 +1,143 @@
package com.willfp.eco.core.events;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* Called on DropQueue push.
*/
public class DropQueuePushEvent extends PlayerEvent implements Cancellable {
/**
* Cancel state.
*/
private boolean cancelled;
/**
* If telekinetic.
*/
private final boolean isTelekinetic;
/**
* The items.
*/
private final Collection<? extends ItemStack> items;
/**
* The xp.
*/
private final int xp;
/**
* The location.
*/
private final Location location;
/**
* Bukkit parity.
*/
private static final HandlerList HANDLERS = new HandlerList();
/**
* Create a new DropQueuePushEvent.
*
* @param player The player.
* @param items The items.
* @param location The location.
* @param xp The xp.
* @param isTelekinetic If the event is telekinetic.
*/
public DropQueuePushEvent(@NotNull final Player player,
@NotNull final Collection<? extends ItemStack> items,
@NotNull final Location location,
final int xp,
final boolean isTelekinetic) {
super(player);
this.items = items;
this.location = location;
this.xp = xp;
this.isTelekinetic = isTelekinetic;
}
/**
* Gets a list of handlers handling this event.
*
* @return A list of handlers handling this event.
*/
@Override
@NotNull
public HandlerList getHandlers() {
return HANDLERS;
}
/**
* Bukkit parity.
*
* @return The handler list.
*/
public static HandlerList getHandlerList() {
return HANDLERS;
}
/**
* Get cancel state.
*
* @return The cancel state.
*/
@Override
public boolean isCancelled() {
return this.cancelled;
}
/**
* Set cancel state.
*
* @param cancelled If cancelled.
*/
@Override
public void setCancelled(final boolean cancelled) {
this.cancelled = cancelled;
}
/**
* Get the items to be dropped.
*
* @return The items.
*/
public Collection<? extends ItemStack> getItems() {
return items;
}
/**
* Get the xp to be dropped.
*
* @return The xp.
*/
public int getXp() {
return xp;
}
/**
* Get the location.
*
* @return The location.
*/
public Location getLocation() {
return location;
}
/**
* Get force telekinesis state.
*
* @return The force telekinesis state.
*/
public boolean isTelekinetic() {
return this.isTelekinetic;
}
}

View File

@@ -0,0 +1,133 @@
package com.willfp.eco.core.events;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Event called when an entity is killed by another entity.
* <p>
* Not sure why spigot doesn't have this event normally.
*/
public class EntityDeathByEntityEvent extends Event {
/**
* Internal, for bukkit.
*/
private static final HandlerList HANDLERS = new HandlerList();
/**
* The {@link LivingEntity} killed.
*/
private final LivingEntity victim;
/**
* The {@link Entity} that killed.
*/
private final Entity killer;
/**
* The associated {@link EntityDeathEvent}.
*/
private final EntityDeathEvent deathEvent;
/**
* The entity drops.
*/
private final List<ItemStack> drops;
/**
* The xp to drop.
*/
private final int xp;
/**
* Create event based off parameters.
*
* @param victim The killed entity
* @param killer The killer
* @param drops The item drops
* @param xp The amount of xp to drop
* @param deathEvent The associated {@link EntityDeathEvent}
*/
public EntityDeathByEntityEvent(@NotNull final LivingEntity victim,
@NotNull final Entity killer,
@NotNull final List<ItemStack> drops,
final int xp,
@NotNull final EntityDeathEvent deathEvent) {
this.victim = victim;
this.killer = killer;
this.drops = drops;
this.xp = xp;
this.deathEvent = deathEvent;
}
/**
* Internal bukkit.
*
* @return Get the handlers.
*/
@Override
public @NotNull HandlerList getHandlers() {
return HANDLERS;
}
/**
* Internal bukkit.
*
* @return The handlers.
*/
public static HandlerList getHandlerList() {
return HANDLERS;
}
/**
* Get the entity killed.
*
* @return The victim.
*/
public LivingEntity getVictim() {
return this.victim;
}
/**
* Get the killer.
*
* @return The killer.
*/
public Entity getKiller() {
return this.killer;
}
/**
* Get the death event that caused this event.
*
* @return The death event.
*/
public EntityDeathEvent getDeathEvent() {
return this.deathEvent;
}
/**
* Get the drops.
*
* @return The drops.
*/
public List<ItemStack> getDrops() {
return this.drops;
}
/**
* Get the experience dropped.
*
* @return The experience.
*/
public int getXp() {
return this.xp;
}
}

View File

@@ -0,0 +1,28 @@
package com.willfp.eco.core.events;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
/**
* Manages listeners for a plugin.
*/
public interface EventManager {
/**
* Register a listener with bukkit.
*
* @param listener The listener to register.
*/
void registerListener(@NotNull Listener listener);
/**
* Unregister a listener with bukkit.
*
* @param listener The listener to unregister.
*/
void unregisterListener(@NotNull Listener listener);
/**
* Unregister all listeners associated with the plugin.
*/
void unregisterAllListeners();
}

View File

@@ -0,0 +1,60 @@
package com.willfp.eco.core.events;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerExpChangeEvent;
import org.jetbrains.annotations.NotNull;
/**
* Event similar to {@link PlayerExpChangeEvent}, except it
* isn't called if the exp is from a bottle.
*/
public class NaturalExpGainEvent extends Event {
/**
* Internal bukkit.
*/
private static final HandlerList HANDLERS = new HandlerList();
/**
* The associated {@link PlayerExpChangeEvent}.
* Use this to modify event parameters.
*/
private final PlayerExpChangeEvent expChangeEvent;
/**
* Create event based off parameters.
*
* @param event The associated PlayerExpChangeEvent.
*/
public NaturalExpGainEvent(@NotNull final PlayerExpChangeEvent event) {
this.expChangeEvent = event;
}
/**
* Internal bukkit.
*
* @return The handlers.
*/
@Override
public @NotNull HandlerList getHandlers() {
return HANDLERS;
}
/**
* Internal bukkit.
*
* @return The handlers.
*/
public static HandlerList getHandlerList() {
return HANDLERS;
}
/**
* Get the event that caused this event.
*
* @return The exp change event.
*/
public PlayerExpChangeEvent getExpChangeEvent() {
return this.expChangeEvent;
}
}

View File

@@ -0,0 +1,84 @@
package com.willfp.eco.core.events;
import org.bukkit.Location;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerMoveEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Event called when a player jumps.
*/
public class PlayerJumpEvent extends PlayerMoveEvent {
/**
* Internal bukkit.
*/
private static final HandlerList HANDLERS = new HandlerList();
/**
* The handled event.
*/
private final PlayerMoveEvent handle;
/**
* Create a new PlayerJumpEvent.
*
* @param event The PlayerMoveEvent.
*/
public PlayerJumpEvent(@NotNull final PlayerMoveEvent event) {
super(event.getPlayer(), event.getFrom(), event.getTo());
this.handle = event;
}
/**
* Internal bukkit.
*
* @return The handlers.
*/
@Override
public @NotNull HandlerList getHandlers() {
return HANDLERS;
}
/**
* Internal bukkit.
*
* @return The handlers.
*/
public static @NotNull HandlerList getHandlerList() {
return HANDLERS;
}
@Override
public boolean isCancelled() {
return handle.isCancelled();
}
@Override
public void setCancelled(final boolean cancel) {
handle.setCancelled(cancel);
}
@NotNull
@Override
public Location getFrom() {
return handle.getFrom();
}
@Override
public void setFrom(@NotNull final Location from) {
handle.setFrom(from);
}
@Nullable
@Override
public Location getTo() {
return handle.getTo();
}
@Override
public void setTo(@NotNull final Location to) {
handle.setTo(to);
}
}

View File

@@ -0,0 +1,160 @@
package com.willfp.eco.core.extensions;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.updating.ConfigHandler;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.logging.Logger;
/**
* An extension is a separate jar file that hooks into the base plugin jar.
* <p>
* If you take PlaceholderAPI as an example, the PAPI expansions are identical to
* extensions.
* <p>
* Syntactically, extensions are very similar to plugins in their own right, except that
* they are loaded by another plugin.
*
* @see <a href="https://auxilor.polymart.org">Extension examples.</a>
*/
public abstract class Extension implements PluginLike {
/**
* The {@link EcoPlugin} that this extension is for.
*/
private final EcoPlugin plugin;
/**
* Create a new extension for a plugin.
*
* @param plugin The plugin.
*/
protected Extension(@NotNull final EcoPlugin plugin) {
this.plugin = plugin;
}
/**
* Metadata containing version and name.
*/
private ExtensionMetadata metadata = null;
/**
* Method to validate metadata and enable extension.
*/
public final void enable() {
Validate.notNull(metadata, "Metadata cannot be null!");
this.onEnable();
}
/**
* Method to disable extension.
*/
public final void disable() {
this.onDisable();
}
/**
* Method to handle after load.
*/
public final void handleAfterLoad() {
this.onAfterLoad();
}
/**
* Method to handle plugin reloads.
*/
public final void handleReload() {
this.onReload();
}
/**
* Called on enabling Extension.
*/
protected abstract void onEnable();
/**
* Called when Extension is disabled.
*/
protected abstract void onDisable();
/**
* Called the once the base plugin is done loading.
*/
protected void onAfterLoad() {
// Override if needed
}
/**
* Called on plugin reload.
*/
protected void onReload() {
// Override if needed
}
/**
* Set the metadata of the extension.
* <p>
* Must be called before enabling.
*
* @param metadata The metadata to set.
*/
public final void setMetadata(@NotNull final ExtensionMetadata metadata) {
this.metadata = metadata;
}
/**
* Get the name of the extension.
*
* @return The name of the metadata attached to the extension.
*/
public final String getName() {
Validate.notNull(metadata, "Metadata cannot be null!");
return this.metadata.name();
}
/**
* Get the author of the extension.
*
* @return The author of the metadata attached to the extension.
*/
public final String getAuthor() {
Validate.notNull(metadata, "Metadata cannot be null!");
return this.metadata.author();
}
/**
* Get the version of the extension.
*
* @return The version of the metadata attached to the extension.
*/
public final String getVersion() {
Validate.notNull(metadata, "Metadata cannot be null!");
return this.metadata.version();
}
@Override
public File getDataFolder() {
return this.plugin.getDataFolder();
}
@Override
public ConfigHandler getConfigHandler() {
return this.plugin.getConfigHandler();
}
@Override
public Logger getLogger() {
return this.plugin.getLogger();
}
/**
* Get the plugin for the extension.
*
* @return The plugin.
*/
protected EcoPlugin getPlugin() {
return this.plugin;
}
}

View File

@@ -0,0 +1,25 @@
package com.willfp.eco.core.extensions;
import java.util.Set;
/**
* Internal component to manage loading and unloading extensions.
*/
public interface ExtensionLoader {
/**
* Load all extensions.
*/
void loadExtensions();
/**
* Unload all loaded extensions.
*/
void unloadExtensions();
/**
* Retrieve a set of all loaded extensions.
*
* @return An {@link Set<Extension>} of all loaded extensions.
*/
Set<Extension> getLoadedExtensions();
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.core.extensions;
import org.jetbrains.annotations.NotNull;
/**
* The extension's metadata.
* <p>
* Stored as a record internally.
*
* @param version The extension version.
* @param name The extension name.
* @param author The extension's author.
*/
public record ExtensionMetadata(@NotNull String version,
@NotNull String name,
@NotNull String author) {
}

View File

@@ -0,0 +1,19 @@
package com.willfp.eco.core.extensions;
import org.jetbrains.annotations.NotNull;
/**
* Potential causes include:
* Missing or invalid extension.yml.
* Invalid filetype.
*/
public class MalformedExtensionException extends RuntimeException {
/**
* Create a new MalformedExtensionException.
*
* @param errorMessage The error message to show.
*/
public MalformedExtensionException(@NotNull final String errorMessage) {
super(errorMessage);
}
}

View File

@@ -0,0 +1,17 @@
package com.willfp.eco.core.factory;
import org.bukkit.metadata.FixedMetadataValue;
import org.jetbrains.annotations.NotNull;
/**
* Factory to create metadata values for a specific plugin.
*/
public interface MetadataValueFactory {
/**
* Create a metadata value for a given plugin and object.
*
* @param value The object to store in metadata.
* @return The metadata value.
*/
FixedMetadataValue create(@NotNull Object value);
}

View File

@@ -0,0 +1,17 @@
package com.willfp.eco.core.factory;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.NotNull;
/**
* Factory to create {@link NamespacedKey}s for a plugin.
*/
public interface NamespacedKeyFactory {
/**
* Create an {@link NamespacedKey} associated with an {@link com.willfp.eco.core.EcoPlugin}.
*
* @param key The key in the {@link NamespacedKey}.
* @return The created {@link NamespacedKey}.
*/
NamespacedKey create(@NotNull String key);
}

View File

@@ -0,0 +1,20 @@
package com.willfp.eco.core.factory;
import com.willfp.eco.core.scheduling.RunnableTask;
import org.jetbrains.annotations.NotNull;
import java.util.function.Consumer;
/**
* Factory to create runnables. Much cleaner syntax than instantiating
* {@link org.bukkit.scheduler.BukkitRunnable}s.
*/
public interface RunnableFactory {
/**
* Create a {@link RunnableTask}.
*
* @param consumer Lambda of the code to run, where the parameter represents the instance of the runnable.
* @return The created {@link RunnableTask}.
*/
RunnableTask create(@NotNull Consumer<RunnableTask> consumer);
}

View File

@@ -0,0 +1,113 @@
package com.willfp.eco.core.fast;
import com.willfp.eco.core.Eco;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* FastItemStack contains methods to modify and read items faster than in default bukkit.
*/
public interface FastItemStack {
/**
* Get all enchantments on an item.
*
* @param checkStored If stored NBT should also be checked.
* @return A map of all enchantments.
*/
Map<Enchantment, Integer> getEnchantmentsOnItem(boolean checkStored);
/**
* Get the level of an enchantment on an item.
*
* @param enchantment The enchantment.
* @param checkStored If the stored NBT should also be checked.
* @return The enchantment level, or 0 if not found.
*/
int getLevelOnItem(@NotNull Enchantment enchantment,
boolean checkStored);
/**
* Set the item lore.
*
* @param lore The lore.
*/
void setLore(@Nullable List<String> lore);
/**
* Get the item lore.
*
* @return The lore.
*/
List<String> getLore();
/**
* Set the rework penalty.
*
* @param cost The rework penalty to set.
*/
void setRepairCost(int cost);
/**
* Get the rework penalty.
* .
*
* @return The rework penalty found on the item.
*/
int getRepairCost();
/**
* Add ItemFlags.
*
* @param hideFlags The flags.
*/
void addItemFlags(@NotNull ItemFlag... hideFlags);
/**
* Remove ItemFlags.
*
* @param hideFlags The flags.
*/
void removeItemFlags(@NotNull ItemFlag... hideFlags);
/**
* Get the ItemFlags.
*
* @return The flags.
*/
Set<ItemFlag> getItemFlags();
/**
* Test the item for a flag.
*
* @param flag The flag.
* @return If the flag is present.
*/
boolean hasItemFlag(@NotNull ItemFlag flag);
/**
* Get the Bukkit ItemStack again.
*
* @return The ItemStack.
*/
ItemStack unwrap();
/**
* Wrap an ItemStack to create a FastItemStack.
*
* @param itemStack The ItemStack.
* @return The FastItemStack.
*/
static FastItemStack wrap(@Nullable final ItemStack itemStack) {
return Eco.getHandler().createFastItemStack(Objects.requireNonNullElseGet(itemStack, () -> new ItemStack(Material.AIR)));
}
}

View File

@@ -0,0 +1,33 @@
package com.willfp.eco.core.gui;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.gui.menu.MenuBuilder;
import com.willfp.eco.core.gui.slot.SlotBuilder;
import com.willfp.eco.core.gui.slot.functional.SlotProvider;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Internal component used by {@link com.willfp.eco.core.gui.menu.Menu#builder(int)}
* and {@link com.willfp.eco.core.gui.slot.Slot#builder(ItemStack)}.
*/
@ApiStatus.Internal
@Eco.HandlerComponent
public interface GUIFactory {
/**
* Create slot builder.
*
* @param provider The provider.
* @return The builder.
*/
SlotBuilder createSlotBuilder(@NotNull SlotProvider provider);
/**
* Create menu builder.
*
* @param rows The amount of rows.
* @return The builder.
*/
MenuBuilder createMenuBuilder(int rows);
}

View File

@@ -0,0 +1,19 @@
package com.willfp.eco.core.gui.menu;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.jetbrains.annotations.NotNull;
/**
* Interface to run on menu close.
*/
@FunctionalInterface
public interface CloseHandler {
/**
* Performs this operation on the given arguments.
*
* @param event The close event.
* @param menu The menu.
*/
void handle(@NotNull InventoryCloseEvent event,
@NotNull Menu menu);
}

View File

@@ -0,0 +1,108 @@
package com.willfp.eco.core.gui.menu;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.gui.slot.Slot;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Set;
/**
* GUI version of {@link Inventory}.
* <p>
* A menu contains slots and is 1-indexed. (Top row has index 1, bottom row has index 6).
*/
public interface Menu {
/**
* Get the amount of rows.
*
* @return The amount of rows.
*/
int getRows();
/**
* Get slot at given row and column.
*
* @param row The row.
* @param column The column.
* @return The row.
*/
Slot getSlot(int row,
int column);
/**
* Get the menu title.
*
* @return The title.
*/
String getTitle();
/**
* Open the inventory for the player.
*
* @param player The player.
* @return The inventory.
*/
Inventory open(@NotNull Player player);
/**
* Get captive items.
*
* @param player The player.
* @return The items.
*/
List<ItemStack> getCaptiveItems(@NotNull Player player);
/**
* Write data.
*
* @param player The player.
* @param key The key.
* @param type The type.
* @param value The value.
* @param <T> The type.
* @param <Z> The type.
*/
<T, Z> void writeData(@NotNull Player player,
@NotNull NamespacedKey key,
@NotNull PersistentDataType<T, Z> type,
@NotNull Z value);
/**
* Read data.
*
* @param player The player.
* @param key The key.
* @param type The type.
* @param <T> The type.
* @param <Z> The type.
* @return The data.
*/
@Nullable <T, Z> T readData(@NotNull Player player,
@NotNull NamespacedKey key,
@NotNull PersistentDataType<T, Z> type);
/**
* Get all data keys for a player.
*
* @param player The player.
* @return The keys.
*/
Set<NamespacedKey> getKeys(@NotNull Player player);
/**
* Create a builder with a given amount of rows.
*
* @param rows The rows.
* @return The builder.
*/
static MenuBuilder builder(final int rows) {
return Eco.getHandler().getGUIFactory().createMenuBuilder(rows);
}
}

View File

@@ -0,0 +1,75 @@
package com.willfp.eco.core.gui.menu;
import com.willfp.eco.core.gui.slot.FillerMask;
import com.willfp.eco.core.gui.slot.Slot;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.jetbrains.annotations.NotNull;
import java.util.function.Consumer;
/**
* Builder to create menus.
*/
public interface MenuBuilder {
/**
* Set the menu title.
*
* @param title The title.
* @return The builder.
*/
MenuBuilder setTitle(@NotNull String title);
/**
* Set a slot.
*
* @param row The row.
* @param column The column.
* @param slot The slot.
* @return The builder.
*/
MenuBuilder setSlot(int row,
int column,
@NotNull Slot slot);
/**
* Run function to modify the builder.
*
* @param modifier The modifier.
* @return The builder.
*/
MenuBuilder modfiy(@NotNull Consumer<MenuBuilder> modifier);
/**
* Set the menu mask.
*
* @param mask The mask.
* @return The builder.
*/
MenuBuilder setMask(@NotNull FillerMask mask);
/**
* Set the menu close handler.
*
* @param action The handler.
* @return The builder.
*/
default MenuBuilder onClose(@NotNull Consumer<InventoryCloseEvent> action) {
onClose((event, menu) -> action.accept(event));
return this;
}
/**
* Set the menu close handler.
*
* @param action The handler.
* @return The builder.
*/
MenuBuilder onClose(@NotNull CloseHandler action);
/**
* Build the menu.
*
* @return The menu.
*/
Menu build();
}

View File

@@ -0,0 +1,92 @@
package com.willfp.eco.core.gui.slot;
import com.willfp.eco.core.items.builder.ItemStackBuilder;
import com.willfp.eco.util.ListUtils;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
/**
* Mask of filler slots.
* <p>
* A pattern consists of 1s and 0s, where a 1 is a filler slot,
* and a 0 isn't.
* <p>
* For example, creating a filler mask for a single-chest sized menu would look like this:
* <p>
* new FillerMask(
* material,
* "11111111"
* "10000001"
* "11111111"
* );
*/
public class FillerMask {
/**
* Mask.
*/
private final List<List<Slot>> mask;
/**
* Create a new filler mask.
*
* @param material The mask material.
* @param pattern The pattern.
*/
public FillerMask(@NotNull final Material material,
@NotNull final String... pattern) {
this(new MaskMaterials(material), pattern);
}
/**
* Create a new filler mask.
*
* @param materials The mask materials.
* @param pattern The pattern.
*/
public FillerMask(@NotNull final MaskMaterials materials,
@NotNull final String... pattern) {
if (Arrays.stream(materials.materials()).anyMatch(material -> material == Material.AIR)) {
throw new IllegalArgumentException("Materials cannot be air!");
}
mask = ListUtils.create2DList(6, 9);
for (int i = 0; i < materials.materials().length; i++) {
ItemStack itemStack = new ItemStackBuilder(materials.materials()[i])
.setDisplayName("&r")
.build();
int row = 0;
for (String patternRow : pattern) {
int column = 0;
if (patternRow.length() != 9) {
throw new IllegalArgumentException("Invalid amount of columns in pattern!");
}
for (char c : patternRow.toCharArray()) {
if (c == '0') {
mask.get(row).set(column, null);
} else if (c == Character.forDigit(i + 1, 10)) {
mask.get(row).set(column, new FillerSlot(itemStack));
}
column++;
}
row++;
}
}
}
/**
* Get the mask.
*
* @return The mask.
*/
public List<List<Slot>> getMask() {
return this.mask;
}
}

View File

@@ -0,0 +1,45 @@
package com.willfp.eco.core.gui.slot;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
/**
* A filler slot is a slot that does nothing when clicked.
* <p>
* Useful for backgrounds.
*/
public class FillerSlot implements Slot {
/**
* The ItemStack.
*/
private final ItemStack itemStack;
/**
* Create new filler slot.
*
* @param itemStack The ItemStack.
*/
public FillerSlot(@NotNull final ItemStack itemStack) {
this.itemStack = itemStack;
}
@Override
public ItemStack getItemStack(@NotNull final Player player) {
return itemStack;
}
@Override
public boolean isCaptive() {
return false;
}
/**
* Get the ItemStack.
*
* @return The ItemStack.
*/
public ItemStack getItemStack() {
return this.itemStack;
}
}

View File

@@ -0,0 +1,14 @@
package com.willfp.eco.core.gui.slot;
import org.bukkit.Material;
import org.jetbrains.annotations.NotNull;
/**
* Mask materials store a set of materials which can be accessed by
* a filler mask.
*
* @param materials The materials.
*/
public record MaskMaterials(@NotNull Material... materials) {
}

View File

@@ -0,0 +1,69 @@
package com.willfp.eco.core.gui.slot;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.gui.slot.functional.SlotProvider;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.function.Function;
/**
* A slot is an item in a GUI that can handle clicks.
*/
public interface Slot {
/**
* Get the ItemStack that would be shown to a player.
*
* @param player The player.
* @return The ItemStack.
*/
ItemStack getItemStack(@NotNull Player player);
/**
* If the slot is captive. (Can items be placed in it).
*
* @return If captive.
*/
boolean isCaptive();
/**
* Create a builder for an ItemStack.
*
* @return The builder.
*/
static SlotBuilder builder() {
return Eco.getHandler().getGUIFactory().createSlotBuilder((player, menu) -> new ItemStack(Material.AIR));
}
/**
* Create a builder for an ItemStack.
*
* @param itemStack The ItemStack.
* @return The builder.
*/
static SlotBuilder builder(@NotNull final ItemStack itemStack) {
return Eco.getHandler().getGUIFactory().createSlotBuilder((player, menu) -> itemStack);
}
/**
* Create a builder for a player-specific ItemStack.
*
* @param provider The provider.
* @return The builder.
*/
static SlotBuilder builder(@NotNull final Function<Player, ItemStack> provider) {
return Eco.getHandler().getGUIFactory().createSlotBuilder((player, menu) -> provider.apply(player));
}
/**
* Create a builder for a player-specific ItemStack.
*
* @param provider The provider.
* @return The builder.
*/
static SlotBuilder builder(@NotNull final SlotProvider provider) {
return Eco.getHandler().getGUIFactory().createSlotBuilder(provider);
}
}

View File

@@ -0,0 +1,125 @@
package com.willfp.eco.core.gui.slot;
import com.willfp.eco.core.gui.slot.functional.SlotHandler;
import com.willfp.eco.core.gui.slot.functional.SlotModifier;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.jetbrains.annotations.NotNull;
import java.util.function.BiConsumer;
/**
* Builder to create slots.
*/
public interface SlotBuilder {
/**
* Set click handler.
*
* @param action The handler.
* @return The builder.
*/
default SlotBuilder onLeftClick(@NotNull BiConsumer<InventoryClickEvent, Slot> action) {
return onLeftClick((event, slot, menu) -> action.accept(event, slot));
}
/**
* Set click handler.
*
* @param handler The handler.
* @return The builder.
*/
SlotBuilder onLeftClick(@NotNull SlotHandler handler);
/**
* Set click handler.
*
* @param action The handler.
* @return The builder.
*/
default SlotBuilder onRightClick(@NotNull BiConsumer<InventoryClickEvent, Slot> action) {
return onRightClick((event, slot, menu) -> action.accept(event, slot));
}
/**
* Set click handler.
*
* @param handler The handler.
* @return The builder.
*/
SlotBuilder onRightClick(@NotNull SlotHandler handler);
/**
* Set click handler.
*
* @param action The handler.
* @return The builder.
*/
default SlotBuilder onShiftLeftClick(@NotNull BiConsumer<InventoryClickEvent, Slot> action) {
return onShiftLeftClick((event, slot, menu) -> action.accept(event, slot));
}
/**
* Set click handler.
*
* @param handler The handler.
* @return The builder.
*/
SlotBuilder onShiftLeftClick(@NotNull SlotHandler handler);
/**
* Set click handler.
*
* @param action The handler.
* @return The builder.
*/
default SlotBuilder onShiftRightClick(@NotNull BiConsumer<InventoryClickEvent, Slot> action) {
return onShiftRightClick((event, slot, menu) -> action.accept(event, slot));
}
/**
* Set click handler.
*
* @param handler The handler.
* @return The builder.
*/
SlotBuilder onShiftRightClick(@NotNull SlotHandler handler);
/**
* Set click handler.
*
* @param action The handler.
* @return The builder.
*/
default SlotBuilder onMiddleClick(@NotNull BiConsumer<InventoryClickEvent, Slot> action) {
return onMiddleClick((event, slot, menu) -> action.accept(event, slot));
}
/**
* Set click handler.
*
* @param handler The handler.
* @return The builder.
*/
SlotBuilder onMiddleClick(@NotNull SlotHandler handler);
/**
* Modify the ItemStack.
*
* @param modifier The modifier.
* @return The builder.
*/
SlotBuilder setModifier(@NotNull SlotModifier modifier);
/**
* Set slot to be a captive slot.
*
* @return The builder.
*/
SlotBuilder setCaptive();
/**
* Build the slot.
*
* @return The slot.
*/
Slot build();
}

View File

@@ -0,0 +1,23 @@
package com.willfp.eco.core.gui.slot.functional;
import com.willfp.eco.core.gui.menu.Menu;
import com.willfp.eco.core.gui.slot.Slot;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.jetbrains.annotations.NotNull;
/**
* Interface to run on slot click.
*/
@FunctionalInterface
public interface SlotHandler {
/**
* Performs this operation on the given arguments.
*
* @param event The click event.
* @param slot The slot
* @param menu The menu.
*/
void handle(@NotNull InventoryClickEvent event,
@NotNull Slot slot,
@NotNull Menu menu);
}

View File

@@ -0,0 +1,23 @@
package com.willfp.eco.core.gui.slot.functional;
import com.willfp.eco.core.gui.menu.Menu;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
/**
* Interface to run on slot modify.
*/
@FunctionalInterface
public interface SlotModifier {
/**
* Performs this operation on the given arguments.
*
* @param player The player.
* @param menu The menu.
* @param previous The previous ItemStack.
*/
void modify(@NotNull Player player,
@NotNull Menu menu,
@NotNull ItemStack previous);
}

View File

@@ -0,0 +1,22 @@
package com.willfp.eco.core.gui.slot.functional;
import com.willfp.eco.core.gui.menu.Menu;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
/**
* Interface to run on slot display.
*/
@FunctionalInterface
public interface SlotProvider {
/**
* Performs this operation on the given arguments.
*
* @param player The player.
* @param menu The menu.
* @return The ItemStack.
*/
ItemStack provide(@NotNull Player player,
@NotNull Menu menu);
}

View File

@@ -0,0 +1,13 @@
package com.willfp.eco.core.integrations;
/**
* Abstract class for integrations.
*/
public interface Integration {
/**
* Get the name of integration.
*
* @return The name.
*/
String getPluginName();
}

View File

@@ -0,0 +1,71 @@
package com.willfp.eco.core.integrations;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
/**
* An integration loader runs a runnable only if a specific plugin is present on the server.
* <p>
* Used by {@link com.willfp.eco.core.EcoPlugin} to load integrations.
*/
public class IntegrationLoader {
/**
* All loaded plugins on the server.
*/
private static final Set<String> LOADED_PLUGINS = Arrays.stream(Bukkit.getPluginManager().getPlugins())
.map(Plugin::getName)
.map(String::toLowerCase)
.collect(Collectors.toSet());
/**
* The lambda to be run if the plugin is present.
*/
private final Runnable runnable;
/**
* The plugin to require to load the integration.
*/
private final String pluginName;
/**
* Create a new Integration Loader.
*
* @param pluginName The plugin to require.
* @param onLoad The lambda to be run if the plugin is present.
*/
public IntegrationLoader(@NotNull final String pluginName,
@NotNull final Runnable onLoad) {
this.runnable = onLoad;
this.pluginName = pluginName;
}
/**
* Load the integration if the specified plugin is present on the server.
*/
public void loadIfPresent() {
if (LOADED_PLUGINS.contains(this.pluginName.toLowerCase())) {
this.load();
}
}
/**
* Load the integration.
*/
public void load() {
runnable.run();
}
/**
* Get the plugin name.
*
* @return The plugin name.
*/
public String getPluginName() {
return this.pluginName;
}
}

View File

@@ -0,0 +1,46 @@
package com.willfp.eco.core.integrations.afk;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/**
* Class to handle afk integrations.
*/
public final class AFKManager {
/**
* A set of all registered integrations.
*/
private static final Set<AFKWrapper> REGISTERED = new HashSet<>();
/**
* Register a new integration.
*
* @param integration The integration to register.
*/
public static void register(@NotNull final AFKWrapper integration) {
REGISTERED.add(integration);
}
/**
* Get if a player is afk.
*
* @param player The player.
* @return If afk.
*/
public static boolean isAfk(@NotNull final Player player) {
for (AFKWrapper afkWrapper : REGISTERED) {
if (afkWrapper.isAfk(player)) {
return true;
}
}
return false;
}
private AFKManager() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.core.integrations.afk;
import com.willfp.eco.core.integrations.Integration;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Wrapper class for afk integrations.
*/
public interface AFKWrapper extends Integration {
/**
* Get if a player is afk.
*
* @param player The player.
* @return If afk.
*/
boolean isAfk(@NotNull Player player);
}

View File

@@ -0,0 +1,56 @@
package com.willfp.eco.core.integrations.anticheat;
import com.willfp.eco.core.EcoPlugin;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/**
* Class to handle anticheat integrations.
*/
public final class AnticheatManager {
/**
* A set of all registered anticheats.
*/
private static final Set<AnticheatWrapper> ANTICHEATS = new HashSet<>();
/**
* Register a new anticheat.
*
* @param plugin The plugin.
* @param anticheat The anticheat to register.
*/
public static void register(@NotNull final EcoPlugin plugin,
@NotNull final AnticheatWrapper anticheat) {
if (anticheat instanceof Listener) {
plugin.getEventManager().registerListener((Listener) anticheat);
}
ANTICHEATS.add(anticheat);
}
/**
* Exempt a player from triggering anticheats.
*
* @param player The player to exempt.
*/
public static void exemptPlayer(@NotNull final Player player) {
ANTICHEATS.forEach(anticheat -> anticheat.exempt(player));
}
/**
* Unexempt a player from triggering anticheats.
* This is ran a tick after it is called to ensure that there are no event timing conflicts.
*
* @param player The player to remove the exemption.
*/
public static void unexemptPlayer(@NotNull final Player player) {
ANTICHEATS.forEach(anticheat -> anticheat.unexempt(player));
}
private AnticheatManager() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -0,0 +1,24 @@
package com.willfp.eco.core.integrations.anticheat;
import com.willfp.eco.core.integrations.Integration;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Wrapper class for anticheat integrations.
*/
public interface AnticheatWrapper extends Integration {
/**
* Exempt a player from checks.
*
* @param player The player to exempt.
*/
void exempt(@NotNull Player player);
/**
* Unexempt a player from checks.
*
* @param player The player to unexempt.
*/
void unexempt(@NotNull Player player);
}

View File

@@ -0,0 +1,102 @@
package com.willfp.eco.core.integrations.antigrief;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/**
* Class to handle antigrief integrations.
*/
public final class AntigriefManager {
/**
* Registered antigriefs.
*/
private static final Set<AntigriefWrapper> REGISTERED = new HashSet<>();
/**
* Register a new AntiGrief/Land Management integration.
*
* @param antigrief The integration to register.
*/
public static void register(@NotNull final AntigriefWrapper antigrief) {
REGISTERED.add(antigrief);
}
/**
* Unregister an AntiGrief/Land Management integration.
*
* @param antigrief The integration to unregister.
*/
public static void unregister(@NotNull final AntigriefWrapper antigrief) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(antigrief.getPluginName()));
REGISTERED.remove(antigrief);
}
/**
* Can player pickup item.
*
* @param player The player.
* @param location The location.
* @return If player can pick up item.
*/
public static boolean canPickupItem(@NotNull final Player player, @NotNull final Location location) {
return REGISTERED.stream().allMatch(antigriefWrapper -> antigriefWrapper.canPickupItem(player, location));
}
/**
* Can player break block.
*
* @param player The player.
* @param block The block.
* @return If player can break block.
*/
public static boolean canBreakBlock(@NotNull final Player player,
@NotNull final Block block) {
return REGISTERED.stream().allMatch(antigriefWrapper -> antigriefWrapper.canBreakBlock(player, block));
}
/**
* Can player create explosion at location.
*
* @param player The player.
* @param location The location.
* @return If player can create explosion.
*/
public static boolean canCreateExplosion(@NotNull final Player player,
@NotNull final Location location) {
return REGISTERED.stream().allMatch(antigriefWrapper -> antigriefWrapper.canCreateExplosion(player, location));
}
/**
* Can player place block.
*
* @param player The player.
* @param block The block.
* @return If player can place block.
*/
public static boolean canPlaceBlock(@NotNull final Player player,
@NotNull final Block block) {
return REGISTERED.stream().allMatch(antigriefWrapper -> antigriefWrapper.canPlaceBlock(player, block));
}
/**
* Can player injure living entity.
*
* @param player The player.
* @param victim The victim.
* @return If player can injure.
*/
public static boolean canInjure(@NotNull final Player player,
@NotNull final LivingEntity victim) {
return REGISTERED.stream().allMatch(antigriefWrapper -> antigriefWrapper.canInjure(player, victim));
}
private AntigriefManager() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -0,0 +1,60 @@
package com.willfp.eco.core.integrations.antigrief;
import com.willfp.eco.core.integrations.Integration;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Wrapper class for antigrief integrations.
*/
public interface AntigriefWrapper extends Integration {
/**
* Can player break block.
*
* @param player The player.
* @param block The block.
* @return If player can break block.
*/
boolean canBreakBlock(@NotNull Player player, @NotNull Block block);
/**
* Can player create explosion at location.
*
* @param player The player.
* @param location The location.
* @return If player can create explosion.
*/
boolean canCreateExplosion(@NotNull Player player, @NotNull Location location);
/**
* Can player place block.
*
* @param player The player.
* @param block The block.
* @return If player can place block.
*/
boolean canPlaceBlock(@NotNull Player player, @NotNull Block block);
/**
* Can player injure living entity.
*
* @param player The player.
* @param victim The victim.
* @return If player can injure.
*/
boolean canInjure(@NotNull Player player, @NotNull LivingEntity victim);
/**
* Can player pick up item.
*
* @param player The player.
* @param location The location.
* @return If player can pick up item.
*/
default boolean canPickupItem(@NotNull Player player, @NotNull Location location) {
return true;
}
}

View File

@@ -0,0 +1,40 @@
package com.willfp.eco.core.integrations.customentities;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/**
* Class to handle custom entity integrations.
*/
public final class CustomEntitiesManager {
/**
* A set of all registered integrations.
*/
private static final Set<CustomEntitiesWrapper> REGISTERED = new HashSet<>();
/**
* Register a new integration.
*
* @param integration The integration to register.
*/
public static void register(@NotNull final CustomEntitiesWrapper integration) {
REGISTERED.add(integration);
}
/**
* Register all the custom entities for a specific plugin into eco.
*
* @see com.willfp.eco.core.entities.Entities
*/
public static void registerAllEntities() {
for (CustomEntitiesWrapper wrapper : REGISTERED) {
wrapper.registerAllEntities();
}
}
private CustomEntitiesManager() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -0,0 +1,15 @@
package com.willfp.eco.core.integrations.customentities;
import com.willfp.eco.core.integrations.Integration;
/**
* Wrapper class for custom item integrations.
*/
public interface CustomEntitiesWrapper extends Integration {
/**
* Register all the custom entities for a specific plugin into eco.
*
* @see com.willfp.eco.core.entities.Entities
*/
void registerAllEntities();
}

Some files were not shown because too many files have changed in this diff Show More