A bunch of changes and improvements - and Kotlin

- Tried to reduce usage of MainPlugin.Instance, ended up refactoring more Component/ButtonPlugin things
- Config fixes, among other things
This commit is contained in:
Norbi Peti 2023-04-14 00:48:18 +02:00
parent d6fba6a495
commit 56d1f0c2a2
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
16 changed files with 684 additions and 697 deletions

View file

@ -24,7 +24,7 @@ public class ChromaCommand extends ICommand2MC {
@Command2.Subcommand
public void reload(CommandSender sender, @Command2.OptionalArg ButtonPlugin plugin) {
if (plugin == null)
plugin = MainPlugin.Instance;
plugin = getPlugin();
if (plugin.tryReloadConfig())
sender.sendMessage("§b" + plugin.getName() + " config reloaded.");
else

View file

@ -1,44 +1,48 @@
package buttondevteam.core;
package buttondevteam.core
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.ButtonPlugin;
import buttondevteam.lib.architecture.Component;
import lombok.val;
import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.ButtonPlugin
import buttondevteam.lib.architecture.Component
import buttondevteam.lib.architecture.Component.Companion.components
import buttondevteam.lib.architecture.Component.Companion.setComponentEnabled
import buttondevteam.lib.architecture.Component.Companion.unregisterComponent
import org.bukkit.plugin.java.JavaPlugin
public final class ComponentManager {
private ComponentManager() {}
private static boolean componentsEnabled = false;
object ComponentManager {
private var componentsEnabled = false
/**
* This flag is used to enable components registered after the others were enabled.
* @return Whether already registered components have been enabled
*/
public static boolean areComponentsEnabled() { return componentsEnabled; }
fun areComponentsEnabled(): Boolean {
return componentsEnabled
}
/**
* Enables components based on a configuration - any component registered afterwards will be also enabled
*/
public static void enableComponents() {
//Component.getComponents().values().stream().filter(c->cs.getConfigurationSection(c.getClass().getSimpleName()).getBoolean("enabled")).forEach(c-> {
Component.getComponents().values().stream().filter(c -> c.shouldBeEnabled.get()).forEach(c -> {
fun enableComponents() {
components.values.stream().filter { c: Component<out JavaPlugin> -> c.shouldBeEnabled.get() }
.forEach { c ->
try {
Component.setComponentEnabled(c, true);
} catch (Exception | NoClassDefFoundError e) {
TBMCCoreAPI.SendException("Failed to enable one of the components: " + c.getClass().getSimpleName(), e, c);
setComponentEnabled(c, true)
} catch (e: Exception) {
TBMCCoreAPI.SendException("Failed to enable one of the components: " + c.javaClass.simpleName, e, c)
} catch (e: NoClassDefFoundError) {
TBMCCoreAPI.SendException("Failed to enable one of the components: " + c.javaClass.simpleName, e, c)
}
});
componentsEnabled = true;
}
componentsEnabled = true
}
/**
* Unregister all components of a plugin that are enabled - called on {@link ButtonPlugin} disable
* Unregister all components of a plugin that are enabled - called on [ButtonPlugin] disable
*/
@SuppressWarnings("unchecked")
public static <T extends ButtonPlugin> void unregComponents(T plugin) {
while (!plugin.getComponentStack().empty()) //Unregister in reverse order
Component.unregisterComponent(plugin, (Component<T>) plugin.getComponentStack().pop()); //Components are pushed on register
//componentsEnabled = false; - continue enabling new components after a plugin gets disabled
@Suppress("UNCHECKED_CAST")
fun <T : ButtonPlugin> unregComponents(plugin: T) {
while (!plugin.componentStack.empty()) //Unregister in reverse order
unregisterComponent(plugin, plugin.componentStack.pop() as Component<T>) //Components are pushed on register
}
/**
@ -47,9 +51,10 @@ public final class ComponentManager {
* @param cl The component class
* @return Whether the component is registered and enabled
*/
public static boolean isEnabled(Class<? extends Component> cl) {
val c = Component.getComponents().get(cl);
return c != null && c.isEnabled();
@JvmStatic
fun isEnabled(cl: Class<out Component<*>?>?): Boolean {
val c = components[cl]
return c != null && c.isEnabled
}
/**
@ -58,9 +63,19 @@ public final class ComponentManager {
* @param cl The component class
* @return The component if it's registered and enabled
*/
@SuppressWarnings("unchecked")
public static <T extends Component> T getIfEnabled(Class<T> cl) {
val c = Component.getComponents().get(cl);
return c != null && c.isEnabled() ? (T) c : null;
@JvmStatic
@Suppress("UNCHECKED_CAST")
fun <T : Component<*>> getIfEnabled(cl: Class<T>): T? {
val c = components[cl]
return if (c != null && c.isEnabled) c as T else null
}
/**
* It will return null if the component is not registered. Use this method if you don't want to check if the component is enabled.
*/
@JvmStatic
@Suppress("UNCHECKED_CAST")
fun <T : Component<*>> get(cl: Class<T>): T? {
return components[cl] as T?
}
}

View file

@ -1,173 +1,194 @@
package buttondevteam.core;
package buttondevteam.core
import buttondevteam.core.component.channel.Channel;
import buttondevteam.core.component.channel.ChannelComponent;
import buttondevteam.core.component.channel.ChatRoom;
import buttondevteam.core.component.members.MemberComponent;
import buttondevteam.core.component.randomtp.RandomTPComponent;
import buttondevteam.core.component.restart.RestartComponent;
import buttondevteam.core.component.spawn.SpawnComponent;
import buttondevteam.core.component.towny.TownyComponent;
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.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.OfflinePlayer;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.RegisteredServiceProvider;
import buttondevteam.core.component.channel.Channel
import buttondevteam.core.component.channel.ChannelComponent
import buttondevteam.core.component.channel.ChatRoom
import buttondevteam.core.component.members.MemberComponent
import buttondevteam.core.component.randomtp.RandomTPComponent
import buttondevteam.core.component.restart.RestartComponent
import buttondevteam.core.component.spawn.SpawnComponent
import buttondevteam.core.component.towny.TownyComponent
import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.ButtonPlugin
import buttondevteam.lib.architecture.Component.Companion.registerComponent
import buttondevteam.lib.chat.Color
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 net.milkbowl.vault.economy.Economy
import net.milkbowl.vault.permission.Permission
import org.bukkit.Bukkit
import org.bukkit.OfflinePlayer
import org.bukkit.command.BlockCommandSender
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
import org.bukkit.command.ConsoleCommandSender
import org.bukkit.entity.Player
import org.bukkit.plugin.Plugin
import java.io.File
import java.io.IOException
import java.nio.file.Files
import java.util.*
import java.util.function.Function
import java.util.function.Supplier
import java.util.logging.Logger
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Supplier;
import java.util.logging.Logger;
class MainPlugin : ButtonPlugin() {
private var logger: Logger? = null
private var economy: Economy? = null
public class MainPlugin extends ButtonPlugin {
public static MainPlugin Instance;
public static Permission permission;
@Nullable
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;
var isChatHandlerEnabled = true
/**
* Sets whether the plugin should write a list of installed plugins in a txt file.
* It can be useful if some other software needs to know the plugins.
*/
private final ConfigData<Boolean> writePluginList = getIConfig().getData("writePluginList", false);
private val writePluginList = iConfig.getData("writePluginList", false)
/**
* The chat format to use for messages from other platforms if Chroma-Chat is not installed.
*/
ConfigData<String> chatFormat = getIConfig().getData("chatFormat", "[{origin}|" +
"{channel}] <{name}> {message}");
@JvmField
var chatFormat = iConfig.getData("chatFormat", "[{origin}|{channel}] <{name}> {message}")
/**
* Print some debug information.
*/
public final ConfigData<Boolean> test = getIConfig().getData("test", false);
@JvmField
val test = iConfig.getData("test", false)
/**
* If a Chroma command clashes with another plugin's command, this setting determines whether the Chroma command should be executed or the other plugin's.
*/
public final ConfigData<Boolean> prioritizeCustomCommands = getIConfig().getData("prioritizeCustomCommands", false);
@Override
public void pluginEnable() {
Instance = this;
PluginDescriptionFile pdf = getDescription();
logger = getLogger();
if (!setupPermissions())
throw new NullPointerException("No permission plugin found!");
val prioritizeCustomCommands = iConfig.getData("prioritizeCustomCommands", false)
public override fun pluginEnable() {
Instance = this
val pdf = description
logger = getLogger()
if (!setupPermissions()) throw NullPointerException("No permission plugin found!")
if (!setupEconomy()) //Though Essentials always provides economy, but we don't require Essentials
getLogger().warning("No economy plugin found! Components using economy will not be registered.");
saveConfig();
Component.registerComponent(this, new RestartComponent());
Component.registerComponent(this, new ChannelComponent());
Component.registerComponent(this, new RandomTPComponent());
Component.registerComponent(this, new MemberComponent());
getLogger().warning("No economy plugin found! Components using economy will not be registered.")
saveConfig()
registerComponent(this, RestartComponent())
registerComponent(this, ChannelComponent())
registerComponent(this, RandomTPComponent())
registerComponent(this, MemberComponent())
if (Bukkit.getPluginManager().isPluginEnabled("Multiverse-Core"))
Component.registerComponent(this, new SpawnComponent());
registerComponent(this, SpawnComponent())
if (Bukkit.getPluginManager().isPluginEnabled("Towny")) //It fails to load the component class otherwise
Component.registerComponent(this, new TownyComponent());
registerComponent(this, TownyComponent())
/*if (Bukkit.getPluginManager().isPluginEnabled("Votifier") && economy != null)
Component.registerComponent(this, new VotifierComponent(economy));*/
ComponentManager.enableComponents();
registerCommand(new ComponentCommand());
registerCommand(new ChromaCommand());
TBMCCoreAPI.RegisterEventsForExceptions(new PlayerListener(), this);
TBMCCoreAPI.RegisterEventsForExceptions(Companion.getCommand2MC(), 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, TBMCPlayer::new);
TBMCChatAPI.RegisterChatChannel(Channel.GlobalChat = new Channel("§fg§f", Color.White, "g", null)); //The /ooc ID has moved to the config
ComponentManager.enableComponents()
registerCommand(ComponentCommand())
registerCommand(ChromaCommand())
TBMCCoreAPI.RegisterEventsForExceptions(PlayerListener(this), this)
TBMCCoreAPI.RegisterEventsForExceptions(command2MC, this)
//Console & cmdblocks
ChromaGamerBase.addConverter { commandSender: CommandSender ->
Optional.ofNullable(
if (commandSender is ConsoleCommandSender || commandSender is BlockCommandSender)
TBMCPlayer.getPlayer(UUID(0, 0), TBMCPlayer::class.java)
else null
)
}
//Players, has higher priority
ChromaGamerBase.addConverter { sender: CommandSender ->
Optional.ofNullable(
if (sender is Player) TBMCPlayer.getPlayer(sender.uniqueId, TBMCPlayer::class.java) else null
)
}
TBMCCoreAPI.RegisterUserClass(TBMCPlayerBase::class.java) { TBMCPlayer() }
TBMCChatAPI.RegisterChatChannel(Channel("§fg§f", Color.White, "g", null)
.also { Channel.GlobalChat = it }) //The /ooc ID has moved to the config
TBMCChatAPI.RegisterChatChannel(Channel("§cADMIN§f", Color.Red, "a", Channel.inGroupFilter(null))
.also { Channel.AdminChat = it })
TBMCChatAPI.RegisterChatChannel(Channel("§9MOD§f", Color.Blue, "mod", Channel.inGroupFilter("mod"))
.also { Channel.ModChat = it })
TBMCChatAPI.RegisterChatChannel(
Channel.AdminChat = new Channel("§cADMIN§f", Color.Red, "a", Channel.inGroupFilter(null)));
TBMCChatAPI.RegisterChatChannel(
Channel.ModChat = new Channel("§9MOD§f", Color.Blue, "mod", Channel.inGroupFilter("mod")));
TBMCChatAPI.RegisterChatChannel(new Channel("§6DEV§f", Color.Gold, "dev", Channel.inGroupFilter("developer")));
TBMCChatAPI.RegisterChatChannel(new ChatRoom("§cRED§f", Color.DarkRed, "red"));
TBMCChatAPI.RegisterChatChannel(new ChatRoom("§6ORANGE§f", Color.Gold, "orange"));
TBMCChatAPI.RegisterChatChannel(new ChatRoom("§eYELLOW§f", Color.Yellow, "yellow"));
TBMCChatAPI.RegisterChatChannel(new ChatRoom("§aGREEN§f", Color.Green, "green"));
TBMCChatAPI.RegisterChatChannel(new ChatRoom("§bBLUE§f", Color.Blue, "blue"));
TBMCChatAPI.RegisterChatChannel(new ChatRoom("§5PURPLE§f", Color.DarkPurple, "purple"));
Supplier<Iterable<String>> playerSupplier = () -> Bukkit.getOnlinePlayers().stream().map(HumanEntity::getName)::iterator;
Companion.getCommand2MC().addParamConverter(OfflinePlayer.class, Bukkit::getOfflinePlayer, "Player not found!", playerSupplier);
Companion.getCommand2MC().addParamConverter(Player.class, Bukkit::getPlayer, "Online player not found!", playerSupplier);
Channel(
"§6DEV§f",
Color.Gold,
"dev",
Channel.inGroupFilter("developer")
)
) // TODO: Make groups configurable
TBMCChatAPI.RegisterChatChannel(ChatRoom("§cRED§f", Color.DarkRed, "red"))
TBMCChatAPI.RegisterChatChannel(ChatRoom("§6ORANGE§f", Color.Gold, "orange"))
TBMCChatAPI.RegisterChatChannel(ChatRoom("§eYELLOW§f", Color.Yellow, "yellow"))
TBMCChatAPI.RegisterChatChannel(ChatRoom("§aGREEN§f", Color.Green, "green"))
TBMCChatAPI.RegisterChatChannel(ChatRoom("§bBLUE§f", Color.Blue, "blue"))
TBMCChatAPI.RegisterChatChannel(ChatRoom("§5PURPLE§f", Color.DarkPurple, "purple"))
val playerSupplier = Supplier { Bukkit.getOnlinePlayers().map { obj: Player -> obj.name }.asIterable() }
command2MC.addParamConverter(OfflinePlayer::class.java, { name: String? ->
Bukkit.getOfflinePlayer(
name!!
)
}, "Player not found!", playerSupplier)
command2MC.addParamConverter<Player>(
Player::class.java, Function { name: String ->
Bukkit.getPlayer(name)
}, "Online player not found!", playerSupplier
)
if (writePluginList.get()) {
try {
Files.write(new File("plugins", "plugins.txt").toPath(), Arrays.stream(Bukkit.getPluginManager().getPlugins()).map(p -> (CharSequence) p.getDataFolder().getName())::iterator);
} catch (IOException e) {
TBMCCoreAPI.SendException("Failed to write plugin list!", e, this);
Files.write(File("plugins", "plugins.txt").toPath(), Iterable {
Arrays.stream(Bukkit.getPluginManager().plugins)
.map { p: Plugin -> p.dataFolder.name as CharSequence }
.iterator()
})
} catch (e: IOException) {
TBMCCoreAPI.SendException("Failed to write plugin list!", e, this)
}
}
if (getServer().getPluginManager().isPluginEnabled("Essentials"))
ess = Essentials.getPlugin(Essentials.class);
logger.info(pdf.getName() + " has been Enabled (V." + pdf.getVersion() + ") Test: " + test.get() + ".");
if (server.pluginManager.isPluginEnabled("Essentials")) ess = getPlugin(
Essentials::class.java
)
logger!!.info(pdf.name + " has been Enabled (V." + pdf.version + ") Test: " + test.get() + ".")
}
@Override
public void pluginDisable() {
logger.info("Saving player data...");
ChromaGamerBase.saveUsers();
logger.info("Player data saved.");
public override fun pluginDisable() {
logger!!.info("Saving player data...")
ChromaGamerBase.saveUsers()
logger!!.info("Player data saved.")
}
private boolean setupPermissions() {
permission = setupProvider(Permission.class);
return (permission != null);
private fun setupPermissions(): Boolean {
permission = setupProvider(Permission::class.java)
return permission != null
}
private boolean setupEconomy() {
economy = setupProvider(Economy.class);
return (economy != null);
private fun setupEconomy(): Boolean {
economy = setupProvider(Economy::class.java)
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;
private fun <T> setupProvider(cl: Class<T>): T? {
val provider = server.servicesManager
.getRegistration(cl)
return provider?.provider
}
@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
sender.sendMessage("§cThis command isn't available."); //In theory, unregistered commands use this method
return true;
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<String>): Boolean {
if (command.name == "dontrunthiscmd") return true //Used in chat preprocess for console
sender.sendMessage("§cThis command isn't available.") //In theory, unregistered commands use this method
return true
}
companion object {
@JvmField
var Instance: MainPlugin = null
@JvmField
var permission: Permission? = null
@JvmField
var ess: Essentials? = null
}
}

View file

@ -1,116 +1,110 @@
package buttondevteam.core;
package buttondevteam.core
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.ChromaGamerBase;
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;
import org.bukkit.event.server.ServerCommandEvent;
import java.util.Arrays;
public class PlayerListener implements Listener {
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.ChromaGamerBase
import buttondevteam.lib.player.TBMCPlayer
import buttondevteam.lib.player.TBMCPlayerBase
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
import org.bukkit.event.server.ServerCommandEvent
class PlayerListener(val plugin: MainPlugin) : Listener {
@EventHandler(priority = EventPriority.NORMAL)
public void OnPlayerJoin(PlayerJoinEvent event) {
var p = event.getPlayer();
TBMCPlayer player = TBMCPlayerBase.getPlayer(p.getUniqueId(), TBMCPlayer.class);
String pname = player.PlayerName.get();
if (pname.length() == 0) {
player.PlayerName.set(p.getName());
MainPlugin.Instance.getLogger().info("Player name saved: " + player.PlayerName.get());
} else if (!p.getName().equals(pname)) {
MainPlugin.Instance.getLogger().info(pname + " renamed to " + p.getName());
player.PlayerName.set(p.getName());
fun onPlayerJoin(event: PlayerJoinEvent) {
val p = event.player
val player = TBMCPlayerBase.getPlayer(p.uniqueId, TBMCPlayer::class.java)
val pname = player.PlayerName.get()
if (pname.isEmpty()) {
player.PlayerName.set(p.name)
plugin.logger.info("Player name saved: " + player.PlayerName.get())
} else if (p.name != pname) {
plugin.logger.info(pname + " renamed to " + p.name)
player.PlayerName.set(p.name)
}
}
@EventHandler(priority = EventPriority.NORMAL)
public void OnPlayerLeave(PlayerQuitEvent event) {
TBMCPlayerBase.getPlayer(event.getPlayer().getUniqueId(), TBMCPlayer.class).uncache();
fun onPlayerLeave(event: PlayerQuitEvent) {
TBMCPlayerBase.getPlayer(event.player.uniqueId, TBMCPlayer::class.java).uncache()
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onSystemChat(TBMCSystemChatEvent event) {
if (event.isHandled())
return; // Only handle here if ButtonChat couldn't - ButtonChat doesn't even handle this
if (Arrays.stream(event.getExceptions()).anyMatch("Minecraft"::equalsIgnoreCase))
return;
Bukkit.getOnlinePlayers().stream().filter(event::shouldSendTo)
.forEach(p -> p.sendMessage(event.getChannel().DisplayName.get().substring(0, 2) + event.getMessage()));
fun onSystemChat(event: TBMCSystemChatEvent) {
if (event.isHandled) return
if (event.exceptions.any { "Minecraft".equals(it, ignoreCase = true) }) return
Bukkit.getOnlinePlayers().stream().filter { sender: CommandSender -> event.shouldSendTo(sender) }
.forEach { p: Player -> p.sendMessage(event.channel.displayName.get().substring(0, 2) + event.message) }
}
@EventHandler
public void onPlayerChatPreprocess(PlayerCommandPreprocessEvent event) {
handlePreprocess(event.getPlayer(), event.getMessage(), event);
fun onPlayerChatPreprocess(event: PlayerCommandPreprocessEvent) {
handlePreprocess(event.player, event.message, event)
}
@EventHandler
public void onSystemChatPreprocess(ServerCommandEvent event) {
handlePreprocess(event.getSender(), "/" + event.getCommand(), event);
if (event.isCancelled()) event.setCommand("dontrunthiscmd"); //Bugfix
fun onSystemChatPreprocess(event: ServerCommandEvent) {
handlePreprocess(event.sender, "/" + event.command, event)
if (event.isCancelled) event.command = "dontrunthiscmd" //Bugfix
}
private void handlePreprocess(CommandSender sender, String message, Cancellable event) {
if (event.isCancelled()) return;
val cg = ChromaGamerBase.getFromSender(sender);
if (cg == null) throw new RuntimeException("Couldn't get user from sender for " + sender.getName() + "!");
val ev = new TBMCCommandPreprocessEvent(sender, cg.channel.get(), message, sender);
Bukkit.getPluginManager().callEvent(ev);
if (ev.isCancelled())
event.setCancelled(true); //Cancel the original event
private fun handlePreprocess(sender: CommandSender, message: String, event: Cancellable) {
if (event.isCancelled) return
val cg = ChromaGamerBase.getFromSender(sender)
?: throw RuntimeException("Couldn't get user from sender for " + sender.name + "!")
val ev = TBMCCommandPreprocessEvent(sender, cg.channel.get(), message, sender)
Bukkit.getPluginManager().callEvent(ev)
if (ev.isCancelled) event.isCancelled = true //Cancel the original event
}
@EventHandler
public void onTBMCPreprocess(TBMCCommandPreprocessEvent event) {
if (event.isCancelled()) return;
fun onTBMCPreprocess(event: TBMCCommandPreprocessEvent) {
if (event.isCancelled) return
try {
event.setCancelled(ButtonPlugin.getCommand2MC().handleCommand(new Command2MCSender(event.getSender(), event.getChannel(), event.getPermCheck()), event.getMessage()));
} catch (Exception e) {
TBMCCoreAPI.SendException("Command processing failed for sender '" + event.getSender() + "' and message '" + event.getMessage() + "'", e, MainPlugin.Instance);
val sender = Command2MCSender(event.sender, event.channel, event.permCheck)
event.isCancelled = ButtonPlugin.command2MC.handleCommand(sender, event.message)
} catch (e: Exception) {
TBMCCoreAPI.SendException(
"Command processing failed for sender '${event.sender}' and message '${event.message}'",
e,
plugin
)
}
}
@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());
fun onPlayerChat(event: AsyncPlayerChatEvent) {
if (event.isCancelled) return //The chat plugin should cancel it after this handler
val cp = TBMCPlayer.getPlayer(event.player.uniqueId, TBMCPlayer::class.java)
TBMCChatAPI.SendChatMessage(ChatMessage.builder(event.player, cp, event.message).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
var channel = event.getChannel();
String msg = MainPlugin.Instance.chatFormat.get()
.replace("{channel}", channel.DisplayName.get())
.replace("{origin}", event.getOrigin().substring(0, 1))
.replace("{name}", ChromaUtils.getDisplayName(event.getSender()))
.replace("{message}", String.format("§%x%s", channel.Color.get().ordinal(), event.getMessage()));
for (Player player : Bukkit.getOnlinePlayers())
if (event.shouldSendTo(player))
player.sendMessage(msg);
Bukkit.getConsoleSender().sendMessage(msg);
fun onPlayerChat(event: TBMCChatEvent) {
if (event.isCancelled) return
if (!plugin.isChatHandlerEnabled) return
if (event.origin == "Minecraft") return //Let other plugins handle MC messages
val channel = event.channel
val msg = plugin.chatFormat.get()
.replace("{channel}", channel.displayName.get())
.replace("{origin}", event.origin.substring(0, 1))
.replace("{name}", ChromaUtils.getDisplayName(event.sender))
.replace("{message}", String.format("§%x%s", channel.color.get().ordinal, event.message))
for (player in Bukkit.getOnlinePlayers()) if (event.shouldSendTo(player)) player.sendMessage(msg)
Bukkit.getConsoleSender().sendMessage(msg)
}
}

View file

@ -1,122 +1,72 @@
package buttondevteam.core.component.channel;
package buttondevteam.core.component.channel
import buttondevteam.core.ComponentManager;
import buttondevteam.core.MainPlugin;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.architecture.IHaveConfig;
import buttondevteam.lib.chat.Color;
import com.google.common.collect.Lists;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import buttondevteam.core.ComponentManager.get
import buttondevteam.core.MainPlugin
import buttondevteam.lib.architecture.ConfigData
import buttondevteam.lib.architecture.IHaveConfig
import buttondevteam.lib.architecture.ListConfigData
import buttondevteam.lib.chat.Color
import org.bukkit.Bukkit
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import java.util.*
import java.util.function.Function
import java.util.function.Predicate
import java.util.stream.Stream
/**
* Represents a chat channel. May only be instantiated after the channel component is registered.
*/
public class Channel {
open class Channel
/**
* Creates a channel.
*
* @param filterAndErrorMSG Checks all senders against the criteria provided here and sends the message if the index matches the sender's - if no score at all, displays the error.<br></br>
* May be null to send to everyone.
*/(
/**
* Specifies a score that means it's OK to send - but it does not define any groups, only send or not send. See {@link #GROUP_EVERYONE}
* The name that should appear at the start of the message. **A chat color is expected at the beginning (§9).**
*/
public static final int SCORE_SEND_OK = 0;
private val defDisplayName: String,
/**
* Specifies a score that means the user doesn't have permission to see or send the message. Any negative value has the same effect.
* The default color of the messages sent in the channel
*/
public static final int SCORE_SEND_NOPE = -1;
private val defColor: Color,
/**
* Send the message to everyone <i>who has access to the channel</i> - this does not necessarily mean all players
* The channel identifier. It's the same as the command to be used for the channel *without / *. For example "mod".
* It's also used for scoreboard objective names.
*/
public static final String GROUP_EVERYONE = "everyone";
val identifier: String,
/**
* A function that determines who has permission to see the channel.
* If the sender doesn't have access, they cannot send the message.
* Only those with access can see the messages.
* If null, everyone has access.
*/
private val filterAndErrorMSG: Function<CommandSender, RecipientTestResult>?
) {
private val config: IHaveConfig? = null // TODO: Use this
private static ChannelComponent component;
private String defDisplayName;
private Color defColor;
private IHaveConfig config;
public final ConfigData<Boolean> Enabled;
@JvmField
val isEnabled = component.config.getData("${this.identifier}.enabled", true)
/**
* Must start with a color code
*/
public final ConfigData<String> DisplayName;
@JvmField
val displayName: ConfigData<String> =
component.config.getData("${this.identifier}.displayName", this.defDisplayName)
public final ConfigData<Color> Color;
public final String ID;
@JvmField
val color: ConfigData<Color> = component.config.getData("${this.identifier}.color",
this.defColor, { c -> Color.valueOf((c as String)) }, Color::toString
)
public ConfigData<String[]> IDs;
@JvmField
val extraIdentifiers: ListConfigData<String> = component.config.getListData("${this.identifier}.IDs", listOf())
/**
* Filters both the sender and the targets
*/
private final Function<CommandSender, RecipientTestResult> filteranderrormsg;
private static final List<Channel> channels = new ArrayList<>();
/**
* Creates a channel.
*
* @param displayname The name that should appear at the start of the message. <b>A chat color is expected at the beginning (§9).</b>
* @param color The default color of the messages sent in the channel
* @param command The command to be used for the channel <i>without /</i>. For example "mod". It's also used for scoreboard objective names.
* @param filteranderrormsg Checks all senders against the criteria provided here and sends the message if the index matches the sender's - if no score at all, displays the error.<br>
* May be null to send to everyone.
*/
public Channel(String displayname, Color color, String command,
Function<CommandSender, RecipientTestResult> filteranderrormsg) {
defDisplayName = displayname;
defColor = color;
ID = command;
this.filteranderrormsg = filteranderrormsg;
init();
Enabled = component.getConfig().getData(ID + ".enabled", true);
DisplayName = component.getConfig().getData(ID + ".displayName", defDisplayName);
Color = component.getConfig().getData(ID + ".color", defColor, c -> buttondevteam.lib.chat.Color.valueOf((String) c), Enum::toString);
//noinspection unchecked
IDs = component.getConfig().getData(ID + ".IDs", new String[0], l -> ((List<String>) l).toArray(new String[0]), Lists::newArrayList);
}
/**
* Must be only called from a subclass - otherwise it'll throw an exception.
*
* @see Channel#Channel(String, Color, String, Function)
*/
@SuppressWarnings("unchecked")
protected <T extends Channel> Channel(String displayname, Color color, String command,
BiFunction<T, CommandSender, RecipientTestResult> filteranderrormsg) {
defDisplayName = displayname;
defColor = color;
ID = command;
this.filteranderrormsg = s -> filteranderrormsg.apply((T) this, s);
init();
Enabled = component.getConfig().getData(ID + ".enabled", true);
DisplayName = component.getConfig().getData(ID + ".displayName", defDisplayName);
Color = component.getConfig().getData(ID + ".color", defColor, c -> buttondevteam.lib.chat.Color.valueOf((String) c), Enum::toString);
//noinspection unchecked
IDs = component.getConfig().getData(ID + ".IDs", new String[0], l -> ((List<String>) l).toArray(new String[0]), Lists::newArrayList);
}
private static void init() {
if (component == null)
component = (ChannelComponent) Component.getComponents().get(ChannelComponent.class);
if (component == null)
throw new RuntimeException("Attempting to create a channel before the component is registered!");
}
public boolean isGlobal() {
return filteranderrormsg == null;
}
val isGlobal: Boolean
get() = filterAndErrorMSG == null
/**
* Note: Errors are sent to the sender automatically
@ -124,114 +74,158 @@ public class Channel {
* @param sender The user we're sending to
* @param score The (source) score to compare with the user's
*/
public boolean shouldSendTo(CommandSender sender, int score) {
return score == getMCScore(sender); //If there's any error, the score won't be equal
fun shouldSendTo(sender: CommandSender, score: Int): Boolean {
return score == getMCScore(sender) //If there's any error, the score won't be equal
}
/**
* Note: Errors are sent to the sender automatically
*/
public int getMCScore(CommandSender sender) {
return getRTR(sender).score; //No need to check if there was an error
fun getMCScore(sender: CommandSender): Int {
return getRTR(sender).score //No need to check if there was an error
}
/**
* Note: Errors are sent to the sender automatically<br>
* <p>
* Note: Errors are sent to the sender automatically<br></br>
*
*
* Null means don't send
*/
@Nullable
public String getGroupID(CommandSender sender) {
return getRTR(sender).groupID; //No need to check if there was an error
fun getGroupID(sender: CommandSender): String? {
return getRTR(sender).groupID //No need to check if there was an error
}
public RecipientTestResult getRTR(CommandSender sender) {
if (filteranderrormsg == null)
return new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE);
return filteranderrormsg.apply(sender);
fun getRTR(sender: CommandSender): RecipientTestResult {
return filterAndErrorMSG?.apply(sender) ?: RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE)
}
class RecipientTestResult {
@JvmField
val errormessage: String?
@JvmField
val score // Anything below 0 is "never send"
: Int
@JvmField
val groupID: String?
/**
* Creates a result that indicates an **error**
*
* @param errormessage The error message to show the sender if they don't meet the criteria.
*/
constructor(errormessage: String?) {
this.errormessage = errormessage
score = SCORE_SEND_NOPE
groupID = null
}
/**
* Creates a result that indicates a **success**
*
* @param score The score that identifies the target group. **Must be non-negative.** For example, the index of the town or nation to send to.
* @param groupID The ID of the target group.
*/
constructor(score: Int, groupID: String?) {
require(score >= 0) { "Score must be non-negative!" }
this.score = score
this.groupID = groupID
errormessage = null
}
companion object {
@JvmField
val ALL = RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE)
}
}
companion object {
/**
* Specifies a score that means it's OK to send - but it does not define any groups, only send or not send. See [.GROUP_EVERYONE]
*/
const val SCORE_SEND_OK = 0
/**
* Specifies a score that means the user doesn't have permission to see or send the message. Any negative value has the same effect.
*/
const val SCORE_SEND_NOPE = -1
/**
* Send the message to everyone *who has access to the channel* - this does not necessarily mean all players
*/
const val GROUP_EVERYONE = "everyone"
private val component: ChannelComponent by lazy {
get(ChannelComponent::class.java)
?: throw RuntimeException("Attempting to create a channel before the component is registered!")
}
private val channels: MutableList<Channel> = ArrayList()
/**
* Get a stream of the enabled channels
*
* @return Only the enabled channels
*/
public static Stream<Channel> getChannels() {
return channels.stream().filter(ch -> ch.Enabled.get());
@JvmStatic
fun getChannels(): Stream<Channel> {
return channels.stream().filter { ch: Channel -> ch.isEnabled.get() }
}
@JvmStatic
val channelList: List<Channel>
/**
* Return all channels whether they're enabled or not
*
* @return A list of all channels
*/
public static List<Channel> getChannelList() {
return Collections.unmodifiableList(channels);
}
get() = Collections.unmodifiableList(channels)
/**
* Convenience method for the function parameter of {@link #Channel(String, Color, String, Function)}. It checks if the sender is OP or optionally has the specified group. The error message is
* Convenience method for the function parameter of [.Channel]. It checks if the sender is OP or optionally has the specified group. The error message is
* generated automatically.
*
* @param permgroup The group that can access the channel or <b>null</b> to only allow OPs.
* @param permgroup The group that can access the channel or **null** to only allow OPs.
* @return If has access
*/
public static Function<CommandSender, RecipientTestResult> inGroupFilter(String permgroup) {
fun inGroupFilter(permgroup: String?): Function<CommandSender, RecipientTestResult> {
return noScoreResult(
s -> s.isOp() || (permgroup != null && (s instanceof Player && MainPlugin.permission != null && MainPlugin.permission.playerInGroup((Player) s, permgroup))),
"You need to be a(n) " + (permgroup != null ? permgroup : "OP") + " to use this channel.");
{ s ->
s.isOp || s is Player && permgroup?.let { pg ->
MainPlugin.permission?.playerInGroup(
s,
pg
)
} ?: false
},
"You need to be a(n) " + (permgroup ?: "OP") + " to use this channel."
)
}
public static Function<CommandSender, RecipientTestResult> noScoreResult(Predicate<CommandSender> filter,
String errormsg) {
return s -> filter.test(s) ? new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE) : new RecipientTestResult(errormsg);
}
public static <T extends Channel> BiFunction<T, CommandSender, RecipientTestResult> noScoreResult(
BiPredicate<T, CommandSender> filter, String errormsg) {
return (this_, s) -> filter.test(this_, s) ? new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE) : new RecipientTestResult(errormsg);
}
public static Channel GlobalChat;
public static Channel AdminChat;
public static Channel ModChat;
public static void RegisterChannel(Channel channel) {
if (!channel.isGlobal() && !ComponentManager.isEnabled(ChannelComponent.class))
return; //Allow registering the global chat (and I guess other chats like the RP chat)
channels.add(channel);
component.registerChannelCommand(channel);
Bukkit.getScheduler().runTask(MainPlugin.Instance, () -> Bukkit.getPluginManager().callEvent(new ChatChannelRegisterEvent(channel))); // Wait for server start
}
public static class RecipientTestResult {
public final String errormessage;
public final int score; // Anything below 0 is "never send"
public final String groupID;
public static final RecipientTestResult ALL = new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE);
/**
* Creates a result that indicates an <b>error</b>
*
* @param errormessage The error message to show the sender if they don't meet the criteria.
*/
public RecipientTestResult(String errormessage) {
this.errormessage = errormessage;
this.score = SCORE_SEND_NOPE;
this.groupID = null;
}
/**
* Creates a result that indicates a <b>success</b>
*
* @param score The score that identifies the target group. <b>Must be non-negative.</b> For example, the index of the town or nation to send to.
* @param groupID The ID of the target group.
*/
public RecipientTestResult(int score, String groupID) {
if (score < 0) throw new IllegalArgumentException("Score must be non-negative!");
this.score = score;
this.groupID = groupID;
this.errormessage = null;
fun noScoreResult(
filter: Predicate<CommandSender>,
errormsg: String?
): Function<CommandSender, RecipientTestResult> {
return Function { s ->
if (filter.test(s)) RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE)
else RecipientTestResult(errormsg)
}
}
@JvmField
var GlobalChat: Channel? = null
var AdminChat: Channel? = null
var ModChat: Channel? = null
@JvmStatic
fun registerChannel(channel: Channel) {
if (!channel.isGlobal && !component.isEnabled) return //Allow registering the global chat (and I guess other chats like the RP chat)
channels.add(channel)
component.registerChannelCommand(channel)
Bukkit.getScheduler().runTask(component.plugin,
Runnable {
Bukkit.getPluginManager().callEvent(ChatChannelRegisterEvent(channel))
}) // Wait for server start
}
}
}

View file

@ -47,12 +47,12 @@ public class ChannelComponent extends Component<JavaPlugin> {
@Override
public String getCommandPath() {
return channel.ID;
return channel.identifier;
}
@Override
public String[] getCommandPaths() {
return channel.IDs.get();
return channel.extraIdentifiers.get();
}
@Command2.Subcommand
@ -74,7 +74,7 @@ public class ChannelComponent extends Component<JavaPlugin> {
if (channel instanceof ChatRoom)
((ChatRoom) channel).joinRoom(sender);
}
sender.sendMessage("§6You are now talking in: §b" + user.channel.get().DisplayName.get());
sender.sendMessage("§6You are now talking in: §b" + user.channel.get().displayName.get());
} else
TBMCChatAPI.SendChatMessage(ChatMessage.builder(sender, user, message).fromCommand(true)
.permCheck(senderMC.getPermCheck()).build(), channel);

View file

@ -1,28 +1,35 @@
package buttondevteam.core.component.channel;
package buttondevteam.core.component.channel
import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.TBMCChatAPI;
import org.bukkit.command.CommandSender;
import buttondevteam.lib.TBMCSystemChatEvent
import buttondevteam.lib.chat.Color
import buttondevteam.lib.chat.TBMCChatAPI
import org.bukkit.command.CommandSender
import java.util.ArrayList;
import java.util.List;
public class ChatRoom extends Channel {
private final List<CommandSender> usersInRoom = new ArrayList<>();
public ChatRoom(String displayname, Color color, String command) {
<ChatRoom>super(displayname, color, command, noScoreResult((this_, s) -> this_.usersInRoom.contains(s),
"Not implemented yet. Please report it to the devs along with which platform you're trying to talk from."));
class ChatRoom(displayname: String, color: Color, command: String) : Channel(
displayname, color, command, null // TODO: Custom filter for rooms using abstract method
) {
private val usersInRoom: MutableList<CommandSender> = ArrayList()
private fun isInRoom(sender: CommandSender): Boolean {
return usersInRoom.contains(sender)
}
public void joinRoom(CommandSender sender) {
usersInRoom.add(sender);
TBMCChatAPI.SendSystemMessage(this, RecipientTestResult.ALL, sender.getName() + " joined the room", TBMCSystemChatEvent.BroadcastTarget.ALL); //Always show message in the same kind of channel
fun joinRoom(sender: CommandSender) {
usersInRoom.add(sender)
TBMCChatAPI.SendSystemMessage(
this,
RecipientTestResult.ALL,
sender.name + " 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", ChannelComponent.roomJoinLeave);
fun leaveRoom(sender: CommandSender) {
usersInRoom.remove(sender)
TBMCChatAPI.SendSystemMessage(
this,
RecipientTestResult.ALL,
sender.name + " left the room",
ChannelComponent.roomJoinLeave
)
}
}

View file

@ -3,7 +3,6 @@ package buttondevteam.lib.architecture
import buttondevteam.buttonproc.HasConfig
import buttondevteam.core.ComponentManager
import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.Component.Companion.updateConfig
import buttondevteam.lib.chat.Command2MC
import buttondevteam.lib.chat.ICommand2MC
import org.bukkit.configuration.InvalidConfigurationException
@ -17,7 +16,8 @@ import java.util.function.Consumer
@HasConfig(global = true)
abstract class ButtonPlugin : JavaPlugin() {
protected val iConfig = IHaveConfig { saveConfig() }
protected var iConfig = getIConfigInstance()
private set
private var yaml: YamlConfiguration? = null
protected val data //TODO
@ -52,12 +52,16 @@ abstract class ButtonPlugin : JavaPlugin() {
IHaveConfig.pregenConfig(this, null)
}
private fun getIConfigInstance(): IHaveConfig {
return IHaveConfig(
::saveConfig,
this.config.getConfigurationSection("global") ?: this.config.createSection("global")
)
}
private fun reloadIConfig(): Boolean {
val config = config
var section = config.getConfigurationSection("global")
if (section == null) section = config.createSection("global")
iConfig.reset(section)
return configLoaded // If loading fails, getConfig() returns a temporary instance
iConfig = getIConfigInstance()
return isConfigLoaded // If loading fails, getConfig() returns a temporary instance
}
override fun onDisable() {
@ -79,7 +83,7 @@ abstract class ButtonPlugin : JavaPlugin() {
fun tryReloadConfig(): Boolean {
if (!justReload()) return false
reloadIConfig()
componentStack.forEach(Consumer { c: Component<*>? -> updateConfig(this, c!!) })
componentStack.forEach(Consumer { c -> c.updateComponentData() })
return true
}
@ -122,10 +126,10 @@ abstract class ButtonPlugin : JavaPlugin() {
}
override fun saveConfig() {
if (configLoaded) super.saveConfig()
if (isConfigLoaded) super.saveConfig()
}
val configLoaded get() = yaml != null
val isConfigLoaded get() = yaml != null
/**
* Registers command and sets its plugin.
@ -142,6 +146,7 @@ abstract class ButtonPlugin : JavaPlugin() {
annotation class ConfigOpts(val disableConfigGen: Boolean = false)
companion object {
//Needs to be static as we don't know the plugin when a command is handled
@JvmStatic
val command2MC = Command2MC()
fun configGenAllowed(obj: Any): Boolean {
return !Optional.ofNullable(obj.javaClass.getAnnotation(ConfigOpts::class.java))

View file

@ -18,10 +18,10 @@ import java.util.stream.Collectors
@HasConfig(global = false) //Used for obtaining javadoc
abstract class Component<TP : JavaPlugin> {
var isEnabled = false
private var wrapper: ButtonComponent<TP>? = null
internal var componentData: ComponentData<TP>? = null
val config get() = wrapper!!.config
val plugin get() = wrapper!!.plugin
val config get() = componentData!!.config
val plugin get() = componentData!!.plugin
private val data //TODO
: IHaveConfig? = null
@ -120,11 +120,34 @@ abstract class Component<TP : JavaPlugin> {
return res
}
private val className: String
get() = javaClass.simpleName
private val className: String get() = javaClass.simpleName
internal fun updateComponentData(plugin: TP = this.plugin) {
componentData = ComponentData(plugin, { plugin.saveConfig() }, this.getConfigSection(plugin))
}
private fun getConfigSection(plugin: JavaPlugin): ConfigurationSection {
var compconf = plugin.config.getConfigurationSection("components")
if (compconf == null) compconf = plugin.config.createSection("components")
var configSect = compconf.getConfigurationSection(className)
if (configSect == null) configSect = compconf.createSection(className)
return configSect
// TODO: Support tests (provide Bukkit configuration for tests)
}
companion object {
private val components = HashMap<Class<out Component<*>>, Component<out JavaPlugin>>()
private val _components = HashMap<Class<out Component<*>>, Component<out JavaPlugin>>()
/**
* Returns the currently registered components<br></br>
*
* @return The currently registered components
*/
@JvmStatic
val components: Map<Class<out Component<*>>, Component<out JavaPlugin>>
get() {
return Collections.unmodifiableMap(_components)
}
/**
* Registers a component checking it's dependencies and calling [.register].<br></br>
@ -178,10 +201,11 @@ abstract class Component<TP : JavaPlugin> {
)
return false
}
val wrapper = ButtonComponent(plugin, { plugin.saveConfig() }, getConfigSection(plugin, component))
component.register(plugin)
components[component.javaClass] = component
if (plugin is ButtonPlugin) (plugin as ButtonPlugin).componentStack.push(component)
// The plugin is saved with this call, so it must be specified
component.updateComponentData(plugin)
_components[component.javaClass] = component
if (plugin is ButtonPlugin) plugin.componentStack.push(component)
if (ComponentManager.areComponentsEnabled() && component.shouldBeEnabled.get()) {
return try { //Enable components registered after the previous ones getting enabled
setComponentEnabled(component, true)
@ -220,7 +244,7 @@ abstract class Component<TP : JavaPlugin> {
}
}
component.unregister(plugin)
components.remove(component.javaClass)
_components.remove(component.javaClass)
}
true
} catch (e: Exception) {
@ -242,7 +266,7 @@ abstract class Component<TP : JavaPlugin> {
if (component.isEnabled == enabled) return //Don't do anything
if (enabled.also { component.isEnabled = it }) {
try {
getConfigSection(component.plugin!!, component)
component.getConfigSection(component.plugin)
component.enable()
if (ButtonPlugin.configGenAllowed(component)) {
IHaveConfig.pregenConfig(component, null)
@ -267,24 +291,5 @@ abstract class Component<TP : JavaPlugin> {
ButtonPlugin.command2MC.unregisterCommands(component)
}
}
private fun getConfigSection(plugin: JavaPlugin, component: Component<*>): ConfigurationSection {
var compconf = plugin.config.getConfigurationSection("components")
if (compconf == null) compconf = plugin.config.createSection("components")
var configSect = compconf.getConfigurationSection(component.className)
if (configSect == null) configSect = compconf.createSection(component.className)
return configSect
// TODO: Support tests (provide Bukkit configuration for tests)
}
/**
* Returns the currently registered components<br></br>
*
* @return The currently registered components
*/
@JvmStatic
fun getComponents(): Map<Class<out Component<*>>, Component<out JavaPlugin>> {
return Collections.unmodifiableMap(components)
}
}
}

View file

@ -6,10 +6,10 @@ import org.bukkit.plugin.java.JavaPlugin
/**
* A wrapper for plugin components. This is used internally.
*/
class ButtonComponent<TP : JavaPlugin>(
class ComponentData<TP : JavaPlugin>(
val plugin: TP,
saveAction: Runnable,
config: ConfigurationSection
) {
val config = IHaveConfig(saveAction, config)
val config = IHaveConfig(saveAction, config) // TODO: Use lateinit instead of this class
}

View file

@ -82,33 +82,6 @@ class ConfigData<T> internal constructor(
private class SaveTask(val task: BukkitTask, val saveAction: Runnable)
class ConfigDataBuilder<T> internal constructor(
private val config: IHaveConfig,
private val path: String,
private val primitiveDef: Any?,
private val getter: Function<Any?, T>,
private val setter: Function<T, Any?>
) {
/**
* Builds a modifiable config representation. Use if you want to change the value *in code*.
*
* @return A ConfigData instance.
*/
fun build(readOnly: Boolean = false): ConfigData<T> {
val config = ConfigData(config, path, primitiveDef, getter, setter, readOnly)
this.config.onConfigBuild(config)
return config
}
fun buildList(readOnly: Boolean = false): ListConfigData<T> {
if (primitiveDef is List<*>) {
val config = ListConfigData(config, path, primitiveDef, getter, setter, readOnly)
this.config.onConfigBuild(config)
return config
}
}
}
companion object {
private val saveTasks = HashMap<Configuration, SaveTask>()
fun signalChange(config: IHaveConfig) {
@ -147,9 +120,5 @@ class ConfigData<T> internal constructor(
}
return false
}
fun <T> builder(config: IHaveConfig, path: String, primitiveDef: Any?, getter: Function<Any?, T>, setter: Function<T, Any?>): ConfigDataBuilder<T> {
return ConfigDataBuilder(config, path, primitiveDef, getter, setter)
}
}
}

View file

@ -2,7 +2,6 @@ package buttondevteam.lib.architecture
import buttondevteam.core.MainPlugin
import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.ConfigData.ConfigDataBuilder
import buttondevteam.lib.architecture.config.IConfigData
import org.bukkit.Bukkit
import org.bukkit.configuration.ConfigurationSection
@ -11,7 +10,6 @@ import java.lang.reflect.InvocationTargetException
import java.util.*
import java.util.function.Function
import java.util.function.Predicate
import java.util.function.Supplier
import java.util.stream.Collectors
/**
@ -30,33 +28,6 @@ class IHaveConfig(
) {
private val datamap = HashMap<String, IConfigData<*>>()
/**
* You may use this method with any data type, but always provide getters and setters that convert to primitive types
* if you're not using primitive types directly.
* These primitive types are strings, numbers, characters, booleans and lists of these things.
*
* @param path The path in config to use
* @param def The value to use by default
* @param getter A function that converts a primitive representation to the correct value
* @param setter A function that converts a value to a primitive representation
* @param primDef Whether the default value is a primitive value that needs to be converted to the correct type using the getter
* @param T The type of this variable (can be any class)
* @return The data object that can be used to get or set the value
*/
@Suppress("UNCHECKED_CAST")
fun <T> getConfig( // TODO: Remove
path: String,
def: T,
getter: Function<Any?, T>? = null,
setter: Function<T, Any?>? = null
): ConfigDataBuilder<T> {
return ConfigData.builder(this, path, if (setter != null) setter.apply(def) else def, getter ?: Function { it as T }, setter ?: Function { it })
}
fun onConfigBuild(config: IConfigData<*>) {
datamap[config.path] = config
}
/**
* You may use this method with any data type, but always provide getters and setters that convert to primitive types
* if you're not using primitive types directly.
@ -109,30 +80,26 @@ class IHaveConfig(
* This method overload should only be used with primitves or String.
*
* @param path The path in config to use
* @param def The value to use by default
* @param <T> The type of this variable (only use primitives or String)
* @return The data object that can be used to get or set the value
</T> */
fun <T> getData(path: String, def: Supplier<T>): ConfigData<T> { // TODO: Remove
var data = datamap[path]
if (data == null) {
val defval = def.get()
datamap[path] = ConfigData(this, path, defval, defval, null, null).also { data = it }
}
@Suppress("UNCHECKED_CAST")
return data as ConfigData<T>
}
/**
* This method overload should only be used with primitves or String.
*
* @param path The path in config to use
* @param <T> The type of this variable (only use primitives or String)
* @return The data object that can be used to get or set the value
</T> */
fun <T> getListData(path: String): ListConfigData<T> {
fun <T> getListData(
path: String,
def: List<T>,
elementGetter: Function<Any?, T>? = null,
elementSetter: Function<T, Any?>? = null,
readOnly: Boolean = false
): ListConfigData<T> {
var data = datamap[path]
if (data == null) datamap[path] = ListConfigData(this, path, ListConfigData.List<T>()).also { data = it }
if (data == null) datamap[path] = ListConfigData(
this,
path,
def,
elementGetter ?: Function { it as T },
elementSetter ?: Function { it },
readOnly
).also { data = it }
@Suppress("UNCHECKED_CAST")
return data as ListConfigData<T>
}

View file

@ -62,7 +62,11 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
@Retention(AnnotationRetention.RUNTIME)
annotation class OptionalArg
protected class ParamConverter<T>(val converter: Function<String, T>, val errormsg: String, val allSupplier: Supplier<Iterable<String>>)
protected class ParamConverter<T>(
val converter: Function<String, T?>,
val errormsg: String,
val allSupplier: Supplier<Iterable<String>>
)
protected val paramConverters = HashMap<Class<*>, ParamConverter<*>>()
private val commandHelp = ArrayList<String>() //Mainly needed by Discord
@ -70,15 +74,17 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
/**
* Adds a param converter that obtains a specific object from a string parameter.
* The converter may return null.
* The converter may return null to signal an error.
*
* @param <T> The type of the result
* @param cl The class of the result object
* @param converter The converter to use
* @param allSupplier The supplier of all possible values (ideally)
</T> */
open fun <T> addParamConverter(cl: Class<T>, converter: Function<String, T>, errormsg: String,
allSupplier: Supplier<Iterable<String>>) {
open fun <T> addParamConverter(
cl: Class<T>, converter: Function<String, T?>, errormsg: String,
allSupplier: Supplier<Iterable<String>>
) {
paramConverters[cl] = ParamConverter(converter, errormsg, allSupplier)
}

View file

@ -125,7 +125,12 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
* Automatically colors the message red.
* {@see super#addParamConverter}
*/
override fun <T> addParamConverter(cl: Class<T>, converter: Function<String, T>, errormsg: String, allSupplier: Supplier<Iterable<String>>) {
override fun <T> addParamConverter(
cl: Class<T>,
converter: Function<String, T?>,
errormsg: String,
allSupplier: Supplier<Iterable<String>>
) {
super.addParamConverter(cl, converter, "§c$errormsg", allSupplier)
}
@ -213,7 +218,7 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
private class BukkitCommand(name: String?) : Command(name) {
override fun execute(sender: CommandSender, commandLabel: String, args: Array<String>): Boolean {
return ButtonPlugin.getCommand2MC().executeCommand(sender, this, commandLabel, args)
return ButtonPlugin.command2MC.executeCommand(sender, this, commandLabel, args)
}
@Throws(IllegalArgumentException::class)
@ -312,10 +317,9 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
j++
continue
}
//Break if converter is not found or for example, the player provided an invalid plugin name
val converter = getParamConverter(params[j].type, command2MC) ?: break
val paramValue = converter.converter.apply(paramValueString)
?: //For example, the player provided an invalid plugin name
break
val paramValue = converter.converter.apply(paramValueString) ?: break
args[j] = paramValue
k++ //Only increment if not CommandSender
j++

View file

@ -35,9 +35,9 @@ public class TBMCChatAPI {
*/
public static boolean SendChatMessage(ChatMessage cm, Channel channel) {
if (!Channel.getChannelList().contains(channel))
throw new RuntimeException("Channel " + channel.DisplayName.get() + " not registered!");
if (!channel.Enabled.get()) {
cm.getSender().sendMessage("§cThe channel '" + channel.DisplayName.get() + "' is disabled!");
throw new RuntimeException("Channel " + channel.getDisplayName().get() + " not registered!");
if (!channel.isEnabled.get()) {
cm.getSender().sendMessage("§cThe channel '" + channel.displayName.get() + "' is disabled!");
return true; //Cancel sending if channel is disabled
}
Supplier<Boolean> task = () -> {
@ -70,11 +70,11 @@ public class TBMCChatAPI {
*/
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())
throw new RuntimeException("Channel " + channel.displayName.get() + " not registered!");
if (!channel.enabled.get())
return true; //Cancel sending
if (!Arrays.asList(exceptions).contains("Minecraft"))
Bukkit.getConsoleSender().sendMessage("[" + channel.DisplayName.get() + "] " + message);
Bukkit.getConsoleSender().sendMessage("[" + channel.displayName.get() + "] " + message);
TBMCSystemChatEvent event = new TBMCSystemChatEvent(channel, message, rtr.score, rtr.groupID, exceptions, target);
return ChromaUtils.callEventAsync(event);
}
@ -92,6 +92,6 @@ public class TBMCChatAPI {
* @param channel A new {@link Channel} to register
*/
public static void RegisterChatChannel(Channel channel) {
Channel.RegisterChannel(channel);
Channel.registerChannel(channel);
}
}

View file

@ -313,5 +313,5 @@ public abstract class ChromaGamerBase {
//-----------------------------------------------------------------
public final ConfigData<Channel> channel = config.getData("channel", Channel.GlobalChat,
id -> Channel.getChannels().filter(ch -> ch.ID.equalsIgnoreCase((String) id)).findAny().orElse(null), ch -> ch.ID);
id -> Channel.getChannels().filter(ch -> ch.identifier.equalsIgnoreCase((String) id)).findAny().orElse(null), ch -> ch.ID);
}