New command system improvements, broadcast toggles, config fixes #62
43 changed files with 920 additions and 280 deletions
2
ButtonCore/.gitignore → .gitignore
vendored
2
ButtonCore/.gitignore → .gitignore
vendored
|
@ -218,7 +218,7 @@ pip-log.txt
|
|||
.mr.developer.cfg
|
||||
.metadata/*
|
||||
TheButtonAutoFlair/out/artifacts/Autoflair/Autoflair.jar
|
||||
*.iml
|
||||
#*.iml
|
||||
*.name
|
||||
.idea/compiler.xml
|
||||
*.xml
|
|
@ -1,13 +1,13 @@
|
|||
<component name="libraryTable">
|
||||
<library name="Maven: org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/spigotmc/spigot-api/1.12.2-R0.1-SNAPSHOT/spigot-api-1.12.2-R0.1-SNAPSHOT.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/spigotmc/spigot-api/1.12.2-R0.1-SNAPSHOT/spigot-api-1.12.2-R0.1-20180712.012057-156.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/spigotmc/spigot-api/1.12.2-R0.1-SNAPSHOT/spigot-api-1.12.2-R0.1-SNAPSHOT-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/spigotmc/spigot-api/1.12.2-R0.1-SNAPSHOT/spigot-api-1.12.2-R0.1-20180712.012057-156-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/spigotmc/spigot-api/1.12.2-R0.1-SNAPSHOT/spigot-api-1.12.2-R0.1-SNAPSHOT-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/spigotmc/spigot-api/1.12.2-R0.1-SNAPSHOT/spigot-api-1.12.2-R0.1-20180712.012057-156-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
|
@ -12,7 +12,6 @@
|
|||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" />
|
||||
<orderEntry type="module" module-name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" />
|
||||
<orderEntry type="library" name="Maven: org.reflections:reflections:0.9.10" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.google.code.findbugs:annotations:2.0.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.javassist:javassist:3.20.0-GA" level="project" />
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" version="4">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
|
||||
<output url="file://$MODULE_DIR$/target/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Maven: org.reflections:reflections:0.9.10" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.google.guava:guava:15.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.google.code.findbugs:annotations:2.0.1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-lang:commons-lang:2.6" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.gson:gson:2.8.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.19" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: net.md-5:bungeecord-chat:1.12-SNAPSHOT" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-io:commons-io:1.3.2" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.github.TBMCPlugins.ButtonCore:Towny:master-98b73aaac3-1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.github.milkbowl:VaultAPI:master-68f14eca20-1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.bukkit:bukkit:1.13.1-R0.1-SNAPSHOT" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.javassist:javassist:3.20.0-GA" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.mockito:mockito-core:2.7.20" level="project" />
|
||||
<orderEntry type="library" scope="RUNTIME" name="Maven: net.bytebuddy:byte-buddy:1.6.11" level="project" />
|
||||
<orderEntry type="library" scope="RUNTIME" name="Maven: net.bytebuddy:byte-buddy-agent:1.6.11" level="project" />
|
||||
<orderEntry type="library" scope="RUNTIME" name="Maven: org.objenesis:objenesis:2.5" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.16.16" level="project" />
|
||||
<orderEntry type="module" module-name="ButtonProcessor" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: net.ess3:Essentials:2.13.1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.vexsoftware:nuvotifier-universal:2.3.4" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:3.8.1" level="project" />
|
||||
</component>
|
||||
</module>
|
|
@ -75,6 +75,7 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0-M3</version>
|
||||
<configuration>
|
||||
<useSystemClassLoader>false
|
||||
</useSystemClassLoader> <!-- https://stackoverflow.com/a/53012553/2703239 -->
|
||||
|
@ -83,6 +84,7 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
|
@ -103,14 +105,18 @@
|
|||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>vault-repo</id>
|
||||
<url>http://nexus.hc.to/content/repositories/pub_releases</url>
|
||||
</repository>
|
||||
<!-- <repository>
|
||||
<id>vault-repo</id>
|
||||
<url>http://nexus.hc.to/content/repositories/pub_releases</url>
|
||||
</repository> -->
|
||||
<repository>
|
||||
<id>ess-repo</id>
|
||||
<url>http://repo.ess3.net/content/repositories/essrel/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>Votifier</id>
|
||||
<url>https://dl.bintray.com/nuvotifier/maven/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
@ -175,6 +181,12 @@
|
|||
<version>2.13.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.vexsoftware</groupId>
|
||||
<artifactId>nuvotifier-universal</artifactId>
|
||||
<version>2.3.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<organization>
|
||||
<name>TBMCPlugins</name>
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package buttondevteam.core;
|
||||
|
||||
import buttondevteam.lib.TBMCCoreAPI;
|
||||
import buttondevteam.lib.architecture.ButtonPlugin;
|
||||
import buttondevteam.lib.architecture.Component;
|
||||
import buttondevteam.lib.chat.Command2MC;
|
||||
import buttondevteam.lib.chat.Command2;
|
||||
import buttondevteam.lib.chat.Command2.Subcommand;
|
||||
import buttondevteam.lib.chat.CommandClass;
|
||||
import buttondevteam.lib.chat.ICommand2MC;
|
||||
import lombok.val;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
@ -12,30 +15,30 @@ import org.bukkit.plugin.Plugin;
|
|||
import java.util.Optional;
|
||||
|
||||
@CommandClass(modOnly = true, helpText = {
|
||||
"§6---- Component command ----",
|
||||
"Component command",
|
||||
"Can be used to enable/disable/list components"
|
||||
})
|
||||
public class ComponentCommand extends Command2MC {
|
||||
public class ComponentCommand extends ICommand2MC {
|
||||
public ComponentCommand() {
|
||||
addParamConverter(Plugin.class, arg -> Bukkit.getPluginManager().getPlugin(arg));
|
||||
|
||||
getManager().addParamConverter(Plugin.class, arg -> Bukkit.getPluginManager().getPlugin(arg), "Plugin not found!");
|
||||
}
|
||||
|
||||
@Subcommand
|
||||
public boolean enable(CommandSender sender, Plugin plugin, String component) {
|
||||
if (plugin == null) return respond(sender, "§cPlugin not found!");
|
||||
plugin.reloadConfig(); //Reload config so the new config values are read - All changes are saved to disk on disable
|
||||
if (plugin instanceof ButtonPlugin)
|
||||
((ButtonPlugin) plugin).justReload();
|
||||
else
|
||||
plugin.reloadConfig(); //Reload config so the new config values are read - All changes are saved to disk on disable
|
||||
return enable_disable(sender, plugin, component, true);
|
||||
}
|
||||
|
||||
@Subcommand
|
||||
public boolean disable(CommandSender sender, Plugin plugin, String component) {
|
||||
if (plugin == null) return respond(sender, "§cPlugin not found!");
|
||||
return enable_disable(sender, plugin, component, false);
|
||||
}
|
||||
|
||||
@Subcommand
|
||||
public boolean list(CommandSender sender, String plugin) {
|
||||
public boolean list(CommandSender sender, @Command2.OptionalArg String plugin) {
|
||||
sender.sendMessage("§6List of components:");
|
||||
Component.getComponents().values().stream().filter(c -> plugin == null || c.getPlugin().getName().equalsIgnoreCase(plugin)) //If plugin is null, don't check
|
||||
.map(c -> c.getPlugin().getName() + " - " + c.getClass().getSimpleName() + " - " + (c.isEnabled() ? "en" : "dis") + "abled").forEach(sender::sendMessage);
|
||||
|
@ -55,7 +58,7 @@ public class ComponentCommand extends Command2MC {
|
|||
return true;
|
||||
}
|
||||
|
||||
private Optional<Component> getComponentOrError(Plugin plugin, String arg, CommandSender sender) {
|
||||
private Optional<Component<?>> getComponentOrError(Plugin plugin, String arg, CommandSender sender) {
|
||||
val oc = Component.getComponents().values().stream()
|
||||
.filter(c -> plugin.getName().equals(c.getPlugin().getName()))
|
||||
.filter(c -> c.getClass().getSimpleName().equalsIgnoreCase(arg)).findAny();
|
||||
|
|
|
@ -34,9 +34,10 @@ public final class ComponentManager {
|
|||
/**
|
||||
* Unregister all components of a plugin that are enabled - called on {@link ButtonPlugin} disable
|
||||
*/
|
||||
public static void unregComponents(ButtonPlugin plugin) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends ButtonPlugin> void unregComponents(T plugin) {
|
||||
while (!plugin.getComponentStack().empty()) //Unregister in reverse order
|
||||
Component.unregisterComponent(plugin, plugin.getComponentStack().pop()); //Components are pushed on register
|
||||
Component.unregisterComponent(plugin, (Component<T>) plugin.getComponentStack().pop()); //Components are pushed on register
|
||||
componentsEnabled = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,17 +9,20 @@ import buttondevteam.core.component.restart.RestartComponent;
|
|||
import buttondevteam.core.component.towny.TownyComponent;
|
||||
import buttondevteam.core.component.updater.PluginUpdater;
|
||||
import buttondevteam.core.component.updater.PluginUpdaterComponent;
|
||||
import buttondevteam.core.component.votifier.VotifierComponent;
|
||||
import buttondevteam.lib.TBMCCoreAPI;
|
||||
import buttondevteam.lib.architecture.ButtonPlugin;
|
||||
import buttondevteam.lib.architecture.Component;
|
||||
import buttondevteam.lib.architecture.ConfigData;
|
||||
import buttondevteam.lib.chat.Color;
|
||||
import buttondevteam.lib.chat.Command2MC;
|
||||
import buttondevteam.lib.chat.TBMCChatAPI;
|
||||
import buttondevteam.lib.player.ChromaGamerBase;
|
||||
import buttondevteam.lib.player.TBMCPlayer;
|
||||
import buttondevteam.lib.player.TBMCPlayerBase;
|
||||
import com.earth2me.essentials.Essentials;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import net.milkbowl.vault.permission.Permission;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.BlockCommandSender;
|
||||
|
@ -42,41 +45,62 @@ import java.util.logging.Logger;
|
|||
|
||||
public class MainPlugin extends ButtonPlugin {
|
||||
public static MainPlugin Instance;
|
||||
@Nullable
|
||||
public static Permission permission;
|
||||
public static boolean Test;
|
||||
public static Essentials ess;
|
||||
|
||||
private Logger logger;
|
||||
@Nullable
|
||||
private Economy economy;
|
||||
/**
|
||||
* Whether the Core's chat handler should be enabled.
|
||||
* Other chat plugins handling messages from other platforms should set this to false.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean chatHandlerEnabled = true;
|
||||
|
||||
private ConfigData<Boolean> writePluginList() {
|
||||
return getIConfig().getData("writePluginList", false);
|
||||
}
|
||||
|
||||
ConfigData<String> chatFormat() {
|
||||
return getIConfig().getData("chatFormat", "[{origin}|" +
|
||||
"{channel}] <{name}> {message}");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pluginEnable() {
|
||||
// Logs "Plugin Enabled", registers commands
|
||||
Instance = this;
|
||||
PluginDescriptionFile pdf = getDescription();
|
||||
logger = getLogger();
|
||||
setupPermissions();
|
||||
if (!setupPermissions())
|
||||
throw new NullPointerException("No permission plugin found!");
|
||||
if (!setupEconomy()) //Though Essentials always provides economy so this shouldn't happen
|
||||
getLogger().warning("No economy plugin found! Components using economy will not be registered.");
|
||||
Test = getConfig().getBoolean("test", true);
|
||||
saveConfig();
|
||||
Component.registerComponent(this, new PluginUpdaterComponent());
|
||||
Component.registerComponent(this, new RestartComponent());
|
||||
//noinspection unchecked - needed for testing
|
||||
Component.registerComponent(this, new ChannelComponent());
|
||||
Component.registerComponent(this, new RandomTPComponent());
|
||||
Component.registerComponent(this, new MemberComponent());
|
||||
Component.registerComponent(this, new TownyComponent());
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("Towny")) //It fails to load the component class otherwise
|
||||
Component.registerComponent(this, new TownyComponent());
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("Votifier") && economy != null)
|
||||
Component.registerComponent(this, new VotifierComponent(economy));
|
||||
ComponentManager.enableComponents();
|
||||
Command2MC.registerCommand(new ComponentCommand());
|
||||
getCommand2MC().registerCommand(new ComponentCommand());
|
||||
getCommand2MC().registerCommand(new ThorpeCommand());
|
||||
TBMCCoreAPI.RegisterEventsForExceptions(new PlayerListener(), this);
|
||||
ChromaGamerBase.addConverter(commandSender -> Optional.ofNullable(commandSender instanceof ConsoleCommandSender || commandSender instanceof BlockCommandSender
|
||||
? TBMCPlayer.getPlayer(new UUID(0, 0), TBMCPlayer.class) : null)); //Console & cmdblocks
|
||||
ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof Player
|
||||
? TBMCPlayer.getPlayer(((Player) sender).getUniqueId(), TBMCPlayer.class) : null)); //Players, has higher priority
|
||||
TBMCCoreAPI.RegisterUserClass(TBMCPlayerBase.class);
|
||||
TBMCChatAPI.RegisterChatChannel(Channel.GlobalChat = new Channel("§fOOC§f", Color.White, "g", null)); //The /ooc ID has moved to the config
|
||||
TBMCChatAPI.RegisterChatChannel(Channel.GlobalChat = new Channel("§fg§f", Color.White, "g", null)); //The /ooc ID has moved to the config
|
||||
TBMCChatAPI.RegisterChatChannel(
|
||||
Channel.AdminChat = new Channel("§cADMIN§f", Color.Red, "a", Channel.inGroupFilter(null)));
|
||||
TBMCChatAPI.RegisterChatChannel(
|
||||
|
@ -122,14 +146,23 @@ public class MainPlugin extends ButtonPlugin {
|
|||
}
|
||||
|
||||
private boolean setupPermissions() {
|
||||
RegisteredServiceProvider<Permission> permissionProvider = getServer().getServicesManager()
|
||||
.getRegistration(Permission.class);
|
||||
if (permissionProvider != null) {
|
||||
permission = permissionProvider.getProvider();
|
||||
}
|
||||
permission = setupProvider(Permission.class);
|
||||
return (permission != null);
|
||||
}
|
||||
|
||||
private boolean setupEconomy() {
|
||||
economy = setupProvider(Economy.class);
|
||||
return (economy != null);
|
||||
}
|
||||
|
||||
private <T> T setupProvider(Class<T> cl) {
|
||||
RegisteredServiceProvider<T> provider = getServer().getServicesManager()
|
||||
.getRegistration(cl);
|
||||
if (provider != null)
|
||||
return provider.getProvider();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (command.getName().equals("dontrunthiscmd")) return true; //Used in chat preprocess for console
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
package buttondevteam.core;
|
||||
|
||||
import buttondevteam.lib.TBMCCommandPreprocessEvent;
|
||||
import buttondevteam.lib.TBMCCoreAPI;
|
||||
import buttondevteam.lib.TBMCSystemChatEvent;
|
||||
import buttondevteam.lib.chat.Command2MC;
|
||||
import buttondevteam.lib.*;
|
||||
import buttondevteam.lib.architecture.ButtonPlugin;
|
||||
import buttondevteam.lib.chat.ChatMessage;
|
||||
import buttondevteam.lib.chat.Command2MCSender;
|
||||
import buttondevteam.lib.chat.TBMCChatAPI;
|
||||
import buttondevteam.lib.player.TBMCPlayer;
|
||||
import buttondevteam.lib.player.TBMCPlayerBase;
|
||||
import lombok.val;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
@ -64,9 +68,36 @@ public class PlayerListener implements Listener {
|
|||
public void onTBMCPreprocess(TBMCCommandPreprocessEvent event) {
|
||||
if (event.isCancelled()) return;
|
||||
try {
|
||||
event.setCancelled(Command2MC.handleCommand(event.getSender(), event.getMessage()));
|
||||
event.setCancelled(ButtonPlugin.getCommand2MC().handleCommand(new Command2MCSender(event.getSender()), event.getMessage()));
|
||||
} catch (Exception e) {
|
||||
TBMCCoreAPI.SendException("Command processing failed for sender '" + event.getSender() + "' and message '" + event.getMessage() + "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH) //The one in the chat plugin is set to highest
|
||||
public void onPlayerChat(AsyncPlayerChatEvent event) {
|
||||
if (event.isCancelled())
|
||||
return; //The chat plugin should cancel it after this handler
|
||||
val cp = TBMCPlayer.getPlayer(event.getPlayer().getUniqueId(), TBMCPlayer.class);
|
||||
TBMCChatAPI.SendChatMessage(ChatMessage.builder(event.getPlayer(), cp, event.getMessage()).build());
|
||||
//Not cancelling the original event here, it's cancelled in the chat plugin
|
||||
//This way other plugins can deal with the MC formatting if the chat plugin isn't present, but other platforms still get the message
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH) //The one in the chat plugin is set to highest
|
||||
public void onPlayerChat(TBMCChatEvent event) {
|
||||
if (event.isCancelled())
|
||||
return;
|
||||
if (!MainPlugin.Instance.isChatHandlerEnabled()) return;
|
||||
if (event.getOrigin().equals("Minecraft")) return; //Let other plugins handle MC messages
|
||||
String msg = MainPlugin.Instance.chatFormat().get()
|
||||
.replace("{channel}", event.getChannel().DisplayName().get())
|
||||
.replace("{origin}", event.getOrigin().substring(0, 1))
|
||||
.replace("{name}", ThorpeUtils.getDisplayName(event.getSender()))
|
||||
.replace("{message}", event.getMessage());
|
||||
for (Player player : Bukkit.getOnlinePlayers())
|
||||
if (event.shouldSendTo(player))
|
||||
player.sendMessage(msg);
|
||||
Bukkit.getConsoleSender().sendMessage(msg);
|
||||
}
|
||||
}
|
|
@ -41,6 +41,7 @@ public class TestPrepare {
|
|||
return cl.isAssignableFrom(invocation.getMethod().getReturnType());
|
||||
}
|
||||
}));
|
||||
//noinspection unchecked
|
||||
Component.registerComponent(Mockito.mock(JavaPlugin.class), new ChannelComponent());
|
||||
TBMCChatAPI.RegisterChatChannel(Channel.GlobalChat = new Channel("§fg§f", Color.White, "g", null));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package buttondevteam.core;
|
||||
|
||||
import buttondevteam.lib.chat.Command2;
|
||||
import buttondevteam.lib.chat.CommandClass;
|
||||
import buttondevteam.lib.chat.ICommand2MC;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
@CommandClass
|
||||
public class ThorpeCommand extends ICommand2MC {
|
||||
@Command2.Subcommand //TODO: Main permissions (groups) like 'mod'
|
||||
public void reload(CommandSender sender) {
|
||||
if (MainPlugin.Instance.tryReloadConfig())
|
||||
sender.sendMessage("§bConfig reloaded.");
|
||||
else
|
||||
sender.sendMessage("§cFailed to reload config. Check console.");
|
||||
}
|
||||
}
|
|
@ -1,22 +1,30 @@
|
|||
package buttondevteam.core.component.channel;
|
||||
|
||||
import buttondevteam.lib.TBMCSystemChatEvent;
|
||||
import buttondevteam.lib.architecture.Component;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* Manages chat channels. If disabled, only global channels will be registered.
|
||||
*/
|
||||
public class ChannelComponent extends Component {
|
||||
static TBMCSystemChatEvent.BroadcastTarget roomJoinLeave;
|
||||
|
||||
@Override
|
||||
protected void register(JavaPlugin plugin) {
|
||||
super.register(plugin);
|
||||
roomJoinLeave = TBMCSystemChatEvent.BroadcastTarget.add("roomJoinLeave"); //Even if it's disabled, global channels continue to work
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregister(JavaPlugin plugin) {
|
||||
super.unregister(plugin);
|
||||
TBMCSystemChatEvent.BroadcastTarget.remove(roomJoinLeave);
|
||||
roomJoinLeave = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enable() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package buttondevteam.core.component.channel;
|
||||
|
||||
import buttondevteam.lib.TBMCSystemChatEvent;
|
||||
import buttondevteam.lib.chat.Color;
|
||||
import buttondevteam.lib.chat.TBMCChatAPI;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
@ -17,11 +18,11 @@ public class ChatRoom extends Channel {
|
|||
|
||||
public void joinRoom(CommandSender sender) {
|
||||
usersInRoom.add(sender);
|
||||
TBMCChatAPI.SendSystemMessage(this, RecipientTestResult.ALL, sender.getName() + " joined the room");
|
||||
TBMCChatAPI.SendSystemMessage(this, RecipientTestResult.ALL, sender.getName() + " joined the room", TBMCSystemChatEvent.BroadcastTarget.ALL); //Always show message in the same kind of channel
|
||||
}
|
||||
|
||||
public void leaveRoom(CommandSender sender) {
|
||||
usersInRoom.remove(sender);
|
||||
TBMCChatAPI.SendSystemMessage(this, RecipientTestResult.ALL, sender.getName() + " left the room");
|
||||
TBMCChatAPI.SendSystemMessage(this, RecipientTestResult.ALL, sender.getName() + " left the room", ChannelComponent.roomJoinLeave);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,56 +1,47 @@
|
|||
package buttondevteam.core.component.members;
|
||||
|
||||
import buttondevteam.core.MainPlugin;
|
||||
import buttondevteam.lib.chat.Command2;
|
||||
import buttondevteam.lib.chat.CommandClass;
|
||||
import buttondevteam.lib.chat.TBMCCommandBase;
|
||||
import lombok.val;
|
||||
import buttondevteam.lib.chat.ICommand2MC;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
@CommandClass(modOnly = true, path = "member")
|
||||
public class MemberCommand extends TBMCCommandBase {
|
||||
@Override
|
||||
public boolean OnCommand(CommandSender sender, String alias, String[] args) {
|
||||
if (args.length < 2)
|
||||
return false;
|
||||
final boolean add;
|
||||
if (args[0].equalsIgnoreCase("add"))
|
||||
add = true;
|
||||
else if (args[0].equalsIgnoreCase("remove"))
|
||||
add = false;
|
||||
else
|
||||
return false;
|
||||
@CommandClass(modOnly = true, path = "member", helpText = { //
|
||||
"Member command", //
|
||||
"Add or remove server members.", //
|
||||
})
|
||||
public class MemberCommand extends ICommand2MC {
|
||||
private final MemberComponent component;
|
||||
|
||||
public MemberCommand(MemberComponent component) {
|
||||
getManager().addParamConverter(OfflinePlayer.class, Bukkit::getOfflinePlayer, "Player not found!");
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
@Command2.Subcommand
|
||||
public boolean add(CommandSender sender, OfflinePlayer player) {
|
||||
return addRemove(sender, player, true);
|
||||
}
|
||||
|
||||
@Command2.Subcommand
|
||||
public boolean remove(CommandSender sender, OfflinePlayer player) {
|
||||
return addRemove(sender, player, false);
|
||||
}
|
||||
|
||||
public boolean addRemove(CommandSender sender, OfflinePlayer op, boolean add) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.Instance, () -> {
|
||||
if (MainPlugin.permission == null) {
|
||||
sender.sendMessage("§cError: No permission plugin found!");
|
||||
return;
|
||||
}
|
||||
val op = Bukkit.getOfflinePlayer(args[1]);
|
||||
if (!op.hasPlayedBefore()) {
|
||||
sender.sendMessage("§cCannot find player or haven't played before.");
|
||||
return;
|
||||
}
|
||||
if (add) {
|
||||
if (MainPlugin.permission.playerAddGroup(null, op, "member"))
|
||||
sender.sendMessage("§b" + op.getName() + " added as a member!");
|
||||
else
|
||||
sender.sendMessage("§cFailed to add " + op.getName() + " as a member!");
|
||||
} else {
|
||||
if (MainPlugin.permission.playerRemoveGroup(null, op, "member"))
|
||||
sender.sendMessage("§b" + op.getName() + " removed as a member!");
|
||||
else
|
||||
sender.sendMessage("§bFailed to remove " + op.getName() + " as a member!");
|
||||
}
|
||||
if (add ? MainPlugin.permission.playerAddGroup(null, op, component.memberGroup().get())
|
||||
: MainPlugin.permission.playerRemoveGroup(null, op, component.memberGroup().get()))
|
||||
sender.sendMessage("§b" + op.getName() + " " + (add ? "added" : "removed") + " as a member!");
|
||||
else
|
||||
sender.sendMessage("§cFailed to " + (add ? "add" : "remove") + " " + op.getName() + " as a member!");
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] GetHelpText(String alias) {
|
||||
return new String[]{ //
|
||||
"06---- Member ----", //
|
||||
"Add or remove server members.", //
|
||||
"Usage: /member <add|remove> <player>" //
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,15 +14,35 @@ import java.util.Date;
|
|||
|
||||
import static buttondevteam.core.MainPlugin.permission;
|
||||
|
||||
public class MemberComponent extends Component implements Listener {
|
||||
private ConfigData<String> memberGroup() {
|
||||
/**
|
||||
* Allows giving a 'member' group over some time elapsed OR played.
|
||||
*/
|
||||
public class MemberComponent extends Component<MainPlugin> implements Listener {
|
||||
/**
|
||||
* The permission group to give to the player
|
||||
*/
|
||||
ConfigData<String> memberGroup() {
|
||||
return getConfig().getData("memberGroup", "member");
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount of hours needed to play before promotion
|
||||
*/
|
||||
private ConfigData<Integer> playedHours() {
|
||||
return getConfig().getData("playedHours", 12);
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount of days passed since first login
|
||||
*/
|
||||
private ConfigData<Integer> registeredForDays() {
|
||||
return getConfig().getData("registeredForDays", 7);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enable() {
|
||||
registerListener(this);
|
||||
registerCommand(new MemberCommand());
|
||||
registerCommand(new MemberCommand(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -32,8 +52,8 @@ public class MemberComponent extends Component implements Listener {
|
|||
@EventHandler
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
if (permission != null && !permission.playerInGroup(event.getPlayer(), memberGroup().get())
|
||||
&& (new Date(event.getPlayer().getFirstPlayed()).toInstant().plus(7, ChronoUnit.DAYS).isBefore(Instant.now())
|
||||
|| event.getPlayer().getStatistic(Statistic.PLAY_ONE_TICK) > 20 * 3600 * 12)) {
|
||||
&& (new Date(event.getPlayer().getFirstPlayed()).toInstant().plus(registeredForDays().get(), ChronoUnit.DAYS).isBefore(Instant.now())
|
||||
|| event.getPlayer().getStatistic(Statistic.PLAY_ONE_TICK) > 20 * 3600 * playedHours().get())) {
|
||||
permission.playerAddGroup(null, event.getPlayer(), memberGroup().get());
|
||||
event.getPlayer().sendMessage("§bYou are a member now. YEEHAW");
|
||||
MainPlugin.Instance.getLogger().info("Added " + event.getPlayer().getName() + " as a member.");
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
package buttondevteam.core.component.randomtp;
|
||||
|
||||
import buttondevteam.core.MainPlugin;
|
||||
import buttondevteam.lib.architecture.Component;
|
||||
|
||||
public class RandomTPComponent extends Component {
|
||||
/**
|
||||
* Teleport player to random location within world border.
|
||||
* Every five players teleport to the same general area, and then a new general area is randomly selected for the next five players.
|
||||
*/
|
||||
public class RandomTPComponent extends Component<MainPlugin> {
|
||||
@Override
|
||||
protected void enable() {
|
||||
new RandomTP().onEnable(this); //It registers it's command
|
||||
|
|
|
@ -1,26 +1,31 @@
|
|||
package buttondevteam.core.component.restart;
|
||||
|
||||
import buttondevteam.core.component.channel.Channel;
|
||||
import buttondevteam.lib.chat.CommandClass;
|
||||
import buttondevteam.lib.chat.TBMCChatAPI;
|
||||
import buttondevteam.lib.chat.TBMCCommandBase;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
@CommandClass(path = "primerestart", modOnly = true)
|
||||
@RequiredArgsConstructor
|
||||
public class PrimeRestartCommand extends TBMCCommandBase {
|
||||
private final RestartComponent component;
|
||||
@Override
|
||||
public boolean OnCommand(CommandSender sender, String alias, String[] args) {
|
||||
loud = args.length > 0;
|
||||
if (Bukkit.getOnlinePlayers().size() > 0) {
|
||||
sender.sendMessage("§bPlayers online, restart delayed.");
|
||||
if (loud)
|
||||
Bukkit.broadcastMessage(ChatColor.DARK_RED + "The server will restart as soon as nobody is online.");
|
||||
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, ChatColor.DARK_RED + "The server will restart as soon as nobody is online.", component.getRestartBroadcast());
|
||||
plsrestart = true;
|
||||
} else {
|
||||
sender.sendMessage("§bNobody is online. Restarting now.");
|
||||
if (loud)
|
||||
Bukkit.broadcastMessage("§cNobody is online. Restarting server.");
|
||||
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§cNobody is online. Restarting server.", component.getRestartBroadcast());
|
||||
Bukkit.spigot().restart();
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -1,29 +1,39 @@
|
|||
package buttondevteam.core.component.restart;
|
||||
|
||||
import buttondevteam.core.MainPlugin;
|
||||
import buttondevteam.core.component.channel.Channel;
|
||||
import buttondevteam.lib.TBMCSystemChatEvent;
|
||||
import buttondevteam.lib.architecture.Component;
|
||||
import buttondevteam.lib.chat.IFakePlayer;
|
||||
import buttondevteam.lib.chat.TBMCChatAPI;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
public class RestartComponent extends Component implements Listener {
|
||||
/**
|
||||
* Provides commands such as /schrestart (restart after a countdown) and /primerestart (restart when nobody is online)
|
||||
*/
|
||||
public class RestartComponent extends Component<MainPlugin> implements Listener {
|
||||
@Override
|
||||
public void enable() {
|
||||
//TODO: Permissions for the commands
|
||||
TBMCChatAPI.AddCommand(this, new ScheduledRestartCommand());
|
||||
TBMCChatAPI.AddCommand(this, new PrimeRestartCommand());
|
||||
registerCommand(new ScheduledRestartCommand(this));
|
||||
TBMCChatAPI.AddCommand(this, new PrimeRestartCommand(this));
|
||||
registerListener(this);
|
||||
restartBroadcast = TBMCSystemChatEvent.BroadcastTarget.add("restartCountdown");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
|
||||
TBMCSystemChatEvent.BroadcastTarget.remove(restartBroadcast);
|
||||
}
|
||||
|
||||
private long lasttime = 0;
|
||||
@Getter
|
||||
private TBMCSystemChatEvent.BroadcastTarget restartBroadcast;
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerLeave(PlayerQuitEvent event) {
|
||||
|
@ -32,12 +42,12 @@ public class RestartComponent extends Component implements Listener {
|
|||
&& !event.getQuitMessage().equalsIgnoreCase("Server is restarting")) {
|
||||
if (Bukkit.getOnlinePlayers().size() <= 1) {
|
||||
if (PrimeRestartCommand.isLoud())
|
||||
Bukkit.broadcastMessage("§cNobody is online anymore. Restarting.");
|
||||
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§cNobody is online anymore. Restarting.", restartBroadcast);
|
||||
Bukkit.spigot().restart();
|
||||
} else if (!(event.getPlayer() instanceof IFakePlayer) && System.nanoTime() - 10 * 1000000000L - lasttime > 0) { //Ten seconds passed since last reminder
|
||||
lasttime = System.nanoTime();
|
||||
if (PrimeRestartCommand.isLoud())
|
||||
Bukkit.broadcastMessage(ChatColor.DARK_RED + "The server will restart as soon as nobody is online.");
|
||||
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, ChatColor.DARK_RED + "The server will restart as soon as nobody is online.", restartBroadcast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package buttondevteam.core.component.restart;
|
||||
|
||||
import buttondevteam.core.MainPlugin;
|
||||
import buttondevteam.core.component.channel.Channel;
|
||||
import buttondevteam.lib.ScheduledServerRestartEvent;
|
||||
import buttondevteam.lib.chat.Command2;
|
||||
import buttondevteam.lib.chat.CommandClass;
|
||||
import buttondevteam.lib.chat.TBMCCommandBase;
|
||||
import buttondevteam.lib.chat.ICommand2MC;
|
||||
import buttondevteam.lib.chat.TBMCChatAPI;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.boss.BarColor;
|
||||
|
@ -14,16 +18,23 @@ import org.bukkit.boss.BossBar;
|
|||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
@CommandClass(modOnly = true, path = "schrestart")
|
||||
public class ScheduledRestartCommand extends TBMCCommandBase {
|
||||
@CommandClass(modOnly = true, path = "schrestart", helpText = {
|
||||
"Scheduled restart", //
|
||||
"This command restarts the server 1 minute after it's executed, warning players every 10 seconds.", //
|
||||
"You can optionally set the amount of seconds to wait before the restart." //
|
||||
})
|
||||
@RequiredArgsConstructor
|
||||
public class ScheduledRestartCommand extends ICommand2MC {
|
||||
@Getter
|
||||
@Setter
|
||||
private int restartCounter;
|
||||
private BukkitTask restarttask;
|
||||
private volatile BossBar restartbar;
|
||||
@Getter
|
||||
private final RestartComponent component;
|
||||
|
||||
@Override
|
||||
public boolean OnCommand(CommandSender sender, String alias, String[] args) {
|
||||
@Command2.Subcommand
|
||||
public boolean def(CommandSender sender, String alias, String[] args) {
|
||||
int secs = 60;
|
||||
try {
|
||||
if (args.length > 0)
|
||||
|
@ -51,20 +62,11 @@ public class ScheduledRestartCommand extends TBMCCommandBase {
|
|||
Bukkit.spigot().restart();
|
||||
}
|
||||
if (restartCounter % 200 == 0)
|
||||
Bukkit.broadcastMessage("§c-- The server is restarting in " + restartCounter / 20 + " seconds! (/press)");
|
||||
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§c-- The server is restarting in " + restartCounter / 20 + " seconds! (/press)", component.getRestartBroadcast());
|
||||
restartbar.setProgress(restartCounter / (double) restarttime);
|
||||
restartbar.setTitle(String.format("Server restart in %.2f", restartCounter / 20f));
|
||||
restartCounter--;
|
||||
}, 1, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] GetHelpText(String alias) {
|
||||
return new String[] { //
|
||||
"§6---- Scheduled restart ----", //
|
||||
"This command restarts the server 1 minute after it's executed, warning players every 10 seconds.", //
|
||||
"You can optionally set the amount of ticks to wait before the restart." //
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package buttondevteam.core.component.towny;
|
||||
|
||||
import buttondevteam.core.ComponentManager;
|
||||
import buttondevteam.core.MainPlugin;
|
||||
import buttondevteam.lib.TBMCCoreAPI;
|
||||
import buttondevteam.lib.architecture.Component;
|
||||
import com.palmergames.bukkit.towny.Towny;
|
||||
|
@ -10,7 +11,10 @@ import com.palmergames.bukkit.towny.object.Resident;
|
|||
import com.palmergames.bukkit.towny.object.TownyUniverse;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
public class TownyComponent extends Component {
|
||||
/**
|
||||
* Automatically renames Towny players if they changed their Minecraft name
|
||||
*/
|
||||
public class TownyComponent extends Component<MainPlugin> {
|
||||
@Override
|
||||
protected void enable() {
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package buttondevteam.core.component.updater;
|
||||
|
||||
import buttondevteam.core.MainPlugin;
|
||||
import buttondevteam.lib.architecture.Component;
|
||||
import buttondevteam.lib.chat.TBMCChatAPI;
|
||||
|
||||
public class PluginUpdaterComponent extends Component {
|
||||
/**
|
||||
* Downloads plugin updates built from their source using JitPack - older code
|
||||
*/
|
||||
public class PluginUpdaterComponent extends Component<MainPlugin> { //TODO: Config
|
||||
@Override
|
||||
public void enable() {
|
||||
TBMCChatAPI.AddCommand(this, new UpdatePluginCommand());
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package buttondevteam.core.component.votifier;
|
||||
|
||||
import buttondevteam.core.MainPlugin;
|
||||
import buttondevteam.lib.architecture.Component;
|
||||
import buttondevteam.lib.architecture.ConfigData;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import com.vexsoftware.votifier.model.VotifierEvent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
|
||||
/**
|
||||
* Do not use (EULA)
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class VotifierComponent extends Component<MainPlugin> {
|
||||
private final Economy economy;
|
||||
|
||||
private ConfigData<Double> rewardAmount() {
|
||||
return getConfig().getData("rewardAmount", 0.0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enable() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disable() {
|
||||
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@SuppressWarnings("deprecation")
|
||||
public void onVotifierEvent(VotifierEvent event) {
|
||||
Vote vote = event.getVote();
|
||||
getPlugin().getLogger().info("Vote: " + vote);
|
||||
org.bukkit.OfflinePlayer op = Bukkit.getOfflinePlayer(vote.getUsername());
|
||||
Player p = Bukkit.getPlayer(vote.getUsername());
|
||||
if (op != null) {
|
||||
economy.depositPlayer(op, rewardAmount().get());
|
||||
}
|
||||
if (p != null) {
|
||||
p.sendMessage("§bThanks for voting! $50 was added to your account.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ public class TBMCCommandPreprocessEvent extends Event implements Cancellable {
|
|||
|
||||
public TBMCCommandPreprocessEvent(CommandSender sender, String message) {
|
||||
this.sender = sender;
|
||||
this.message = message; //TODO: Actually call from Discord as well
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
package buttondevteam.lib;
|
||||
|
||||
import buttondevteam.core.component.channel.Channel;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Make sure to only send the message to users who {@link #shouldSendTo(CommandSender)} returns true.
|
||||
*
|
||||
|
@ -14,15 +22,17 @@ import org.bukkit.event.HandlerList;
|
|||
@Getter
|
||||
public class TBMCSystemChatEvent extends TBMCChatEventBase {
|
||||
private final String[] exceptions;
|
||||
private final BroadcastTarget target;
|
||||
private boolean handled;
|
||||
|
||||
public void setHandled() {
|
||||
handled = true;
|
||||
}
|
||||
|
||||
public TBMCSystemChatEvent(Channel channel, String message, int score, String groupid, String[] exceptions) { // TODO: Rich message
|
||||
public TBMCSystemChatEvent(Channel channel, String message, int score, String groupid, String[] exceptions, BroadcastTarget target) { // TODO: Rich message
|
||||
super(channel, message, score, groupid);
|
||||
this.exceptions = exceptions;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
@ -35,4 +45,30 @@ public class TBMCSystemChatEvent extends TBMCChatEventBase {
|
|||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public static class BroadcastTarget {
|
||||
private final @Getter String name;
|
||||
private static final HashSet<BroadcastTarget> targets = new HashSet<>();
|
||||
public static final BroadcastTarget ALL = new BroadcastTarget("ALL");
|
||||
|
||||
public static BroadcastTarget add(String name) {
|
||||
val bt = new BroadcastTarget(Objects.requireNonNull(name));
|
||||
targets.add(bt);
|
||||
return bt;
|
||||
}
|
||||
|
||||
public static void remove(BroadcastTarget target) {
|
||||
targets.remove(target);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static BroadcastTarget get(String name) {
|
||||
return targets.stream().filter(bt -> bt.name.equalsIgnoreCase(name)).findAny().orElse(null);
|
||||
}
|
||||
|
||||
public static Stream<BroadcastTarget> stream() {
|
||||
return targets.stream();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,4 +35,20 @@ public final class ThorpeUtils {
|
|||
*/
|
||||
String getFancyFullName();
|
||||
}
|
||||
|
||||
public static Number convertNumber(Number number, Class<? extends Number> targetcl) {
|
||||
if (targetcl == long.class || Long.class.isAssignableFrom(targetcl))
|
||||
return number.longValue();
|
||||
else if (targetcl == int.class || Integer.class.isAssignableFrom(targetcl))
|
||||
return number.intValue(); //Needed because the parser can get longs
|
||||
else if (targetcl == short.class || Short.class.isAssignableFrom(targetcl))
|
||||
return number.shortValue();
|
||||
else if (targetcl == byte.class || Byte.class.isAssignableFrom(targetcl))
|
||||
return number.byteValue();
|
||||
else if (targetcl == float.class || Float.class.isAssignableFrom(targetcl))
|
||||
return number.floatValue();
|
||||
else if (targetcl == double.class || Double.class.isAssignableFrom(targetcl))
|
||||
return number.doubleValue();
|
||||
return number;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package buttondevteam.lib.architecture;
|
|||
|
||||
import buttondevteam.core.ComponentManager;
|
||||
import buttondevteam.lib.TBMCCoreAPI;
|
||||
import buttondevteam.lib.chat.Command2MC;
|
||||
import buttondevteam.lib.chat.TBMCChatAPI;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
|
@ -10,14 +11,20 @@ import org.bukkit.plugin.java.JavaPlugin;
|
|||
|
||||
import java.util.Stack;
|
||||
|
||||
@HasConfig
|
||||
public abstract class ButtonPlugin extends JavaPlugin {
|
||||
@Getter
|
||||
private static Command2MC command2MC = new Command2MC();
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
private IHaveConfig iConfig;
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
private IHaveConfig data; //TODO
|
||||
private boolean loaded = false;
|
||||
/**
|
||||
* Used to unregister components in the right order
|
||||
* Used to unregister components in the right order - and to reload configs
|
||||
*/
|
||||
@Getter
|
||||
private Stack<Component> componentStack = new Stack<>();
|
||||
private Stack<Component<?>> componentStack = new Stack<>();
|
||||
|
||||
protected abstract void pluginEnable();
|
||||
|
||||
|
@ -34,9 +41,7 @@ public abstract class ButtonPlugin extends JavaPlugin {
|
|||
|
||||
@Override
|
||||
public final void onEnable() {
|
||||
var section = super.getConfig().getConfigurationSection("global");
|
||||
if (section == null) section = super.getConfig().createSection("global");
|
||||
iConfig = new IHaveConfig(section);
|
||||
loadConfig();
|
||||
try {
|
||||
pluginEnable();
|
||||
} catch (Exception e) {
|
||||
|
@ -44,6 +49,12 @@ public abstract class ButtonPlugin extends JavaPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
private void loadConfig() {
|
||||
var section = super.getConfig().getConfigurationSection("global");
|
||||
if (section == null) section = super.getConfig().createSection("global");
|
||||
iConfig = new IHaveConfig(section, this::saveConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onDisable() {
|
||||
try {
|
||||
|
@ -57,4 +68,26 @@ public abstract class ButtonPlugin extends JavaPlugin {
|
|||
TBMCCoreAPI.SendException("Error while disabling plugin " + getName() + "!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadConfig() {
|
||||
tryReloadConfig();
|
||||
}
|
||||
|
||||
public boolean tryReloadConfig() {
|
||||
if (!justReload()) return false;
|
||||
loadConfig();
|
||||
componentStack.forEach(c -> Component.updateConfig(this, c));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean justReload() {
|
||||
if (loaded && ConfigData.saveNow(getConfig())) {
|
||||
getLogger().warning("Saved pending configuration changes to the file, didn't reload (try again).");
|
||||
return false;
|
||||
}
|
||||
super.reloadConfig();
|
||||
loaded = true; //Needed because for the first time it uses reloadConfig() to load it
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,34 +3,39 @@ package buttondevteam.lib.architecture;
|
|||
import buttondevteam.core.ComponentManager;
|
||||
import buttondevteam.lib.TBMCCoreAPI;
|
||||
import buttondevteam.lib.architecture.exceptions.UnregisteredComponentException;
|
||||
import buttondevteam.lib.chat.Command2MC;
|
||||
import buttondevteam.lib.chat.ICommand2MC;
|
||||
import buttondevteam.lib.chat.TBMCChatAPI;
|
||||
import buttondevteam.lib.chat.TBMCCommandBase;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.experimental.var;
|
||||
import lombok.val;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Configuration is based on class name
|
||||
*/
|
||||
public abstract class Component {
|
||||
private static HashMap<Class<? extends Component>, Component> components = new HashMap<>();
|
||||
@HasConfig //Used for obtaining javadoc
|
||||
public abstract class Component<TP extends JavaPlugin> {
|
||||
private static HashMap<Class<? extends Component>, Component<? extends JavaPlugin>> components = new HashMap<>();
|
||||
|
||||
@Getter
|
||||
private boolean enabled = false;
|
||||
@Getter
|
||||
@NonNull
|
||||
private JavaPlugin plugin;
|
||||
private TP plugin;
|
||||
@NonNull
|
||||
private @Getter
|
||||
IHaveConfig config;
|
||||
private @Getter IHaveConfig data; //TODO
|
||||
|
||||
public final ConfigData<Boolean> shouldBeEnabled() {
|
||||
return config.getData("enabled", true);
|
||||
|
@ -45,7 +50,7 @@ public abstract class Component {
|
|||
* @param component The component to register
|
||||
* @return Whether the component is registered successfully (it may have failed to enable)
|
||||
*/
|
||||
public static boolean registerComponent(JavaPlugin plugin, Component component) {
|
||||
public static <T extends JavaPlugin> boolean registerComponent(T plugin, Component<T> component) {
|
||||
return registerUnregisterComponent(plugin, component, true);
|
||||
}
|
||||
|
||||
|
@ -57,11 +62,11 @@ public abstract class Component {
|
|||
* @param component The component to unregister
|
||||
* @return Whether the component is unregistered successfully (it also got disabled)
|
||||
*/
|
||||
public static boolean unregisterComponent(JavaPlugin plugin, Component component) {
|
||||
public static <T extends JavaPlugin> boolean unregisterComponent(T plugin, Component<T> component) {
|
||||
return registerUnregisterComponent(plugin, component, false);
|
||||
}
|
||||
|
||||
public static boolean registerUnregisterComponent(JavaPlugin plugin, Component component, boolean register) {
|
||||
public static <T extends JavaPlugin> boolean registerUnregisterComponent(T plugin, Component<T> component, boolean register) {
|
||||
try {
|
||||
val metaAnn = component.getClass().getAnnotation(ComponentMetadata.class);
|
||||
if (metaAnn != null) {
|
||||
|
@ -135,16 +140,16 @@ public abstract class Component {
|
|||
}
|
||||
}
|
||||
|
||||
private static void updateConfig(JavaPlugin plugin, Component component) {
|
||||
public static void updateConfig(JavaPlugin plugin, Component component) {
|
||||
if (plugin.getConfig() != null) { //Production
|
||||
var compconf = plugin.getConfig().getConfigurationSection("components");
|
||||
if (compconf == null) compconf = plugin.getConfig().createSection("components");
|
||||
var configSect = compconf.getConfigurationSection(component.getClassName());
|
||||
if (configSect == null)
|
||||
configSect = compconf.createSection(component.getClassName());
|
||||
component.config = new IHaveConfig(configSect);
|
||||
component.config = new IHaveConfig(configSect, plugin::saveConfig);
|
||||
} else //Testing
|
||||
component.config = new IHaveConfig(null);
|
||||
component.config = new IHaveConfig(null, plugin::saveConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,7 +157,7 @@ public abstract class Component {
|
|||
*
|
||||
* @return The currently registered components
|
||||
*/
|
||||
public static Map<Class<? extends Component>, Component> getComponents() {
|
||||
public static Map<Class<? extends Component>, Component<? extends JavaPlugin>> getComponents() {
|
||||
return Collections.unmodifiableMap(components);
|
||||
}
|
||||
|
||||
|
@ -197,8 +202,8 @@ public abstract class Component {
|
|||
*
|
||||
* @param commandBase Custom coded command class
|
||||
*/
|
||||
protected final void registerCommand(Command2MC commandBase) {
|
||||
Command2MC.registerCommand(commandBase);
|
||||
protected final void registerCommand(ICommand2MC commandBase) {
|
||||
ButtonPlugin.getCommand2MC().registerCommand(commandBase);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -221,6 +226,28 @@ public abstract class Component {
|
|||
return listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of configs that are under the given key.
|
||||
* @param key The key to use
|
||||
* @param defaultProvider A mapping between config paths and config generators
|
||||
* @return A map containing configs
|
||||
*/
|
||||
protected Map<String, IHaveConfig> getConfigMap(String key, Map<String, Consumer<IHaveConfig>> defaultProvider) {
|
||||
val c=getConfig().getConfig();
|
||||
var cs=c.getConfigurationSection(key);
|
||||
if(cs==null) cs=c.createSection(key);
|
||||
val res = cs.getValues(false).entrySet().stream().filter(e -> e.getValue() instanceof ConfigurationSection)
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, kv -> new IHaveConfig((ConfigurationSection) kv.getValue(), getPlugin()::saveConfig)));
|
||||
if (res.size() == 0) {
|
||||
for (val entry : defaultProvider.entrySet()) {
|
||||
val conf = new IHaveConfig(cs.createSection(entry.getKey()), getPlugin()::saveConfig);
|
||||
entry.getValue().accept(conf);
|
||||
res.put(entry.getKey(), conf);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private String getClassName() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
|
|
|
@ -8,5 +8,5 @@ import java.lang.annotation.Target;
|
|||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ComponentMetadata {
|
||||
Class<? extends Component>[] depends();
|
||||
Class<? extends Component>[] depends() default {};
|
||||
}
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
package buttondevteam.lib.architecture;
|
||||
|
||||
import buttondevteam.core.MainPlugin;
|
||||
import buttondevteam.lib.ThorpeUtils;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -14,7 +23,8 @@ import java.util.function.Function;
|
|||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
//@AllArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
public class ConfigData<T> { //TODO: Save after a while
|
||||
public class ConfigData<T> {
|
||||
private static final HashMap<Configuration, SaveTask> saveTasks = new HashMap<>();
|
||||
/**
|
||||
* May be null for testing
|
||||
*/
|
||||
|
@ -22,6 +32,7 @@ public class ConfigData<T> { //TODO: Save after a while
|
|||
private final String path;
|
||||
private final T def;
|
||||
private final Object primitiveDef;
|
||||
private final Runnable saveAction;
|
||||
/**
|
||||
* The parameter is of a primitive type as returned by {@link YamlConfiguration#get(String)}
|
||||
*/
|
||||
|
@ -35,15 +46,20 @@ public class ConfigData<T> { //TODO: Save after a while
|
|||
* The config value should not change outside this instance
|
||||
*/
|
||||
private T value;
|
||||
/**
|
||||
* Whether the default value is saved in the yaml
|
||||
*/
|
||||
private boolean saved = false;
|
||||
|
||||
public ConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
|
||||
//This constructor is needed because it sets the getter and setter
|
||||
public ConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter, Runnable saveAction) {
|
||||
this.config = config;
|
||||
this.path = path;
|
||||
this.def = def;
|
||||
this.primitiveDef = primitiveDef;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
this.saveAction=saveAction;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -54,7 +70,10 @@ public class ConfigData<T> { //TODO: Save after a while
|
|||
val = primitiveDef;
|
||||
}
|
||||
if (!saved && Objects.equals(val, primitiveDef)) { //String needs .equals()
|
||||
set(def); //Save default value - def is always set
|
||||
if (def == null && config != null) //In Discord's case def may be null
|
||||
config.set(path, primitiveDef);
|
||||
else
|
||||
set(def); //Save default value - def is always set
|
||||
saved = true;
|
||||
}
|
||||
if (getter != null) {
|
||||
|
@ -62,28 +81,49 @@ public class ConfigData<T> { //TODO: Save after a while
|
|||
if (hmm == null) hmm = def; //Set if the getter returned null
|
||||
return hmm;
|
||||
}
|
||||
if (val instanceof Number) {
|
||||
if (def instanceof Long)
|
||||
val = ((Number) val).longValue();
|
||||
else if (def instanceof Short)
|
||||
val = ((Number) val).shortValue();
|
||||
else if (def instanceof Byte)
|
||||
val = ((Number) val).byteValue();
|
||||
else if (def instanceof Float)
|
||||
val = ((Number) val).floatValue();
|
||||
else if (def instanceof Double)
|
||||
val = ((Number) val).doubleValue();
|
||||
}
|
||||
return (T) val;
|
||||
if (val instanceof Number && def != null)
|
||||
val = ThorpeUtils.convertNumber((Number) val,
|
||||
(Class<? extends Number>) def.getClass());
|
||||
if (val instanceof List && def != null && def.getClass().isArray())
|
||||
val = ((List<T>) val).toArray((T[]) Array.newInstance(def.getClass().getComponentType(), 0));
|
||||
return value = (T) val; //Always cache, if not cached yet
|
||||
}
|
||||
|
||||
public void set(T value) {
|
||||
Object val;
|
||||
if (setter != null)
|
||||
if (setter != null && value != null)
|
||||
val = setter.apply(value);
|
||||
else val = value;
|
||||
if (config != null)
|
||||
if (config != null) {
|
||||
config.set(path, val);
|
||||
if(!saveTasks.containsKey(config.getRoot())) {
|
||||
synchronized (saveTasks) {
|
||||
saveTasks.put(config.getRoot(), new SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, () -> {
|
||||
synchronized (saveTasks) {
|
||||
saveTasks.remove(config.getRoot());
|
||||
saveAction.run();
|
||||
}
|
||||
}, 100), saveAction));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
private static class SaveTask {
|
||||
BukkitTask task;
|
||||
Runnable saveAction;
|
||||
}
|
||||
|
||||
public static boolean saveNow(Configuration config) {
|
||||
SaveTask st = saveTasks.get(config);
|
||||
if (st != null) {
|
||||
st.task.cancel();
|
||||
saveTasks.remove(config);
|
||||
st.saveAction.run();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package buttondevteam.lib.architecture;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Used to generate documentation for the config
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Inherited
|
||||
public @interface HasConfig {
|
||||
}
|
|
@ -3,6 +3,8 @@ package buttondevteam.lib.architecture;
|
|||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.function.Function;
|
||||
|
@ -15,14 +17,16 @@ public final class IHaveConfig {
|
|||
private final HashMap<String, ConfigData<?>> datamap = new HashMap<>();
|
||||
@Getter
|
||||
private ConfigurationSection config;
|
||||
private final Runnable saveAction;
|
||||
|
||||
/**
|
||||
* May be used in testing.
|
||||
*
|
||||
* @param section May be null for testing
|
||||
*/
|
||||
IHaveConfig(ConfigurationSection section) {
|
||||
IHaveConfig(ConfigurationSection section, Runnable saveAction) {
|
||||
config = section;
|
||||
this.saveAction=saveAction;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,7 +40,7 @@ public final class IHaveConfig {
|
|||
@SuppressWarnings("unchecked")
|
||||
public <T> ConfigData<T> getData(String path, T def) {
|
||||
ConfigData<?> data = datamap.get(path);
|
||||
if (data == null) datamap.put(path, data = new ConfigData<>(config, path, def, def));
|
||||
if (data == null) datamap.put(path, data = new ConfigData<>(config, path, def, def, saveAction));
|
||||
return (ConfigData<T>) data;
|
||||
}
|
||||
|
||||
|
@ -54,7 +58,7 @@ public final class IHaveConfig {
|
|||
public <T> ConfigData<T> getData(String path, T def, Function<Object, T> getter, Function<T, Object> setter) {
|
||||
ConfigData<?> data = datamap.get(path);
|
||||
if (data == null)
|
||||
datamap.put(path, data = new ConfigData<>(config, path, def, setter.apply(def), getter, setter));
|
||||
datamap.put(path, data = new ConfigData<>(config, path, def, setter.apply(def), getter, setter, saveAction));
|
||||
return (ConfigData<T>) data;
|
||||
}
|
||||
|
||||
|
@ -72,7 +76,7 @@ public final class IHaveConfig {
|
|||
public <T> ConfigData<T> getDataPrimDef(String path, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
|
||||
ConfigData<?> data = datamap.get(path);
|
||||
if (data == null)
|
||||
datamap.put(path, data = new ConfigData<>(config, path, getter.apply(primitiveDef), primitiveDef, getter, setter));
|
||||
datamap.put(path, data = new ConfigData<>(config, path, getter.apply(primitiveDef), primitiveDef, getter, setter, saveAction));
|
||||
return (ConfigData<T>) data;
|
||||
}
|
||||
|
||||
|
@ -89,7 +93,7 @@ public final class IHaveConfig {
|
|||
ConfigData<?> data = datamap.get(path);
|
||||
if (data == null) {
|
||||
val defval = def.get();
|
||||
datamap.put(path, data = new ConfigData<>(config, path, defval, defval));
|
||||
datamap.put(path, data = new ConfigData<>(config, path, defval, defval, saveAction));
|
||||
}
|
||||
return (ConfigData<T>) data;
|
||||
}
|
||||
|
@ -109,7 +113,7 @@ public final class IHaveConfig {
|
|||
ConfigData<?> data = datamap.get(path);
|
||||
if (data == null) {
|
||||
val defval = def.get();
|
||||
datamap.put(path, data = new ConfigData<>(config, path, defval, setter.apply(defval), getter, setter));
|
||||
datamap.put(path, data = new ConfigData<>(config, path, defval, setter.apply(defval), getter, setter, saveAction));
|
||||
}
|
||||
return (ConfigData<T>) data;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package buttondevteam.lib.chat;
|
||||
|
||||
import buttondevteam.lib.TBMCCoreAPI;
|
||||
import buttondevteam.lib.ThorpeUtils;
|
||||
import buttondevteam.lib.player.ChromaGamerBase;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.var;
|
||||
import lombok.val;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
|
@ -13,43 +14,25 @@ import java.lang.annotation.ElementType;
|
|||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The method name is the subcommand, use underlines (_) to add further subcommands.
|
||||
* The args may be null if the conversion failed.
|
||||
* The args may be null if the conversion failed and it's optional.
|
||||
*/
|
||||
public abstract class Command2 {
|
||||
/**
|
||||
* Default handler for commands, can be used to copy the args too.
|
||||
*
|
||||
* @param sender The sender which ran the command
|
||||
* @param args All of the arguments passed as is
|
||||
* @return The success of the command
|
||||
*/
|
||||
public boolean def(CommandSender sender, @TextArg String args) {
|
||||
return false;
|
||||
public abstract class Command2<TC extends ICommand2, TP extends Command2Sender> {
|
||||
protected Command2() {
|
||||
commandHelp.add("§6---- Commands ----");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method. Return with this.
|
||||
*
|
||||
* @param sender The sender of the command
|
||||
* @param message The message to send to the sender
|
||||
* @return Always true so that the usage isn't shown
|
||||
*/
|
||||
protected boolean respond(CommandSender sender, String message) {
|
||||
sender.sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: @CommandClass(helpText=...)
|
||||
* Parameters annotated with this receive all of the remaining arguments
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
|
@ -69,17 +52,29 @@ public abstract class Command2 {
|
|||
String[] helpText() default {};
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
protected static class SubcommandData<T extends Command2> {
|
||||
public final Method method;
|
||||
public final T command;
|
||||
public final String[] helpText;
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface OptionalArg {
|
||||
}
|
||||
|
||||
public Command2() {
|
||||
path = getcmdpath();
|
||||
@AllArgsConstructor
|
||||
protected static class SubcommandData<T extends ICommand2> {
|
||||
public final Method method;
|
||||
public final T command;
|
||||
public String[] helpText;
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
protected static class ParamConverter<T> {
|
||||
public final Function<String, T> converter;
|
||||
public final String errormsg;
|
||||
}
|
||||
|
||||
private HashMap<String, SubcommandData<TC>> subcommands = new HashMap<>();
|
||||
private HashMap<Class<?>, ParamConverter<?>> paramConverters = new HashMap<>();
|
||||
|
||||
private ArrayList<String> commandHelp = new ArrayList<>(); //Mainly needed by Discord
|
||||
|
||||
/**
|
||||
* Adds a param converter that obtains a specific object from a string parameter.
|
||||
* The converter may return null.
|
||||
|
@ -88,16 +83,23 @@ public abstract class Command2 {
|
|||
* @param converter The converter to use
|
||||
* @param <T> The type of the result
|
||||
*/
|
||||
protected static <T> void addParamConverter(Class<T> cl, Function<String, T> converter, HashMap<Class<?>, Function<String, ?>> map) {
|
||||
map.put(cl, converter);
|
||||
public <T> void addParamConverter(Class<T> cl, Function<String, T> converter, String errormsg) {
|
||||
paramConverters.put(cl, new ParamConverter<>(converter, errormsg));
|
||||
}
|
||||
|
||||
protected static <T extends Command2> boolean handleCommand(CommandSender sender, String commandline,
|
||||
HashMap<String, SubcommandData<T>> subcommands, HashMap<Class<?>, Function<String, ?>> paramConverters) throws Exception {
|
||||
public boolean handleCommand(TP sender, String commandline) throws Exception {
|
||||
for (int i = commandline.length(); i != -1; i = commandline.lastIndexOf(' ', i - 1)) {
|
||||
String subcommand = commandline.substring(0, i).toLowerCase();
|
||||
SubcommandData sd = subcommands.get(subcommand); //O(1)
|
||||
if (sd == null) continue; //TODO: This will run each time someone runs any command
|
||||
SubcommandData<TC> sd = subcommands.get(subcommand); //O(1)
|
||||
if (sd == null) continue;
|
||||
if (sd.method == null || sd.command == null) { //Main command not registered, but we have subcommands
|
||||
sender.sendMessage(sd.helpText);
|
||||
return true;
|
||||
}
|
||||
if (!hasPermission(sender, sd.command)) {
|
||||
sender.sendMessage("§cYou don't have permission to use this command");
|
||||
return true;
|
||||
}
|
||||
val params = new ArrayList<Object>(sd.method.getParameterCount());
|
||||
int j = subcommand.length(), pj;
|
||||
Class<?>[] parameterTypes = sd.method.getParameterTypes();
|
||||
|
@ -107,77 +109,142 @@ public abstract class Command2 {
|
|||
final ChromaGamerBase cg;
|
||||
if (sendertype.isAssignableFrom(sender.getClass()))
|
||||
params.add(sender); //The command either expects a CommandSender or it is a Player, or some other expected type
|
||||
else if (sender instanceof Command2MCSender
|
||||
&& sendertype.isAssignableFrom(((Command2MCSender) sender).getSender().getClass()))
|
||||
params.add(((Command2MCSender) sender).getSender());
|
||||
else if (ChromaGamerBase.class.isAssignableFrom(sendertype)
|
||||
&& (cg = ChromaGamerBase.getFromSender(sender)) != null
|
||||
&& sender instanceof Command2MCSender
|
||||
&& (cg = ChromaGamerBase.getFromSender(((Command2MCSender) sender).getSender())) != null
|
||||
&& cg.getClass() == sendertype) //The command expects a user of our system
|
||||
params.add(cg);
|
||||
else {
|
||||
sender.sendMessage("§cYou need to be a " + sendertype.getSimpleName() + " to use this command.");
|
||||
return true;
|
||||
}
|
||||
val paramArr = sd.method.getParameters();
|
||||
for (int i1 = 1; i1 < parameterTypes.length; i1++) {
|
||||
Class<?> cl = parameterTypes[i1];
|
||||
pj = j + 1; //Start index
|
||||
if (pj == commandline.length() + 1) { //No param given
|
||||
params.add(null);
|
||||
continue; //Fill the remaining params with nulls
|
||||
if (paramArr[i1].isAnnotationPresent(OptionalArg.class)) {
|
||||
params.add(null);
|
||||
continue; //Fill the remaining params with nulls
|
||||
} else {
|
||||
sender.sendMessage(sd.helpText); //Required param missing
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (paramArr[i1].isVarArgs()) {
|
||||
params.add(commandline.substring(j + 1).split(" +"));
|
||||
continue;
|
||||
}
|
||||
j = commandline.indexOf(' ', j + 1); //End index
|
||||
if (j == -1) //Last parameter
|
||||
if (j == -1 || paramArr[i1].isAnnotationPresent(TextArg.class)) //Last parameter
|
||||
j = commandline.length();
|
||||
String param = commandline.substring(pj, j);
|
||||
if (cl == String.class) {
|
||||
params.add(param);
|
||||
continue;
|
||||
} else if (Number.class.isAssignableFrom(cl) || cl.isPrimitive()) {
|
||||
try {
|
||||
//System.out.println("Converting "+param+" param to "+cl.getSimpleName());
|
||||
//noinspection unchecked
|
||||
Number n = ThorpeUtils.convertNumber(NumberFormat.getInstance().parse(param), (Class<? extends Number>) cl);
|
||||
//System.out.println(n.getClass().getSimpleName()+" with value "+n);
|
||||
params.add(n);
|
||||
} catch (ParseException e) {
|
||||
sender.sendMessage("§c'" + param + "' is not a number.");
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
val conv = paramConverters.get(cl);
|
||||
if (conv == null)
|
||||
throw new Exception("No suitable converter found for parameter type '" + cl.getCanonicalName() + "' for command '" + sd.method.toString() + "'");
|
||||
params.add(conv.apply(param));
|
||||
val cparam = conv.converter.apply(param);
|
||||
if (cparam == null) {
|
||||
sender.sendMessage(conv.errormsg); //Param conversion failed - ex. plugin not found
|
||||
return true;
|
||||
}
|
||||
params.add(cparam);
|
||||
}
|
||||
//System.out.println("Our params: "+params);
|
||||
val ret = sd.method.invoke(sd.command, params.toArray()); //I FORGOT TO TURN IT INTO AN ARRAY (for a long time)
|
||||
if (ret instanceof Boolean) {
|
||||
if (!(boolean) ret) //Show usage
|
||||
sender.sendMessage(sd.helpText);
|
||||
} else if (ret != null)
|
||||
throw new Exception("Wrong return type! Must return a boolean or void. Return value: "+ret);
|
||||
return true; //We found a method
|
||||
try {
|
||||
val ret = sd.method.invoke(sd.command, params.toArray()); //I FORGOT TO TURN IT INTO AN ARRAY (for a long time)
|
||||
if (ret instanceof Boolean) {
|
||||
if (!(boolean) ret) //Show usage
|
||||
sender.sendMessage(sd.helpText);
|
||||
} else if (ret != null)
|
||||
throw new Exception("Wrong return type! Must return a boolean or void. Return value: " + ret);
|
||||
return true; //We found a method
|
||||
} catch (InvocationTargetException e) {
|
||||
TBMCCoreAPI.SendException("An error occurred in a command handler!", e.getCause());
|
||||
}
|
||||
}
|
||||
return false; //Didn't handle
|
||||
} //TODO: Add to the help
|
||||
|
||||
protected static <T extends Command2> void registerCommand(T command, HashMap<String, SubcommandData<T>> subcommands, char commandChar) {
|
||||
public abstract void registerCommand(TC command);
|
||||
|
||||
protected void registerCommand(TC command, char commandChar) {
|
||||
val path = command.getCommandPath();
|
||||
int x = path.indexOf(' ');
|
||||
val mainPath = commandChar + path.substring(0, x == -1 ? path.length() : x);
|
||||
//var scmdmap = subcommandStrings.computeIfAbsent(mainPath, k -> new HashSet<>()); //Used to display subcommands
|
||||
val scmdHelpList = new ArrayList<String>();
|
||||
Method mainMethod = null;
|
||||
boolean nosubs = true;
|
||||
boolean isSubcommand = x != -1;
|
||||
try { //Register the default handler first so it can be reliably overwritten
|
||||
val method = command.getClass().getMethod("def", CommandSender.class, String.class);
|
||||
mainMethod = command.getClass().getMethod("def", Command2Sender.class, String.class);
|
||||
val cc = command.getClass().getAnnotation(CommandClass.class);
|
||||
var ht = cc == null ? new String[0] : cc.helpText();
|
||||
String[] both = Arrays.copyOf(ht, ht.length + 1);
|
||||
both[ht.length] = "Usage: " + commandChar + path; //TODO: Print subcommands
|
||||
ht = both;
|
||||
subcommands.put(commandChar + path, new SubcommandData<>(method, command, ht)); //TODO: Disable components when the plugin is disabled
|
||||
var ht = cc == null || isSubcommand ? new String[0] : cc.helpText(); //If it's not the main command, don't add it
|
||||
if (ht.length > 0)
|
||||
ht[0] = "§6---- " + ht[0] + " ----";
|
||||
scmdHelpList.addAll(Arrays.asList(ht));
|
||||
if (!isSubcommand)
|
||||
scmdHelpList.add("§6Subcommands:");
|
||||
if (!commandHelp.contains(mainPath))
|
||||
commandHelp.add(mainPath);
|
||||
} catch (Exception e) {
|
||||
TBMCCoreAPI.SendException("Could not register default handler for command /" + path, e);
|
||||
} //Continue on
|
||||
}
|
||||
for (val method : command.getClass().getMethods()) {
|
||||
val ann = method.getAnnotation(Subcommand.class);
|
||||
if (ann != null) {
|
||||
val cc = command.getClass().getAnnotation(CommandClass.class);
|
||||
var ht = ann.helpText().length != 0 || cc == null ? ann.helpText() : cc.helpText(); //If cc is null then it's empty array
|
||||
if (ann == null) continue; //Don't call the method on non-subcommands because they're not in the yaml
|
||||
var ht = command.getHelpText(method, ann);
|
||||
if (ht != null) {
|
||||
val subcommand = commandChar + path + //Add command path (class name by default)
|
||||
(method.getName().equals("def") ? "" : " " + method.getName().replace('_', ' ').toLowerCase()); //Add method name, unless it's 'def'
|
||||
ht = getHelpText(method, ht, subcommand);
|
||||
subcommands.put(subcommand, new SubcommandData<>(method, command, ht)); //Result of the above (def) is that it will show the help text
|
||||
scmdHelpList.add(subcommand);
|
||||
nosubs = false;
|
||||
}
|
||||
}
|
||||
if (nosubs && scmdHelpList.size() > 0)
|
||||
scmdHelpList.remove(scmdHelpList.size() - 1); //Remove Subcommands header
|
||||
if (mainMethod != null && !subcommands.containsKey(commandChar + path)) //Command specified by the class
|
||||
subcommands.put(commandChar + path, new SubcommandData<>(mainMethod, command, scmdHelpList.toArray(new String[0])));
|
||||
if (mainMethod != null && !mainPath.equals(commandChar + path)) { //Main command, typically the same as the above
|
||||
if (isSubcommand) { //The class itself is a subcommand
|
||||
val scmd = subcommands.computeIfAbsent(mainPath, p -> new SubcommandData<>(null, null, new String[]{"§6---- Subcommands ----"}));
|
||||
val scmdHelp = Arrays.copyOf(scmd.helpText, scmd.helpText.length + scmdHelpList.size());
|
||||
for (int i = 0; i < scmdHelpList.size(); i++)
|
||||
scmdHelp[scmd.helpText.length + i] = scmdHelpList.get(i);
|
||||
scmd.helpText = scmdHelp;
|
||||
} else if (!subcommands.containsKey(mainPath))
|
||||
subcommands.put(mainPath, new SubcommandData<>(null, null, scmdHelpList.toArray(new String[0])));
|
||||
}
|
||||
}
|
||||
|
||||
private static String[] getHelpText(Method method, String[] ht, String subcommand) { //TODO: helpText[0]="§6---- "+helpText[0]+" ----";
|
||||
private String[] getHelpText(Method method, String[] ht, String subcommand) {
|
||||
val str = method.getDeclaringClass().getResourceAsStream("/commands.yml");
|
||||
if (str == null)
|
||||
TBMCCoreAPI.SendException("Error while getting command data!", new Exception("Resource not found!"));
|
||||
else {
|
||||
if (ht.length > 0)
|
||||
ht[0] = "§6---- " + ht[0] + " ----";
|
||||
YamlConfiguration yc = YamlConfiguration.loadConfiguration(new InputStreamReader(str)); //Generated by ButtonProcessor
|
||||
val ccs = yc.getConfigurationSection(method.getDeclaringClass().getCanonicalName());
|
||||
if (ccs != null) {
|
||||
|
@ -185,10 +252,11 @@ public abstract class Command2 {
|
|||
if (cs != null) {
|
||||
val mname = cs.getString("method");
|
||||
val params = cs.getString("params");
|
||||
val goodname = method.getName() + "(" + Arrays.stream(method.getParameterTypes()).map(cl -> cl.getCanonicalName()).collect(Collectors.joining(",")) + ")";
|
||||
if (goodname.equals(mname) && params != null) {
|
||||
//val goodname = method.getName() + "(" + Arrays.stream(method.getGenericParameterTypes()).map(cl -> cl.getTypeName()).collect(Collectors.joining(",")) + ")";
|
||||
int i = mname.indexOf('('); //Check only the name - the whole method is still stored for backwards compatibility and in case it may be useful
|
||||
if (i != -1 && method.getName().equals(mname.substring(0, i)) && params != null) {
|
||||
String[] both = Arrays.copyOf(ht, ht.length + 1);
|
||||
both[ht.length] = "Usage: " + subcommand + " " + params;
|
||||
both[ht.length] = "§6Usage:§r " + subcommand + " " + params;
|
||||
ht = both;
|
||||
} else
|
||||
TBMCCoreAPI.SendException("Error while getting command data for " + method + "!", new Exception("Method '" + method.toString() + "' != " + mname + " or params is " + params));
|
||||
|
@ -200,28 +268,15 @@ public abstract class Command2 {
|
|||
return ht;
|
||||
}
|
||||
|
||||
private final String path;
|
||||
public abstract boolean hasPermission(TP sender, TC command);
|
||||
|
||||
/**
|
||||
* The command's path, or name if top-level command.<br>
|
||||
* For example:<br>
|
||||
* "u admin updateplugin" or "u" for the top level one<br>
|
||||
* <u>The path must be lowercase!</u><br>
|
||||
*
|
||||
* @return The command path, <i>which is the command class name by default</i> (removing any "command" from it) - Change via the {@link CommandClass} annotation
|
||||
*/
|
||||
public final String getCommandPath() {
|
||||
return path;
|
||||
public String[] getCommandsText() {
|
||||
return commandHelp.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private String getcmdpath() {
|
||||
if (!getClass().isAnnotationPresent(CommandClass.class))
|
||||
throw new RuntimeException(
|
||||
"No @CommandClass annotation on command class " + getClass().getSimpleName() + "!");
|
||||
Function<Class<?>, String> getFromClass = cl -> cl.getSimpleName().toLowerCase().replace("commandbase", "") // <-- ...
|
||||
.replace("command", "");
|
||||
String path = getClass().getAnnotation(CommandClass.class).path();
|
||||
path = path.length() == 0 ? getFromClass.apply(getClass()) : path;
|
||||
return path;
|
||||
public String[] getHelpText(String path) {
|
||||
val scmd = subcommands.get(path);
|
||||
if (scmd == null) return null;
|
||||
return scmd.helpText;
|
||||
}
|
||||
} //TODO: Test support of Player instead of CommandSender
|
||||
}
|
||||
|
|
|
@ -1,24 +1,43 @@
|
|||
package buttondevteam.lib.chat;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import buttondevteam.core.MainPlugin;
|
||||
import lombok.val;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class Command2MC extends Command2 {
|
||||
|
||||
private static HashMap<String, SubcommandData<Command2MC>> subcommands = new HashMap<>();
|
||||
private static HashMap<Class<?>, Function<String, ?>> paramConverters = new HashMap<>();
|
||||
|
||||
public static boolean handleCommand(CommandSender sender, String commandLine) throws Exception {
|
||||
return handleCommand(sender, commandLine, subcommands, paramConverters);
|
||||
public class Command2MC extends Command2<ICommand2MC, Command2MCSender> {
|
||||
@Override
|
||||
public void registerCommand(ICommand2MC command) {
|
||||
super.registerCommand(command, '/');
|
||||
}
|
||||
|
||||
public static void registerCommand(Command2MC command) {
|
||||
registerCommand(command, subcommands, '/');
|
||||
@Override
|
||||
public boolean hasPermission(Command2MCSender sender, ICommand2MC command) {
|
||||
return modOnly(command)
|
||||
? MainPlugin.permission.has(sender.getSender(), "tbmc.admin") //TODO: Change when groups are implemented
|
||||
: MainPlugin.permission.has(sender.getSender(), "thorpe.command." + command.getCommandPath().replace(' ', '.'));
|
||||
}
|
||||
|
||||
public static <T> void addParamConverter(Class<T> cl, Function<String, T> converter) {
|
||||
addParamConverter(cl, converter, paramConverters);
|
||||
/**
|
||||
* Returns true if this class or <u>any</u> of the superclasses are mod only.
|
||||
*
|
||||
* @param command The command to check
|
||||
* @return Whether the command is mod only
|
||||
*/
|
||||
private boolean modOnly(ICommand2MC command) {
|
||||
for (Class<?> cl = command.getClass(); cl != null; cl = cl.getSuperclass()) {
|
||||
val cc = command.getClass().getAnnotation(CommandClass.class);
|
||||
if (cc != null && cc.modOnly()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically colors the message red.
|
||||
* {@see super#addParamConverter}
|
||||
*/
|
||||
@Override
|
||||
public <T> void addParamConverter(Class<T> cl, Function<String, T> converter, String errormsg) {
|
||||
super.addParamConverter(cl, converter, "§c" + errormsg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package buttondevteam.lib.chat;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class Command2MCSender implements Command2Sender {
|
||||
private @Getter final CommandSender sender;
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String[] message) {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package buttondevteam.lib.chat;
|
||||
|
||||
public interface Command2Sender { //We don't need the 'extras' of CommandSender on Discord
|
||||
void sendMessage(String message);
|
||||
|
||||
void sendMessage(String[] message);
|
||||
}
|
|
@ -38,7 +38,8 @@ public @interface CommandClass {
|
|||
boolean excludeFromPath() default false;
|
||||
|
||||
/**
|
||||
* The help text to show for the players. A usage message will be also shown below it.
|
||||
* The help text to show for the players. A usage message will be also shown below it.<br>
|
||||
* <b>The fist line will be converted to a header.</b>
|
||||
*
|
||||
* @return The help text
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
package buttondevteam.lib.chat;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.function.Function;
|
||||
|
||||
public abstract class ICommand2<TP extends Command2Sender> {
|
||||
/**
|
||||
* Default handler for commands, can be used to copy the args too.
|
||||
*
|
||||
* @param sender The sender which ran the command
|
||||
* @param args All of the arguments passed as is
|
||||
* @return The success of the command
|
||||
*/
|
||||
public boolean def(TP sender, @Command2.TextArg String args) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method. Return with this.
|
||||
*
|
||||
* @param sender The sender of the command
|
||||
* @param message The message to send to the sender
|
||||
* @return Always true so that the usage isn't shown
|
||||
*/
|
||||
protected boolean respond(TP sender, String message) {
|
||||
sender.sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return null to not add any help text, return an empty array to only print subcommands.<br>
|
||||
* By default, returns null if the Subcommand annotation is not present and returns an empty array if no help text can be found.
|
||||
*
|
||||
* @param method The method of the subcommand
|
||||
* @return The help text, empty array or null
|
||||
*/
|
||||
public String[] getHelpText(Method method, Command2.Subcommand ann) {
|
||||
val cc = getClass().getAnnotation(CommandClass.class);
|
||||
return ann.helpText().length != 0 || cc == null ? ann.helpText() : cc.helpText(); //If cc is null then it's empty array
|
||||
}
|
||||
|
||||
private final String path;
|
||||
@Getter
|
||||
private final Command2<?, TP> manager; //TIL that if I use a raw type on a variable then none of the type args will work (including what's defined on a method, not on the type)
|
||||
|
||||
public <T extends ICommand2> ICommand2(Command2<T, TP> manager) {
|
||||
path = getcmdpath();
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* The command's path, or name if top-level command.<br>
|
||||
* For example:<br>
|
||||
* "u admin updateplugin" or "u" for the top level one<br>
|
||||
* <u>The path must be lowercase!</u><br>
|
||||
*
|
||||
* @return The command path, <i>which is the command class name by default</i> (removing any "command" from it) - Change via the {@link CommandClass} annotation
|
||||
*/
|
||||
public String getCommandPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
private String getcmdpath() {
|
||||
if (!getClass().isAnnotationPresent(CommandClass.class))
|
||||
throw new RuntimeException(
|
||||
"No @CommandClass annotation on command class " + getClass().getSimpleName() + "!");
|
||||
Function<Class<?>, String> getFromClass = cl -> cl.getSimpleName().toLowerCase().replace("commandbase", "") // <-- ...
|
||||
.replace("command", "");
|
||||
String path = getClass().getAnnotation(CommandClass.class).path(),
|
||||
prevpath = path = path.length() == 0 ? getFromClass.apply(getClass()) : path;
|
||||
for (Class<?> cl = getClass().getSuperclass(); cl != null
|
||||
&& !cl.getPackage().getName().equals(TBMCCommandBase.class.getPackage().getName()); cl = cl
|
||||
.getSuperclass()) { //
|
||||
String newpath;
|
||||
if (!cl.isAnnotationPresent(CommandClass.class)
|
||||
|| (newpath = cl.getAnnotation(CommandClass.class).path()).length() == 0
|
||||
|| newpath.equals(prevpath)) {
|
||||
if ((Modifier.isAbstract(cl.getModifiers()) && !cl.isAnnotationPresent(CommandClass.class))
|
||||
|| cl.getAnnotation(CommandClass.class).excludeFromPath()) // <--
|
||||
continue;
|
||||
newpath = getFromClass.apply(cl);
|
||||
}
|
||||
path = (prevpath = newpath) + " " + path;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package buttondevteam.lib.chat;
|
||||
|
||||
import buttondevteam.lib.architecture.ButtonPlugin;
|
||||
|
||||
public class ICommand2MC extends ICommand2<Command2MCSender> {
|
||||
public ICommand2MC() {
|
||||
super(ButtonPlugin.getCommand2MC());
|
||||
}
|
||||
}
|
|
@ -311,12 +311,12 @@ public class TBMCChatAPI {
|
|||
* @param exceptions Platforms where this message shouldn't be sent (same as {@link ChatMessage#getOrigin()}
|
||||
* @return The event cancelled state
|
||||
*/
|
||||
public static boolean SendSystemMessage(Channel channel, RecipientTestResult rtr, String message, String... exceptions) {
|
||||
public static boolean SendSystemMessage(Channel channel, RecipientTestResult rtr, String message, TBMCSystemChatEvent.BroadcastTarget target, String... exceptions) {
|
||||
if (!Channel.getChannelList().contains(channel))
|
||||
throw new RuntimeException("Channel " + channel.DisplayName().get() + " not registered!");
|
||||
if (!channel.Enabled().get())
|
||||
return true; //Cancel sending
|
||||
TBMCSystemChatEvent event = new TBMCSystemChatEvent(channel, message, rtr.score, rtr.groupID, exceptions);
|
||||
TBMCSystemChatEvent event = new TBMCSystemChatEvent(channel, message, rtr.score, rtr.groupID, exceptions, target);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
return event.isCancelled();
|
||||
}
|
||||
|
|
|
@ -15,4 +15,9 @@ commands:
|
|||
description: Add or remove a member
|
||||
component:
|
||||
description: Enable or disable or list components
|
||||
dontrunthiscmd:
|
||||
dontrunthiscmd:
|
||||
depend:
|
||||
- Vault
|
||||
softdepend:
|
||||
- Towny
|
||||
- Votifier
|
|
@ -46,6 +46,7 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0-M3</version>
|
||||
<configuration>
|
||||
<useSystemClassLoader>false
|
||||
</useSystemClassLoader> <!-- https://stackoverflow.com/a/53012553/2703239 -->
|
||||
|
|
|
@ -8,14 +8,11 @@ import javax.annotation.processing.RoundEnvironment;
|
|||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.*;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
@ -25,6 +22,8 @@ import java.util.stream.Collectors;
|
|||
public class ButtonProcessor extends AbstractProcessor {
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
if (configProcessor == null)
|
||||
configProcessor = new ConfigProcessor(processingEnv);
|
||||
for (TypeElement te : annotations) {
|
||||
Set<? extends Element> classes = roundEnv.getElementsAnnotatedWith(te);
|
||||
for (Element targetcl : classes) {
|
||||
|
@ -47,6 +46,8 @@ public class ButtonProcessor extends AbstractProcessor {
|
|||
//System.out.println("Type: " + type);
|
||||
}
|
||||
processSubcommands(targetcl, annotationMirrors);
|
||||
if (hasAnnotation.apply("HasConfig"))
|
||||
configProcessor.process(targetcl);
|
||||
}
|
||||
}
|
||||
try {
|
||||
|
@ -63,6 +64,7 @@ public class ButtonProcessor extends AbstractProcessor {
|
|||
|
||||
private YamlConfiguration yc = new YamlConfiguration();
|
||||
private boolean found = false;
|
||||
private ConfigProcessor configProcessor;
|
||||
|
||||
private void processSubcommands(Element targetcl, List<? extends AnnotationMirror> annotationMirrors) {
|
||||
if (!(targetcl instanceof ExecutableElement))
|
||||
|
@ -78,7 +80,7 @@ public class ButtonProcessor extends AbstractProcessor {
|
|||
cs.set("params", ((ExecutableElement) targetcl).getParameters().stream().skip(1).map(p -> {
|
||||
//String tn=p.asType().toString();
|
||||
//return tn.substring(tn.lastIndexOf('.')+1)+" "+p.getSimpleName();
|
||||
boolean optional = p.getAnnotationMirrors().stream().anyMatch(am -> am.getAnnotationType().toString().endsWith("Optional"));
|
||||
boolean optional = p.getAnnotationMirrors().stream().anyMatch(am -> am.getAnnotationType().toString().endsWith("OptionalArg"));
|
||||
if (optional)
|
||||
return "[" + p.getSimpleName() + "]";
|
||||
return "<" + p.getSimpleName() + ">";
|
||||
|
@ -91,20 +93,4 @@ public class ButtonProcessor extends AbstractProcessor {
|
|||
public SourceVersion getSupportedSourceVersion() {
|
||||
return SourceVersion.latestSupported();
|
||||
}
|
||||
|
||||
private String fetchSourcePath() {
|
||||
try {
|
||||
JavaFileObject generationForPath = processingEnv.getFiler().createSourceFile("PathFor" + getClass().getSimpleName());
|
||||
Writer writer = generationForPath.openWriter();
|
||||
String sourcePath = generationForPath.toUri().getPath();
|
||||
writer.close();
|
||||
generationForPath.delete();
|
||||
|
||||
return sourcePath;
|
||||
} catch (IOException e) {
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "Unable to determine source file path!");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package buttondevteam.buttonproc;
|
||||
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ConfigProcessor {
|
||||
private final ProcessingEnvironment procEnv;
|
||||
private final YamlConfiguration yaml;
|
||||
private final File file;
|
||||
|
||||
public ConfigProcessor(ProcessingEnvironment procEnv) {
|
||||
this.procEnv = procEnv;
|
||||
FileObject file = null;
|
||||
try {
|
||||
file = procEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "config.yml");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
yaml = new YamlConfiguration();
|
||||
this.file = new File(file.toUri());
|
||||
}
|
||||
|
||||
public void process(Element targetcl) {
|
||||
if (targetcl.getModifiers().contains(Modifier.ABSTRACT)) return;
|
||||
String javadoc = procEnv.getElementUtils().getDocComment(targetcl);
|
||||
if (javadoc == null) return;
|
||||
System.out.println("JAVADOC"); //TODO: Config methods
|
||||
System.out.println(javadoc);
|
||||
yaml.set("components." + targetcl.getSimpleName() + "._doc", javadoc);
|
||||
try {
|
||||
yaml.save(file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue