PexTabChat

A Bukkit-Plugin based around the PermissionEx-Api. The core features are a colored tablist and a chatformat.
git clone git://git.oshgnacknak.de/PexTabChat.git
Log | Files | Refs | README | LICENSE

commit 44f28608695e9932399460e2a557d498c60f581a
parent 76eeea6a5f6e56d5b02abca0afa8bb274af8edb7
Author: osh <osh@osh-peppermint>
Date:   Thu, 19 Jul 2018 19:49:34 +0200

Adding full source code th repo
Diffstat:
APexTabChat/.classpath | 8++++++++
APexTabChat/.gitignore | 1+
APexTabChat/.project | 17+++++++++++++++++
APexTabChat/.settings/org.eclipse.jdt.core.prefs | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APexTabChat/src/config.yml | 31+++++++++++++++++++++++++++++++
APexTabChat/src/de/oshgnacknak/PexTabChat/Config.java | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APexTabChat/src/de/oshgnacknak/PexTabChat/EventListener.java | 47+++++++++++++++++++++++++++++++++++++++++++++++
APexTabChat/src/de/oshgnacknak/PexTabChat/Main.java | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APexTabChat/src/de/oshgnacknak/PexTabChat/TablistManager.java | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APexTabChat/src/plugin.yml | 32++++++++++++++++++++++++++++++++
10 files changed, 513 insertions(+), 0 deletions(-)

