Multiverse-Core/src/main/java/org/mvplugins/multiverse/core/MultiverseCore.java

426 lines
15 KiB
Java

/******************************************************************************
* Multiverse 2 Copyright (c) the Multiverse Team 2011. *
* Multiverse 2 is licensed under the BSD License. *
* For more information please check the README.md file included *
* with this project. *
******************************************************************************/
package org.mvplugins.multiverse.core;
import java.io.File;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import me.main__.util.SerializationConfig.SerializationConfig;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.glassfish.hk2.api.MultiException;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.ServiceLocator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.api.Destination;
import org.mvplugins.multiverse.core.api.MVCore;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.commandtools.PluginLocales;
import org.mvplugins.multiverse.core.config.MVCoreConfig;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.economy.MVEconomist;
import org.mvplugins.multiverse.core.inject.InjectableListener;
import org.mvplugins.multiverse.core.inject.PluginInjection;
import org.mvplugins.multiverse.core.placeholders.MultiverseCorePlaceholders;
import org.mvplugins.multiverse.core.utils.TestingMode;
import org.mvplugins.multiverse.core.utils.metrics.MetricsConfigurator;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.config.NullLocation;
import org.mvplugins.multiverse.core.world.config.SpawnLocation;
/**
* The implementation of the Multiverse-{@link MVCore}.
*/
@Service
public class MultiverseCore extends JavaPlugin implements MVCore {
private static final int PROTOCOL = 50;
private ServiceLocator serviceLocator;
@Inject
private Provider<MVCoreConfig> configProvider;
@Inject
private Provider<WorldManager> worldManagerProvider;
@Inject
private Provider<AnchorManager> anchorManagerProvider;
@Inject
private Provider<MVCommandManager> commandManagerProvider;
@Inject
private Provider<DestinationsProvider> destinationsProviderProvider;
@Inject
private Provider<MetricsConfigurator> metricsConfiguratorProvider;
@Inject
private Provider<MVEconomist> economistProvider;
@Inject
private Provider<PluginLocales> pluginLocalesProvider;
// Counter for the number of plugins that have registered with us
private int pluginCount;
/**
* This is the constructor for the MultiverseCore.
*/
public MultiverseCore() {
super();
}
@Override
public void onLoad() {
// Setup our Logging
Logging.init(this);
// Create our DataFolder
if (!getDataFolder().exists() && !getDataFolder().mkdirs()) {
Logging.severe("Failed to create data folder!");
getServer().getPluginManager().disablePlugin(this);
return;
}
// Register our config classes
SerializationConfig.initLogging(Logging.getLogger());
ConfigurationSerialization.registerClass(NullLocation.class);
ConfigurationSerialization.registerClass(SpawnLocation.class);
}
/**
* {@inheritDoc}
*/
@Override
public void onEnable() {
initializeDependencyInjection();
// Load our configs first as we need them for everything else.
var config = configProvider.get();
if (!config.isLoaded()) {
Logging.severe("Your configs were not loaded.");
Logging.severe("Please check your configs and restart the server.");
this.getServer().getPluginManager().disablePlugin(this);
return;
}
Logging.setShowingConfig(shouldShowConfig());
// Initialize the worlds
worldManagerProvider.get().initAllWorlds().andThenTry(() -> {
// Setup economy here so vault is loaded
loadEconomist();
// Init all the other stuff
loadAnchors();
registerEvents();
setUpLocales();
registerCommands();
registerDestinations();
setupMetrics();
loadPlaceholderApiIntegration();
saveAllConfigs();
logEnableMessage();
}).onFailure(e -> {
Logging.severe("Failed to multiverse core! Disabling...");
e.printStackTrace();
getServer().getPluginManager().disablePlugin(this);
});
}
/**
* {@inheritDoc}
*/
@Override
public void onDisable() {
saveAllConfigs();
shutdownDependencyInjection();
Logging.shutdown();
}
private void initializeDependencyInjection() {
serviceLocator = PluginInjection.createServiceLocator(new MultiverseCorePluginBinder(this))
.andThenTry(locator -> {
PluginInjection.enable(this, locator);
})
.getOrElseThrow(exception -> {
Logging.severe("Failed to initialize dependency injection");
getServer().getPluginManager().disablePlugin(this);
return new RuntimeException(exception);
});
}
private void shutdownDependencyInjection() {
if (serviceLocator != null) {
PluginInjection.disable(this, serviceLocator);
serviceLocator = null;
}
}
private boolean shouldShowConfig() {
return !configProvider.get().getSilentStart();
}
private void loadEconomist() {
Try.run(() -> economistProvider.get())
.onFailure(e -> {
Logging.severe("Failed to load economy integration");
e.printStackTrace();
});
}
private void loadAnchors() {
Try.of(() -> anchorManagerProvider.get())
.onSuccess(AnchorManager::loadAnchors)
.onFailure(e -> {
Logging.severe("Failed to load anchors");
e.printStackTrace();
});
}
/**
* Function to Register all the Events needed.
*/
private void registerEvents() {
var pluginManager = getServer().getPluginManager();
Try.run(() -> serviceLocator.getAllServices(InjectableListener.class).forEach(
listener -> pluginManager.registerEvents(listener, this)))
.onFailure(e -> {
throw new RuntimeException("Failed to register listeners. Terminating...", e);
});
}
/**
* Register Multiverse-Core commands to Command Manager.
*/
private void registerCommands() {
Try.of(() -> commandManagerProvider.get())
.andThenTry(commandManager -> {
serviceLocator.getAllServices(MultiverseCommand.class)
.forEach(commandManager::registerCommand);
})
.onFailure(e -> {
Logging.severe("Failed to register commands");
e.printStackTrace();
});
}
/**
* Register locales.
*/
private void setUpLocales() {
Try.of(() -> commandManagerProvider.get())
.andThen(commandManager -> {
commandManager.usePerIssuerLocale(true, true);
})
.mapTry(commandManager -> pluginLocalesProvider.get())
.andThen(pluginLocales -> {
pluginLocales.addFileResClassLoader(this);
pluginLocales.addMessageBundles("multiverse-core");
})
.onFailure(e -> {
Logging.severe("Failed to register locales");
e.printStackTrace();
});
}
/**
* Register all the destinations.
*/
private void registerDestinations() {
Try.of(() -> destinationsProviderProvider.get())
.andThenTry(destinationsProvider -> {
serviceLocator.getAllServices(Destination.class)
.forEach(destinationsProvider::registerDestination);
})
.onFailure(e -> {
Logging.severe("Failed to register destinations");
e.printStackTrace();
});
}
/**
* Setup bstats Metrics.
*/
private void setupMetrics() {
if (TestingMode.isDisabled()) {
// Load metrics
Try.of(() -> metricsConfiguratorProvider.get())
.onFailure(e -> {
Logging.severe("Failed to setup metrics");
e.printStackTrace();
});
} else {
Logging.info("Metrics are disabled in testing mode.");
}
}
/**
* Logs the enable message.
*/
private void logEnableMessage() {
Logging.config("Version %s (API v%s) Enabled - By %s", this.getDescription().getVersion(), PROTOCOL, getAuthors());
if (configProvider.get().isShowingDonateMessage()) {
Logging.config("Help dumptruckman keep this project alive. Become a patron! https://www.patreon.com/dumptruckman");
Logging.config("One time donations are also appreciated: https://www.paypal.me/dumptruckman");
}
}
private void loadPlaceholderApiIntegration() {
if (configProvider.get().isRegisterPapiHook()
&& getServer().getPluginManager().getPlugin("PlaceholderAPI") != null) {
Try.run(() -> serviceLocator.createAndInitialize(MultiverseCorePlaceholders.class))
.onFailure(e -> {
Logging.severe("Failed to load PlaceholderAPI integration.");
e.printStackTrace();
});
}
}
/**
* {@inheritDoc}
*/
@Override
public MVCore getCore() {
return this;
}
/**
* {@inheritDoc}
*/
@Override
public int getProtocolVersion() {
return PROTOCOL;
}
/**
* {@inheritDoc}
*/
@Override
public String getAuthors() {
List<String> authorsList = this.getDescription().getAuthors();
if (authorsList.isEmpty()) {
return "";
}
StringBuilder authors = new StringBuilder();
authors.append(authorsList.get(0));
for (int i = 1; i < authorsList.size(); i++) {
if (i == authorsList.size() - 1) {
authors.append(" and ").append(authorsList.get(i));
} else {
authors.append(", ").append(authorsList.get(i));
}
}
return authors.toString();
}
/**
* {@inheritDoc}
*/
@Override
public int getPluginCount() {
return this.pluginCount;
}
@NotNull
@Override
public Logger getLogger() {
return Logging.getLogger();
}
/**
* {@inheritDoc}
*/
@Override
public void incrementPluginCount() {
this.pluginCount += 1;
}
/**
* {@inheritDoc}
*/
@Override
public void decrementPluginCount() {
this.pluginCount -= 1;
}
/**
* {@inheritDoc}
*/
@Override
public boolean saveAllConfigs() {
// TODO: Make this all Try<Void>
return configProvider.get().save().isSuccess()
&& worldManagerProvider.get().saveWorldsConfig()
&& anchorManagerProvider.get().saveAnchors().isSuccess();
}
/**
* Gets the best service from this plugin that implements the given contract or has the given implementation.
*
* @param contractOrImpl The contract or concrete implementation to get the best instance of
* @param qualifiers The set of qualifiers that must match this service definition
* @param <T> The type of the contract to get
* @return An instance of the contract or impl if it is a service and is already instantiated, null otherwise
* @throws MultiException if there was an error during service lookup
*/
@Nullable
public <T> T getService(@NotNull Class<T> contractOrImpl, Annotation... qualifiers) throws MultiException {
var handle = serviceLocator.getServiceHandle(contractOrImpl, qualifiers);
if (handle != null && handle.isActive()) {
return handle.getService();
}
return null;
}
/**
* Gets all services from this plugin that implement the given contract or have the given implementation and have
* the provided qualifiers.
*
* @param contractOrImpl The contract or concrete implementation to get the best instance of
* @param qualifiers The set of qualifiers that must match this service definition
* @param <T> The type of the contract to get
* @return A list of services implementing this contract or concrete implementation. May not return null, but may
* return an empty list.
* @throws MultiException if there was an error during service lookup
*/
@NotNull
public <T> List<T> getAllServices(
@NotNull Class<T> contractOrImpl,
Annotation... qualifiers) throws MultiException {
var handles = serviceLocator.getAllServiceHandles(contractOrImpl, qualifiers);
return handles.stream()
.filter(ServiceHandle::isActive)
.map(ServiceHandle::getService)
.collect(Collectors.toList());
}
/**
* This is for unit testing ONLY. Do not use this constructor.
*
* @param loader The PluginLoader to use.
* @param description The Description file to use.
* @param dataFolder The folder that other datafiles can be found in.
* @param file The location of the plugin.
*/
public MultiverseCore(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) {
super(loader, description, dataFolder, file);
}
}