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,66 +1,81 @@
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() {}
object ComponentManager {
private var componentsEnabled = false
private static boolean componentsEnabled = false;
/**
* This flag is used to enable components registered after the others were enabled.
* @return Whether already registered components have been enabled
*/
fun areComponentsEnabled(): Boolean {
return componentsEnabled
}
/**
* 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; }
/**
* Enables components based on a configuration - any component registered afterwards will be also enabled
*/
fun enableComponents() {
components.values.stream().filter { c: Component<out JavaPlugin> -> c.shouldBeEnabled.get() }
.forEach { c ->
try {
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
}
/**
* 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 -> {
try {
Component.setComponentEnabled(c, true);
} catch (Exception | NoClassDefFoundError e) {
TBMCCoreAPI.SendException("Failed to enable one of the components: " + c.getClass().getSimpleName(), e, c);
}
});
componentsEnabled = true;
}
/**
* Unregister all components of a plugin that are enabled - called on [ButtonPlugin] disable
*/
@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
}
/**
* Unregister all components of a plugin that are enabled - called on {@link 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
}
/**
* Will also return false if the component is not registered.
*
* @param cl The component class
* @return Whether the component is registered and enabled
*/
@JvmStatic
fun isEnabled(cl: Class<out Component<*>?>?): Boolean {
val c = components[cl]
return c != null && c.isEnabled
}
/**
* Will also return false if the component is not registered.
*
* @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();
}
/**
* Will also return null if the component is not registered.
*
* @param cl The component class
* @return The component if it's registered and enabled
*/
@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
}
/**
* Will also return null if the component is not registered.
*
* @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;
}
}
/**
* 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;
/**
* Whether the Core's chat handler should be enabled.
* Other chat plugins handling messages from other platforms should set this to false.
*/
var isChatHandlerEnabled = true
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;
/**
* 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 val writePluginList = iConfig.getData("writePluginList", false)
/**
* 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);
/**
* The chat format to use for messages from other platforms if Chroma-Chat is not installed.
*/
@JvmField
var chatFormat = iConfig.getData("chatFormat", "[{origin}|{channel}] <{name}> {message}")
/**
* 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}");
/**
* Print some debug information.
*/
@JvmField
val test = iConfig.getData("test", false)
/**
* Print some debug information.
*/
public final ConfigData<Boolean> test = getIConfig().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!");
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());
if (Bukkit.getPluginManager().isPluginEnabled("Multiverse-Core"))
Component.registerComponent(this, new SpawnComponent());
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)
/**
* 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.
*/
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()
registerComponent(this, RestartComponent())
registerComponent(this, ChannelComponent())
registerComponent(this, RandomTPComponent())
registerComponent(this, MemberComponent())
if (Bukkit.getPluginManager().isPluginEnabled("Multiverse-Core"))
registerComponent(this, SpawnComponent())
if (Bukkit.getPluginManager().isPluginEnabled("Towny")) //It fails to load the component class otherwise
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
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);
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);
}
}
if (getServer().getPluginManager().isPluginEnabled("Essentials"))
ess = Essentials.getPlugin(Essentials.class);
logger.info(pdf.getName() + " has been Enabled (V." + pdf.getVersion() + ") Test: " + test.get() + ".");
}
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(
"§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(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 (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 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
import java.util.Arrays;
class PlayerListener(val plugin: MainPlugin) : Listener {
@EventHandler(priority = EventPriority.NORMAL)
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)
}
}
public class PlayerListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL)
fun onPlayerLeave(event: PlayerQuitEvent) {
TBMCPlayerBase.getPlayer(event.player.uniqueId, TBMCPlayer::class.java).uncache()
}
@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());
}
}
@EventHandler(priority = EventPriority.HIGHEST)
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(priority = EventPriority.NORMAL)
public void OnPlayerLeave(PlayerQuitEvent event) {
TBMCPlayerBase.getPlayer(event.getPlayer().getUniqueId(), TBMCPlayer.class).uncache();
}
@EventHandler
fun onPlayerChatPreprocess(event: PlayerCommandPreprocessEvent) {
handlePreprocess(event.player, event.message, event)
}
@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()));
}
@EventHandler
fun onSystemChatPreprocess(event: ServerCommandEvent) {
handlePreprocess(event.sender, "/" + event.command, event)
if (event.isCancelled) event.command = "dontrunthiscmd" //Bugfix
}
@EventHandler
public void onPlayerChatPreprocess(PlayerCommandPreprocessEvent event) {
handlePreprocess(event.getPlayer(), event.getMessage(), 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 onSystemChatPreprocess(ServerCommandEvent event) {
handlePreprocess(event.getSender(), "/" + event.getCommand(), event);
if (event.isCancelled()) event.setCommand("dontrunthiscmd"); //Bugfix
}
@EventHandler
fun onTBMCPreprocess(event: TBMCCommandPreprocessEvent) {
if (event.isCancelled) return
try {
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
)
}
}
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
}
@EventHandler(priority = EventPriority.HIGH) //The one in the chat plugin is set to highest
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
public void onTBMCPreprocess(TBMCCommandPreprocessEvent event) {
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);
}
}
@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
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);
}
@EventHandler(priority = EventPriority.HIGH) //The one in the chat plugin is set to highest
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,237 +1,231 @@
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 {
/**
* 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}
*/
public static final int 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.
*/
public static final int SCORE_SEND_NOPE = -1;
/**
* Send the message to everyone <i>who has access to the channel</i> - this does not necessarily mean all players
*/
public static final String GROUP_EVERYONE = "everyone";
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.
*/(
/**
* The name that should appear at the start of the message. **A chat color is expected at the beginning (§9).**
*/
private val defDisplayName: String,
/**
* The default color of the messages sent in the channel
*/
private val defColor: Color,
/**
* 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.
*/
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;
@JvmField
val isEnabled = component.config.getData("${this.identifier}.enabled", true)
private String defDisplayName;
private Color defColor;
/**
* Must start with a color code
*/
@JvmField
val displayName: ConfigData<String> =
component.config.getData("${this.identifier}.displayName", this.defDisplayName)
private IHaveConfig config;
@JvmField
val color: ConfigData<Color> = component.config.getData("${this.identifier}.color",
this.defColor, { c -> Color.valueOf((c as String)) }, Color::toString
)
public final ConfigData<Boolean> Enabled;
@JvmField
val extraIdentifiers: ListConfigData<String> = component.config.getListData("${this.identifier}.IDs", listOf())
/**
* Must start with a color code
*/
public final ConfigData<String> DisplayName;
val isGlobal: Boolean
get() = filterAndErrorMSG == null
public final ConfigData<Color> Color;
public final String ID;
/**
* Note: Errors are sent to the sender automatically
*
* @param sender The user we're sending to
* @param score The (source) score to compare with the user's
*/
fun shouldSendTo(sender: CommandSender, score: Int): Boolean {
return score == getMCScore(sender) //If there's any error, the score won't be equal
}
public ConfigData<String[]> IDs;
/**
* Note: Errors are sent to the sender automatically
*/
fun getMCScore(sender: CommandSender): Int {
return getRTR(sender).score //No need to check if there was an error
}
/**
* Filters both the sender and the targets
*/
private final Function<CommandSender, RecipientTestResult> filteranderrormsg;
/**
* Note: Errors are sent to the sender automatically<br></br>
*
*
* Null means don't send
*/
fun getGroupID(sender: CommandSender): String? {
return getRTR(sender).groupID //No need to check if there was an error
}
private static final List<Channel> channels = new ArrayList<>();
fun getRTR(sender: CommandSender): RecipientTestResult {
return filterAndErrorMSG?.apply(sender) ?: RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE)
}
/**
* 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);
}
class RecipientTestResult {
@JvmField
val errormessage: String?
/**
* 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);
}
@JvmField
val score // Anything below 0 is "never send"
: Int
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!");
}
@JvmField
val groupID: String?
public boolean isGlobal() {
return filteranderrormsg == null;
}
/**
* 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
}
/**
* Note: Errors are sent to the sender automatically
*
* @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
}
/**
* 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
}
/**
* 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
}
companion object {
@JvmField
val ALL = RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE)
}
}
/**
* Note: Errors are sent to the sender automatically<br>
* <p>
* Null means don't send
*/
@Nullable
public String getGroupID(CommandSender sender) {
return getRTR(sender).groupID; //No need to check if there was an error
}
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
public RecipientTestResult getRTR(CommandSender sender) {
if (filteranderrormsg == null)
return new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE);
return filteranderrormsg.apply(sender);
}
/**
* 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
/**
* 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());
}
/**
* 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()
/**
* 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 a stream of the enabled channels
*
* @return Only the enabled channels
*/
@JvmStatic
fun getChannels(): Stream<Channel> {
return channels.stream().filter { ch: Channel -> ch.isEnabled.get() }
}
/**
* 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
* generated automatically.
*
* @param permgroup The group that can access the channel or <b>null</b> to only allow OPs.
* @return If has access
*/
public static Function<CommandSender, RecipientTestResult> inGroupFilter(String permgroup) {
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.");
}
@JvmStatic
val channelList: List<Channel>
/**
* Return all channels whether they're enabled or not
*
* @return A list of all channels
*/
get() = Collections.unmodifiableList(channels)
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);
}
/**
* 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 **null** to only allow OPs.
* @return If has access
*/
fun inGroupFilter(permgroup: String?): Function<CommandSender, RecipientTestResult> {
return noScoreResult(
{ 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 <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);
}
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)
}
}
public static Channel GlobalChat;
public static Channel AdminChat;
public static Channel ModChat;
@JvmField
var GlobalChat: Channel? = null
var AdminChat: Channel? = null
var ModChat: Channel? = null
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
}
@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
}
}
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;
}
}
}
}

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;
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 class ChatRoom extends Channel {
private final List<CommandSender> usersInRoom = new ArrayList<>();
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 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."));
}
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
}
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
@Suppress("UNCHECKED_CAST")
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) {
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> {
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);
}