diff --git a/PexTabChat/.classpath b/PexTabChat/.classpath @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="lib" path="/home/osh/minecraft programming/chatPlugin/spigot-1.8.8-R0.1-SNAPSHOT-latest.jar"/> + <classpathentry kind="lib" path="/home/osh/minecraft programming/chatPlugin/plugins/pex.jar"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/PexTabChat/.gitignore b/PexTabChat/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/PexTabChat/.project b/PexTabChat/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>PexTabChat</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/PexTabChat/.settings/org.eclipse.jdt.core.prefs b/PexTabChat/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,110 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.APILeak=warning +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning +org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore +org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=error +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled +org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/PexTabChat/src/config.yml b/PexTabChat/src/config.yml @@ -0,0 +1,31 @@ +# placeholders (Case sensitive): +# {prefix}: the prefix from pex +# {suffix}: the suffix from pex +# {display}: the players display name +# {name}: the players real name +# {message}: the message the player wrote (use only in chatfomat) +# ==== +# enabled: false in joinquit does not mean that there are no messages! +# To disable the messages completely, make them empty strings (like ""). +# This behavour is the default. +# ==== +# Placeholders in Tablist are not yet supported + +tablisttext: + enabled: true + header: | + &r + &cYourserver + &r + footer: | + &r + &aPlugin By &6Oshgnacknak + &r +chatformat: + enabled: true + format: '{prefix}{name}{suffix} &0>> &r{message}' +joinquit: + enabled: true + join: '' + quit: '' +tablistnames: true diff --git a/PexTabChat/src/de/oshgnacknak/PexTabChat/Config.java b/PexTabChat/src/de/oshgnacknak/PexTabChat/Config.java @@ -0,0 +1,84 @@ +package de.oshgnacknak.PexTabChat; + +import java.io.File; +import java.io.IOException; + +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +//This class is for loading and holding the config.yml in the working memory + +public class Config { + // SIGLETON + public static final Config global = new Config(); + + private Config() { + } + + public TablistText tablisttext; + public Format chatformat; + public boolean tablistnames; + public Joinquit joinquit; + + public void load() throws IOException, InvalidConfigurationException { + File file = new File("plugins/" + Main.getPlugin().getName() + "/config.yml"); + + file.getParentFile().mkdirs(); + if (!file.exists()) { + Main.getPlugin().getConfig().options().copyDefaults(true); + Main.getPlugin().saveConfig(); + } + + YamlConfiguration config = new YamlConfiguration(); + config.load(file); + + tablisttext = new TablistText(config.getBoolean("tablisttext.enabled"), config.getString("tablisttext.header"), + config.getString("tablisttext.footer")); + + chatformat = new Format(config.getBoolean("chatformat.enabled"), config.getString("chatformat.format")); + + tablistnames = config.getBoolean("tablistnames"); + + joinquit = new Joinquit(config.getBoolean("joinquit.enabled"), config.getString("joinquit.join"), + config.getString("joinquit.quit")); + } + + public class TablistText { + public final boolean enabled; + public final String header; + public final String footer; + + public TablistText(boolean enabled, String header, String footer) { + this.enabled = enabled; + this.header = header; + this.footer = footer; + } + } + + public class Format { // For chatformat + public final boolean enabled; + public final String format; + + public Format(boolean enabled, String format) { + this.enabled = enabled; + this.format = format; + } + + @Override + public String toString() { + return "enabled=" + enabled + " format" + format; + } + } + + public class Joinquit { + public final boolean enabled; + public final String join; + public final String quit; + + public Joinquit(boolean enabled, String join, String quit) { + this.enabled = enabled; + this.join = join; + this.quit = quit; + } + } +} diff --git a/PexTabChat/src/de/oshgnacknak/PexTabChat/EventListener.java b/PexTabChat/src/de/oshgnacknak/PexTabChat/EventListener.java @@ -0,0 +1,47 @@ +package de.oshgnacknak.PexTabChat; + +import org.bukkit.entity.Player; +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.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import net.md_5.bungee.api.ChatColor; + +public class EventListener implements Listener { + + @EventHandler(priority = EventPriority.LOWEST) + void onChat(AsyncPlayerChatEvent e) { + if (Config.global.chatformat.enabled) { + e.setFormat(Main.formatChat(e.getPlayer(), Config.global.chatformat.format, e.getMessage())); + } + } + + @EventHandler(priority = EventPriority.LOWEST) + void onJoin(PlayerJoinEvent e) { + Player p = e.getPlayer(); + + if (Config.global.joinquit.enabled) { + e.setJoinMessage(Main.formatMSG(p, Config.global.joinquit.join)); + } + + if (Config.global.tablisttext.enabled) { + String header = ChatColor.translateAlternateColorCodes('&', Config.global.tablisttext.header); + String footer = ChatColor.translateAlternateColorCodes('&', Config.global.tablisttext.footer); + TablistManager.sendTabHeaderAndFooter(p, header , footer); + } + + if (Config.global.tablistnames) { + TablistManager.global.addPlayer(p); + } + } + + @EventHandler(priority = EventPriority.LOWEST) + void onQuit(PlayerQuitEvent e) { + if (Config.global.joinquit.enabled) { + e.setQuitMessage(Main.formatMSG(e.getPlayer(), Config.global.joinquit.quit)); + } + } +} diff --git a/PexTabChat/src/de/oshgnacknak/PexTabChat/Main.java b/PexTabChat/src/de/oshgnacknak/PexTabChat/Main.java @@ -0,0 +1,87 @@ +package de.oshgnacknak.PexTabChat; + +import java.io.IOException; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +import net.md_5.bungee.api.ChatColor; +import ru.tehkode.permissions.PermissionUser; +import ru.tehkode.permissions.bukkit.PermissionsEx; + +public class Main extends JavaPlugin { + + static private Main plugin; + + @Override + public void onEnable() { + if (!getServer().getPluginManager().isPluginEnabled("PermissionsEx")) { + System.err.println("PexTabChat: PermissionsEx not enabled, disabling."); + getServer().getPluginManager().disablePlugin(this); + } + + plugin = this; + + getServer().getPluginManager().registerEvents(new EventListener(), this); + + try { + Config.global.load(); + } catch (Exception e) { + System.err.println("PexTabChat: Cannot load config, disabling."); + e.printStackTrace(); + getServer().getPluginManager().disablePlugin(this); + } + + TablistManager.global.update(); + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (command.getName().equalsIgnoreCase("pextabchatreload")) { + try { + reloadConfig(); + Config.global.load(); + sender.sendMessage(ChatColor.GREEN + "PexTabChat: reloaded."); + } catch (IOException | InvalidConfigurationException e) { + sender.sendMessage(ChatColor.RED + "PexTabChat: Cannot load config."); + e.printStackTrace(); + } + + TablistManager.global.update(); + } + return true; + } + + @Override + public void onDisable() { + plugin = null; + } + + static Main getPlugin() { + return plugin; + } + + public static String formatMSG(Player p, String format) { + PermissionUser u = PermissionsEx.getUser(p); + + format = format.replaceAll("\\{prefix\\}", u.getPrefix()); + format = format.replaceAll("\\{suffix\\}", u.getSuffix()); + format = format.replaceAll("\\{display\\}", p.getDisplayName()); + format = format.replaceAll("\\{name\\}", p.getName()); + + return ChatColor.translateAlternateColorCodes('&', format); + } + + public static String formatChat(Player p, String format, String message) { + + format = formatMSG(p, format); + + format = format.replaceAll("\\{message\\}", + p.hasPermission("pextabchat.color") ? ChatColor.translateAlternateColorCodes('&', message) : message); + + return format; + } +} diff --git a/PexTabChat/src/de/oshgnacknak/PexTabChat/TablistManager.java b/PexTabChat/src/de/oshgnacknak/PexTabChat/TablistManager.java @@ -0,0 +1,96 @@ +package de.oshgnacknak.PexTabChat; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.scoreboard.Scoreboard; + +import net.md_5.bungee.api.ChatColor; +import net.minecraft.server.v1_8_R3.IChatBaseComponent; +import net.minecraft.server.v1_8_R3.IChatBaseComponent.ChatSerializer; +import net.minecraft.server.v1_8_R3.PacketPlayOutPlayerListHeaderFooter; +import ru.tehkode.libs.com.google.gson.JsonParseException; +import ru.tehkode.permissions.PermissionGroup; +import ru.tehkode.permissions.PermissionUser; +import ru.tehkode.permissions.bukkit.PermissionsEx; + +public class TablistManager { + + // SIGLETON + public static final TablistManager global = new TablistManager(); + + private Scoreboard sb; + + private HashMap<String, String> sbTeams = new HashMap<>(); + + private TablistManager() { + } + + public void update() { + sb = Main.getPlugin().getServer().getScoreboardManager().getNewScoreboard(); + + List<PermissionGroup> groups = new ArrayList<PermissionGroup>(PermissionsEx.getPermissionManager().getGroupList()); + groups.sort(new Comparator<PermissionGroup>() { + public int compare(PermissionGroup a, PermissionGroup b) { + return a.getRank() - b.getRank(); + }; + }); + + for (int i = 0; i < groups.size(); ++i) { + PermissionGroup g = groups.get(i); + + String tName = String.format("%010d", i); + + sb.registerNewTeam(tName); + sb.getTeam(tName).setPrefix(ChatColor.translateAlternateColorCodes('&', g.getPrefix())); + sb.getTeam(tName).setSuffix(ChatColor.translateAlternateColorCodes('&', g.getSuffix())); + + sbTeams.put(g.getName(), tName); + } + } + + @SuppressWarnings({ "deprecation" }) + public void addPlayer(Player p) { + PermissionUser u = PermissionsEx.getUser(p); + + sb.getTeam(sbTeams.get(u.getGroups()[0].getName())).addPlayer(p); + System.out.println("Groups: " + Arrays.toString(u.getGroupNames())); + + for (Player all : Bukkit.getOnlinePlayers()) { + all.setScoreboard(sb); + } + + } + + public static void sendTabHeaderAndFooter(Player p, String header, String footer) { + + PacketPlayOutPlayerListHeaderFooter tabPacket = null; + try { + IChatBaseComponent tabHeader = ChatSerializer.a("{'text':'" + header + "'}"); + IChatBaseComponent tabFooter = ChatSerializer.a("{'text':'" + footer + "'}"); + + tabPacket = new PacketPlayOutPlayerListHeaderFooter(tabHeader); + Field field = tabPacket.getClass().getDeclaredField("b"); + field.setAccessible(true); + field.set(tabPacket, tabFooter); + } catch (JsonParseException e) { + System.err.println( + "Cannot changer headers and footers. Please only use this syntax(header: '\n&cbla\n' within config.yml's tablist section"); + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (tabPacket != null) { + ((CraftPlayer) p).getHandle().playerConnection.sendPacket(tabPacket); + } + } + } + +} diff --git a/PexTabChat/src/plugin.yml b/PexTabChat/src/plugin.yml @@ -0,0 +1,31 @@ +name: PexTabChat +main: de.oshgnacknak.PexTabChat.Main +depend: + - PermissionsEx +version: 0.1 +author: Oshgnacknak +description: | + This Plugin will read the prefixes and suffixes from players out of the pex api. + Then it will format tablist headers, footers and displaynames as well as the chatformat. + The Players in the tablist will be sorted by the groupranks + In addition it will change the format of join/quit messages (disabling is also possible using by empty formats). + And colorcodes for chat are supported with permissions. + +commands: + PexTabChatReload: + description: Reload the config. + usage: /<command> + aliases: + - ptcrelaod + permission: pextabchat.reload + +permissions: + pextabchat.*: + description: allows all beneath + children: + pextabchat.reload: true + pextabchat.color: true + pextabchat.reload: + description: "Allows to reload the plugin" + pextabchat.color: + description: "With this permission, you can write colorcodes in the chat (&4admin etc.)"+ \ No newline at end of file