diff --git a/Core.ipr b/Core.ipr
new file mode 100644
index 0000000..c3fa9de
--- /dev/null
+++ b/Core.ipr
@@ -0,0 +1,502 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Core.iws b/Core.iws
new file mode 100644
index 0000000..bb382f3
--- /dev/null
+++ b/Core.iws
@@ -0,0 +1,1047 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ options
+ Bungee
+ 998959932
+ cc.fyre.stark
+ net.centilehcf.core.engine
+ cc.fyre.stark.engine
+ Stark.instance
+ permissionsex
+ adapter
+ autoupdate
+ final
+ Missionary
+
+
+
+ net.centilehcf.core.tab
+ net.centilehcf.core
+ Core.get()
+
+
+ C:\Users\William\Desktop\Centile Dev Folder\Core
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ C:\Users\William\AppData\Roaming\Subversion
+
+
+
+
+ 1557106025124
+
+
+ 1557106025124
+
+
+ 1557108591621
+
+
+
+ 1557108591621
+
+
+ 1558903468156
+
+
+
+ 1558903468156
+
+
+ 1559094557126
+
+
+
+ 1559094557126
+
+
+ 1559244209742
+
+
+
+ 1559244209742
+
+
+ 1559701611816
+
+
+
+ 1559701611816
+
+
+ 1559762033909
+
+
+
+ 1559762033909
+
+
+ 1559840922844
+
+
+
+ 1559840922844
+
+
+ 1559847398997
+
+
+
+ 1559847398997
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Kotlin SDK
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1.8
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 912b41e..8c2b327 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,2 @@
-# scoreboardapi-skid-of-zoot
-don't fuck with my friends server pussy
+# Core
+# Core
diff --git a/core.iml b/core.iml
new file mode 100644
index 0000000..653f74b
--- /dev/null
+++ b/core.iml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/PermissionsEx-1.23.4.jar b/lib/PermissionsEx-1.23.4.jar
new file mode 100644
index 0000000..bb4b5f5
Binary files /dev/null and b/lib/PermissionsEx-1.23.4.jar differ
diff --git a/lib/phoenix-lang-1.0-SNAPSHOT.jar b/lib/phoenix-lang-1.0-SNAPSHOT.jar
new file mode 100644
index 0000000..3fa3fee
Binary files /dev/null and b/lib/phoenix-lang-1.0-SNAPSHOT.jar differ
diff --git a/lib/pidgin-1.0-SNAPSHOT.jar b/lib/pidgin-1.0-SNAPSHOT.jar
new file mode 100644
index 0000000..a891b57
Binary files /dev/null and b/lib/pidgin-1.0-SNAPSHOT.jar differ
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..6d5533f
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,218 @@
+
+
+ 4.0.0
+
+ net.centilehcf
+ core
+ 1.2-SNAPSHOT
+
+
+ 1.3.31
+ 1.8
+ 1.8
+
+
+
+ src/main/java
+
+
+ pl.project13.maven
+ git-commit-id-plugin
+ 2.2.2
+
+
+
+ revision
+
+
+
+
+ dd.MM.yyyy HH:mm:ss
+ false
+ ${project.basedir}/.git
+ false
+ true
+
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${kotlin.version}
+
+
+ compile
+ compile
+
+ compile
+
+
+
+
+
+
+
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.5.1
+
+
+
+ default-compile
+ none
+
+
+
+ default-testCompile
+ none
+
+
+ java-compile
+ compile
+ compile
+
+
+ java-test-compile
+ test-compile
+ testCompile
+
+
+
+
+ 1.8
+
+
+ org.projectlombok
+ lombok
+ LATEST
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 2.2-beta-5
+
+
+ make-assembly
+ package
+ single
+
+ false
+
+ jar-with-dependencies
+
+
+
+
+
+
+
+
+
+
+ spigot-repo
+ https://hub.spigotmc.org/nexus/content/repositories/snapshots/
+
+
+ vault-repo
+ http://nexus.hc.to/content/repositories/pub_releases
+
+
+
+
+ gg.manny
+ genericspigot-api
+ 1.7.10-R0.1-SNAPSHOT
+ provided
+
+
+ gg.manny
+ genericspigot
+ 1.7.10-R0.1-SNAPSHOT
+ provided
+
+
+ org.projectlombok
+ lombok
+ LATEST
+ provided
+
+
+ redis.clients
+ jedis
+ 3.0.1
+
+
+ com.qrakn
+ honcho
+ 1.0-SNAPSHOT
+ compile
+
+
+ com.google.code.gson
+ gson
+ LATEST
+ compile
+
+
+ org.mongodb
+ mongo-java-driver
+ 3.8.2
+ compile
+
+
+ com.qrakn
+ phoenix-lang
+ 1.0-SNAPSHOT
+ compile
+
+
+ com.comphenix.protocol
+ ProtocolLib
+ 4.4.0-SNAPSHOT
+ provided
+
+
+ com.minexd
+ pidgin
+ 1.0-SNAPSHOT
+ compile
+
+
+ net.milkbowl.vault
+ VaultAPI
+ 1.7
+ provided
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib-jdk8
+ ${kotlin.version}
+ compile
+
+
+ org.jetbrains.kotlin
+ kotlin-test
+ ${kotlin.version}
+ test
+
+
+
\ No newline at end of file
diff --git a/src/main/java/net/centilehcf/core/Core.java b/src/main/java/net/centilehcf/core/Core.java
new file mode 100644
index 0000000..94d7a0a
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/Core.java
@@ -0,0 +1,334 @@
+package net.centilehcf.core;
+
+import com.comphenix.protocol.ProtocolLibrary;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.minexd.pidgin.Pidgin;
+import net.centilehcf.core.chat.Chat;
+import net.centilehcf.core.chat.command.MuteChatCommand;
+import net.centilehcf.core.chat.listener.ChatListener;
+import net.centilehcf.core.essentials.Essentials;
+import net.centilehcf.core.essentials.command.*;
+import net.centilehcf.core.essentials.listener.DurabilityListener;
+import net.centilehcf.core.essentials.listener.EssentialsListener;
+import net.centilehcf.core.hook.VaultProvider;
+import net.centilehcf.core.network.NetworkPacketListener;
+import net.centilehcf.core.network.packet.*;
+import net.centilehcf.core.prefix.Prefix;
+import net.centilehcf.core.prefix.PrefixHandler;
+import net.centilehcf.core.prefix.command.*;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.ProfileTypeAdapter;
+import net.centilehcf.core.profile.command.OptionsCommand;
+import net.centilehcf.core.profile.command.StaffChatToggleCommand;
+import net.centilehcf.core.profile.command.individualperms.AddIndividualPermissionCommand;
+import net.centilehcf.core.profile.command.individualperms.ListIndividualPermissionsCommand;
+import net.centilehcf.core.profile.command.individualperms.RemoveIndividualPermissionCommand;
+import net.centilehcf.core.profile.grant.command.ClearGrantsCommand;
+import net.centilehcf.core.profile.grant.command.GrantCommand;
+import net.centilehcf.core.profile.grant.command.GrantsCommand;
+import net.centilehcf.core.profile.grant.command.SetRankCommand;
+import net.centilehcf.core.profile.grant.listener.GrantListener;
+import net.centilehcf.core.profile.ProfileListener;
+import net.centilehcf.core.profile.option.commands.TogglePrivateMessagesCommand;
+import net.centilehcf.core.profile.punishment.command.*;
+import net.centilehcf.core.profile.command.AltsCommand;
+import net.centilehcf.core.profile.punishment.listener.PunishmentListener;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.rank.RankTypeAdapter;
+import net.centilehcf.core.profile.command.StaffChatCommand;
+import net.centilehcf.core.rank.command.*;
+import net.centilehcf.core.tab.TabAdapter;
+import net.centilehcf.core.tab.TabEngine;
+import net.centilehcf.core.tab.test.TestLayoutProvider;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.TaskUtil;
+import net.centilehcf.core.util.duration.Duration;
+import net.centilehcf.core.util.duration.DurationTypeAdapter;
+import net.centilehcf.core.util.menu.MenuListener;
+import net.centilehcf.core.uuid.UUIDCache;
+import com.mongodb.MongoClient;
+import com.mongodb.MongoClientOptions;
+import com.mongodb.MongoCredential;
+import com.mongodb.ServerAddress;
+import com.mongodb.client.MongoDatabase;
+import com.qrakn.honcho.Honcho;
+import com.qrakn.phoenix.lang.file.type.BasicConfigurationFile;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Level;
+
+import lombok.Getter;
+import net.centilehcf.core.network.packet.PacketDeleteGrant;
+import net.centilehcf.core.network.packet.PacketDeleteRank;
+import net.centilehcf.core.network.packet.PacketStaffSwitchServer;
+import net.centilehcf.core.prefix.command.AddPrefixCommand;
+import net.centilehcf.core.prefix.command.ListPrefixCommand;
+import net.centilehcf.core.prefix.command.PrefixTypeAdapter;
+import net.centilehcf.core.prefix.command.SetPrefixCommand;
+import net.milkbowl.vault.permission.Permission;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.ServicePriority;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.scheduler.BukkitRunnable;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+
+@Getter
+public class Core extends JavaPlugin {
+
+ public static Gson GSON = new Gson();
+ public static Type LIST_STRING_TYPE = new TypeToken>() {
+ }.getType();
+ private static Core core;
+ private BasicConfigurationFile mainConfig;
+ private Honcho honcho;
+ private MongoDatabase mongoDatabase;
+ private JedisPool jedisPool;
+ private UUIDCache uuidCache;
+ private Essentials essentials;
+ private PrefixHandler prefixHandler;
+ private Chat chat;
+ private Pidgin pidgin;
+ private boolean loaded;
+
+ @Override
+ public void onEnable() {
+ core = this;
+
+ getServer().getServicesManager().register(Permission.class, new VaultProvider(), this, ServicePriority.Highest);
+
+ mainConfig = new BasicConfigurationFile(this, "config");
+
+ loadMongo();
+ loadRedis();
+
+ uuidCache = new UUIDCache(this);
+ essentials = new Essentials(this);
+ chat = new Chat(this);
+
+ honcho = new Honcho(this);
+
+ Arrays.asList(
+ new BroadcastCommand(),
+ new ClearCommand(),
+ new DayCommand(),
+ new GameModeCommand(),
+ new HealCommand(),
+ new HidePlayerCommand(),
+ new MoreCommand(),
+ new NightCommand(),
+ new SetSlotsCommand(),
+ new SetSpawnCommand(),
+ new ShowPlayerCommand(),
+ new SpawnCommand(),
+ new SunsetCommand(),
+ new AltsCommand(),
+ new BanCommand(),
+ new BlacklistCommand(),
+ new UnblacklistCommand(),
+ new HistoryCommand(),
+ new KickCommand(),
+ new MuteCommand(),
+ new UnbanCommand(),
+ new UnmuteCommand(),
+ new WarnCommand(),
+ new GrantCommand(),
+ new GrantsCommand(),
+ new StaffChatCommand(),
+ new StaffChatToggleCommand(),
+ new MuteChatCommand(),
+ new OptionsCommand(),
+ new RankAddPermissionCommand(),
+ new RankCreateCommand(),
+ new RankDeleteCommand(),
+ new RankRenameCommand(),
+ new RankRemovePermissionCommand(),
+ new RankSetColorCommand(),
+ new RankSetPrefixCommand(),
+ new RankSetSuffixCommand(),
+ new RankSetWeightCommand(),
+ new RankAuditCommand(),
+ new RankDumpCommand(),
+ new RankListCommand(),
+ new PingCommand(),
+ new AddIndividualPermissionCommand(),
+ new ListIndividualPermissionsCommand(),
+ new RemoveIndividualPermissionCommand(),
+ new PrefixCommand(),
+ new AddPrefixCommand(),
+ new DeletePrefixCommand(),
+ new ListPrefixCommand(),
+ new SetPrefixCommand(),
+ new SetPrefixCommand(),
+ new TeleportAllCommand(),
+ new TeleportHereCommand(),
+ new MasssayCommand(),
+ new RawCommand(),
+ new TeleportPositionCommand(),
+ new MessageCommand(),
+ new ReplyCommand(),
+ new TeleportCommand(),
+ new InvseeCommand(),
+ new StreamingCommand(),
+ new WorldCommand(),
+ new ClearChatCommand(),
+ new SpawnerCommand(),
+ new RequestCommand(),
+ new ListCommand(),
+ new ClearGrantsCommand(),
+ new ClearPunishmentsCommand(),
+ new TogglePrivateMessagesCommand(),
+ new CraftCommand(),
+ new RenameCommand(),
+ new SetRankCommand()
+ ).forEach(honcho::registerCommand);
+
+
+ honcho.registerTypeAdapter(Rank.class, new RankTypeAdapter());
+ honcho.registerTypeAdapter(Profile.class, new ProfileTypeAdapter());
+ honcho.registerTypeAdapter(Duration.class, new DurationTypeAdapter());
+ honcho.registerTypeAdapter(Prefix.class, new PrefixTypeAdapter());
+
+ Arrays.asList(
+ new ProfileListener(this),
+ new MenuListener(this),
+ new EssentialsListener(this),
+ new ChatListener(this),
+ new GrantListener(this),
+ new PunishmentListener(this),
+ new DurabilityListener(this)
+ ).forEach(listener -> getServer().getPluginManager().registerEvents(listener, this));
+
+ pidgin = new Pidgin("zoot",
+ mainConfig.getString("REDIS.HOST"),
+ mainConfig.getInteger("REDIS.PORT"),
+ mainConfig.getBoolean("REDIS.AUTHENTICATION.ENABLED") ?
+ mainConfig.getString("REDIS.AUTHENTICATION.PASSWORD") : null
+ );
+
+ Arrays.asList(
+ PacketAddGrant.class,
+ PacketBroadcastPunishment.class,
+ PacketDeleteGrant.class,
+ PacketDeleteRank.class,
+ PacketRefreshRank.class,
+ PacketStaffChat.class,
+ PacketStaffJoinNetwork.class,
+ PacketStaffLeaveNetwork.class,
+ PacketStaffSwitchServer.class,
+ PacketUpdatePrefix.class,
+ PacketDeletePrefix.class,
+ PacketRequestCommand.class,
+ PacketClearGrants.class,
+ PacketServerRestart.class,
+ PacketRemovePunishments.class
+ ).forEach(pidgin::registerPacket);
+
+ pidgin.registerListener(new NetworkPacketListener(this));
+ Rank.init();
+ Profile.init();
+
+ ProtocolLibrary.getProtocolManager().addPacketListener(new TabAdapter());
+
+/* TabEngine.init();
+ TabEngine.setLayoutProvider(new TestLayoutProvider());*/
+
+ this.prefixHandler = new PrefixHandler();
+
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ for (Profile profile : Profile.getProfiles().values()) {
+ profile.checkGrants();
+ }
+ }
+ }.runTaskTimerAsynchronously(this, 20L, 20L);
+ TaskUtil.runLater(() -> this.loaded = true, 60L);
+ Core.get().getPidgin().sendPacket(new PacketServerRestart( Core.get().getMainConfig().getString("SERVER_NAME"), CC.GREEN + "Online"));
+ }
+
+ @Override
+ public void onDisable() {
+ Core.get().getPidgin().sendPacket(new PacketServerRestart( Core.get().getMainConfig().getString("SERVER_NAME"), CC.RED +"Offline"));
+ try {
+ jedisPool.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Prints a message and exception to console. If the server is not in debug mode, the messages will be suppressed.
+ *
+ * @param level The log level.
+ * @param message The message.
+ * @param exception The thrown exception.
+ */
+ public void debug(Level level, String message, Exception exception) {
+ getLogger().log(level, message);
+ exception.printStackTrace();
+ }
+
+ /**
+ * Prints a message to console and server operators.
+ *
+ * @param message The message.
+ */
+ public void debug(String message) {
+ broadcastOps(CC.translate("&e(Debug) &r" + message));
+ }
+
+ /**
+ * Prints a message triggered by an action a player performed to console and server operators.
+ *
+ * @param player The player that triggered this log.
+ * @param message The message.
+ */
+ public void debug(Player player, String message) {
+ broadcastOps(CC.translate("&e(Debug) &r" + player.getDisplayName() + ": " + message));
+ }
+
+ /**
+ * Broadcasts a message to all server operators.
+ *
+ * @param message The message.
+ */
+ public static void broadcastOps(String message) {
+ Bukkit.getOnlinePlayers().stream().filter(Player::isOp).forEach(op -> op.sendMessage(message));
+ }
+
+ private void loadMongo() {
+ if (mainConfig.getBoolean("MONGO.AUTHENTICATION.ENABLED")) {
+ mongoDatabase = new MongoClient(
+ new ServerAddress(
+ mainConfig.getString("MONGO.HOST"),
+ mainConfig.getInteger("MONGO.PORT")),
+ MongoCredential.createCredential(
+ mainConfig.getString("MONGO.AUTHENTICATION.USERNAME"),
+ "admin", mainConfig.getString("MONGO.AUTHENTICATION.PASSWORD").toCharArray()),
+ MongoClientOptions.builder().build()
+ ).getDatabase("zoot");
+ } else {
+ mongoDatabase = new MongoClient(mainConfig.getString("MONGO.HOST"), mainConfig.getInteger("MONGO.PORT"))
+ .getDatabase("zoot");
+ }
+ }
+
+ private void loadRedis() {
+ jedisPool = new JedisPool(mainConfig.getString("REDIS.HOST"), mainConfig.getInteger("REDIS.PORT"));
+
+ if (mainConfig.getBoolean("REDIS.AUTHENTICATION.ENABLED")) {
+ try (Jedis jedis = jedisPool.getResource()) {
+ jedis.auth(mainConfig.getString("REDIS.AUTHENTICATION.PASSWORD"));
+ }
+ }
+ }
+
+ public static Core get() {
+ return core;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/Locale.java b/src/main/java/net/centilehcf/core/Locale.java
new file mode 100644
index 0000000..3dacda3
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/Locale.java
@@ -0,0 +1,34 @@
+package net.centilehcf.core;
+
+import lombok.AllArgsConstructor;
+import org.bukkit.ChatColor;
+
+import java.text.MessageFormat;
+
+@AllArgsConstructor
+public enum Locale {
+
+ FAILED_TO_LOAD_PROFILE("COMMON_ERRORS.FAILED_TO_LOAD_PROFILE"),
+ COULD_NOT_RESOLVE_PLAYER("COMMON_ERRORS.COULD_NOT_RESOLVE_PLAYER"),
+ PLAYER_NOT_FOUND("COMMON_ERRORS.PLAYER_NOT_FOUND"),
+ RANK_NOT_FOUND("COMMON_ERRORS.RANK_NOT_FOUND"),
+ STAFF_CHAT("STAFF.CHAT"),
+ STAFF_REQUEST("STAFF.REQUEST"),
+ STAFF_BROADCAST_PREFIX("STAFF.BROADCAST_PREFIX"),
+ STAFF_REQUEST_PREFIX("STAFF.REQUEST_PREFIX"),
+ STAFF_JOIN_NETWORK("STAFF.JOIN_NETWORK"),
+ STAFF_SWITCH_SERVER("STAFF.SWITCH_SERVER"),
+ STAFF_LEAVE_NETWORK("STAFF.LEAVE_NETWORK"),
+ SERVER_STATUS("SERVER.STATUS"),
+ SERVER_STATUS_PREFIX("SERVER.PREFIX"),
+ NETWORK_BROADCAST_PREFIX("NETWORK.BROADCAST_PREFIX"),
+ NETWORK_RANK_REFRESHED("NETWORK.RANK_REFRESH"),
+ NETWORK_RANK_DELETED("NETWORK.RANK_DELETE");
+
+ private String path;
+
+ public String format(Object... objects) {
+ return new MessageFormat(ChatColor.translateAlternateColorCodes('&',
+ Core.get().getMainConfig().getString(path))).format(objects);
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/board/Board.java b/src/main/java/net/centilehcf/core/board/Board.java
new file mode 100644
index 0000000..57ccd1e
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/board/Board.java
@@ -0,0 +1,81 @@
+package net.centilehcf.core.board;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+import lombok.Getter;
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
+import org.bukkit.entity.Player;
+import org.bukkit.scoreboard.DisplaySlot;
+import org.bukkit.scoreboard.Objective;
+import org.bukkit.scoreboard.Scoreboard;
+
+@Getter
+public class Board {
+
+ // We assign a unique identifier (random string of ChatColor values)
+ // to each board entry to: bypass the 32 char limit, using
+ // a team's prefix & suffix and a team entry's display name, and to
+ // track the order of entries;
+ private final List entries = new ArrayList<>();
+
+ private final List identifiers = new ArrayList<>();
+
+ private Scoreboard scoreboard;
+
+ private Objective objective;
+
+ public Board(Player player) {
+ this.setup(player);
+ }
+
+ private void setup(Player player) {
+ // Register new scoreboard if needed
+ if (player.getScoreboard().equals(Bukkit.getScoreboardManager().getMainScoreboard())) {
+ this.scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
+ } else {
+ this.scoreboard = player.getScoreboard();
+ }
+
+ // Setup sidebar objective
+ this.objective = this.scoreboard.registerNewObjective("Default", "dummy");
+ this.objective.setDisplaySlot(DisplaySlot.SIDEBAR);
+ this.objective.setDisplayName(MainBoard.getInstance().getAdapter().getTitle(player));
+
+ // Update scoreboard
+ player.setScoreboard(this.scoreboard);
+ }
+
+ public BoardEntry getEntryAtPosition(int pos) {
+ if (pos >= this.entries.size()) {
+ return null;
+ } else {
+ return this.entries.get(pos);
+ }
+ }
+
+ public String getUniqueIdentifier(String text) {
+ String identifier = getRandomChatColor() + ChatColor.WHITE;
+
+ while (this.identifiers.contains(identifier)) {
+ identifier = identifier + getRandomChatColor() + ChatColor.WHITE;
+ }
+
+ // This is rare, but just in case, make the method recursive
+ if (identifier.length() > 16) {
+ return this.getUniqueIdentifier(text);
+ }
+
+ // Add our identifier to the list so there are no duplicates
+ this.identifiers.add(identifier);
+
+ return identifier;
+ }
+
+ // Gets a random ChatColor and returns the String value of it
+ private static String getRandomChatColor() {
+ return ChatColor.values()[ThreadLocalRandom.current().nextInt(ChatColor.values().length)].toString();
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/board/BoardAdapter.java b/src/main/java/net/centilehcf/core/board/BoardAdapter.java
new file mode 100644
index 0000000..d486727
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/board/BoardAdapter.java
@@ -0,0 +1,14 @@
+package net.centilehcf.core.board;
+
+import java.util.List;
+import org.bukkit.entity.Player;
+
+public interface BoardAdapter {
+
+ String getTitle(Player player);
+
+ List getLines(Player player);
+
+ void preLoop();
+
+}
diff --git a/src/main/java/net/centilehcf/core/board/BoardEntry.java b/src/main/java/net/centilehcf/core/board/BoardEntry.java
new file mode 100644
index 0000000..b61f7cf
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/board/BoardEntry.java
@@ -0,0 +1,98 @@
+package net.centilehcf.core.board;
+
+import lombok.Setter;
+import org.bukkit.ChatColor;
+import org.bukkit.scoreboard.Score;
+import org.bukkit.scoreboard.Scoreboard;
+import org.bukkit.scoreboard.Team;
+
+public class BoardEntry {
+
+ private final Board board;
+
+ @Setter
+ private String text;
+
+ private String identifier;
+
+ private Team team;
+
+ public BoardEntry(Board board, String text) {
+ this.board = board;
+ this.text = text;
+ this.identifier = this.board.getUniqueIdentifier(text);
+
+ this.setup();
+ }
+
+ public void setup() {
+ final Scoreboard scoreboard = this.board.getScoreboard();
+
+ String teamName = this.identifier;
+
+ // This shouldn't happen, but just in case
+ if (teamName.length() > 16) {
+ teamName = teamName.substring(0, 16);
+ }
+
+ Team team = scoreboard.getTeam(teamName);
+
+ // Register the team if it does not exist
+ if (team == null) {
+ team = scoreboard.registerNewTeam(teamName);
+ }
+
+ // Add the entry to the team
+ if (!team.getEntries().contains(this.identifier)) {
+ team.addEntry(this.identifier);
+ }
+
+ // Add the entry if it does not exist
+ if (!this.board.getEntries().contains(this)) {
+ this.board.getEntries().add(this);
+ }
+
+ this.team = team;
+ }
+
+ public void send(int position) {
+ if (this.text.length() > 16) {
+ String prefix = this.text.substring(0, 16);
+ String suffix;
+
+ if (prefix.charAt(15) == ChatColor.COLOR_CHAR) {
+ prefix = prefix.substring(0, 15);
+ suffix = this.text.substring(15);
+ } else if (prefix.charAt(14) == ChatColor.COLOR_CHAR) {
+ prefix = prefix.substring(0, 14);
+ suffix = this.text.substring(14);
+ } else {
+ if (ChatColor.getLastColors(prefix).equalsIgnoreCase(ChatColor.getLastColors(this.identifier))) {
+ suffix = this.text.substring(16);
+ } else {
+ suffix = ChatColor.getLastColors(prefix) + this.text.substring(16);
+ }
+ }
+
+ if (suffix.length() > 16) {
+ suffix = suffix.substring(0, 16);
+ }
+
+ this.team.setPrefix(prefix);
+ this.team.setSuffix(suffix);
+ } else {
+ this.team.setPrefix(this.text);
+ this.team.setSuffix("");
+ }
+
+ final Score score = this.board.getObjective().getScore(this.identifier);
+
+ score.setScore(position);
+ }
+
+ public void remove() {
+ this.board.getIdentifiers().remove(this.identifier);
+ this.board.getScoreboard().resetScores(this.identifier);
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/board/BoardListener.java b/src/main/java/net/centilehcf/core/board/BoardListener.java
new file mode 100644
index 0000000..128214f
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/board/BoardListener.java
@@ -0,0 +1,52 @@
+package net.centilehcf.core.board;
+
+import net.centilehcf.core.board.events.BoardCreateEvent;
+import net.centilehcf.core.board.events.BoardDestroyEvent;
+import org.bukkit.Bukkit;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.event.server.PluginDisableEvent;
+
+public class BoardListener implements Listener {
+
+ @EventHandler
+ public void onPlayerJoin(PlayerJoinEvent event) {
+
+ final BoardCreateEvent createEvent = new BoardCreateEvent(event.getPlayer());
+
+ Bukkit.getPluginManager().callEvent(createEvent);
+
+ if (createEvent.isCancelled()) {
+ return;
+ }
+
+ MainBoard.getInstance().getBoards().put(event.getPlayer().getUniqueId(), new Board(event.getPlayer()));
+ }
+
+ @EventHandler
+ public void onPlayerQuit(PlayerQuitEvent event) {
+
+ final BoardDestroyEvent destroyEvent = new BoardDestroyEvent(event.getPlayer());
+
+ Bukkit.getPluginManager().callEvent(destroyEvent);
+
+ if (destroyEvent.isCancelled()) {
+ return;
+ }
+
+ MainBoard.getInstance().getBoards().remove(event.getPlayer().getUniqueId());
+ }
+
+ @EventHandler
+ public void onPluginDisable(PluginDisableEvent event) {
+
+ if (MainBoard.getInstance().getThread() == null) {
+ return;
+ }
+
+ MainBoard.getInstance().getThread().stop();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/centilehcf/core/board/BoardStyle.java b/src/main/java/net/centilehcf/core/board/BoardStyle.java
new file mode 100644
index 0000000..e18b4de
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/board/BoardStyle.java
@@ -0,0 +1,24 @@
+package net.centilehcf.core.board;
+
+public enum BoardStyle {
+
+ KOHI(true, 15),
+ VIPER(true, -1),
+ MODERN(false, 1);
+
+ private boolean decending;
+ private int startNumber;
+
+ BoardStyle(boolean decending, int startNumber) {
+ this.decending = decending;
+ this.startNumber = startNumber;
+ }
+
+ public boolean isDecending() {
+ return this.decending;
+ }
+
+ public int getStartNumber() {
+ return this.startNumber;
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/board/BoardThread.java b/src/main/java/net/centilehcf/core/board/BoardThread.java
new file mode 100644
index 0000000..55a5538
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/board/BoardThread.java
@@ -0,0 +1,114 @@
+package net.centilehcf.core.board;
+
+import org.bukkit.ChatColor;
+import org.bukkit.entity.Player;
+import org.bukkit.scoreboard.Objective;
+import org.bukkit.scoreboard.Scoreboard;
+
+import java.util.Collections;
+import java.util.List;
+
+public class BoardThread extends Thread {
+
+ private MainBoard mainBoard;
+
+ BoardThread(MainBoard mainBoard) {
+ this.mainBoard = mainBoard;
+ this.start();
+ }
+
+ @Override
+ public void run() {
+ while(true) {
+ //Tick
+ try {
+ tick();
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ }
+ //Thread Sleep
+ try {
+ sleep(mainBoard.getTicks() * 50);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void tick() {
+
+ mainBoard.getAdapter().preLoop();
+
+ for (Player player : this.mainBoard.getPlugin().getServer().getOnlinePlayers()) {
+ final Board board = this.mainBoard.getBoards().get(player.getUniqueId());
+
+ // This shouldn't happen, but just in case
+ if (board == null) {
+ continue;
+ }
+
+ final Scoreboard scoreboard = board.getScoreboard();
+ final Objective objective = board.getObjective();
+
+ // Just make a variable so we don't have to²
+ // process the same thing twice
+ final String title = ChatColor.translateAlternateColorCodes('&', this.mainBoard.getAdapter().getTitle(player));
+
+ // Update the title if needed
+ if (!objective.getDisplayName().equals(title)) {
+ objective.setDisplayName(title);
+ }
+
+ final List newLines = this.mainBoard.getAdapter().getLines(player);
+
+ // Allow adapter to return null/empty list to display nothing
+ if (newLines == null || newLines.isEmpty()) {
+ board.getEntries().forEach(BoardEntry::remove);
+ board.getEntries().clear();
+ } else {
+ // Reverse the lines because scoreboard scores are in descending order
+ if (!this.mainBoard.getBoardStyle().isDecending()) {
+ Collections.reverse(newLines);
+ }
+
+ // Remove excessive amount of board entries
+ if (board.getEntries().size() > newLines.size()) {
+ for (int i = newLines.size(); i < board.getEntries().size(); i++) {
+ final BoardEntry entry = board.getEntryAtPosition(i);
+
+ if (entry != null) {
+ entry.remove();
+ }
+ }
+ }
+
+ // Update existing entries / add new entries
+ int cache = this.mainBoard.getBoardStyle().getStartNumber();
+ for (int i = 0; i < newLines.size(); i++) {
+ BoardEntry entry = board.getEntryAtPosition(i);
+
+ // Translate any colors
+ final String line = ChatColor.translateAlternateColorCodes('&', newLines.get(i));
+
+ // If the entry is null, just create a new one.
+ // Creating a new BoardEntry instance will add
+ // itself to the provided board's entries list.
+ if (entry == null) {
+ entry = new BoardEntry(board, line);
+ }
+
+ // Update text, setup the team, and update the display values
+ entry.setText(line);
+ entry.setup();
+ entry.send(
+ this.mainBoard.getBoardStyle().isDecending() ? cache-- : cache++
+ );
+ }
+ }
+
+ if (player.getScoreboard() != scoreboard) {
+ player.setScoreboard(scoreboard);
+ }
+ }
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/board/MainBoard.java b/src/main/java/net/centilehcf/core/board/MainBoard.java
new file mode 100644
index 0000000..eb3f9d2
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/board/MainBoard.java
@@ -0,0 +1,66 @@
+package net.centilehcf.core.board;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import net.centilehcf.core.Core;
+import org.bukkit.Bukkit;
+import org.bukkit.plugin.java.JavaPlugin;
+
+@Getter
+public class MainBoard {
+
+ //Instance
+ @Getter private static MainBoard instance;
+
+ private JavaPlugin plugin;
+ private BoardAdapter adapter;
+ private Map boards;
+ private BoardThread thread;
+
+ //Scoreboard Ticks
+ @Setter
+ private long ticks = 2;
+
+ //Default Scoreboard Style
+ @Setter
+ private BoardStyle boardStyle = BoardStyle.MODERN;
+
+ public MainBoard(JavaPlugin plugin, BoardAdapter adapter) {
+
+ if (instance != null) {
+ throw new RuntimeException("MainBoard has already been instantiated!");
+ }
+
+ if (plugin == null) {
+ throw new RuntimeException("MainBoard can not be instantiated without a plugin instance!");
+ }
+
+ instance = this;
+
+ this.plugin = plugin;
+ this.adapter = adapter;
+ this.boards = new ConcurrentHashMap<>();
+
+ this.setup();
+ }
+
+ private void setup() {
+ //Register Events
+ Bukkit.getPluginManager().registerEvents(new BoardListener(), Core.get());
+
+ //Ensure that the thread has stopped running
+ if (this.thread != null) {
+ this.thread.stop();
+ this.thread = null;
+ }
+
+ //Start Thread
+ this.thread = new BoardThread(this);
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/board/events/BoardCreateEvent.java b/src/main/java/net/centilehcf/core/board/events/BoardCreateEvent.java
new file mode 100644
index 0000000..77c4a7d
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/board/events/BoardCreateEvent.java
@@ -0,0 +1,28 @@
+package net.centilehcf.core.board.events;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+@Getter
+public class BoardCreateEvent extends Event implements Cancellable {
+
+ @Getter public static HandlerList HANDLER_LIST = new HandlerList();
+
+ private Player player;
+
+ @Setter
+ private boolean cancelled = false;
+
+ public BoardCreateEvent(Player player) {
+ this.player = player;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLER_LIST;
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/board/events/BoardDestroyEvent.java b/src/main/java/net/centilehcf/core/board/events/BoardDestroyEvent.java
new file mode 100644
index 0000000..719817b
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/board/events/BoardDestroyEvent.java
@@ -0,0 +1,31 @@
+package net.centilehcf.core.board.events;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+@Getter
+public class BoardDestroyEvent extends Event implements Cancellable {
+
+ @Getter
+ public static HandlerList HANDLER_LIST = new HandlerList();
+
+ private Player player;
+
+ @Setter
+ private boolean cancelled = false;
+
+ public BoardDestroyEvent(Player player) {
+ this.player = player;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLER_LIST;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/bootstrap/Bootstrapped.java b/src/main/java/net/centilehcf/core/bootstrap/Bootstrapped.java
new file mode 100644
index 0000000..a0ce2e0
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/bootstrap/Bootstrapped.java
@@ -0,0 +1,14 @@
+package net.centilehcf.core.bootstrap;
+
+import net.centilehcf.core.Core;
+import lombok.Getter;
+
+@Getter
+public class Bootstrapped {
+
+ protected final Core core;
+
+ public Bootstrapped(Core core) {
+ this.core = core;
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/bootstrap/BootstrappedListener.java b/src/main/java/net/centilehcf/core/bootstrap/BootstrappedListener.java
new file mode 100644
index 0000000..c2a156a
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/bootstrap/BootstrappedListener.java
@@ -0,0 +1,11 @@
+package net.centilehcf.core.bootstrap;
+
+import net.centilehcf.core.Core;
+import org.bukkit.event.Listener;
+
+public class BootstrappedListener extends Bootstrapped implements Listener {
+
+ public BootstrappedListener(Core core) {
+ super(core);
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/chat/Chat.java b/src/main/java/net/centilehcf/core/chat/Chat.java
new file mode 100644
index 0000000..88ed62f
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/chat/Chat.java
@@ -0,0 +1,67 @@
+package net.centilehcf.core.chat;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.bootstrap.Bootstrapped;
+import net.centilehcf.core.chat.filter.ChatFilter;
+import net.centilehcf.core.profile.Profile;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
+import org.bukkit.entity.Player;
+
+public class Chat extends Bootstrapped {
+
+ public Chat(Core core) {
+ super(core);
+ }
+
+ @Getter @Setter private int delayTime = 3;
+ @Getter private boolean publicChatMuted = false;
+ @Getter private boolean publicChatDelayed = false;
+ @Getter private final List filters = new ArrayList<>();
+ @Getter private List filteredPhrases = new ArrayList<>();
+ @Getter private List linkWhitelist = new ArrayList<>();
+
+ public void setPublicChatMuted(boolean muted) {
+ if (this.publicChatMuted == muted) {
+ return;
+ }
+
+ publicChatMuted = !publicChatMuted;
+ }
+
+ public ChatAttempt attemptChatMessage(Player player, String message) {
+ Profile profile = Profile.getProfiles().get(player.getUniqueId());
+
+ if (profile.getActiveMute() != null) {
+ return new ChatAttempt(ChatAttempt.Response.PLAYER_MUTED, profile.getActiveMute());
+ }
+
+ if (publicChatMuted && !player.hasPermission("core.mod")) {
+ return new ChatAttempt(ChatAttempt.Response.CHAT_MUTED);
+ }
+
+ String msg = message.toLowerCase()
+ .replace("3", "e")
+ .replace("1", "i")
+ .replace("!", "i")
+ .replace("@", "a")
+ .replace("7", "t")
+ .replace("0", "o")
+ .replace("5", "s")
+ .replace("8", "b")
+ .replaceAll("\\p{Punct}|\\d", "").trim();
+
+ String[] words = msg.trim().split(" ");
+
+ for (ChatFilter chatFilter : this.filters) {
+ if (chatFilter.isFiltered(msg, words)) {
+ return new ChatAttempt(ChatAttempt.Response.MESSAGE_FILTERED);
+ }
+ }
+
+ return new ChatAttempt(ChatAttempt.Response.ALLOWED);
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/chat/ChatAttempt.java b/src/main/java/net/centilehcf/core/chat/ChatAttempt.java
new file mode 100644
index 0000000..d399e68
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/chat/ChatAttempt.java
@@ -0,0 +1,38 @@
+package net.centilehcf.core.chat;
+
+import net.centilehcf.core.chat.filter.ChatFilter;
+import net.centilehcf.core.profile.punishment.Punishment;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class ChatAttempt {
+
+ private Response response;
+ private ChatFilter filterFlagged;
+ private Punishment punishment;
+
+ public ChatAttempt(Response response) {
+ this.response = response;
+ }
+
+ public ChatAttempt(Response response, ChatFilter filterFlagged) {
+ this.response = response;
+ this.filterFlagged = filterFlagged;
+ }
+
+ public ChatAttempt(Response response, Punishment punishment) {
+ this.response = response;
+ this.punishment = punishment;
+ }
+
+ public enum Response {
+ ALLOWED,
+ MESSAGE_FILTERED,
+ PLAYER_MUTED,
+ CHAT_MUTED,
+ CHAT_DELAYED
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/chat/command/MuteChatCommand.java b/src/main/java/net/centilehcf/core/chat/command/MuteChatCommand.java
new file mode 100644
index 0000000..5bfbace
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/chat/command/MuteChatCommand.java
@@ -0,0 +1,16 @@
+package net.centilehcf.core.chat.command;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = "mutechat", permission = "core.seniormod")
+public class MuteChatCommand {
+
+ public void execute(CommandSender sender) {
+ Core.get().getChat().setPublicChatMuted(!Core.get().getChat().isPublicChatMuted());
+ Core.get().getServer().broadcastMessage((CC.PINK + "Public chat has been {context} by " + sender.getName())
+ .replace("{context}", Core.get().getChat().isPublicChatMuted() ? "muted" : "unmuted"));
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/chat/event/ChatAttemptEvent.java b/src/main/java/net/centilehcf/core/chat/event/ChatAttemptEvent.java
new file mode 100644
index 0000000..050a808
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/chat/event/ChatAttemptEvent.java
@@ -0,0 +1,27 @@
+package net.centilehcf.core.chat.event;
+
+import net.centilehcf.core.chat.ChatAttempt;
+import net.centilehcf.core.util.BaseEvent;
+import lombok.Getter;
+import lombok.Setter;
+import net.centilehcf.core.chat.ChatAttempt;
+import net.centilehcf.core.util.BaseEvent;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+
+@Getter
+public class ChatAttemptEvent extends BaseEvent implements Cancellable {
+
+ private final Player player;
+ private final ChatAttempt chatAttempt;
+ @Setter private String chatMessage;
+ @Setter private boolean cancelled;
+ @Setter private String cancelReason = "";
+
+ public ChatAttemptEvent(Player player, ChatAttempt chatAttempt, String chatMessage) {
+ this.player = player;
+ this.chatAttempt = chatAttempt;
+ this.chatMessage = chatMessage;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/chat/filter/ChatFilter.java b/src/main/java/net/centilehcf/core/chat/filter/ChatFilter.java
new file mode 100644
index 0000000..c0d2825
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/chat/filter/ChatFilter.java
@@ -0,0 +1,26 @@
+package net.centilehcf.core.chat.filter;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.bootstrap.Bootstrapped;
+import org.bukkit.entity.Player;
+
+public abstract class ChatFilter extends Bootstrapped {
+
+ private String command;
+
+ public ChatFilter(Core core, String command) {
+ super(core);
+
+ this.command = command;
+ }
+
+ public abstract boolean isFiltered(String message, String[] words);
+
+ public void punish(Player player) {
+ if (command != null) {
+ core.getServer().dispatchCommand(core.getServer().getConsoleSender(), command
+ .replace("{player}", player.getName())
+ .replace("{player-uuid}", player.getUniqueId().toString()));
+ }
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/chat/filter/impl/ContainsFilter.java b/src/main/java/net/centilehcf/core/chat/filter/impl/ContainsFilter.java
new file mode 100644
index 0000000..ec34144
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/chat/filter/impl/ContainsFilter.java
@@ -0,0 +1,30 @@
+package net.centilehcf.core.chat.filter.impl;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.chat.filter.ChatFilter;
+
+public class ContainsFilter extends ChatFilter {
+
+ private final String phrase;
+
+ public ContainsFilter(Core core, String phrase) {
+ this(core, phrase, null);
+ }
+
+ public ContainsFilter(Core core, String phrase, String command) {
+ super(core, command);
+ this.phrase = phrase;
+ }
+
+ @Override
+ public boolean isFiltered(String message, String[] words) {
+ for (String word : words) {
+ if (word.contains(this.phrase)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/chat/filter/impl/LinkFilter.java b/src/main/java/net/centilehcf/core/chat/filter/impl/LinkFilter.java
new file mode 100644
index 0000000..77dcd73
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/chat/filter/impl/LinkFilter.java
@@ -0,0 +1,50 @@
+package net.centilehcf.core.chat.filter.impl;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.chat.filter.ChatFilter;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class LinkFilter extends ChatFilter {
+
+ private static final Pattern URL_REGEX = Pattern.compile(
+ "^(http://www\\.|https://www\\.|http://|https://)?[a-z0-9]+([\\-.][a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(/.*)?$");
+ private static final Pattern IP_REGEX = Pattern.compile(
+ "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])([.,])){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$");
+
+ public LinkFilter(Core core) {
+ super(core, null);
+ }
+
+ @Override
+ public boolean isFiltered(String message, String[] words) {
+ for (String word : message.replace("(dot)", ".").replace("[dot]", ".").trim().split(" ")) {
+ boolean continueIt = false;
+
+ for (String phrase : this.core.getChat().getLinkWhitelist()) {
+ if (word.toLowerCase().contains(phrase)) {
+ continueIt = true;
+ break;
+ }
+ }
+
+ if (!continueIt) {
+ Matcher matcher = IP_REGEX.matcher(word);
+
+ if (matcher.matches()) {
+ return true;
+ }
+
+ matcher = URL_REGEX.matcher(word);
+
+ if (matcher.matches()) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/chat/listener/ChatListener.java b/src/main/java/net/centilehcf/core/chat/listener/ChatListener.java
new file mode 100644
index 0000000..5bb3964
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/chat/listener/ChatListener.java
@@ -0,0 +1,76 @@
+package net.centilehcf.core.chat.listener;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.bootstrap.BootstrappedListener;
+import net.centilehcf.core.chat.ChatAttempt;
+import net.centilehcf.core.chat.event.ChatAttemptEvent;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.Cooldown;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.player.AsyncPlayerChatEvent;
+
+public class ChatListener extends BootstrappedListener {
+
+ public ChatListener(Core core) {
+ super(core);
+ }
+
+ @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
+ public void onAsyncPlayerChatEvent(AsyncPlayerChatEvent event) {
+ Player player = event.getPlayer();
+ Profile profile = Profile.getByUuid(player.getUniqueId());
+
+ ChatAttempt chatAttempt = core.getChat().attemptChatMessage(event.getPlayer(), event.getMessage());
+ ChatAttemptEvent chatAttemptEvent = new ChatAttemptEvent(event.getPlayer(), chatAttempt, event.getMessage());
+
+ if (!player.hasPermission("core.staff")) {
+ if (!profile.getChatCooldown().hasExpired()) {
+ player.sendMessage(CC.translate("&cYou can chat again in &c&l%TIME_LEFT%s&c.".replace("%TIME_LEFT%", profile.getChatCooldown().getTimeLeft())));
+ event.setCancelled(true);
+ return;
+ } else {
+ profile.setChatCooldown(new Cooldown(Core.get().getChat().getDelayTime() * 1000));
+ }
+ }
+
+ core.getServer().getPluginManager().callEvent(chatAttemptEvent);
+
+ if (!chatAttemptEvent.isCancelled()) {
+ switch (chatAttempt.getResponse()) {
+ case ALLOWED: {
+ if (profile.getPrefix() == Core.get().getPrefixHandler().getDefaultPrefix()) {
+ event.setFormat("%1$s" + profile.getActiveGrant().getRank().getSuffix() + CC.RESET + ": %2$s");
+ } else {
+ event.setFormat(profile.getPrefix().getPrefix() + "%1$s" + profile.getActiveGrant().getRank().getSuffix() + CC.RESET + ": %2$s");
+ }
+ }
+ break;
+ case MESSAGE_FILTERED: {
+ event.setCancelled(true);
+ chatAttempt.getFilterFlagged().punish(event.getPlayer());
+ }
+ break;
+ case PLAYER_MUTED: {
+ event.setCancelled(true);
+ event.getPlayer().sendMessage(CC.RED + "You are currently muted.");
+ event.getPlayer().sendMessage(CC.RED + "Reason: " + CC.YELLOW + chatAttempt.getPunishment().getAddedReason());
+ event.getPlayer().sendMessage(CC.RED + "Expires: " + CC.YELLOW + chatAttempt.getPunishment().getTimeRemaining());
+ }
+ break;
+ case CHAT_MUTED: {
+ event.setCancelled(true);
+ event.getPlayer().sendMessage(CC.RED + "The public chat is currently muted.");
+ }
+ break;
+ case CHAT_DELAYED: {
+ event.setCancelled(true);
+ event.getPlayer().sendMessage(CC.RED + "Slow down! You may chat again in {time}.");
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/chat/util/ChatComponentBuilder.java b/src/main/java/net/centilehcf/core/chat/util/ChatComponentBuilder.java
new file mode 100644
index 0000000..f8bb702
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/chat/util/ChatComponentBuilder.java
@@ -0,0 +1,219 @@
+package net.centilehcf.core.chat.util;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.chat.BaseComponent;
+import net.md_5.bungee.api.chat.ClickEvent;
+import net.md_5.bungee.api.chat.ComponentBuilder;
+import net.md_5.bungee.api.chat.HoverEvent;
+import net.md_5.bungee.api.chat.TextComponent;
+
+public class ChatComponentBuilder extends ComponentBuilder {
+
+ private static Field partsField;
+ private static Field currField;
+
+ static {
+ try {
+ currField = ComponentBuilder.class.getDeclaredField("current");
+ partsField = ComponentBuilder.class.getDeclaredField("parts");
+
+ currField.setAccessible(true);
+ partsField.setAccessible(true);
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public ChatComponentBuilder(String text) {
+ super("");
+ this.parse(text);
+ }
+
+ public TextComponent getCurrent() {
+ try {
+ return (TextComponent) currField.get(this);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public void setCurrent(TextComponent tc) {
+ try {
+ currField.set(this, tc);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public List getParts() {
+ try {
+ return (List) partsField.get(this);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ public ChatComponentBuilder setCurrentHoverEvent(HoverEvent hoverEvent) {
+ this.getCurrent().setHoverEvent(hoverEvent);
+
+ return this;
+ }
+
+ public ChatComponentBuilder setCurrentClickEvent(ClickEvent clickEvent) {
+ this.getCurrent().setClickEvent(clickEvent);
+
+ return this;
+ }
+
+ public ChatComponentBuilder attachToEachPart(HoverEvent hoverEvent) {
+ for (Object part : getParts()) {
+ TextComponent component = (TextComponent) part;
+
+ if (component.getHoverEvent() == null) {
+ component.setHoverEvent(hoverEvent);
+ }
+ }
+
+ this.getCurrent().setHoverEvent(hoverEvent);
+
+ return this;
+ }
+
+ public ChatComponentBuilder attachToEachPart(ClickEvent clickEvent) {
+ for (Object part : getParts()) {
+ TextComponent component = (TextComponent) part;
+
+ if (component.getClickEvent() == null) {
+ component.setClickEvent(clickEvent);
+ }
+ }
+
+ this.getCurrent().setClickEvent(clickEvent);
+
+ return this;
+ }
+
+ public ChatComponentBuilder parse(String text) {
+ String regex = "[&ยง]{1}([a-fA-Fl-oL-O0-9-r]){1}";
+ text = text.replaceAll(regex, "ยง$1");
+
+ if (!Pattern.compile(regex).matcher(text).find()) {
+ if (getParts().isEmpty() && getCurrent() != null && getCurrent().getText().isEmpty()) {
+ getCurrent().setText(text);
+ } else {
+ this.append(text);
+ }
+
+ return this;
+ }
+
+ String[] words = text.split(regex);
+ int index = words[0].length();
+
+ for (String word : words) {
+ try {
+ if (index != words[0].length()) {
+ if (getParts().isEmpty() && getCurrent() != null && getCurrent().getText().isEmpty()) {
+ getCurrent().setText(word);
+ } else {
+ this.append(word);
+ }
+
+ ChatColor color = ChatColor.getByChar(text.charAt(index - 1));
+
+ if (color == ChatColor.BOLD) {
+ this.bold(true);
+ } else if (color == ChatColor.STRIKETHROUGH) {
+ this.strikethrough(true);
+ } else if (color == ChatColor.MAGIC) {
+ this.obfuscated(true);
+ } else if (color == ChatColor.UNDERLINE) {
+ this.underlined(true);
+ } else if (color == ChatColor.RESET) {
+ this.bold(false);
+ this.strikethrough(false);
+ this.obfuscated(false);
+ this.underlined(false);
+ } else {
+ this.color(color);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ index += word.length() + 2;
+ }
+
+ return this;
+ }
+
+ public ChatComponentBuilder append(BaseComponent[] components) {
+ for (BaseComponent component : components) {
+ append((TextComponent) component);
+ }
+
+ return this;
+ }
+
+ public ChatComponentBuilder append(TextComponent textComponent) {
+ if (textComponent == null) {
+ return this;
+ }
+
+ String text = textComponent.getText();
+ ChatColor color = textComponent.getColor();
+ boolean bold = textComponent.isBold();
+ boolean underline = textComponent.isUnderlined();
+ boolean italic = textComponent.isUnderlined();
+ boolean strike = textComponent.isStrikethrough();
+ HoverEvent he = textComponent.getHoverEvent();
+ ClickEvent ce = textComponent.getClickEvent();
+
+ append(text);
+ color(color);
+ underlined(underline);
+ italic(italic);
+ strikethrough(strike);
+ event(he);
+ event(ce);
+
+ if (textComponent.getExtra() != null) {
+ for (BaseComponent bc : textComponent.getExtra()) {
+ if (bc instanceof TextComponent) {
+ append((TextComponent) bc);
+ }
+ }
+ }
+
+ return this;
+ }
+
+ @Override
+ public BaseComponent[] create() {
+ List components = new ArrayList<>(getParts());
+ components.add(getCurrent());
+
+ TextComponent first = components.get(0);
+
+ if (first.getText().isEmpty()) {
+ components.remove(0);
+ }
+
+ TextComponent last = components.get(components.size() - 1);
+
+ if (last.getText().isEmpty()) {
+ components.remove(components.size() - 1);
+ }
+
+ return components.toArray(new BaseComponent[components.size()]);
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/chat/util/ChatComponentExtras.java b/src/main/java/net/centilehcf/core/chat/util/ChatComponentExtras.java
new file mode 100644
index 0000000..ea9afc3
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/chat/util/ChatComponentExtras.java
@@ -0,0 +1,15 @@
+package net.centilehcf.core.chat.util;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import net.md_5.bungee.api.chat.ClickEvent;
+import net.md_5.bungee.api.chat.HoverEvent;
+
+@AllArgsConstructor
+@Data
+public class ChatComponentExtras {
+
+ private HoverEvent hoverEvent;
+ private ClickEvent clickEvent;
+
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/Essentials.java b/src/main/java/net/centilehcf/core/essentials/Essentials.java
new file mode 100644
index 0000000..1f8793d
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/Essentials.java
@@ -0,0 +1,89 @@
+package net.centilehcf.core.essentials;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.bootstrap.Bootstrapped;
+import net.centilehcf.core.essentials.event.SpawnTeleportEvent;
+import net.centilehcf.core.util.LocationUtil;
+import java.io.IOException;
+
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.Player;
+
+public class Essentials extends Bootstrapped {
+
+ private Location spawn;
+
+ public Essentials(Core core) {
+ super(core);
+
+ spawn = LocationUtil.deserialize(core.getMainConfig().getStringOrDefault("ESSENTIAL.SPAWN_LOCATION", null));
+ }
+
+ public void setSpawn(Location location) {
+ spawn = location;
+
+ if (spawn == null) {
+ core.getMainConfig().getConfiguration().set("ESSENTIAL.SPAWN_LOCATION", null);
+ } else {
+ core.getMainConfig().getConfiguration().set("ESSENTIAL.SPAWN_LOCATION", LocationUtil.serialize(this.spawn));
+ }
+
+ try {
+ core.getMainConfig().getConfiguration().save(core.getMainConfig().getFile());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void teleportToSpawn(Player player) {
+ Location location = spawn == null ? core.getServer().getWorlds().get(0).getSpawnLocation() : spawn;
+
+ SpawnTeleportEvent event = new SpawnTeleportEvent(player, location);
+ event.call();
+
+ if (!event.isCancelled() && event.getLocation() != null) {
+ player.teleport(event.getLocation());
+ }
+ }
+
+ public int clearEntities(World world) {
+ int removed = 0;
+
+ for (Entity entity : world.getEntities()) {
+ if (entity.getType() == EntityType.PLAYER) {
+ continue;
+ }
+
+ removed++;
+ entity.remove();
+ }
+
+ return removed;
+ }
+
+ public int clearEntities(World world, EntityType... excluded) {
+ int removed = 0;
+
+ entityLoop:
+ for (Entity entity : world.getEntities()) {
+ for (EntityType type : excluded) {
+ if (entity.getType() == EntityType.PLAYER) {
+ continue entityLoop;
+ }
+
+ if (entity.getType() == type) {
+ continue entityLoop;
+ }
+ }
+
+ removed++;
+ entity.remove();
+ }
+
+ return removed;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/BroadcastCommand.java b/src/main/java/net/centilehcf/core/essentials/command/BroadcastCommand.java
new file mode 100644
index 0000000..19b8fe0
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/BroadcastCommand.java
@@ -0,0 +1,16 @@
+package net.centilehcf.core.essentials.command;
+
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import com.qrakn.honcho.command.CommandOption;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = { "broadcast", "bc" }, options = "r", permission = "rank.manager")
+public class BroadcastCommand {
+
+ public void execute(CommandSender sender, CommandOption option, String broadcast) {
+ String message = broadcast.replaceAll("(&([a-f0-9l-or]))", "\u00A7$2");
+ Bukkit.broadcastMessage(CC.translate((option == null ? "&6[Broadcast] &r" : "") + message));
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/ClearChatCommand.java b/src/main/java/net/centilehcf/core/essentials/command/ClearChatCommand.java
new file mode 100644
index 0000000..832fb0b
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/ClearChatCommand.java
@@ -0,0 +1,30 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.util.CC;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/12/2019.
+ */
+@CommandMeta(label = {"clearchat", "cc", "removeallshitinthechat"}, permission = "rank.mod")
+public class ClearChatCommand {
+ public void execute(Player player) {
+ StringBuilder builder = new StringBuilder();
+
+ for (int i = 0; i < 100; i++) {
+ builder.append("§a §b §c §d §e §f §0 §r \n");
+ }
+
+ final String clear = builder.toString();
+
+ for (Player player2 : Core.get().getServer().getOnlinePlayers()) {
+ if (!player2.hasPermission("plib.staff")) {
+ player2.sendMessage(clear);
+ }
+
+ player2.sendMessage(CC.GREEN + "Chat has been cleared!");
+ }
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/ClearCommand.java b/src/main/java/net/centilehcf/core/essentials/command/ClearCommand.java
new file mode 100644
index 0000000..54613b8
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/ClearCommand.java
@@ -0,0 +1,27 @@
+package net.centilehcf.core.essentials.command;
+
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+
+@CommandMeta(label = { "clearinv", "clear", "ci" }, permission = "rank.admin")
+public class ClearCommand {
+
+ public void execute(Player player) {
+ player.getInventory().setContents(new ItemStack[36]);
+ player.getInventory().setArmorContents(new ItemStack[4]);
+ player.updateInventory();
+ player.sendMessage(CC.GOLD + "You cleared your inventory.");
+ }
+
+ public void execute(CommandSender sender, Player player) {
+ player.getInventory().setContents(new ItemStack[36]);
+ player.getInventory().setArmorContents(new ItemStack[4]);
+ player.updateInventory();
+ player.sendMessage(CC.GOLD + "Your inventory has been cleared by " + sender.getName());
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/CraftCommand.java b/src/main/java/net/centilehcf/core/essentials/command/CraftCommand.java
new file mode 100644
index 0000000..a453a05
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/CraftCommand.java
@@ -0,0 +1,16 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 5/28/2019.
+ */
+@CommandMeta(label = "craft", permission = "command.craft")
+public class CraftCommand {
+
+ public void execute(Player player) {
+ player.openWorkbench(player.getLocation(), true);
+ }
+}
+
diff --git a/src/main/java/net/centilehcf/core/essentials/command/DayCommand.java b/src/main/java/net/centilehcf/core/essentials/command/DayCommand.java
new file mode 100644
index 0000000..fed0452
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/DayCommand.java
@@ -0,0 +1,16 @@
+package net.centilehcf.core.essentials.command;
+
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "day")
+public class DayCommand {
+
+ public void execute(Player player) {
+ player.setPlayerTime(6000L, false);
+ player.sendMessage(CC.GREEN + "It's now day time.");
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/GameModeCommand.java b/src/main/java/net/centilehcf/core/essentials/command/GameModeCommand.java
new file mode 100644
index 0000000..26027d2
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/GameModeCommand.java
@@ -0,0 +1,42 @@
+package net.centilehcf.core.essentials.command;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.util.CC;
+import org.bukkit.GameMode;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = { "gamemode", "gm" }, permission = "rank.admin")
+public class GameModeCommand {
+
+ public void execute(Player player, GameMode gameMode) {
+ if (gameMode == null) {
+ player.sendMessage(CC.RED + "That game mode is not valid.");
+ return;
+ }
+
+ player.setGameMode(gameMode);
+ player.updateInventory();
+ player.sendMessage(CC.GOLD + "You updated your game mode.");
+ }
+
+ public void execute(CommandSender sender, Player target, GameMode gameMode) {
+ if (target == null) {
+ sender.sendMessage(Locale.PLAYER_NOT_FOUND.format());
+ return;
+ }
+
+ if (gameMode == null) {
+ sender.sendMessage(CC.RED + "That game mode is not valid.");
+ return;
+ }
+
+ target.setGameMode(gameMode);
+ target.updateInventory();
+ target.sendMessage(CC.GOLD + "Your game mode has been updated by " + sender.getName());
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/HealCommand.java b/src/main/java/net/centilehcf/core/essentials/command/HealCommand.java
new file mode 100644
index 0000000..c3b11c2
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/HealCommand.java
@@ -0,0 +1,35 @@
+package net.centilehcf.core.essentials.command;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "heal", permission = "rank.admin")
+public class HealCommand {
+
+ public void execute(Player player) {
+ player.setHealth(20.0);
+ player.setFoodLevel(20);
+ player.setSaturation(5.0F);
+ player.updateInventory();
+ player.sendMessage(CC.GOLD + "You healed yourself.");
+ }
+
+ public void execute(CommandSender sender, Player player) {
+ if (player == null) {
+ sender.sendMessage(Locale.PLAYER_NOT_FOUND.format());
+ return;
+ }
+
+ player.setHealth(20.0);
+ player.setFoodLevel(20);
+ player.setSaturation(5.0F);
+ player.updateInventory();
+ player.sendMessage(CC.GOLD + "You have been healed by " + sender.getName());
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/HidePlayerCommand.java b/src/main/java/net/centilehcf/core/essentials/command/HidePlayerCommand.java
new file mode 100644
index 0000000..9916e6d
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/HidePlayerCommand.java
@@ -0,0 +1,13 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "hideplayer", permission = "rank.manager")
+public class HidePlayerCommand {
+
+ public void execute(Player player, Player target) {
+ player.hidePlayer(target);
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/InvseeCommand.java b/src/main/java/net/centilehcf/core/essentials/command/InvseeCommand.java
new file mode 100644
index 0000000..c3de6a8
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/InvseeCommand.java
@@ -0,0 +1,14 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/5/2019.
+ */
+@CommandMeta(label = "invsee", permission = "rank.mod")
+public class InvseeCommand {
+ public void execute(Player player, Player target) {
+ player.openInventory(target.getInventory());
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/ListCommand.java b/src/main/java/net/centilehcf/core/essentials/command/ListCommand.java
new file mode 100644
index 0000000..c600670
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/ListCommand.java
@@ -0,0 +1,47 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.rank.comparator.RankComparator;
+import net.centilehcf.core.util.CC;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.craftbukkit.libs.joptsimple.internal.Strings;
+import org.bukkit.entity.Player;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@CommandMeta(label = {"list", "who", "online"}, async = true)
+public class ListCommand {
+
+ public void execute(CommandSender sender) {
+ List ranks = Rank.getRanks().values().stream().sorted(new RankComparator()).collect(Collectors.toList());
+ List rankStr = ranks.stream().filter(rank -> !rank.getPermissions().contains("hidden")).map(rank -> rank.getColor() + rank.getDisplayName()).collect(Collectors.toList());
+ sender.sendMessage(Strings.join(rankStr, CC.GRAY + ", "));
+ List onlinePlayers = Bukkit.getOnlinePlayers().stream().sorted(Comparator.comparingInt(player -> Profile.getByUuid(player.getUniqueId()).getActiveRank().getWeight())).collect(Collectors.toList());
+ Collections.reverse(onlinePlayers);
+ String onlinePlayersOutOfMaximumPlayers = CC.WHITE + "(" + Bukkit.getOnlinePlayers().size() + "/" + Bukkit.getMaxPlayers() + ") ";
+ if (sender.hasPermission("core.staff")) {
+ List list = new ArrayList<>();
+ for (Player player : onlinePlayers) {
+ String vanished = (player.hasMetadata("VANISHED") ? CC.GRAY + "*" : "") + Profile.getByUuid(player.getUniqueId()).getColoredUsername();
+ list.add(vanished);
+ }
+ sender.sendMessage(onlinePlayersOutOfMaximumPlayers + Strings.join(list, CC.GRAY + ", "));
+ } else {
+ List list = new ArrayList<>();
+ for (Player player : onlinePlayers) {
+ if (!player.hasMetadata("VANISHED")) {
+ String coloredUsername = Profile.getByUuid(player.getUniqueId()).getColoredUsername();
+ list.add(coloredUsername);
+ }
+ }
+ sender.sendMessage(onlinePlayersOutOfMaximumPlayers + Strings.join(list, CC.GRAY + ", "));
+ }
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/MasssayCommand.java b/src/main/java/net/centilehcf/core/essentials/command/MasssayCommand.java
new file mode 100644
index 0000000..134105f
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/MasssayCommand.java
@@ -0,0 +1,21 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 3/30/2019.
+ */
+@CommandMeta(label = "masssay", permission = "rank.manager")
+public class MasssayCommand {
+
+ public void execute(Player player, String message) {
+ message = CC.translate(message); // translate the colors
+ for (Player players : Bukkit.getOnlinePlayers()) {
+ players.chat(message);
+ }
+ player.sendMessage(CC.GREEN + "Just made all players type '" + message + CC.GREEN + "'");
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/MessageCommand.java b/src/main/java/net/centilehcf/core/essentials/command/MessageCommand.java
new file mode 100644
index 0000000..6792b0b
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/MessageCommand.java
@@ -0,0 +1,45 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.util.CC;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/5/2019.
+ */
+@CommandMeta(label = {"message", "msg", "m", "tell", "whisper"})
+public class MessageCommand {
+ public void execute(Player player, Player target, String message) {
+ if (target == null) {
+ player.sendMessage(CC.RED + "This player is not online.");
+ return;
+ }
+
+ Profile targetprofile = Profile.getByUuid(target.getUniqueId());
+ Profile profile = Profile.getByUuid(player.getUniqueId());
+
+ if (!targetprofile.getOptions().isPrivateChatEnabled()) {
+ player.sendMessage(CC.RED + "This player has messages disabled.");
+ return;
+ }
+
+ if (!profile.getOptions().isPrivateChatEnabled()) {
+ player.sendMessage(CC.RED + "You have messages disabled.");
+ return;
+ }
+
+ Profile targetData = Profile.getByUuid(target.getUniqueId());
+ String senderName = CC.RESET + player.getDisplayName();
+ String targetName = CC.RESET + target.getDisplayName();
+
+ profile.setReplyTo(target.getUniqueId());
+ targetData.setReplyTo(player.getUniqueId());
+
+ String toMessage = CC.GRAY + "(To " + targetName + CC.GRAY + ") " + message;
+ String fromMessage = CC.GRAY + "(From " + senderName + CC.GRAY + ") " + message;
+
+ player.sendMessage(toMessage);
+ target.sendMessage(fromMessage);
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/MoreCommand.java b/src/main/java/net/centilehcf/core/essentials/command/MoreCommand.java
new file mode 100644
index 0000000..f9318b7
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/MoreCommand.java
@@ -0,0 +1,21 @@
+package net.centilehcf.core.essentials.command;
+
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "more", permission = "rank.admin")
+public class MoreCommand {
+
+ public void execute(Player player) {
+ if (player.getItemInHand() == null) {
+ player.sendMessage(CC.RED + "There is nothing in your hand.");
+ return;
+ }
+
+ player.getItemInHand().setAmount(64);
+ player.updateInventory();
+ player.sendMessage(CC.GREEN + "You gave yourself more of the item in your hand.");
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/NightCommand.java b/src/main/java/net/centilehcf/core/essentials/command/NightCommand.java
new file mode 100644
index 0000000..ba30d40
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/NightCommand.java
@@ -0,0 +1,15 @@
+package net.centilehcf.core.essentials.command;
+
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "night")
+public class NightCommand {
+
+ public void execute(Player player) {
+ player.setPlayerTime(18000L, false);
+ player.sendMessage(CC.GREEN + "It's now night time.");
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/PingCommand.java b/src/main/java/net/centilehcf/core/essentials/command/PingCommand.java
new file mode 100644
index 0000000..55b8a77
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/PingCommand.java
@@ -0,0 +1,23 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.craftbukkit.v1_7_R4.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/6/2019.
+ */
+@CommandMeta(label = "ping")
+public class PingCommand {
+ public void execute(Player player, Player target) {
+
+ if (target == null) {
+ player.sendMessage(CC.RED + "Player isn't online!");
+ return;
+ }
+
+ int ping = ((CraftPlayer) player).getHandle().ping;
+ player.sendMessage(target.getDisplayName() + CC.YELLOW + "'s ping: " + (ping > 100 ? CC.RED : (ping > 50 ? CC.YELLOW : CC.GREEN)) + ping);
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/PrefixCommand.java b/src/main/java/net/centilehcf/core/essentials/command/PrefixCommand.java
new file mode 100644
index 0000000..a12b815
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/PrefixCommand.java
@@ -0,0 +1,14 @@
+package net.centilehcf.core.essentials.command;
+
+import net.centilehcf.core.prefix.menu.PrefixSelectionMenu;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.prefix.menu.PrefixSelectionMenu;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "prefix")
+public class PrefixCommand {
+
+ public void execute(Player player){
+ new PrefixSelectionMenu().openMenu(player);
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/RawCommand.java b/src/main/java/net/centilehcf/core/essentials/command/RawCommand.java
new file mode 100644
index 0000000..ff2db66
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/RawCommand.java
@@ -0,0 +1,18 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import com.qrakn.honcho.command.CommandOption;
+import net.centilehcf.core.util.CC;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 3/30/2019.
+ */
+@CommandMeta(label = "raw", options = "r", permission = "rank.manager", async = true)
+public class RawCommand {
+ public void execute(CommandSender sender, CommandOption option, String broadcast) {
+ String message = broadcast.replaceAll("(&([a-f0-9l-or]))", "\u00A7$2");
+ Bukkit.broadcastMessage(CC.translate((option == null ? "" : "") + message));
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/RenameCommand.java b/src/main/java/net/centilehcf/core/essentials/command/RenameCommand.java
new file mode 100644
index 0000000..12e5950
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/RenameCommand.java
@@ -0,0 +1,55 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 5/28/2019.
+ */
+@CommandMeta(label = "rename", permission = "command.rename")
+public class RenameCommand {
+
+ public static final List DENIED_NAMES = Arrays.asList("nigger", "n1gger");
+
+ public void execute(Player player, String newName) {
+ ItemStack stack = player.getItemInHand();
+ if (stack == null || stack.getType() == Material.AIR) {
+ player.sendMessage(CC.RED + "You have nothing in your hand.");
+ return;
+ }
+
+ ItemMeta meta = stack.getItemMeta();
+ String oldname = meta.getDisplayName();
+
+ if (oldname != null) {
+ oldname = oldname.trim();
+ }
+
+ if (oldname == null && newName == null) {
+ player.sendMessage(CC.RED + "Item already has no name.");
+ return;
+ }
+
+ if (newName != null) {
+ for (String word : DENIED_NAMES) {
+ if (newName.toLowerCase().contains(word)) {
+ player.sendMessage(CC.RED + "This name is not allowed.");
+ Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "warn -s " + player + " Disallowed Rename Name.");
+ return;
+ }
+ }
+ }
+
+ meta.setDisplayName(CC.translate(newName));
+ stack.setItemMeta(meta);
+ player.sendMessage(CC.translate("&aItem has been renamed from &c" + oldname + " &ato &c" + newName + "&a."));
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/ReplyCommand.java b/src/main/java/net/centilehcf/core/essentials/command/ReplyCommand.java
new file mode 100644
index 0000000..0bc3a65
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/ReplyCommand.java
@@ -0,0 +1,42 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.util.CC;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/5/2019.
+ */
+@CommandMeta(label = {"reply", "r"})
+public class ReplyCommand {
+ public void execute(Player player, String message) {
+ Profile profile = Profile.getByUuid(player.getUniqueId());
+
+ if (profile.getReplyTo() == null) {
+ player.sendMessage(CC.RED + "You have nobody to reply to.");
+ return;
+ }
+
+ final Player target = Bukkit.getPlayer(profile.getReplyTo());
+
+ if (target == null || !target.isOnline()) {
+ player.sendMessage(CC.RED + "That player is no longer online.");
+ return;
+ }
+
+ Profile targetData = Profile.getByUuid(target.getUniqueId());
+ String senderName = CC.RESET + player.getDisplayName();
+ String targetName = CC.RESET + target.getDisplayName();
+
+ profile.setReplyTo(target.getUniqueId());
+ targetData.setReplyTo(player.getUniqueId());
+
+ String toMessage = CC.GRAY + "(To " + targetName + CC.GRAY + ") " + message;
+ String fromMessage = CC.GRAY + "(From " + senderName + CC.GRAY + ") " + message;
+
+ player.sendMessage(toMessage);
+ target.sendMessage(fromMessage);
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/RequestCommand.java b/src/main/java/net/centilehcf/core/essentials/command/RequestCommand.java
new file mode 100644
index 0000000..4feaa46
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/RequestCommand.java
@@ -0,0 +1,33 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.network.packet.PacketRequestCommand;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.Cooldown;
+import org.bukkit.entity.Player;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/21/2019.
+ */
+@CommandMeta(label = {"request", "helpop"})
+public class RequestCommand {
+
+ private static final long requestcooldown = TimeUnit.SECONDS.toMillis(60);
+
+ public void execute(Player player, String request) {
+ Profile profile = Profile.getByUuid(player.getUniqueId());
+
+ if (!profile.getRequestCooldown().hasExpired()) {
+ player.sendMessage(CC.RED + "You must wait before you can request assistance again.");
+ return;
+ }
+
+ player.sendMessage(CC.GREEN + "We have received your request and will help soon. Please be patient.");
+ profile.setRequestCooldown(new Cooldown(requestcooldown));
+ Core.get().getPidgin().sendPacket(new PacketRequestCommand(Core.get().getMainConfig().getString("SERVER_NAME"), player.getDisplayName(), request));
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/SetSlotsCommand.java b/src/main/java/net/centilehcf/core/essentials/command/SetSlotsCommand.java
new file mode 100644
index 0000000..41ff897
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/SetSlotsCommand.java
@@ -0,0 +1,16 @@
+package net.centilehcf.core.essentials.command;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.util.BukkitReflection;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = "setslots", async = true, permission = "rank.owner")
+public class SetSlotsCommand {
+
+ public void execute(CommandSender sender, int slots) {
+ BukkitReflection.setMaxPlayers(Core.get().getServer(), slots);
+ sender.sendMessage(CC.GOLD + "You set the max slots to " + slots + ".");
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/SetSpawnCommand.java b/src/main/java/net/centilehcf/core/essentials/command/SetSpawnCommand.java
new file mode 100644
index 0000000..a22364d
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/SetSpawnCommand.java
@@ -0,0 +1,16 @@
+package net.centilehcf.core.essentials.command;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "setspawn", permission = "rank.manager")
+public class SetSpawnCommand {
+
+ public void execute(Player player) {
+ Core.get().getEssentials().setSpawn(player.getLocation());
+ player.sendMessage(CC.GREEN + "You updated this world's spawn.");
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/ShowPlayerCommand.java b/src/main/java/net/centilehcf/core/essentials/command/ShowPlayerCommand.java
new file mode 100644
index 0000000..6bd7ab7
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/ShowPlayerCommand.java
@@ -0,0 +1,12 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "showplayer", permission = "rank.manager")
+public class ShowPlayerCommand {
+
+ public void execute(Player player, Player target) {
+ player.showPlayer(target);
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/SpawnCommand.java b/src/main/java/net/centilehcf/core/essentials/command/SpawnCommand.java
new file mode 100644
index 0000000..1f75808
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/SpawnCommand.java
@@ -0,0 +1,15 @@
+package net.centilehcf.core.essentials.command;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "spawn", permission = "rank.trialmod")
+public class SpawnCommand {
+
+ public void execute(Player player) {
+ Core.get().getEssentials().teleportToSpawn(player);
+ player.sendMessage(CC.GREEN + "You teleported to this world's spawn.");
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/SpawnerCommand.java b/src/main/java/net/centilehcf/core/essentials/command/SpawnerCommand.java
new file mode 100644
index 0000000..3e5f8ac
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/SpawnerCommand.java
@@ -0,0 +1,21 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.ItemBuilder;
+import org.apache.commons.lang.WordUtils;
+import org.bukkit.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.Inventory;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/12/2019.
+ */
+@CommandMeta(label = "spawner", permission = "rank.manager")
+public class SpawnerCommand {
+ public void execute(Player player, Player target, String spawner) {
+ Inventory inventory = target.getInventory();
+ inventory.addItem(new ItemBuilder(Material.MOB_SPAWNER).name(CC.GREEN + "Spawner").lore(CC.WHITE + WordUtils.capitalizeFully(spawner)).build());
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/StreamingCommand.java b/src/main/java/net/centilehcf/core/essentials/command/StreamingCommand.java
new file mode 100644
index 0000000..6156df5
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/StreamingCommand.java
@@ -0,0 +1,16 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/8/2019.
+ */
+@CommandMeta(label = "streaming", permission = "rank.media")
+public class StreamingCommand {
+ public void execute(Player player, String url) {
+ Bukkit.broadcastMessage(player.getDisplayName() + CC.YELLOW + " is currently streaming! " + CC.GRAY + "(" + url + ")");
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/SunsetCommand.java b/src/main/java/net/centilehcf/core/essentials/command/SunsetCommand.java
new file mode 100644
index 0000000..8ecfcff
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/SunsetCommand.java
@@ -0,0 +1,16 @@
+package net.centilehcf.core.essentials.command;
+
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "sunset")
+public class SunsetCommand {
+
+ public void execute(Player player) {
+ player.setPlayerTime(12000, false);
+ player.sendMessage(CC.GREEN + "It's now sunset.");
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/TeleportAllCommand.java b/src/main/java/net/centilehcf/core/essentials/command/TeleportAllCommand.java
new file mode 100644
index 0000000..830681c
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/TeleportAllCommand.java
@@ -0,0 +1,21 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/4/2019.
+ */
+@CommandMeta(label = "tpall", permission = "rank.manager")
+public class TeleportAllCommand {
+ public void execute(Player player) {
+ for (Player other : Bukkit.getOnlinePlayers()) {
+ if (other == player)
+ continue;
+ other.teleport(player);
+ }
+ player.sendMessage(CC.GREEN + "All players have been teleported to you.");
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/TeleportCommand.java b/src/main/java/net/centilehcf/core/essentials/command/TeleportCommand.java
new file mode 100644
index 0000000..f5e7aeb
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/TeleportCommand.java
@@ -0,0 +1,21 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/4/2019.
+ */
+@CommandMeta(label = { "teleport", "tp" }, permission = "rank.trialmod")
+public class TeleportCommand {
+ public void execute(Player player, Player target) {
+ if (target == null) {
+ player.sendMessage(CC.RED + "This player is not online.");
+ return;
+ }
+
+ player.teleport(target);
+ player.sendMessage(CC.translate(CC.translate("&6Teleported to %TELEPORTER%&6.")).replace("%TELEPORTER%", target.getDisplayName()));
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/TeleportHereCommand.java b/src/main/java/net/centilehcf/core/essentials/command/TeleportHereCommand.java
new file mode 100644
index 0000000..b165aba
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/TeleportHereCommand.java
@@ -0,0 +1,22 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/4/2019.
+ */
+@CommandMeta(label = "tphere", permission = "rank.seniormod")
+public class TeleportHereCommand {
+
+ public void execute(Player player, Player target) {
+ if (target == null) {
+ player.sendMessage(CC.RED + "This player is not online.");
+ return;
+ }
+
+ target.teleport(player);
+ player.sendMessage(CC.translate(CC.translate("&6Teleported %PLAYER% &6to %TELEPORTER%&6.")).replace("%PLAYER%", target.getDisplayName()).replace("%TELEPORTER%", player.getDisplayName()));
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/TeleportPositionCommand.java b/src/main/java/net/centilehcf/core/essentials/command/TeleportPositionCommand.java
new file mode 100644
index 0000000..74e27a8
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/TeleportPositionCommand.java
@@ -0,0 +1,20 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/4/2019.
+ */
+@CommandMeta(label = "tppos", permission = "rank.trialmod")
+public class TeleportPositionCommand {
+
+ public void execute(Player player, Double x, Double y, Double z) {
+ Location location = new Location(player.getWorld(), x, y, z);
+
+ player.teleport(location);
+ player.sendMessage(CC.translate(CC.translate("&6Teleported to x:&f %X% &6y: &f%Y%&6 z:&f%Z%")).replace("%X%", "" + x).replace("%Y%", "" + y).replace("%Z%", "" + z));
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/command/WorldCommand.java b/src/main/java/net/centilehcf/core/essentials/command/WorldCommand.java
new file mode 100644
index 0000000..5f551dd
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/command/WorldCommand.java
@@ -0,0 +1,31 @@
+package net.centilehcf.core.essentials.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/8/2019.
+ */
+@CommandMeta(label = "world", permission = "rank.admin")
+public class WorldCommand {
+ public void execute(Player player, String newworld) {
+
+ World world = Bukkit.getWorld(newworld);
+ if (world == null) {
+ player.sendMessage(CC.RED + "World not found!");
+ }
+
+ if (player.getWorld().equals(world)) {
+ player.sendMessage(CC.RED + "You are already in this world!");
+ }
+
+ Location playerlocation = player.getLocation();
+ Location newlocation = new Location(world, playerlocation.getX(), playerlocation.getY(), playerlocation.getZ(), playerlocation.getYaw(), playerlocation.getPitch());
+ player.teleport(newlocation);
+ player.sendMessage(CC.GREEN + "You have been sent to world " + CC.PINK + world + CC.GREEN + " !");
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/event/SpawnTeleportEvent.java b/src/main/java/net/centilehcf/core/essentials/event/SpawnTeleportEvent.java
new file mode 100644
index 0000000..89c90b0
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/event/SpawnTeleportEvent.java
@@ -0,0 +1,21 @@
+package net.centilehcf.core.essentials.event;
+
+import net.centilehcf.core.util.BaseEvent;
+import lombok.Getter;
+import lombok.Setter;
+import net.centilehcf.core.util.BaseEvent;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+
+public class SpawnTeleportEvent extends BaseEvent implements Cancellable {
+
+ @Getter private final Player player;
+ @Getter @Setter private Location location;
+ @Getter @Setter private boolean cancelled;
+
+ public SpawnTeleportEvent(Player player, Location location) {
+ this.player = player;
+ this.location = location;
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/essentials/listener/DurabilityListener.java b/src/main/java/net/centilehcf/core/essentials/listener/DurabilityListener.java
new file mode 100644
index 0000000..751a253
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/listener/DurabilityListener.java
@@ -0,0 +1,33 @@
+package net.centilehcf.core.essentials.listener;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.bootstrap.BootstrappedListener;
+import org.bukkit.Material;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerItemDamageEvent;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/12/2019.
+ */
+public class DurabilityListener extends BootstrappedListener {
+
+ private List ALLOWED = Arrays.asList(Material.GOLD_HELMET, Material.GOLD_CHESTPLATE, Material.GOLD_LEGGINGS, Material.GOLD_BOOTS, Material.IRON_HELMET, Material.IRON_CHESTPLATE, Material.IRON_LEGGINGS, Material.IRON_BOOTS, Material.CHAINMAIL_HELMET, Material.CHAINMAIL_CHESTPLATE, Material.CHAINMAIL_LEGGINGS, Material.CHAINMAIL_BOOTS, Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS, Material.DIAMOND_HELMET, Material.DIAMOND_CHESTPLATE, Material.DIAMOND_LEGGINGS, Material.DIAMOND_BOOTS);
+
+ public DurabilityListener(Core core) {
+ super(core);
+ }
+
+ @EventHandler
+ public void onItemDamage(PlayerItemDamageEvent event) {
+ ItemStack stack = event.getItem();
+ if (stack != null && ALLOWED.contains(stack.getType()) && ThreadLocalRandom.current().nextInt(3) != 0) {
+ event.setCancelled(true);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/centilehcf/core/essentials/listener/EssentialsListener.java b/src/main/java/net/centilehcf/core/essentials/listener/EssentialsListener.java
new file mode 100644
index 0000000..74e9fa6
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/essentials/listener/EssentialsListener.java
@@ -0,0 +1,54 @@
+package net.centilehcf.core.essentials.listener;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.bootstrap.BootstrappedListener;
+import net.centilehcf.core.util.CC;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.block.BlockFadeEvent;
+import org.bukkit.event.entity.FoodLevelChangeEvent;
+import org.bukkit.event.player.PlayerCommandPreprocessEvent;
+
+public class EssentialsListener extends BootstrappedListener {
+
+ public EssentialsListener(Core core) {
+ super(core);
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST)
+ public void onCommandProcess(PlayerCommandPreprocessEvent event) {
+ Player player = event.getPlayer();
+ final String lowercase = event.getMessage().toLowerCase();
+ if (lowercase.startsWith("//calc") ||
+ lowercase.startsWith("//eval") ||
+ lowercase.startsWith("//solve") ||
+ lowercase.startsWith("/me") ||
+ lowercase.startsWith("/bukkit:me") ||
+ lowercase.startsWith("/minecraft:") ||
+ lowercase.startsWith("/minecraft:me")) {
+ player.sendMessage(CC.WHITE + "Unknown command.");
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
+ public void foodFix(FoodLevelChangeEvent event) {
+ if ((event.getEntity() instanceof Player)) {
+ Player player = (Player) event.getEntity();
+ player.setSaturation(10.0F);
+ }
+ }
+
+ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
+ public void onBlockFade(BlockFadeEvent event) {
+ if (event.getBlock().getType() == Material.ICE || event.getBlock().getType() == Material.PACKED_ICE || event.getBlock().getType() == Material.SNOW_BLOCK) {
+ event.setCancelled(true);
+ }
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/hook/VaultProvider.java b/src/main/java/net/centilehcf/core/hook/VaultProvider.java
new file mode 100644
index 0000000..f889270
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/hook/VaultProvider.java
@@ -0,0 +1,236 @@
+package net.centilehcf.core.hook;
+
+
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.rank.Rank;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.rank.Rank;
+import net.milkbowl.vault.permission.Permission;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
+public class VaultProvider extends Permission {
+
+ @Override
+ public String getName() {
+ return "E";
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean hasGroupSupport() {
+ return true;
+ }
+
+ @Override
+ public boolean hasSuperPermsCompat() {
+ return true;
+ }
+
+ @Override
+ public boolean playerAddGroup(Player player, String group) {
+ Rank rank = Rank.getRankByDisplayName(group);
+
+ if (rank != null) {
+ Profile profile = Profile.getByUuid(player.getUniqueId());
+
+ if (profile != null) {
+ profile.getGrants().add(new Grant(UUID.randomUUID(), rank, null,
+ System.currentTimeMillis(), "VaultAPI", Integer.MAX_VALUE));
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean playerAddGroup(String world, String playerName, String group) {
+ Rank rank = Rank.getRankByDisplayName(group);
+
+ if (rank != null) {
+ Player player = Bukkit.getPlayer(playerName);
+
+ if (player != null) {
+ Profile profile = Profile.getByUuid(player.getUniqueId());
+
+ if (profile != null) {
+ profile.getGrants().add(new Grant(UUID.randomUUID(), rank, null,
+ System.currentTimeMillis(), "VaultAPI", Integer.MAX_VALUE));
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public String[] getPlayerGroups(String world, String playerName) {
+ List rankNames = new ArrayList<>();
+
+ Player player = Bukkit.getPlayer(playerName);
+
+ if (player != null) {
+ Profile profile = Profile.getByUuid(player.getUniqueId());
+
+ if (profile != null) {
+ for (Grant grant : profile.getGrants()) {
+ if (!grant.isRemoved() && !grant.hasExpired()) {
+ if (!rankNames.contains(grant.getRank().getDisplayName())) {
+ rankNames.add(grant.getRank().getDisplayName());
+ }
+ }
+ }
+ }
+ }
+
+ return new String[0];
+ }
+
+ @Override
+ public boolean playerHas(String world, String playerName, String permission) {
+ Player player = Bukkit.getPlayer(playerName);
+
+ if (player != null) {
+ Profile profile = Profile.getByUuid(player.getUniqueId());
+
+ if (profile != null) {
+ for (Grant grant : profile.getGrants()) {
+ if (!grant.isRemoved() && !grant.hasExpired()) {
+ if (grant.getRank().getAllPermissions().contains(permission)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean playerAdd(String world, String playerName, String permission) {
+ return false;
+ }
+
+ @Override
+ public boolean playerRemove(String world, String playerName, String permission) {
+ return false;
+ }
+
+ @Override
+ public boolean groupHas(String world, String group, String permission) {
+ Rank rank = Rank.getRankByDisplayName(group);
+
+ if (rank != null) {
+ return rank.getAllPermissions().contains(permission);
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean groupAdd(String world, String group, String permission) {
+ Rank rank = Rank.getRankByDisplayName(group);
+
+ if (rank != null) {
+ if (!rank.hasPermission(permission)) {
+ rank.addPermission(permission);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean groupRemove(String world, String group, String permission) {
+ Rank rank = Rank.getRankByDisplayName(group);
+
+ if (rank != null) {
+ if (rank.hasPermission(permission)) {
+ rank.deletePermission(permission);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean playerInGroup(String world, String playerName, String group) {
+ Player player = Bukkit.getPlayer(playerName);
+
+ if (player != null) {
+ Profile profile = Profile.getByUuid(player.getUniqueId());
+
+ if (profile != null) {
+ for (Grant grant : profile.getGrants()) {
+ if (grant.getRank().getDisplayName().equalsIgnoreCase(group)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean playerRemoveGroup(String world, String playerName, String group) {
+ Player player = Bukkit.getPlayer(playerName);
+
+ if (player != null) {
+ Profile profile = Profile.getByUuid(player.getUniqueId());
+
+ if (profile != null) {
+ for (Grant grant : profile.getGrants()) {
+ if (grant.getRank().getDisplayName().equalsIgnoreCase(group)) {
+ grant.setRemoved(true);
+ grant.setRemovedBy(null);
+ grant.setRemovedAt(System.currentTimeMillis());
+ grant.setRemovedReason("VaultAPI");
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public String getPrimaryGroup(String world, String playerName) {
+ Player player = Bukkit.getPlayer(playerName);
+
+ if (player != null) {
+ Profile profile = Profile.getByUuid(player.getUniqueId());
+
+ if (profile != null) {
+ return profile.getActiveGrant().getRank().getDisplayName();
+ }
+ }
+
+ return "";
+ }
+
+ @Override
+ public String[] getGroups() {
+ return new ArrayList<>(Rank.getRanks().values()).stream()
+ .map(Rank::getDisplayName)
+ .collect(Collectors.toList())
+ .toArray(new String[Rank.getRanks().values().size()]);
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/network/NetworkPacketListener.java b/src/main/java/net/centilehcf/core/network/NetworkPacketListener.java
new file mode 100644
index 0000000..afb0dd3
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/NetworkPacketListener.java
@@ -0,0 +1,184 @@
+package net.centilehcf.core.network;
+
+import com.minexd.pidgin.packet.handler.IncomingPacketHandler;
+import com.minexd.pidgin.packet.listener.PacketListener;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.network.event.ReceiveRequestCommandEvent;
+import net.centilehcf.core.network.event.ReceiveStaffChatEvent;
+import net.centilehcf.core.network.packet.*;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.profile.grant.event.GrantAppliedEvent;
+import net.centilehcf.core.profile.grant.event.GrantExpireEvent;
+import net.centilehcf.core.profile.punishment.Punishment;
+import net.centilehcf.core.profile.punishment.PunishmentType;
+import net.centilehcf.core.rank.Rank;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.util.Objects;
+
+public class NetworkPacketListener implements PacketListener {
+
+ private Core core;
+
+ public NetworkPacketListener(Core core) {
+ this.core = core;
+ }
+
+ @IncomingPacketHandler
+ public void onAddGrant(PacketAddGrant packet) {
+ Player player = Bukkit.getPlayer(packet.getPlayerUuid());
+ Grant grant = packet.getGrant();
+
+ if (player != null) {
+ Profile profile = Profile.getProfiles().get(player.getUniqueId());
+ profile.getGrants().removeIf(other -> Objects.equals(other, grant));
+ profile.getGrants().add(grant);
+
+ new GrantAppliedEvent(player, grant);
+ }
+ }
+
+ @IncomingPacketHandler
+ public void onDeleteGrant(PacketDeleteGrant packet) {
+ Player player = Bukkit.getPlayer(packet.getPlayerUuid());
+ Grant grant = packet.getGrant();
+
+ if (player != null) {
+ Profile profile = Profile.getProfiles().get(player.getUniqueId());
+ profile.getGrants().removeIf(other -> Objects.equals(other, grant));
+ profile.getGrants().add(grant);
+ profile.checkGrants();
+ new GrantExpireEvent(player, grant);
+ }
+ }
+
+ @IncomingPacketHandler
+ public void onBroadcastPunishment(PacketBroadcastPunishment packet) {
+ Punishment punishment = packet.getPunishment();
+ punishment.broadcast(packet.getStaff(), packet.getTarget(), packet.isSilent());
+
+ Player player = Bukkit.getPlayer(packet.getTargetUuid());
+
+ if (player != null) {
+ Profile profile = Profile.getProfiles().get(player.getUniqueId());
+ profile.getPunishments().removeIf(other -> Objects.equals(other, punishment));
+ profile.getPunishments().add(punishment);
+
+ if (punishment.getType().isBan()) {
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ player.kickPlayer(punishment.getKickMessage());
+ }
+ }.runTask(Core.get());
+ }
+
+ if (punishment.getType() == PunishmentType.BLACKLIST) {
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ player.kickPlayer(punishment.getKickMessage());
+ }
+ }.runTask(Core.get());
+ }
+ }
+ }
+
+ @IncomingPacketHandler
+ public void onRankRefresh(PacketRefreshRank packet) {
+ Rank rank = Rank.getRankByUuid(packet.getUuid());
+
+ if (rank == null) {
+ rank = new Rank(packet.getUuid(), packet.getName());
+ }
+
+ rank.load();
+
+ Core.broadcastOps(Locale.NETWORK_RANK_REFRESHED.format(Locale.NETWORK_BROADCAST_PREFIX.format(),
+ rank.getDisplayName()));
+ }
+
+ @IncomingPacketHandler
+ public void onRankDelete(PacketDeleteRank packet) {
+ Rank rank = Rank.getRanks().remove(packet.getUuid());
+
+ if (rank != null) {
+ Core.broadcastOps(Locale.NETWORK_RANK_DELETED.format(Locale.NETWORK_BROADCAST_PREFIX.format(),
+ rank.getDisplayName()));
+ }
+ }
+
+ @IncomingPacketHandler
+ public void onStaffChat(PacketStaffChat packet) {
+ core.getServer().getOnlinePlayers().stream()
+ .filter(onlinePlayer -> onlinePlayer.hasPermission("core.staff"))
+ .forEach(onlinePlayer -> {
+ ReceiveStaffChatEvent event = new ReceiveStaffChatEvent(onlinePlayer);
+
+ core.getServer().getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ if (Profile.getProfiles().get(event.getPlayer().getUniqueId()).getStaffOptions().isStaffChatEnabled()) {
+ onlinePlayer.sendMessage(Locale.STAFF_CHAT.format(Locale.STAFF_BROADCAST_PREFIX.format(),
+ packet.getPlayerName(), packet.getServerName(), packet.getChatMessage()
+ ));
+ }
+ }
+ });
+ }
+
+ @IncomingPacketHandler
+ public void onRequestCommand(PacketRequestCommand packet) {
+ core.getServer().getOnlinePlayers().stream()
+ .filter(onlinePlayer -> onlinePlayer.hasPermission("core.staff"))
+ .forEach(onlinePlayer -> {
+ ReceiveRequestCommandEvent event = new ReceiveRequestCommandEvent(onlinePlayer);
+ core.getServer().getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ onlinePlayer.sendMessage(Locale.STAFF_REQUEST.format(Locale.STAFF_REQUEST_PREFIX.format(),
+ packet.getServerName(), packet.getPlayerName(), packet.getRequest()
+ ));
+ }
+ });
+ }
+
+ @IncomingPacketHandler
+ public void getServerStatus(PacketServerRestart packet) {
+ core.getServer().getOnlinePlayers().stream()
+ .filter(onlinePlayer -> onlinePlayer.hasPermission("core.staff"))
+ .forEach(onlinePlayer -> onlinePlayer.sendMessage(Locale.SERVER_STATUS.format(Locale.SERVER_STATUS_PREFIX.format(),
+ packet.getServerName(), packet.getStatus())));
+ }
+
+ @IncomingPacketHandler
+ public void onStaffJoinNetwork(PacketStaffJoinNetwork packet) {
+ core.getServer().broadcast(Locale.STAFF_JOIN_NETWORK.format(Locale.STAFF_BROADCAST_PREFIX.format(),
+ packet.getPlayerName(), packet.getServerName()), "core.staff");
+ }
+
+ @IncomingPacketHandler
+ public void onStaffLeaveNetwork(PacketStaffLeaveNetwork packet) {
+ core.getServer().broadcast(Locale.STAFF_LEAVE_NETWORK.format(Locale.STAFF_BROADCAST_PREFIX.format(),
+ packet.getPlayerName(), packet.getServerName()), "core.staff");
+ }
+
+ @IncomingPacketHandler
+ public void onStaffSwitchServer(PacketStaffSwitchServer packet) {
+ core.getServer().broadcast(Locale.STAFF_SWITCH_SERVER.format(Locale.STAFF_BROADCAST_PREFIX.format(),
+ packet.getPlayerName(), packet.getToServerName(), packet.getFromServerName()), "core.staff");
+ }
+
+ @IncomingPacketHandler
+ public void onPacketRemovePunishments(PacketRemovePunishments packet) {
+ Player player = Bukkit.getPlayer(packet.getUuid()); // We don't care if they aren't online
+ if (player != null) {
+ Profile profile = Profile.getByUuid(packet.getUuid());
+ profile.getPunishments().clear();
+ }
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/network/event/ReceiveRequestCommandEvent.java b/src/main/java/net/centilehcf/core/network/event/ReceiveRequestCommandEvent.java
new file mode 100644
index 0000000..be7c072
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/event/ReceiveRequestCommandEvent.java
@@ -0,0 +1,22 @@
+package net.centilehcf.core.network.event;
+
+import lombok.Getter;
+import lombok.Setter;
+import net.centilehcf.core.util.BaseEvent;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/21/2019.
+ */
+public class ReceiveRequestCommandEvent extends BaseEvent implements Cancellable {
+
+ @Getter
+ private Player player;
+ @Getter @Setter
+ private boolean cancelled;
+
+ public ReceiveRequestCommandEvent(Player player) {
+ this.player = player;
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/network/event/ReceiveStaffChatEvent.java b/src/main/java/net/centilehcf/core/network/event/ReceiveStaffChatEvent.java
new file mode 100644
index 0000000..7b5544b
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/event/ReceiveStaffChatEvent.java
@@ -0,0 +1,17 @@
+package net.centilehcf.core.network.event;
+
+import net.centilehcf.core.util.BaseEvent;
+import lombok.Getter;
+import lombok.Setter;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+
+public class ReceiveStaffChatEvent extends BaseEvent implements Cancellable {
+
+ @Getter private Player player;
+ @Getter @Setter private boolean cancelled;
+
+ public ReceiveStaffChatEvent(Player player) {
+ this.player = player;
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/network/packet/PacketAddGrant.java b/src/main/java/net/centilehcf/core/network/packet/PacketAddGrant.java
new file mode 100644
index 0000000..2b9cbb2
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/packet/PacketAddGrant.java
@@ -0,0 +1,45 @@
+package net.centilehcf.core.network.packet;
+
+import com.google.gson.JsonObject;
+import com.minexd.pidgin.packet.Packet;
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.util.json.JsonChain;
+import java.util.UUID;
+import lombok.Getter;
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.util.json.JsonChain;
+
+public class PacketAddGrant implements Packet {
+
+ @Getter private UUID playerUuid;
+ @Getter private Grant grant;
+
+ public PacketAddGrant() {
+
+ }
+
+ public PacketAddGrant(UUID playerUuid, Grant grant) {
+ this.playerUuid = playerUuid;
+ this.grant = grant;
+ }
+
+ @Override
+ public int id() {
+ return 1;
+ }
+
+ @Override
+ public JsonObject serialize() {
+ return new JsonChain()
+ .addProperty("playerUuid", playerUuid.toString())
+ .add("grant", Grant.SERIALIZER.serialize(grant))
+ .get();
+ }
+
+ @Override
+ public void deserialize(JsonObject jsonObject) {
+ playerUuid = UUID.fromString(jsonObject.get("playerUuid").getAsString());
+ grant = Grant.DESERIALIZER.deserialize(jsonObject.get("grant").getAsJsonObject());
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/network/packet/PacketBroadcastPunishment.java b/src/main/java/net/centilehcf/core/network/packet/PacketBroadcastPunishment.java
new file mode 100644
index 0000000..6c6ada3
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/packet/PacketBroadcastPunishment.java
@@ -0,0 +1,55 @@
+package net.centilehcf.core.network.packet;
+
+import com.google.gson.JsonObject;
+import com.minexd.pidgin.packet.Packet;
+import net.centilehcf.core.profile.punishment.Punishment;
+import net.centilehcf.core.util.json.JsonChain;
+import java.util.UUID;
+import lombok.Getter;
+
+public class PacketBroadcastPunishment implements Packet {
+
+ @Getter private Punishment punishment;
+ @Getter private String staff;
+ @Getter private String target;
+ @Getter private UUID targetUuid;
+ @Getter private boolean silent;
+
+ public PacketBroadcastPunishment() {
+
+ }
+
+ public PacketBroadcastPunishment(Punishment punishment, String staff, String target, UUID targetUuid, boolean silent) {
+ this.punishment = punishment;
+ this.staff = staff;
+ this.target = target;
+ this.targetUuid = targetUuid;
+ this.silent = silent;
+ }
+
+ @Override
+ public int id() {
+ return 2;
+ }
+
+ @Override
+ public JsonObject serialize() {
+ return new JsonChain()
+ .add("punishment", Punishment.SERIALIZER.serialize(punishment))
+ .addProperty("staff", staff)
+ .addProperty("target", target)
+ .addProperty("targetUuid", targetUuid.toString())
+ .addProperty("silent", silent)
+ .get();
+ }
+
+ @Override
+ public void deserialize(JsonObject object) {
+ punishment = Punishment.DESERIALIZER.deserialize(object.get("punishment").getAsJsonObject());
+ staff = object.get("staff").getAsString();
+ target = object.get("target").getAsString();
+ targetUuid = UUID.fromString(object.get("targetUuid").getAsString());
+ silent = object.get("silent").getAsBoolean();
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/network/packet/PacketClearGrants.java b/src/main/java/net/centilehcf/core/network/packet/PacketClearGrants.java
new file mode 100644
index 0000000..50eb908
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/packet/PacketClearGrants.java
@@ -0,0 +1,39 @@
+package net.centilehcf.core.network.packet;
+
+import com.google.gson.JsonObject;
+import com.minexd.pidgin.packet.Packet;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import net.centilehcf.core.util.json.JsonChain;
+
+import java.util.UUID;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 5/14/2019.
+ */
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+public class PacketClearGrants implements Packet {
+
+ private UUID uuid;
+
+ @Override
+ public int id() {
+ return 13;
+ }
+
+ @Override
+ public JsonObject serialize() {
+ return new JsonChain()
+ .addProperty("uuid", uuid.toString())
+ .get();
+ }
+
+ @Override
+ public void deserialize(JsonObject object) {
+ uuid = UUID.fromString(object.get("uuid").getAsString());
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/network/packet/PacketDeleteGrant.java b/src/main/java/net/centilehcf/core/network/packet/PacketDeleteGrant.java
new file mode 100644
index 0000000..6491389
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/packet/PacketDeleteGrant.java
@@ -0,0 +1,45 @@
+package net.centilehcf.core.network.packet;
+
+import com.google.gson.JsonObject;
+import com.minexd.pidgin.packet.Packet;
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.util.json.JsonChain;
+import java.util.UUID;
+import lombok.Getter;
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.util.json.JsonChain;
+
+public class PacketDeleteGrant implements Packet {
+
+ @Getter private UUID playerUuid;
+ @Getter private Grant grant;
+
+ public PacketDeleteGrant() {
+
+ }
+
+ public PacketDeleteGrant(UUID playerUuid, Grant grant) {
+ this.playerUuid = playerUuid;
+ this.grant = grant;
+ }
+
+ @Override
+ public int id() {
+ return 3;
+ }
+
+ @Override
+ public JsonObject serialize() {
+ return new JsonChain()
+ .addProperty("playerUuid", playerUuid.toString())
+ .add("grant", Grant.SERIALIZER.serialize(grant))
+ .get();
+ }
+
+ @Override
+ public void deserialize(JsonObject jsonObject) {
+ playerUuid = UUID.fromString(jsonObject.get("playerUuid").getAsString());
+ grant = Grant.DESERIALIZER.deserialize(jsonObject.get("grant").getAsJsonObject());
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/network/packet/PacketDeletePrefix.java b/src/main/java/net/centilehcf/core/network/packet/PacketDeletePrefix.java
new file mode 100644
index 0000000..b7ae43d
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/packet/PacketDeletePrefix.java
@@ -0,0 +1,38 @@
+package net.centilehcf.core.network.packet;
+
+
+import com.google.gson.JsonObject;
+import com.minexd.pidgin.packet.Packet;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.util.json.JsonChain;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor
+public class PacketDeletePrefix implements Packet {
+
+ private String prefix;
+
+ public PacketDeletePrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ @Override
+ public int id() {
+ return 11;
+ }
+
+ @Override
+ public JsonObject serialize() {
+ return new JsonChain().addProperty("name", prefix).get();
+ }
+
+ @Override
+ public void deserialize(JsonObject object) {
+ Profile.getProfiles().forEach((uuid, profile) -> {
+ if (profile.getPrefix().getName().equalsIgnoreCase(object.get("name").getAsString())) {
+ profile.setPrefix(Core.get().getPrefixHandler().getDefaultPrefix());
+ }
+ });
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/network/packet/PacketDeleteRank.java b/src/main/java/net/centilehcf/core/network/packet/PacketDeleteRank.java
new file mode 100644
index 0000000..e69e92c
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/packet/PacketDeleteRank.java
@@ -0,0 +1,38 @@
+package net.centilehcf.core.network.packet;
+
+import com.google.gson.JsonObject;
+import com.minexd.pidgin.packet.Packet;
+import net.centilehcf.core.util.json.JsonChain;
+import java.util.UUID;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import net.centilehcf.core.util.json.JsonChain;
+
+@AllArgsConstructor
+@Getter
+public class PacketDeleteRank implements Packet {
+
+ private UUID uuid;
+
+ public PacketDeleteRank() {
+
+ }
+
+ @Override
+ public int id() {
+ return 4;
+ }
+
+ @Override
+ public JsonObject serialize() {
+ return new JsonChain()
+ .addProperty("uuid", uuid.toString())
+ .get();
+ }
+
+ @Override
+ public void deserialize(JsonObject jsonObject) {
+ uuid = UUID.fromString(jsonObject.get("uuid").getAsString());
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/centilehcf/core/network/packet/PacketRefreshRank.java b/src/main/java/net/centilehcf/core/network/packet/PacketRefreshRank.java
new file mode 100644
index 0000000..72ee8aa
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/packet/PacketRefreshRank.java
@@ -0,0 +1,41 @@
+package net.centilehcf.core.network.packet;
+
+import com.google.gson.JsonObject;
+import com.minexd.pidgin.packet.Packet;
+import net.centilehcf.core.util.json.JsonChain;
+import java.util.UUID;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import net.centilehcf.core.util.json.JsonChain;
+
+@AllArgsConstructor
+@Getter
+public class PacketRefreshRank implements Packet {
+
+ private UUID uuid;
+ private String name;
+
+ public PacketRefreshRank() {
+
+ }
+
+ @Override
+ public int id() {
+ return 5;
+ }
+
+ @Override
+ public JsonObject serialize() {
+ return new JsonChain()
+ .addProperty("uuid", uuid.toString())
+ .addProperty("name", name)
+ .get();
+ }
+
+ @Override
+ public void deserialize(JsonObject jsonObject) {
+ uuid = UUID.fromString(jsonObject.get("uuid").getAsString());
+ name = jsonObject.get("name").getAsString();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/centilehcf/core/network/packet/PacketRemovePunishments.java b/src/main/java/net/centilehcf/core/network/packet/PacketRemovePunishments.java
new file mode 100644
index 0000000..605515e
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/packet/PacketRemovePunishments.java
@@ -0,0 +1,34 @@
+package net.centilehcf.core.network.packet;
+
+import com.google.gson.JsonObject;
+import com.minexd.pidgin.packet.Packet;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import net.centilehcf.core.util.json.JsonChain;
+
+import java.util.UUID;
+
+@Getter
+@AllArgsConstructor
+public class PacketRemovePunishments implements Packet {
+
+ private UUID uuid;
+
+ public PacketRemovePunishments() {
+ }
+
+ @Override
+ public int id() {
+ return 15;
+ }
+
+ @Override
+ public JsonObject serialize() {
+ return new JsonChain().addProperty("uuid", uuid.toString()).get();
+ }
+
+ @Override
+ public void deserialize(JsonObject object) {
+ this.uuid = UUID.fromString(object.get("uuid").getAsString());
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/network/packet/PacketRequestCommand.java b/src/main/java/net/centilehcf/core/network/packet/PacketRequestCommand.java
new file mode 100644
index 0000000..25dd988
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/packet/PacketRequestCommand.java
@@ -0,0 +1,50 @@
+package net.centilehcf.core.network.packet;
+
+import com.google.gson.JsonObject;
+import com.minexd.pidgin.packet.Packet;
+import lombok.Getter;
+import lombok.Setter;
+import net.centilehcf.core.util.json.JsonChain;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/21/2019.
+ */
+@Getter
+@Setter
+public class PacketRequestCommand implements Packet {
+
+ private String playerName;
+ private String serverName;
+ private String request;
+
+ public PacketRequestCommand() {
+
+ }
+
+ public PacketRequestCommand(String serverName, String playerName, String request) {
+ this.serverName = serverName;
+ this.playerName = playerName;
+ this.request = request;
+ }
+
+ public int id() {
+ return 12;
+ }
+
+ @Override
+ public JsonObject serialize() {
+ return new JsonChain()
+ .addProperty("serverName", serverName)
+ .addProperty("playerName", playerName)
+ .addProperty("request", request)
+ .get();
+ }
+
+ @Override
+ public void deserialize(JsonObject jsonObject) {
+ serverName = jsonObject.get("serverName").getAsString();
+ playerName = jsonObject.get("playerName").getAsString();
+ request = jsonObject.get("request").getAsString();
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/network/packet/PacketServerRestart.java b/src/main/java/net/centilehcf/core/network/packet/PacketServerRestart.java
new file mode 100644
index 0000000..204d116
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/packet/PacketServerRestart.java
@@ -0,0 +1,44 @@
+package net.centilehcf.core.network.packet;
+
+import com.google.gson.JsonObject;
+import com.minexd.pidgin.packet.Packet;
+import lombok.Getter;
+import lombok.Setter;
+import net.centilehcf.core.util.json.JsonChain;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 5/21/2019.
+ */
+@Getter
+@Setter
+public class PacketServerRestart implements Packet {
+
+ private String serverName;
+ private String status;
+
+ public PacketServerRestart() {
+ }
+
+ public PacketServerRestart(String serverName, String status) {
+ this.serverName = serverName;
+ this.status = status;
+ }
+
+ public int id() {
+ return 14;
+ }
+
+ @Override
+ public JsonObject serialize() {
+ return new JsonChain()
+ .addProperty("serverName", serverName)
+ .addProperty("status", status)
+ .get();
+ }
+
+ @Override
+ public void deserialize(JsonObject jsonObject) {
+ serverName = jsonObject.get("serverName").getAsString();
+ status = jsonObject.get("status").getAsString();
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/network/packet/PacketStaffChat.java b/src/main/java/net/centilehcf/core/network/packet/PacketStaffChat.java
new file mode 100644
index 0000000..599a1fc
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/packet/PacketStaffChat.java
@@ -0,0 +1,48 @@
+package net.centilehcf.core.network.packet;
+
+import com.google.gson.JsonObject;
+import com.minexd.pidgin.packet.Packet;
+import net.centilehcf.core.util.json.JsonChain;
+import lombok.Getter;
+import lombok.Setter;
+import net.centilehcf.core.util.json.JsonChain;
+
+@Getter
+@Setter
+public class PacketStaffChat implements Packet {
+
+ private String playerName;
+ private String serverName;
+ private String chatMessage;
+
+ public PacketStaffChat() {
+
+ }
+
+ public PacketStaffChat(String playerName, String serverName, String chatMessage) {
+ this.playerName = playerName;
+ this.serverName = serverName;
+ this.chatMessage = chatMessage;
+ }
+
+ public int id() {
+ return 6;
+ }
+
+ @Override
+ public JsonObject serialize() {
+ return new JsonChain()
+ .addProperty("playerName", playerName)
+ .addProperty("serverName", serverName)
+ .addProperty("chatMessage", chatMessage)
+ .get();
+ }
+
+ @Override
+ public void deserialize(JsonObject jsonObject) {
+ playerName = jsonObject.get("playerName").getAsString();
+ serverName = jsonObject.get("serverName").getAsString();
+ chatMessage = jsonObject.get("chatMessage").getAsString();
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/network/packet/PacketStaffJoinNetwork.java b/src/main/java/net/centilehcf/core/network/packet/PacketStaffJoinNetwork.java
new file mode 100644
index 0000000..ff65ef9
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/packet/PacketStaffJoinNetwork.java
@@ -0,0 +1,43 @@
+package net.centilehcf.core.network.packet;
+
+import com.google.gson.JsonObject;
+import com.minexd.pidgin.packet.Packet;
+import net.centilehcf.core.util.json.JsonChain;
+import lombok.Getter;
+import net.centilehcf.core.util.json.JsonChain;
+
+@Getter
+public class PacketStaffJoinNetwork implements Packet {
+
+ private String playerName;
+ private String serverName;
+
+ public PacketStaffJoinNetwork() {
+
+ }
+
+ public PacketStaffJoinNetwork(String playerName, String serverName) {
+ this.playerName = playerName;
+ this.serverName = serverName;
+ }
+
+ @Override
+ public int id() {
+ return 7;
+ }
+
+ @Override
+ public JsonObject serialize() {
+ return new JsonChain()
+ .addProperty("playerName", playerName)
+ .addProperty("serverName", serverName)
+ .get();
+ }
+
+ @Override
+ public void deserialize(JsonObject jsonObject) {
+ playerName = jsonObject.get("playerName").getAsString();
+ serverName = jsonObject.get("serverName").getAsString();
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/network/packet/PacketStaffLeaveNetwork.java b/src/main/java/net/centilehcf/core/network/packet/PacketStaffLeaveNetwork.java
new file mode 100644
index 0000000..40790f8
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/packet/PacketStaffLeaveNetwork.java
@@ -0,0 +1,43 @@
+package net.centilehcf.core.network.packet;
+
+import com.google.gson.JsonObject;
+import com.minexd.pidgin.packet.Packet;
+import net.centilehcf.core.util.json.JsonChain;
+import lombok.Getter;
+import net.centilehcf.core.util.json.JsonChain;
+
+@Getter
+public class PacketStaffLeaveNetwork implements Packet {
+
+ private String playerName;
+ private String serverName;
+
+ public PacketStaffLeaveNetwork() {
+
+ }
+
+ public PacketStaffLeaveNetwork(String playerName, String serverName) {
+ this.playerName = playerName;
+ this.serverName = serverName;
+ }
+
+ @Override
+ public int id() {
+ return 8;
+ }
+
+ @Override
+ public JsonObject serialize() {
+ return new JsonChain()
+ .addProperty("playerName", playerName)
+ .addProperty("serverName", serverName)
+ .get();
+ }
+
+ @Override
+ public void deserialize(JsonObject jsonObject) {
+ playerName = jsonObject.get("playerName").getAsString();
+ serverName = jsonObject.get("serverName").getAsString();
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/network/packet/PacketStaffSwitchServer.java b/src/main/java/net/centilehcf/core/network/packet/PacketStaffSwitchServer.java
new file mode 100644
index 0000000..9cfeb21
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/packet/PacketStaffSwitchServer.java
@@ -0,0 +1,47 @@
+package net.centilehcf.core.network.packet;
+
+import com.google.gson.JsonObject;
+import com.minexd.pidgin.packet.Packet;
+import net.centilehcf.core.util.json.JsonChain;
+import lombok.Getter;
+import net.centilehcf.core.util.json.JsonChain;
+
+@Getter
+public class PacketStaffSwitchServer implements Packet {
+
+ private String playerName;
+ private String fromServerName;
+ private String toServerName;
+
+ public PacketStaffSwitchServer() {
+
+ }
+
+ public PacketStaffSwitchServer(String playerName, String fromServerName, String toServerName) {
+ this.playerName = playerName;
+ this.fromServerName = fromServerName;
+ this.toServerName = toServerName;
+ }
+
+ @Override
+ public int id() {
+ return 9;
+ }
+
+ @Override
+ public JsonObject serialize() {
+ return new JsonChain()
+ .addProperty("playerName", playerName)
+ .addProperty("fromServerName", fromServerName)
+ .addProperty("toServerName", toServerName)
+ .get();
+ }
+
+ @Override
+ public void deserialize(JsonObject jsonObject) {
+ playerName = jsonObject.get("playerName").getAsString();
+ fromServerName = jsonObject.get("fromServerName").getAsString();
+ toServerName = jsonObject.get("toServerName").getAsString();
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/network/packet/PacketUpdatePrefix.java b/src/main/java/net/centilehcf/core/network/packet/PacketUpdatePrefix.java
new file mode 100644
index 0000000..8f752e4
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/network/packet/PacketUpdatePrefix.java
@@ -0,0 +1,34 @@
+package net.centilehcf.core.network.packet;
+
+import com.google.gson.JsonObject;
+import com.minexd.pidgin.packet.Packet;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.util.json.JsonChain;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Getter
+@NoArgsConstructor
+public class PacketUpdatePrefix implements Packet {
+
+ private String prefixName;
+
+ public PacketUpdatePrefix(String prefixName) {
+ this.prefixName = prefixName;
+ }
+
+ @Override
+ public int id() {
+ return 10;
+ }
+
+ @Override
+ public JsonObject serialize() {
+ return new JsonChain().addProperty("name", prefixName).get();
+ }
+
+ @Override
+ public void deserialize(JsonObject object) {
+ Core.get().getPrefixHandler().loadPrefixByName(object.get("name").getAsString());
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/prefix/Prefix.java b/src/main/java/net/centilehcf/core/prefix/Prefix.java
new file mode 100644
index 0000000..2a94f33
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/prefix/Prefix.java
@@ -0,0 +1,30 @@
+package net.centilehcf.core.prefix;
+
+import net.centilehcf.core.util.CC;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import net.centilehcf.core.util.CC;
+
+@Getter
+@Setter
+@EqualsAndHashCode
+public class Prefix implements Comparable {
+
+ private String name;
+ private String prefix = "";
+ private int weight;
+
+ public Prefix(String name) {
+ this.name = name;
+ }
+
+ public String getPrefixInfo() {
+ return this.getName() + CC.RESET + "(W: " + this.getWeight() + ") (P: " + this.getPrefix() + ")";
+ }
+
+ @Override
+ public int compareTo(Prefix other) {
+ return Integer.compare(this.weight, other.weight);
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/prefix/PrefixHandler.java b/src/main/java/net/centilehcf/core/prefix/PrefixHandler.java
new file mode 100644
index 0000000..19390e4
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/prefix/PrefixHandler.java
@@ -0,0 +1,110 @@
+package net.centilehcf.core.prefix;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.network.packet.PacketDeletePrefix;
+import net.centilehcf.core.network.packet.PacketUpdatePrefix;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.model.Filters;
+import com.mongodb.client.model.ReplaceOptions;
+import lombok.Getter;
+import org.bson.Document;
+import org.bukkit.ChatColor;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+public class PrefixHandler {
+
+ private final Core plugin = Core.get();
+
+ private final MongoCollection prefixCollection;
+
+ @Getter
+ private final List prefixes;
+
+ public PrefixHandler() {
+ this.prefixes = new ArrayList<>();
+ this.prefixCollection = plugin.getMongoDatabase().getCollection("prefixes");
+ loadPrefixes();
+ }
+
+ public Prefix getPrefixByName(String search) {
+ return this.prefixes.stream().filter(prefix -> prefix.getName().equalsIgnoreCase(search)).findFirst().orElse(null);
+ }
+
+ public Prefix getDefaultPrefix() {
+ return this.prefixes.stream().filter(prefix -> prefix.getName().equalsIgnoreCase("Default")).findFirst().orElse(null);
+
+ }
+
+ private void loadPrefixes() {
+ for (Document document : prefixCollection.find()) {
+ Prefix prefix = new Prefix(document.getString("name"));
+ prefix.setPrefix(ChatColor.translateAlternateColorCodes('&', document.getString("prefix")));
+ prefix.setWeight(document.getInteger("weight"));
+ savePrefix(prefix);
+ }
+
+ // Create the default prefix if it doesn't exist!
+ if (getDefaultPrefix() == null) {
+ Prefix prefix = new Prefix("Default");
+ prefix.setPrefix("");
+ prefix.setWeight(-1);
+ savePrefix(prefix);
+ }
+ }
+
+ public Optional getPrefixDocumentFromDb(String name){
+ return Optional.ofNullable(prefixCollection.find(Filters.eq("name", name)).first());
+ }
+
+
+ public void loadPrefixByName(String name) {
+ plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
+ getPrefixDocumentFromDb(name).ifPresent(document -> {
+ Prefix prefix = getPrefixByName(name);
+
+ if (prefix != null) {
+ prefix.setPrefix(ChatColor.translateAlternateColorCodes('&', document.getString("prefix")));
+ prefix.setWeight(document.getInteger("weight"));
+ } else {
+ prefix = new Prefix(document.getString("name"));
+ prefix.setPrefix(ChatColor.translateAlternateColorCodes('&', document.getString("prefix")));
+ prefix.setWeight(document.getInteger("weight"));
+ if (prefixes.contains(prefix)){
+ prefixes.remove(prefix);
+ prefixes.add(prefix);
+ } else {
+ prefixes.add(prefix);
+ }
+ }
+ });
+ });
+ }
+
+ public void savePrefix(Prefix prefix) {
+ if (!prefixes.contains(prefix)) {
+ prefixes.add(prefix);
+ }
+ Document document = new Document();
+ document.put("name", prefix.getName());
+ document.put("prefix", prefix.getPrefix().replace(ChatColor.COLOR_CHAR, '&'));
+ document.put("weight", prefix.getWeight());
+
+ plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
+ prefixCollection.replaceOne(Filters.eq("name", prefix.getName()), document, new ReplaceOptions().upsert(true));
+ Core.get().getPidgin().sendPacket(new PacketUpdatePrefix(prefix.getName()));
+ });
+ }
+
+ public void removePrefix(Prefix prefix) {
+ prefixes.remove(prefix);
+ plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
+ prefixCollection.deleteOne(Filters.eq("name", prefix.getName())); // Deletes the prefix
+ Core.get().getPidgin().sendPacket(new PacketDeletePrefix(prefix.getName()));
+ plugin.getMongoDatabase().getCollection("profiles") // Everyone that had that prefix gets their current one set to the default
+ .updateMany(Filters.eq("prefix", prefix.getName()), new Document("$set", getDefaultPrefix().getName()));
+ });
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/prefix/command/AddPrefixCommand.java b/src/main/java/net/centilehcf/core/prefix/command/AddPrefixCommand.java
new file mode 100644
index 0000000..3876058
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/prefix/command/AddPrefixCommand.java
@@ -0,0 +1,22 @@
+package net.centilehcf.core.prefix.command;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.prefix.Prefix;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = "prefix add", permission = "rank.manager")
+public class AddPrefixCommand {
+
+ public void execute(CommandSender sender, String name){
+ if (Core.get().getPrefixHandler().getPrefixByName(name) != null){
+ sender.sendMessage(CC.RED + "A prefix with the name " + "\'" + name + "\' already exists!");
+ return;
+ }
+
+ Prefix prefix = new Prefix(name);
+ Core.get().getPrefixHandler().savePrefix(prefix);
+ sender.sendMessage(CC.GREEN + "Created a new prefix with the name \'" + name + "\'.");
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/prefix/command/DeletePrefixCommand.java b/src/main/java/net/centilehcf/core/prefix/command/DeletePrefixCommand.java
new file mode 100644
index 0000000..8daaa71
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/prefix/command/DeletePrefixCommand.java
@@ -0,0 +1,16 @@
+package net.centilehcf.core.prefix.command;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.prefix.Prefix;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = "prefix delete", permission = "rank.manager")
+public class DeletePrefixCommand {
+
+ public void execute(CommandSender sender, Prefix prefix) {
+ Core.get().getPrefixHandler().removePrefix(prefix);
+ sender.sendMessage(CC.GREEN + "Removed the prefix \'" + prefix.getName() + "\'.");
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/prefix/command/ListPrefixCommand.java b/src/main/java/net/centilehcf/core/prefix/command/ListPrefixCommand.java
new file mode 100644
index 0000000..4b2c2c1
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/prefix/command/ListPrefixCommand.java
@@ -0,0 +1,20 @@
+package net.centilehcf.core.prefix.command;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.prefix.Prefix;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = "prefix list", permission = "rank.manager")
+public class ListPrefixCommand {
+
+ public void execute(CommandSender sender) {
+ sender.sendMessage(CC.CHAT_BAR);
+ sender.sendMessage(CC.YELLOW + "Listing all prefixes");
+ for (Prefix prefix : Core.get().getPrefixHandler().getPrefixes()) {
+ sender.sendMessage(CC.RED + prefix.getName() + CC.YELLOW + " " + prefix.getPrefixInfo() + CC.GRAY + " (Displays as: " + prefix.getPrefix() + CC.GRAY + ")");
+ }
+ sender.sendMessage(CC.CHAT_BAR);
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/prefix/command/PrefixTypeAdapter.java b/src/main/java/net/centilehcf/core/prefix/command/PrefixTypeAdapter.java
new file mode 100644
index 0000000..3438a98
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/prefix/command/PrefixTypeAdapter.java
@@ -0,0 +1,21 @@
+package net.centilehcf.core.prefix.command;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.prefix.Prefix;
+import com.qrakn.honcho.command.adapter.CommandTypeAdapter;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class PrefixTypeAdapter implements CommandTypeAdapter {
+
+ @Override
+ public T convert(String string, Class type) {
+ return type.cast(Core.get().getPrefixHandler().getPrefixByName(string));
+ }
+
+ @Override
+ public List tabComplete(String string, Class type) {
+ return Core.get().getPrefixHandler().getPrefixes().stream().map(Prefix::getName).collect(Collectors.toList());
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/prefix/command/SetPrefixCommand.java b/src/main/java/net/centilehcf/core/prefix/command/SetPrefixCommand.java
new file mode 100644
index 0000000..4f03cde
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/prefix/command/SetPrefixCommand.java
@@ -0,0 +1,18 @@
+package net.centilehcf.core.prefix.command;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.prefix.Prefix;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = "prefix setprefix", permission = "rank.manager")
+public class SetPrefixCommand {
+
+ public void execute(CommandSender sender, Prefix prefix, String name) {
+ prefix.setPrefix(CC.translate(name));
+ sender.sendMessage(CC.GREEN + "Set the prefix of " + prefix.getName() + " to " + prefix.getPrefix() + '.');
+ Core.get().getPrefixHandler().savePrefix(prefix);
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/prefix/command/SetPrefixWeightCommand.java b/src/main/java/net/centilehcf/core/prefix/command/SetPrefixWeightCommand.java
new file mode 100644
index 0000000..01c31ff
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/prefix/command/SetPrefixWeightCommand.java
@@ -0,0 +1,16 @@
+package net.centilehcf.core.prefix.command;
+
+import net.centilehcf.core.prefix.Prefix;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = "prefix setprefix", permission = "rank.manager")
+public class SetPrefixWeightCommand {
+
+ public void execute(CommandSender sender, Prefix prefix, int weight) {
+ prefix.setWeight(weight);
+ sender.sendMessage(CC.GREEN + "Set the weight of \'" + prefix.getName() + "\' to " + weight + '.');
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/prefix/menu/PrefixSelectionMenu.java b/src/main/java/net/centilehcf/core/prefix/menu/PrefixSelectionMenu.java
new file mode 100644
index 0000000..93b88da
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/prefix/menu/PrefixSelectionMenu.java
@@ -0,0 +1,103 @@
+package net.centilehcf.core.prefix.menu;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.prefix.Prefix;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.ItemBuilder;
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.Menu;
+import lombok.AllArgsConstructor;
+import org.bukkit.ChatColor;
+import org.bukkit.DyeColor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.*;
+
+public class PrefixSelectionMenu extends Menu {
+
+ @Override
+ public String getTitle(Player player) {
+ return ChatColor.YELLOW.toString() + ChatColor.BOLD.toString() + "Choose a prefix.";
+ }
+
+ @Override
+ public Map getButtons(Player player) {
+ final Map buttons = new HashMap<>();
+ Core.get().getPrefixHandler().getPrefixes().sort(Comparator.comparingInt(Prefix::getWeight).reversed());
+ Core.get().getPrefixHandler().getPrefixes().forEach(prefix -> {
+ buttons.put(buttons.size(), new PrefixSelectionButton(prefix));
+ });
+ buttons.put(49, new Button() {
+ @Override
+ public ItemStack getButtonItem(Player player) {
+ return new ItemBuilder(Material.REDSTONE_BLOCK).name(CC.RED + "Reset prefix").lore(
+ Arrays.asList(
+ CC.SB_BAR,
+ CC.YELLOW + "Click to remove your prefix",
+ CC.SB_BAR
+ )
+ ).build();
+ }
+
+ @Override
+ public void clicked(Player player, int slot, ClickType clickType, int hotbarButton) {
+ Profile profile = Profile.getByUuid(player.getUniqueId());
+ if (profile != null) {
+ profile.setPrefix(Core.get().getPrefixHandler().getDefaultPrefix());
+ }
+ player.sendMessage(CC.YELLOW + "You have reset your prefix.");
+ }
+ });
+ return buttons;
+ }
+
+ @AllArgsConstructor
+ private static class PrefixSelectionButton extends Button {
+
+ private Prefix prefix;
+
+ @Override
+ public ItemStack getButtonItem(Player player) {
+
+ final List lore = new ArrayList<>();
+
+ lore.add(0, CC.SB_BAR);
+ lore.add(CC.YELLOW + "Shows as " + prefix.getPrefix());
+ lore.add(CC.SB_BAR);
+
+ byte color;
+
+ if (Profile.getByUuid(player.getUniqueId()).getPrefix().equals(prefix)) {
+ color = DyeColor.GREEN.getData();
+ } else if (player.hasPermission("prefix." + prefix.getName())) {
+ color = DyeColor.SILVER.getData();
+ } else {
+ color = DyeColor.RED.getData();
+ }
+
+ return new ItemBuilder(Material.WOOL)
+ .name(ChatColor.GOLD + prefix.getName())
+ .lore(lore)
+ .durability(color)
+ .build();
+ }
+
+ @Override
+ public void clicked(Player player, int slot, ClickType clickType, int hotbarSlot) {
+ if (!player.hasPermission("prefix." + prefix.getName())) {
+ player.sendMessage(CC.RED + "You don't have access for that prefix! Purchase it on the store!");
+ return;
+ }
+
+ player.closeInventory();
+
+ player.sendMessage(CC.GREEN + "You have updated your prefix to: " + CC.WHITE + prefix.getName());
+
+ Profile.getByUuid(player.getUniqueId()).setPrefix(prefix);
+ }
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/profile/Profile.java b/src/main/java/net/centilehcf/core/profile/Profile.java
new file mode 100644
index 0000000..db24f64
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/Profile.java
@@ -0,0 +1,375 @@
+package net.centilehcf.core.profile;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.prefix.Prefix;
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.profile.grant.event.GrantAppliedEvent;
+import net.centilehcf.core.profile.grant.event.GrantExpireEvent;
+import net.centilehcf.core.profile.option.ProfileOptions;
+import net.centilehcf.core.profile.option.ProfileStaffOptions;
+import net.centilehcf.core.profile.punishment.Punishment;
+import net.centilehcf.core.profile.punishment.PunishmentType;
+import net.centilehcf.core.rank.Rank;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoCursor;
+import com.mongodb.client.model.Filters;
+import com.mongodb.client.model.ReplaceOptions;
+import lombok.Getter;
+import lombok.Setter;
+import net.centilehcf.core.util.Cooldown;
+import org.bson.Document;
+import org.bukkit.Bukkit;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.entity.Player;
+import org.bukkit.permissions.PermissionAttachment;
+import org.bukkit.permissions.PermissionAttachmentInfo;
+
+import java.util.*;
+
+public class Profile {
+
+ @Getter
+ private static Map profiles = new HashMap<>();
+ private static MongoCollection collection;
+ @Getter
+ private final UUID uuid;
+ @Getter
+ private final ProfileOptions options = new ProfileOptions();
+ @Getter
+ private final ProfileStaffOptions staffOptions = new ProfileStaffOptions();
+ @Getter
+ private final List grants;
+ @Getter
+ private final List punishments;
+ @Getter
+ @Setter
+ private String username;
+ @Getter
+ @Setter
+ private Long firstSeen;
+ @Getter
+ @Setter
+ private Long lastSeen;
+ @Getter
+ @Setter
+ private String currentAddress;
+ @Getter
+ private List ipAddresses = new ArrayList<>();
+ @Getter
+ private List knownAlts = new ArrayList<>();
+ @Getter
+ private List individualPermissions;
+ @Getter
+ private Grant activeGrant;
+ @Getter
+ @Setter
+ private Prefix prefix;
+ @Getter
+ @Setter
+ private UUID replyTo;
+ @Getter
+ @Setter
+ private boolean loaded;
+ @Getter
+ @Setter
+ private Cooldown requestCooldown = new Cooldown(0);
+ @Setter
+ @Getter
+ private Cooldown chatCooldown = new Cooldown(1000);
+
+ public Profile(String username, UUID uuid) {
+ this.username = username;
+ this.uuid = uuid;
+ this.grants = new ArrayList<>();
+ this.punishments = new ArrayList<>();
+ this.individualPermissions = new ArrayList<>();
+
+ load();
+ }
+
+ public static void init() {
+ collection = Core.get().getMongoDatabase().getCollection("profiles");
+ }
+
+ public static Profile getByUuid(UUID uuid) {
+ if (profiles.containsKey(uuid)) {
+ return profiles.get(uuid);
+ }
+
+ return new Profile(null, uuid);
+ }
+
+ public static Profile getByUsername(String username) {
+ Player player = Bukkit.getPlayer(username);
+
+ if (player != null) {
+ return profiles.get(player.getUniqueId());
+ }
+
+ OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(username);
+
+ if (offlinePlayer.hasPlayedBefore()) {
+ if (profiles.containsKey(offlinePlayer.getUniqueId())) {
+ return profiles.get(offlinePlayer.getUniqueId());
+ }
+
+ return new Profile(offlinePlayer.getName(), offlinePlayer.getUniqueId());
+ }
+
+ UUID uuid = Core.get().getUuidCache().getUuid(username);
+
+ if (uuid != null) {
+ if (profiles.containsKey(uuid)) {
+ return profiles.get(uuid);
+ }
+
+ return new Profile(username, uuid);
+ }
+
+ return null;
+ }
+
+ public static List getByIpAddress(String ipAddress) {
+ List profiles = new ArrayList<>();
+
+ try (MongoCursor cursor = collection.find(Filters.eq("currentAddress", ipAddress)).iterator()) {
+ while (cursor.hasNext()) {
+ Document document = cursor.next();
+ profiles.add(new Profile(document.getString("username"), UUID.fromString(document.getString("uuid"))));
+ }
+ }
+
+ return profiles;
+ }
+
+ public Player getPlayer() {
+ return Bukkit.getPlayer(uuid);
+ }
+
+ public String getColoredUsername() {
+ return activeGrant.getRank().getColor() + username;
+ }
+
+ public Punishment getActiveMute() {
+ for (Punishment punishment : punishments) {
+ if (punishment.getType() == PunishmentType.MUTE && punishment.isActive()) {
+ return punishment;
+ }
+ }
+
+ return null;
+ }
+
+ public Punishment getActiveBlacklist() {
+ for (Punishment punishment : punishments) {
+ if (punishment.getType() == PunishmentType.BLACKLIST && punishment.isActive()) {
+ return punishment;
+ }
+ }
+ return null;
+ }
+
+ public Punishment getActiveBan() {
+ for (Punishment punishment : punishments) {
+ if (punishment.getType().isBan() && punishment.isActive()) {
+ return punishment;
+ }
+ }
+
+ return null;
+ }
+
+ public int getPunishmentCountByType(PunishmentType type) {
+ int i = 0;
+
+ for (Punishment punishment : punishments) {
+ if (punishment.getType() == type) i++;
+ }
+
+ return i;
+ }
+
+ public Rank getActiveRank() {
+ return activeGrant.getRank();
+ }
+
+ public void setActiveGrant(Grant grant) {
+ activeGrant = grant;
+
+ Player player = getPlayer();
+ if (player != null) {
+ player.setDisplayName(grant.getRank().getPrefix() + player.getName() + grant.getRank().getSuffix());
+ }
+ }
+
+ public void activateNextGrant() {
+ List grants = new ArrayList<>(this.grants);
+
+ grants.sort(Comparator.comparingInt(grant -> grant.getRank().getWeight()));
+
+ for (Grant grant : grants) {
+ if (!grant.isRemoved() && !grant.hasExpired()) {
+ setActiveGrant(grant);
+ }
+ }
+ }
+
+ public void checkGrants() {
+ for (Grant grant : grants) {
+ if (!grant.isRemoved() && grant.hasExpired()) {
+ grant.setRemovedAt(System.currentTimeMillis());
+ grant.setRemovedReason("Grant expired");
+ grant.setRemoved(true);
+
+ if (activeGrant != null && activeGrant.equals(grant)) {
+ activeGrant = null;
+ }
+
+ Player player = getPlayer();
+
+ if (player != null) {
+ new GrantExpireEvent(player, grant).call();
+ }
+ }
+ }
+
+ if (activeGrant == null) {
+ activateNextGrant();
+
+ if (activeGrant != null) {
+ return;
+ }
+
+ Grant grant = new Grant(UUID.randomUUID(), Rank.getDefaultRank(), null,
+ System.currentTimeMillis(), "Default", Integer.MAX_VALUE);
+ this.grants.add(grant);
+
+ setActiveGrant(grant);
+
+ Player player = getPlayer();
+
+ if (player != null) {
+ new GrantAppliedEvent(player, grant).call();
+ }
+ }
+ }
+
+ public void setupPermissionsAttachment(Core core, Player player) {
+ for (PermissionAttachmentInfo attachmentInfo : player.getEffectivePermissions()) {
+ if (attachmentInfo.getAttachment() == null) {
+ continue;
+ }
+
+ attachmentInfo.getAttachment().getPermissions().forEach((permission, value) -> {
+ attachmentInfo.getAttachment().unsetPermission(permission);
+ });
+ }
+
+ PermissionAttachment attachment = player.addAttachment(core);
+
+ for (String perm : activeGrant.getRank().getAllPermissions()) { // Rank permissions
+ attachment.setPermission(perm, true);
+ }
+
+ // Check for 'null' permissions
+ individualPermissions.removeIf(s -> s == null || s.isEmpty());
+
+ for (String permission : individualPermissions) { // Individual permissions
+ attachment.setPermission(permission, true);
+ }
+
+ player.recalculatePermissions();
+ }
+
+ public void load() {
+
+ final Document document = collection.find(Filters.eq("uuid", uuid.toString())).first();
+
+ if (document != null) {
+ if (username == null) {
+ username = document.getString("username");
+ }
+
+ firstSeen = document.getLong("firstSeen");
+ lastSeen = document.getLong("lastSeen");
+ currentAddress = document.getString("currentAddress");
+ ipAddresses = Core.GSON.fromJson(document.getString("ipAddresses"), Core.LIST_STRING_TYPE);
+ this.individualPermissions = Core.GSON.fromJson(document.getString("individualPermissions"), Core.LIST_STRING_TYPE);
+
+ Prefix prefix = Core.get().getPrefixHandler().getPrefixByName(document.getString("prefix"));
+ if (prefix != null) {
+ this.prefix = prefix;
+ } else {
+ this.prefix = Core.get().getPrefixHandler().getDefaultPrefix();
+ }
+
+ Document optionsDocument = (Document) document.get("options");
+ options.setPublicChatEnabled(optionsDocument.getBoolean("publicChatEnabled"));
+ options.setPrivateChatEnabled(optionsDocument.getBoolean("privateChatEnabled"));
+ options.setPrivateChatSoundsEnabled(optionsDocument.getBoolean("privateChatSoundsEnabled"));
+
+ JsonArray grants = new JsonParser().parse(document.getString("grants")).getAsJsonArray();
+ for (JsonElement jsonElement : grants) {
+ JsonObject jsonObject = jsonElement.getAsJsonObject();
+ Rank rank = Rank.getRankByUuid(UUID.fromString(jsonObject.get("rank").getAsString()));
+
+ if (rank != null) {
+ this.grants.add(Grant.DESERIALIZER.deserialize(jsonObject));
+ }
+ }
+
+ JsonArray punishments = new JsonParser().parse(document.getString("punishments")).getAsJsonArray();
+ for (JsonElement jsonElement : punishments) {
+ JsonObject jsonObject = jsonElement.getAsJsonObject();
+ this.punishments.add(Punishment.DESERIALIZER.deserialize(jsonObject));
+ }
+ }
+
+ checkGrants();
+
+ // Set loaded to true
+ loaded = true;
+ }
+
+ public void save() {
+ Document document = new Document();
+ document.put("username", username);
+ document.put("uuid", uuid.toString());
+ document.put("firstSeen", firstSeen);
+ document.put("lastSeen", lastSeen);
+ document.put("currentAddress", currentAddress);
+ document.put("ipAddresses", Core.GSON.toJson(ipAddresses, Core.LIST_STRING_TYPE));
+ document.put("individualPermissions", Core.GSON.toJson(individualPermissions, Core.LIST_STRING_TYPE));
+
+ if (this.prefix != null) {
+ document.put("prefix", this.prefix.getName());
+ } else {
+ document.put("prefix", Core.get().getPrefixHandler().getDefaultPrefix().getName());
+ }
+
+ Document optionsDocument = new Document();
+ optionsDocument.put("publicChatEnabled", options.isPublicChatEnabled());
+ optionsDocument.put("privateChatEnabled", options.isPrivateChatEnabled());
+ optionsDocument.put("privateChatSoundsEnabled", options.isPrivateChatSoundsEnabled());
+ document.put("options", optionsDocument);
+
+ JsonArray grants = new JsonArray();
+ for (Grant grant : this.grants) {
+ grants.add(Grant.SERIALIZER.serialize(grant));
+ }
+ document.put("grants", grants.toString());
+
+ JsonArray punishments = new JsonArray();
+ for (Punishment punishment : this.punishments) {
+ punishments.add(Punishment.SERIALIZER.serialize(punishment));
+ }
+ document.put("punishments", punishments.toString());
+
+ collection.replaceOne(Filters.eq("uuid", uuid.toString()), document, new ReplaceOptions().upsert(true));
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/ProfileInfo.java b/src/main/java/net/centilehcf/core/profile/ProfileInfo.java
new file mode 100644
index 0000000..673ae12
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/ProfileInfo.java
@@ -0,0 +1,51 @@
+package net.centilehcf.core.profile;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.util.BukkitReflection;
+import org.bukkit.entity.Player;
+import java.util.UUID;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 3/29/2019.
+ */
+public class ProfileInfo {
+ private UUID uuid;
+ private String name;
+
+ public ProfileInfo(Player player) {
+ this.uuid = player.getUniqueId();
+ this.name = player.getName();
+ }
+
+ public ProfileInfo(UUID uuid, String name) {
+ this.uuid = uuid;
+ this.name = name;
+ }
+
+ public Player toPlayer() {
+ Player player = Core.get().getServer().getPlayer(this.getUuid());
+ return player != null && player.isOnline() ? player : null;
+ }
+
+ public String getDisplayName() {
+ Player player = this.toPlayer();
+ return player == null ? this.getName() : player.getDisplayName();
+ }
+
+ public int getPing() {
+ Player player = Core.get().getServer().getPlayer(this.getUuid());
+ return player == null ? 0 : BukkitReflection.getPing(player);
+ }
+
+ public UUID getUuid() {
+ return this.uuid;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/profile/ProfileListener.java b/src/main/java/net/centilehcf/core/profile/ProfileListener.java
new file mode 100644
index 0000000..aeeffe3
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/ProfileListener.java
@@ -0,0 +1,156 @@
+package net.centilehcf.core.profile;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.bootstrap.BootstrappedListener;
+import net.centilehcf.core.network.packet.PacketStaffJoinNetwork;
+import net.centilehcf.core.network.packet.PacketStaffLeaveNetwork;
+import net.centilehcf.core.prefix.Prefix;
+import net.centilehcf.core.prefix.PrefixHandler;
+import net.centilehcf.core.profile.punishment.Punishment;
+import net.centilehcf.core.util.CC;
+
+import java.util.List;
+import java.util.logging.Level;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.scheduler.BukkitRunnable;
+
+public class ProfileListener extends BootstrappedListener {
+
+ public ProfileListener(Core core) {
+ super(core);
+ }
+
+ @EventHandler
+ public void onAsyncPlayerPreLogin(AsyncPlayerPreLoginEvent event) {
+ Player player = core.getServer().getPlayer(event.getUniqueId());
+
+ if (!Core.get().isLoaded()) {
+ event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
+ event.setKickMessage(CC.RED + "The server is starting...");
+ return;
+ }
+ // Need to check if player is still logged in when receiving another login attempt
+ // This happens when a player using a custom client can access the server list while in-game (and reconnecting)
+ if (player != null && player.isOnline()) {
+ event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
+ event.setKickMessage(CC.RED + "You tried to login too quickly after disconnecting.\nTry again in a few seconds.");
+ core.getServer().getScheduler().runTask(core, () -> player.kickPlayer(CC.RED + "Duplicate login kick"));
+ return;
+ }
+
+ Profile profile = null;
+
+ try {
+ profile = new Profile(event.getName(), event.getUniqueId());
+
+ if (!profile.isLoaded()) {
+ event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
+ event.setKickMessage(Locale.FAILED_TO_LOAD_PROFILE.format());
+ return;
+ }
+
+ if (profile.getActiveBan() != null) {
+ handleBan(event, profile.getActiveBan());
+ return;
+ }
+
+ if (profile.getActiveBlacklist() != null) {
+ handleBan(event, profile.getActiveBlacklist());
+ }
+
+ profile.setUsername(event.getName());
+
+ if (profile.getFirstSeen() == null) {
+ profile.setFirstSeen(System.currentTimeMillis());
+ }
+
+ profile.setLastSeen(System.currentTimeMillis());
+
+ if (profile.getCurrentAddress() == null) {
+ profile.setCurrentAddress(event.getAddress().getHostAddress());
+ }
+
+ if (!profile.getIpAddresses().contains(event.getAddress().getHostAddress())) {
+ profile.getIpAddresses().add(event.getAddress().getHostAddress());
+ }
+
+ if (!profile.getCurrentAddress().equals(event.getAddress().getHostAddress())) {
+ List alts = Profile.getByIpAddress(event.getAddress().getHostAddress());
+
+ for (Profile alt : alts) {
+ if (alt.getActiveBan() != null) {
+ handleBan(event, alt.getActiveBan());
+ return;
+ }
+ profile.getKnownAlts().add(alt.getUuid());
+ }
+ }
+
+ profile.save();
+ } catch (Exception e) {
+ core.debug(Level.SEVERE, "Failed to load profile for " + event.getName(), e);
+ }
+
+ if (profile == null || !profile.isLoaded()) {
+ event.setKickMessage(Locale.FAILED_TO_LOAD_PROFILE.format());
+ event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
+ return;
+ }
+
+ Profile.getProfiles().put(profile.getUuid(), profile);
+
+ core.getUuidCache().update(event.getName(), event.getUniqueId());
+ }
+
+ @EventHandler
+ public void onPlayerJoin(PlayerJoinEvent event) {
+ event.setJoinMessage(null);
+ Player player = event.getPlayer();
+ Profile profile = Profile.getProfiles().get(player.getUniqueId());
+ profile.setupPermissionsAttachment(core, event.getPlayer());
+
+ player.setDisplayName(profile.getActiveGrant().getRank().getPrefix() + player.getName() + profile.getActiveGrant().getRank().getSuffix());
+
+ if (player.hasPermission("core.staff")) {
+ String server = Core.get().getMainConfig().getConfiguration().getString("SERVER_NAME");
+ Core.get().getPidgin().sendPacket(new PacketStaffJoinNetwork(player.getDisplayName(), server));
+ }
+ }
+
+ @EventHandler
+ public void onPlayerQuit(PlayerQuitEvent event) {
+ event.setQuitMessage(null);
+ Profile profile = Profile.getProfiles().remove(event.getPlayer().getUniqueId());
+ profile.setLastSeen(System.currentTimeMillis());
+ Player player = event.getPlayer();
+
+ if (player.hasPermission("core.staff")) {
+ String server = Core.get().getMainConfig().getConfiguration().getString("SERVER_NAME");
+ Core.get().getPidgin().sendPacket(new PacketStaffLeaveNetwork(player.getDisplayName(), server));
+ }
+
+ if (profile.isLoaded()) {
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ try {
+ profile.save();
+ } catch (Exception e) {
+ core.debug(Level.SEVERE, "Failed to save profile " + event.getPlayer().getName(), e);
+ }
+ }
+ }.runTaskAsynchronously(Core.get());
+ }
+ }
+
+ private void handleBan(AsyncPlayerPreLoginEvent event, Punishment punishment) {
+ event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
+ event.setKickMessage(punishment.getKickMessage());
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/profile/ProfileTypeAdapter.java b/src/main/java/net/centilehcf/core/profile/ProfileTypeAdapter.java
new file mode 100644
index 0000000..b52ae79
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/ProfileTypeAdapter.java
@@ -0,0 +1,28 @@
+package net.centilehcf.core.profile;
+
+import com.qrakn.honcho.command.adapter.CommandTypeAdapter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ProfileTypeAdapter implements CommandTypeAdapter {
+
+ public T convert(String string, Class type) {
+ return type.cast(Profile.getByUsername(string));
+ }
+
+ @Override
+ public List tabComplete(String string, Class type) {
+ List completed = new ArrayList<>();
+
+ for (Profile profile : Profile.getProfiles().values()) {
+ if (profile.getUsername() == null) continue;
+
+ if (profile.getUsername().toLowerCase().startsWith(string.toLowerCase())) {
+ completed.add(profile.getUsername());
+ }
+ }
+
+ return completed;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/command/AltsCommand.java b/src/main/java/net/centilehcf/core/profile/command/AltsCommand.java
new file mode 100644
index 0000000..c1f2587
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/command/AltsCommand.java
@@ -0,0 +1,41 @@
+package net.centilehcf.core.profile.command;
+
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.util.CC;
+import net.minecraft.util.com.google.common.base.Joiner;
+import org.bukkit.command.CommandSender;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+@CommandMeta(label = "alts", async = true, permission = "rank.mod")
+public class AltsCommand {
+
+ public void execute(CommandSender sender, @CPL("player") Profile profile) {
+ if (profile == null || !profile.isLoaded()) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ List alts = new ArrayList<>();
+
+ for (UUID altUuid : profile.getKnownAlts()) {
+ String cachedName = Core.get().getUuidCache().getName(altUuid);
+ if (cachedName != null) {
+ alts.add(cachedName);
+ }
+ }
+
+ if (alts.isEmpty()) {
+ sender.sendMessage(CC.RED + "This player has no known alt accounts.");
+ } else {
+ sender.sendMessage(CC.GOLD + "Alts: " + CC.RESET + Joiner.on(", ").join(alts));
+ }
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/command/OptionsCommand.java b/src/main/java/net/centilehcf/core/profile/command/OptionsCommand.java
new file mode 100644
index 0000000..6eaffde
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/command/OptionsCommand.java
@@ -0,0 +1,15 @@
+package net.centilehcf.core.profile.command;
+
+import net.centilehcf.core.profile.option.menu.ProfileOptionsMenu;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.profile.option.menu.ProfileOptionsMenu;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "options")
+public class OptionsCommand {
+
+ public void execute(Player player) {
+ new ProfileOptionsMenu().openMenu(player);
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/command/StaffChatCommand.java b/src/main/java/net/centilehcf/core/profile/command/StaffChatCommand.java
new file mode 100644
index 0000000..e1efb5b
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/command/StaffChatCommand.java
@@ -0,0 +1,24 @@
+package net.centilehcf.core.profile.command;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.network.packet.PacketStaffChat;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = { "staffchat", "sc" }, permission = "rank.staff")
+public class StaffChatCommand {
+
+ public void execute(Player player, String message) {
+ Profile profile = Profile.getProfiles().get(player.getUniqueId());
+
+ if (!profile.getStaffOptions().isStaffChatEnabled()) {
+ player.sendMessage(CC.RED + "Your staff chat is disabled.");
+ return;
+ }
+
+ Core.get().getPidgin().sendPacket(new PacketStaffChat(player.getDisplayName(), Core.get().getMainConfig().getString("SERVER_NAME"), message));
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/command/StaffChatToggleCommand.java b/src/main/java/net/centilehcf/core/profile/command/StaffChatToggleCommand.java
new file mode 100644
index 0000000..6ba3639
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/command/StaffChatToggleCommand.java
@@ -0,0 +1,18 @@
+package net.centilehcf.core.profile.command;
+
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "toggle", permission = "rank.staff")
+public class StaffChatToggleCommand {
+
+ public void execute(Player player) {
+ Profile profile = Profile.getProfiles().get(player.getUniqueId());
+ profile.getStaffOptions().setStaffChatEnabled(!profile.getStaffOptions().isStaffChatEnabled());
+
+ player.sendMessage(profile.getStaffOptions().isStaffChatEnabled() ? CC.GREEN + "You have enabled your staff chat." : CC.RED + "You have disabled your staff chat.");
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/profile/command/individualperms/AddIndividualPermissionCommand.java b/src/main/java/net/centilehcf/core/profile/command/individualperms/AddIndividualPermissionCommand.java
new file mode 100644
index 0000000..e436b5e
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/command/individualperms/AddIndividualPermissionCommand.java
@@ -0,0 +1,37 @@
+package net.centilehcf.core.profile.command.individualperms;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "addindividualperm", permission = "rank.manager", async = true)
+public class AddIndividualPermissionCommand {
+
+ public void execute(CommandSender sender, @CPL("player") Profile profile, String permission) {
+ if (profile == null || !profile.isLoaded()) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ if (profile.getIndividualPermissions().contains(permission)) {
+ sender.sendMessage(CC.RED + profile.getUsername() + " already has the permission " + permission + '!');
+ return;
+ }
+
+ profile.getIndividualPermissions().add(permission);
+ Player player = profile.getPlayer();
+ if (player != null) {
+ Core.get().getServer().getScheduler().runTask(Core.get(), () -> {
+ profile.setupPermissionsAttachment(Core.get(), player);
+ sender.sendMessage(CC.YELLOW + "Recalculated permissions for " + player.getName() + '.');
+ });
+ }
+ sender.sendMessage(CC.GREEN + "Added permission " + "\'" + permission + "\' to " + profile.getUsername() + '.');
+ }
+}
+
diff --git a/src/main/java/net/centilehcf/core/profile/command/individualperms/ListIndividualPermissionsCommand.java b/src/main/java/net/centilehcf/core/profile/command/individualperms/ListIndividualPermissionsCommand.java
new file mode 100644
index 0000000..b4aa17a
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/command/individualperms/ListIndividualPermissionsCommand.java
@@ -0,0 +1,27 @@
+package net.centilehcf.core.profile.command.individualperms;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = "listindividualperms", permission = "rank.manager", async = true)
+public class ListIndividualPermissionsCommand {
+
+ public void execute(CommandSender sender, @CPL("player") Profile profile) {
+ if (profile == null || !profile.isLoaded()) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ sender.sendMessage(CC.CHAT_BAR);
+ sender.sendMessage(CC.YELLOW + "Listing individual permissions for " + CC.RED + profile.getUsername() + CC.YELLOW + '.');
+ for (String permission : profile.getIndividualPermissions()) {
+ sender.sendMessage(CC.RED + permission);
+ }
+ sender.sendMessage(CC.CHAT_BAR);
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/profile/command/individualperms/RemoveIndividualPermissionCommand.java b/src/main/java/net/centilehcf/core/profile/command/individualperms/RemoveIndividualPermissionCommand.java
new file mode 100644
index 0000000..271706a
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/command/individualperms/RemoveIndividualPermissionCommand.java
@@ -0,0 +1,36 @@
+package net.centilehcf.core.profile.command.individualperms;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "removeindividualperm", permission = "rank.manager", async = true)
+public class RemoveIndividualPermissionCommand {
+
+ public void execute(CommandSender sender, @CPL("player") Profile profile, String permission) {
+ if (profile == null || !profile.isLoaded()) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ if (!profile.getIndividualPermissions().contains(permission)) {
+ sender.sendMessage(CC.RED + profile.getUsername() + " doesn't have the permission " + permission + '!');
+ return;
+ }
+
+ profile.getIndividualPermissions().remove(permission);
+ Player player = profile.getPlayer();
+ if (player != null) {
+ Core.get().getServer().getScheduler().runTask(Core.get(), () -> {
+ profile.setupPermissionsAttachment(Core.get(), player);
+ sender.sendMessage(CC.YELLOW + "Recalculated permissions for " + player.getName() + '.');
+ });
+ }
+ sender.sendMessage(CC.GREEN + "Removed permission " + "\'" + permission + "\' for " + profile.getUsername() + '.');
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/profile/grant/Grant.java b/src/main/java/net/centilehcf/core/profile/grant/Grant.java
new file mode 100644
index 0000000..2f428b9
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/grant/Grant.java
@@ -0,0 +1,61 @@
+package net.centilehcf.core.profile.grant;
+
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.TimeUtil;
+import java.util.Date;
+import java.util.UUID;
+import lombok.Getter;
+import lombok.Setter;
+import net.centilehcf.core.util.TimeUtil;
+
+public class Grant {
+
+ public static GrantJsonSerializer SERIALIZER = new GrantJsonSerializer();
+ public static GrantJsonDeserializer DESERIALIZER = new GrantJsonDeserializer();
+
+ @Getter private final UUID uuid;
+ @Getter private final Rank rank;
+ @Getter @Setter private UUID addedBy;
+ @Getter private final long addedAt;
+ @Getter private final String addedReason;
+ @Getter private final long duration;
+ @Getter @Setter private UUID removedBy;
+ @Getter @Setter private long removedAt;
+ @Getter @Setter private String removedReason;
+ @Getter @Setter private boolean removed;
+
+ public Grant(UUID uuid, Rank rank, UUID addedBy, long addedAt, String addedReason, long duration) {
+ this.uuid = uuid;
+ this.rank = rank;
+ this.addedBy = addedBy;
+ this.addedAt = addedAt;
+ this.addedReason = addedReason;
+ this.duration = duration;
+ }
+
+ public boolean isPermanent() {
+ return duration == Integer.MAX_VALUE;
+ }
+
+ public boolean hasExpired() {
+ return (!isPermanent()) && (System.currentTimeMillis() >= addedAt + duration);
+ }
+
+ public String getAddedAtDate() {
+ return TimeUtil.dateToString(new Date(addedAt));
+ }
+
+ public String getExpiresAtDate() {
+ return duration == Integer.MAX_VALUE ? "Never" : TimeUtil.dateToString(new Date(addedAt + duration));
+ }
+
+ public String getRemovedAtDate() {
+ return TimeUtil.dateToString(new Date(addedAt));
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ return object != null && object instanceof Grant && ((Grant) object).uuid.equals(uuid);
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/grant/GrantJsonDeserializer.java b/src/main/java/net/centilehcf/core/profile/grant/GrantJsonDeserializer.java
new file mode 100644
index 0000000..e1c7fdc
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/grant/GrantJsonDeserializer.java
@@ -0,0 +1,48 @@
+package net.centilehcf.core.profile.grant;
+
+import com.google.gson.JsonObject;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.json.JsonDeserializer;
+import net.centilehcf.core.util.json.JsonDeserializer;
+
+import java.util.UUID;
+
+public class GrantJsonDeserializer implements JsonDeserializer {
+
+ @Override
+ public Grant deserialize(JsonObject object) {
+ Rank rank = Rank.getRankByUuid(UUID.fromString(object.get("rank").getAsString()));
+
+ Grant grant = new Grant(
+ UUID.fromString(object.get("uuid").getAsString()),
+ rank,
+ null,
+ object.get("addedAt").getAsLong(),
+ object.get("addedReason").getAsString(),
+ object.get("duration").getAsLong()
+ );
+
+ if (!object.get("addedBy").isJsonNull()) {
+ grant.setAddedBy(UUID.fromString(object.get("addedBy").getAsString()));
+ }
+
+ if (!object.get("removedBy").isJsonNull()) {
+ grant.setRemovedBy(UUID.fromString(object.get("removedBy").getAsString()));
+ }
+
+ if (!object.get("removedAt").isJsonNull()) {
+ grant.setRemovedAt(object.get("removedAt").getAsLong());
+ }
+
+ if (!object.get("removedReason").isJsonNull()) {
+ grant.setRemovedReason(object.get("removedReason").getAsString());
+ }
+
+ if (!object.get("removed").isJsonNull()) {
+ grant.setRemoved(object.get("removed").getAsBoolean());
+ }
+
+ return grant;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/grant/GrantJsonSerializer.java b/src/main/java/net/centilehcf/core/profile/grant/GrantJsonSerializer.java
new file mode 100644
index 0000000..1eaa3f7
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/grant/GrantJsonSerializer.java
@@ -0,0 +1,25 @@
+package net.centilehcf.core.profile.grant;
+
+import com.google.gson.JsonObject;
+import net.centilehcf.core.util.json.JsonSerializer;
+import net.centilehcf.core.util.json.JsonSerializer;
+
+public class GrantJsonSerializer implements JsonSerializer {
+
+ @Override
+ public JsonObject serialize(Grant grant) {
+ JsonObject object = new JsonObject();
+ object.addProperty("uuid", grant.getUuid().toString());
+ object.addProperty("rank", grant.getRank().getUuid().toString());
+ object.addProperty("addedBy", grant.getAddedBy() == null ? null : grant.getAddedBy().toString());
+ object.addProperty("addedAt", grant.getAddedAt());
+ object.addProperty("addedReason", grant.getAddedReason());
+ object.addProperty("duration", grant.getDuration());
+ object.addProperty("removedBy", grant.getRemovedBy() == null ? null : grant.getRemovedBy().toString());
+ object.addProperty("removedAt", grant.getRemovedAt());
+ object.addProperty("removedReason", grant.getRemovedReason());
+ object.addProperty("removed", grant.isRemoved());
+ return object;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/grant/command/ClearGrantsCommand.java b/src/main/java/net/centilehcf/core/profile/grant/command/ClearGrantsCommand.java
new file mode 100644
index 0000000..590772d
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/grant/command/ClearGrantsCommand.java
@@ -0,0 +1,32 @@
+package net.centilehcf.core.profile.grant.command;
+
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.network.packet.PacketClearGrants;
+import net.centilehcf.core.profile.Profile;
+import org.bukkit.ChatColor;
+import org.bukkit.command.CommandSender;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 5/14/2019.
+ */
+@CommandMeta(label = "cleargrants", permission = "rank.manager", async = true)
+public class ClearGrantsCommand {
+
+ public void execute(CommandSender sender, @CPL("player") Profile profile) {
+ if (profile == null) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ profile.getGrants().clear();
+ profile.save();
+
+ Core.get().getPidgin().sendPacket(new PacketClearGrants(profile.getUuid()));
+
+ sender.sendMessage(ChatColor.GREEN + "Cleared grants of " + profile.getPlayer().getName() + "!");
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/grant/command/GrantCommand.java b/src/main/java/net/centilehcf/core/profile/grant/command/GrantCommand.java
new file mode 100644
index 0000000..3b400ad
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/grant/command/GrantCommand.java
@@ -0,0 +1,34 @@
+package net.centilehcf.core.profile.grant.command;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.network.packet.PacketAddGrant;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.profile.grant.event.GrantAppliedEvent;
+import net.centilehcf.core.profile.grant.menu.RankSelectionMenu;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.TimeUtil;
+import net.centilehcf.core.util.duration.Duration;
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+
+import java.util.Date;
+import java.util.UUID;
+
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "grant", async = true, permission = "rank.manager")
+public class GrantCommand {
+
+ public void execute(CommandSender sender, @CPL("player") Profile profile) {
+
+ if (profile == null || !profile.isLoaded()) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+ new RankSelectionMenu(profile).openMenu((Player) sender);
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/profile/grant/command/GrantsCommand.java b/src/main/java/net/centilehcf/core/profile/grant/command/GrantsCommand.java
new file mode 100644
index 0000000..0cd61de
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/grant/command/GrantsCommand.java
@@ -0,0 +1,21 @@
+package net.centilehcf.core.profile.grant.command;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.grant.menu.GrantsMenu;
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "grants", async = true, permission = "rank.manager")
+public class GrantsCommand {
+
+ public void execute(Player player, @CPL("player") Profile profile) {
+ if (profile == null || !profile.isLoaded()) {
+ player.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ new GrantsMenu(profile).openMenu(player);
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/profile/grant/command/SetRankCommand.java b/src/main/java/net/centilehcf/core/profile/grant/command/SetRankCommand.java
new file mode 100644
index 0000000..b515c0b
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/grant/command/SetRankCommand.java
@@ -0,0 +1,66 @@
+package net.centilehcf.core.profile.grant.command;
+
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.network.packet.PacketAddGrant;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.profile.grant.event.GrantAppliedEvent;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.TimeUtil;
+import net.centilehcf.core.util.duration.Duration;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+import java.util.Date;
+import java.util.UUID;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 7/20/2019.
+ */
+@CommandMeta(label = "setrank", async = true, permission = "rank.manager")
+public class SetRankCommand {
+
+ public void execute(CommandSender sender, @CPL("player") Profile profile, Rank rank, Duration duration, String reason) {
+ if (rank == null) {
+ sender.sendMessage(Locale.RANK_NOT_FOUND.format());
+ return;
+ }
+
+ if (profile == null || !profile.isLoaded()) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ if (duration.getValue() == -1) {
+ sender.sendMessage(CC.RED + "That duration is not valid.");
+ sender.sendMessage(CC.RED + "Example: [perm/1y1m1w1d]");
+ return;
+ }
+
+ UUID addedBy = sender instanceof Player ? ((Player) sender).getUniqueId() : null;
+ Grant grant = new Grant(UUID.randomUUID(), rank, addedBy, System.currentTimeMillis(), reason,
+ duration.getValue());
+
+ profile.getGrants().add(grant);
+ profile.save();
+ profile.activateNextGrant();
+
+ Core.get().getPidgin().sendPacket(new PacketAddGrant(profile.getUuid(), grant));
+
+ sender.sendMessage(CC.GREEN + "You applied a `{rank}` grant to `{player}` for {time-remaining}."
+ .replace("{rank}", rank.getDisplayName())
+ .replace("{player}", profile.getUsername())
+ .replace("{time-remaining}", duration.getValue() == Integer.MAX_VALUE ? "forever"
+ : TimeUtil.dateToString(new Date(System.currentTimeMillis() + duration.getValue()))));
+
+ Player player = profile.getPlayer();
+
+ if (player != null) {
+ new GrantAppliedEvent(player, grant).call();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/centilehcf/core/profile/grant/event/GrantAppliedEvent.java b/src/main/java/net/centilehcf/core/profile/grant/event/GrantAppliedEvent.java
new file mode 100644
index 0000000..7f2e3a6
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/grant/event/GrantAppliedEvent.java
@@ -0,0 +1,17 @@
+package net.centilehcf.core.profile.grant.event;
+
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.util.BaseEvent;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import net.centilehcf.core.util.BaseEvent;
+import org.bukkit.entity.Player;
+
+@AllArgsConstructor
+@Getter
+public class GrantAppliedEvent extends BaseEvent {
+
+ private Player player;
+ private Grant grant;
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/grant/event/GrantExpireEvent.java b/src/main/java/net/centilehcf/core/profile/grant/event/GrantExpireEvent.java
new file mode 100644
index 0000000..3097907
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/grant/event/GrantExpireEvent.java
@@ -0,0 +1,17 @@
+package net.centilehcf.core.profile.grant.event;
+
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.util.BaseEvent;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import net.centilehcf.core.util.BaseEvent;
+import org.bukkit.entity.Player;
+
+@AllArgsConstructor
+@Getter
+public class GrantExpireEvent extends BaseEvent {
+
+ private Player player;
+ private Grant grant;
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/grant/listener/GrantListener.java b/src/main/java/net/centilehcf/core/profile/grant/listener/GrantListener.java
new file mode 100644
index 0000000..8d6a438
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/grant/listener/GrantListener.java
@@ -0,0 +1,122 @@
+package net.centilehcf.core.profile.grant.listener;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.bootstrap.BootstrappedListener;
+import net.centilehcf.core.network.packet.PacketDeleteGrant;
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.profile.grant.event.GrantAppliedEvent;
+import net.centilehcf.core.profile.grant.event.GrantExpireEvent;
+import net.centilehcf.core.profile.grant.procedure.GrantProcedure;
+import net.centilehcf.core.profile.grant.procedure.GrantProcedureStage;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.TimeUtil;
+import net.centilehcf.core.util.callback.TypeCallback;
+import net.centilehcf.core.util.menu.menus.ConfirmMenu;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.player.AsyncPlayerChatEvent;
+
+import java.util.Date;
+
+public class GrantListener extends BootstrappedListener {
+
+ public GrantListener(Core core) {
+ super(core);
+ }
+
+ @EventHandler
+ public void onGrantAppliedEvent(GrantAppliedEvent event) {
+ Player player = event.getPlayer();
+ Grant grant = event.getGrant();
+
+ player.sendMessage(CC.GREEN + ("A `{rank}` grant has been applied to you for {time-remaining}.")
+ .replace("{rank}", grant.getRank().getDisplayName())
+ .replace("{time-remaining}", grant.getDuration() == Integer.MAX_VALUE ?
+ "forever" : TimeUtil.millisToRoundedTime(
+ System.currentTimeMillis() - (grant.getAddedAt() + grant.getDuration()))));
+ }
+
+ @EventHandler
+ public void onGrantExpireEvent(GrantExpireEvent event) {
+ Player player = event.getPlayer();
+ Grant grant = event.getGrant();
+
+ player.sendMessage(CC.RED + ("Your `{rank}` grant has expired.")
+ .replace("{rank}", grant.getRank().getDisplayName())
+ .replace("", ""));
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST)
+ public void onAsyncPlayerChatEvent(AsyncPlayerChatEvent event) {
+ if (!event.getPlayer().hasPermission("rank.manager")) {
+ return;
+ }
+
+ GrantProcedure procedure = GrantProcedure.getByPlayer(event.getPlayer());
+
+ if (procedure != null && procedure.getStage() == GrantProcedureStage.REQUIRE_TEXT) {
+ event.setCancelled(true);
+
+ if (event.getMessage().equalsIgnoreCase("cancel")) {
+ GrantProcedure.getProcedures().remove(procedure);
+ event.getPlayer().sendMessage(CC.RED + "You have cancelled the grant procedure.");
+ return;
+ }
+
+ switch (procedure.getType()) {
+ // TODO: 5/28/2019 Finish
+ /*case GRANT: {
+
+ new ConfirmMenu(CC.YELLOW + "Add this grant?", (TypeCallback) data -> {
+ if (data) {
+ procedure.finish();
+ event.getPlayer().sendMessage(CC.GREEN + "You applied a `{rank}` grant to `{player}` for {time-remaining}."
+ .replace("{rank}", procedure.getGrant().getRank().getDisplayName())
+ .replace("{player}", procedure.getRecipient().getUsername())
+ .replace("{time-remaining}", procedure.getGrant().getDuration() == Integer.MAX_VALUE ? "forever"
+ : TimeUtil.dateToString(new Date(System.currentTimeMillis() + procedure.getGrant().getDuration()))));
+ } else {
+ procedure.cancel();
+ event.getPlayer().sendMessage(CC.RED + "You have cancelled the grant procedure.");
+ }
+ }, true) {
+ @Override
+ public void onClose(Player player) {
+ if (!isClosedByMenu()) {
+ procedure.cancel();
+ event.getPlayer().sendMessage(CC.RED + "You have cancelled the grant procedure.");
+ }
+ }
+ }.openMenu(event.getPlayer());
+ }*/
+ case REMOVE: {
+ new ConfirmMenu(CC.YELLOW + "Delete this grant?", (TypeCallback) data -> {
+ if (data) {
+ procedure.getGrant().setRemovedBy(event.getPlayer().getUniqueId());
+ procedure.getGrant().setRemovedAt(System.currentTimeMillis());
+ procedure.getGrant().setRemovedReason(event.getMessage());
+ procedure.getGrant().setRemoved(true);
+ procedure.finish();
+ event.getPlayer().sendMessage(CC.GREEN + "The grant has been removed.");
+
+ Core.get().getPidgin().sendPacket(new PacketDeleteGrant(procedure.getRecipient().getUuid(), procedure.getGrant()));
+ } else {
+ procedure.cancel();
+ event.getPlayer().sendMessage(CC.RED + "You did not confirm to remove the grant.");
+ }
+ }, true) {
+ @Override
+ public void onClose(Player player) {
+ if (!isClosedByMenu()) {
+ procedure.cancel();
+ event.getPlayer().sendMessage(CC.RED + "You did not confirm to remove the grant.");
+ }
+ }
+ }.openMenu(event.getPlayer());
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/grant/menu/GrantsMenu.java b/src/main/java/net/centilehcf/core/profile/grant/menu/GrantsMenu.java
new file mode 100644
index 0000000..8263262
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/grant/menu/GrantsMenu.java
@@ -0,0 +1,123 @@
+package net.centilehcf.core.profile.grant.menu;
+
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.grant.procedure.GrantProcedure;
+import net.centilehcf.core.profile.grant.procedure.GrantProcedureStage;
+import net.centilehcf.core.profile.grant.procedure.GrantProcedureType;
+import net.centilehcf.core.util.ItemBuilder;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.pagination.PaginatedMenu;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.AllArgsConstructor;
+import net.centilehcf.core.profile.grant.procedure.GrantProcedure;
+import net.centilehcf.core.profile.grant.procedure.GrantProcedureStage;
+import net.centilehcf.core.profile.grant.procedure.GrantProcedureType;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.ItemBuilder;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+@AllArgsConstructor
+public class GrantsMenu extends PaginatedMenu {
+
+ private Profile profile;
+
+ @Override
+ public String getPrePaginatedTitle(Player player) {
+ return "&6Grants of " + profile.getUsername();
+ }
+
+ @Override
+ public Map getAllPagesButtons(Player player) {
+ Map buttons = new HashMap<>();
+
+ for (Grant grant : profile.getGrants()) {
+ buttons.put(buttons.size(), new GrantInfoButton(profile, grant));
+ }
+
+ return buttons;
+ }
+
+ @AllArgsConstructor
+ private class GrantInfoButton extends Button {
+
+ private Profile profile;
+ private Grant grant;
+
+ @Override
+ public ItemStack getButtonItem(Player player) {
+ String addedBy = "Console";
+
+ if (grant.getAddedBy() != null) {
+ addedBy = "Could not fetch...";
+
+ Profile addedByProfile = Profile.getByUuid(grant.getAddedBy());
+
+ if (addedByProfile != null && addedByProfile.isLoaded()) {
+ addedBy = addedByProfile.getUsername();
+ }
+ }
+
+ List lore = new ArrayList<>();
+
+ lore.add(CC.MENU_BAR);
+ lore.add("&eAdded by: &c" + addedBy);
+ lore.add("&eAdded for: &c" + grant.getAddedReason());
+ lore.add("&eAdded at: &c" + grant.getAddedAtDate());
+
+ if (!grant.isRemoved()) {
+ if (!grant.hasExpired()) {
+ lore.add("&eExpires at: &c" + grant.getExpiresAtDate());
+ }
+ } else {
+ String removedBy = "Console";
+
+ if (grant.getRemovedBy() != null) {
+ removedBy = "Could not fetch...";
+
+ Profile removedByProfile = Profile.getByUuid(grant.getRemovedBy());
+
+ if (removedByProfile != null && removedByProfile.isLoaded()) {
+ removedBy = removedByProfile.getUsername();
+ }
+ }
+
+ lore.add(CC.MENU_BAR);
+ lore.add("&eRemoved by: &c" + removedBy);
+ lore.add("&eRemoved for: &c" + grant.getRemovedReason());
+ lore.add("&eRemoved at: &c" + grant.getRemovedAtDate());
+ }
+
+ lore.add(CC.MENU_BAR);
+
+ if (!grant.isRemoved()) {
+ lore.add("&eRight click to remove this grant");
+ lore.add(CC.MENU_BAR);
+ }
+
+ return new ItemBuilder(Material.PAPER)
+ .name(grant.getRank().getColor() + grant.getRank().getDisplayName())
+ .lore(lore)
+ .build();
+ }
+
+ @Override
+ public void clicked(Player player, ClickType clickType) {
+ if (clickType == ClickType.RIGHT && !grant.isRemoved()) {
+ GrantProcedure procedure = new GrantProcedure(player, profile, GrantProcedureType.REMOVE, GrantProcedureStage.REQUIRE_TEXT);
+ procedure.setGrant(grant);
+
+ player.sendMessage(CC.GREEN + "Type a reason for removing this grant in chat...");
+ player.closeInventory();
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/grant/menu/RankSelectionMenu.java b/src/main/java/net/centilehcf/core/profile/grant/menu/RankSelectionMenu.java
new file mode 100644
index 0000000..5e557d6
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/grant/menu/RankSelectionMenu.java
@@ -0,0 +1,100 @@
+package net.centilehcf.core.profile.grant.menu;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.network.packet.PacketAddGrant;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.profile.grant.event.GrantAppliedEvent;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.rank.comparator.RankComparator;
+import net.centilehcf.core.util.BukkitUtils;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.ItemBuilder;
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.menus.ConfirmMenu;
+import net.centilehcf.core.util.menu.pagination.PaginatedMenu;
+import org.bukkit.ChatColor;
+import org.bukkit.DyeColor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 7/20/2019.
+ */
+public class RankSelectionMenu extends PaginatedMenu
+{
+ private Profile profile;
+
+ @Override
+ public String getPrePaginatedTitle(Player player) {
+ return CC.YELLOW + CC.BOLD + "Chose a rank.";
+ }
+
+ @Override
+ public Map getAllPagesButtons(Player player) {
+ Map buttons = new HashMap<>();
+
+ Rank.getRanks().values().stream().sorted(new RankComparator()).collect(Collectors.toList()).forEach(rank -> buttons.put(buttons.size(), new RankDisplayButton(rank, this.profile)));
+ return buttons;
+ }
+
+ public RankSelectionMenu(Profile profile) {
+ this.profile = profile;
+ }
+
+ private static class RankDisplayButton extends Button
+ {
+ private Rank rank;
+ private Profile targetData;
+
+ @Override
+ public ItemStack getButtonItem(Player player) {
+ List lore = new ArrayList<>();
+ lore.add(CC.SB_BAR);
+ lore.add(ChatColor.BLUE + "Click to grant " + this.targetData.getPlayer().getName() + ChatColor.BLUE + " the " + this.rank.getDisplayName() + ChatColor.BLUE + " rank.");
+ lore.add(CC.SB_BAR);
+ ChatColor chatColor = ChatColor.getByChar(this.rank.getColor().getChar());
+ DyeColor color = BukkitUtils.toDyeColor(chatColor);
+ short dura = (color == null) ? 0 : ((short)color.getWoolData());
+ return new ItemBuilder(Material.WOOL).name(ChatColor.GOLD + this.rank.getDisplayName()).durability(dura).lore(lore).build();
+ }
+
+ @Override
+ public void clicked(Player player, int slot, ClickType clickType, int hotbarSlot) {
+ player.closeInventory();
+ Button button = new Button() {
+ @Override
+ public ItemStack getButtonItem(Player player) {
+ return new ItemBuilder(Material.PAPER).name(CC.YELLOW + "Are you sure you want to grant " + RankDisplayButton.this.targetData.getPlayer().getName() + " " + RankDisplayButton.this.rank.getDisplayName() + "?").lore(Arrays.asList(CC.SB_BAR, CC.YELLOW + "Rank: " + RankDisplayButton.this.rank.getDisplayName(), CC.SB_BAR, CC.YELLOW + "Are you sure you want", CC.YELLOW + "grant " + RankDisplayButton.this.rank.getDisplayName(), CC.YELLOW + "to " + RankDisplayButton.this.targetData.getPlayer().getName() + "?", CC.SB_BAR)).build();
+ }
+ };
+ Button[] middleButtons = { button, button, button };
+ final Grant[] grant = new Grant[1];
+ new ConfirmMenu("Confirm grant?", data -> {
+ if (data) {
+ player.sendMessage(CC.GREEN + "You have updated " + this.targetData.getPlayer().getName() + CC.GREEN + " rank to: " + this.rank.getDisplayName());
+ grant[0] = new Grant(UUID.randomUUID(), this.rank, player.getUniqueId(), System.currentTimeMillis(), "Granted", Integer.MAX_VALUE);
+ this.targetData.getGrants().add(grant[0]);
+ this.targetData.save();
+ this.targetData.activateNextGrant();
+ Core.get().getPidgin().sendPacket(new PacketAddGrant(this.targetData.getUuid(), grant[0]));
+ new GrantAppliedEvent(player, grant[0]).call();
+ }
+ else {
+ player.sendMessage(CC.RED + "Cancelled the grant procedure for " + this.targetData.getPlayer().getName() + '.');
+ }
+ }, true, middleButtons).openMenu(player);
+ }
+
+ public RankDisplayButton(Rank rank, Profile targetData) {
+ this.rank = rank;
+ this.targetData = targetData;
+ }
+ }
+}
+
diff --git a/src/main/java/net/centilehcf/core/profile/grant/procedure/GrantProcedure.java b/src/main/java/net/centilehcf/core/profile/grant/procedure/GrantProcedure.java
new file mode 100644
index 0000000..763ac54
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/grant/procedure/GrantProcedure.java
@@ -0,0 +1,49 @@
+package net.centilehcf.core.profile.grant.procedure;
+
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.grant.Grant;
+import java.util.HashSet;
+import java.util.Set;
+import lombok.Getter;
+import lombok.Setter;
+import org.bukkit.entity.Player;
+
+public class GrantProcedure {
+
+ @Getter private static final Set procedures = new HashSet<>();
+
+ @Getter private final Player issuer;
+ @Getter private final Profile recipient;
+ @Getter private final GrantProcedureType type;
+ @Getter private GrantProcedureStage stage;
+ @Getter @Setter private Grant grant;
+
+ public GrantProcedure(Player issuer, Profile recipient, GrantProcedureType type, GrantProcedureStage stage) {
+ this.issuer = issuer;
+ this.recipient = recipient;
+ this.type = type;
+ this.stage = stage;
+
+ procedures.add(this);
+ }
+
+ public void finish() {
+ this.recipient.save();
+ procedures.remove(this);
+ }
+
+ public void cancel() {
+ procedures.remove(this);
+ }
+
+ public static GrantProcedure getByPlayer(Player player) {
+ for (GrantProcedure procedure : procedures) {
+ if (procedure.issuer.equals(player)) {
+ return procedure;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/grant/procedure/GrantProcedureStage.java b/src/main/java/net/centilehcf/core/profile/grant/procedure/GrantProcedureStage.java
new file mode 100644
index 0000000..b41f184
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/grant/procedure/GrantProcedureStage.java
@@ -0,0 +1,9 @@
+package net.centilehcf.core.profile.grant.procedure;
+
+public enum GrantProcedureStage {
+
+ REQUIRE_CLICK,
+ REQUIRE_TEXT,
+ REQUIRE_CONFIRMATION,
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/grant/procedure/GrantProcedureType.java b/src/main/java/net/centilehcf/core/profile/grant/procedure/GrantProcedureType.java
new file mode 100644
index 0000000..d182489
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/grant/procedure/GrantProcedureType.java
@@ -0,0 +1,8 @@
+package net.centilehcf.core.profile.grant.procedure;
+
+public enum GrantProcedureType {
+
+ GRANT,
+ REMOVE
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/option/ProfileOptions.java b/src/main/java/net/centilehcf/core/profile/option/ProfileOptions.java
new file mode 100644
index 0000000..4b77895
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/option/ProfileOptions.java
@@ -0,0 +1,12 @@
+package net.centilehcf.core.profile.option;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class ProfileOptions {
+
+ @Getter @Setter private boolean publicChatEnabled = true;
+ @Getter @Setter private boolean privateChatEnabled = true;
+ @Getter @Setter private boolean privateChatSoundsEnabled = true;
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/option/ProfileStaffOptions.java b/src/main/java/net/centilehcf/core/profile/option/ProfileStaffOptions.java
new file mode 100644
index 0000000..bc711cf
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/option/ProfileStaffOptions.java
@@ -0,0 +1,10 @@
+package net.centilehcf.core.profile.option;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class ProfileStaffOptions {
+
+ @Getter @Setter private boolean staffChatEnabled = true;
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/option/commands/TogglePrivateMessagesCommand.java b/src/main/java/net/centilehcf/core/profile/option/commands/TogglePrivateMessagesCommand.java
new file mode 100644
index 0000000..3d1ff19
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/option/commands/TogglePrivateMessagesCommand.java
@@ -0,0 +1,20 @@
+package net.centilehcf.core.profile.option.commands;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.util.CC;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 5/28/2019.
+ */
+@CommandMeta(label = {"togglepm", "togglepms", "tpm", "tpms"})
+public class TogglePrivateMessagesCommand {
+ public void execute(Player player) {
+ Profile profile = Profile.getProfiles().get(player.getUniqueId());
+ profile.getOptions().setPrivateChatEnabled(!profile.getOptions().isPrivateChatEnabled());
+
+ player.sendMessage(profile.getOptions().isPrivateChatEnabled() ? CC.GREEN + "You have enabled private messages." : CC.RED + "You have disabled private messages.");
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/profile/option/event/OptionsOpenedEvent.java b/src/main/java/net/centilehcf/core/profile/option/event/OptionsOpenedEvent.java
new file mode 100644
index 0000000..84653fe
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/option/event/OptionsOpenedEvent.java
@@ -0,0 +1,20 @@
+package net.centilehcf.core.profile.option.event;
+
+import net.centilehcf.core.profile.option.menu.ProfileOptionButton;
+import net.centilehcf.core.util.BaseEvent;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import net.centilehcf.core.profile.option.menu.ProfileOptionButton;
+import net.centilehcf.core.util.BaseEvent;
+import org.bukkit.entity.Player;
+
+@RequiredArgsConstructor
+@Getter
+public class OptionsOpenedEvent extends BaseEvent {
+
+ private final Player player;
+ private List buttons = new ArrayList<>();
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/option/menu/ProfileOptionButton.java b/src/main/java/net/centilehcf/core/profile/option/menu/ProfileOptionButton.java
new file mode 100644
index 0000000..4046a2c
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/option/menu/ProfileOptionButton.java
@@ -0,0 +1,57 @@
+package net.centilehcf.core.profile.option.menu;
+
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.ItemBuilder;
+import net.centilehcf.core.util.TextSplitter;
+import net.centilehcf.core.util.menu.Button;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.AllArgsConstructor;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.ItemBuilder;
+import org.apache.commons.lang.StringEscapeUtils;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+@AllArgsConstructor
+public abstract class ProfileOptionButton extends Button {
+
+ @Override
+ public ItemStack getButtonItem(Player player) {
+ ItemBuilder itemBuilder = new ItemBuilder(isEnabled(player) ? getEnabledItem(player) : getDisabledItem(player));
+
+ List lore = new ArrayList<>();
+ lore.add("");
+ lore.addAll(TextSplitter.split(40, getDescription(), CC.GRAY));
+ lore.add("");
+ lore.add((isEnabled(player) ? CC.BLUE + StringEscapeUtils.unescapeJava(" » ") : " ") + "&e" + getEnabledOption());
+ lore.add((!isEnabled(player) ? CC.BLUE + StringEscapeUtils.unescapeJava(" » ") : " ") + "&e" + getDisabledOption());
+ lore.add("");
+ lore.add("&eClick to toggle this option.");
+
+ return itemBuilder.name(getOptionName())
+ .lore(lore)
+ .build();
+ }
+
+ public abstract ItemStack getEnabledItem(Player player);
+
+ public abstract ItemStack getDisabledItem(Player player);
+
+ public abstract String getOptionName();
+
+ public abstract String getDescription();
+
+ public abstract String getEnabledOption();
+
+ public abstract String getDisabledOption();
+
+ public abstract boolean isEnabled(Player player);
+
+ @Override
+ public boolean shouldUpdate(Player player, ClickType clickType) {
+ return true;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/option/menu/ProfileOptionsMenu.java b/src/main/java/net/centilehcf/core/profile/option/menu/ProfileOptionsMenu.java
new file mode 100644
index 0000000..7132113
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/option/menu/ProfileOptionsMenu.java
@@ -0,0 +1,41 @@
+package net.centilehcf.core.profile.option.menu;
+
+import net.centilehcf.core.profile.option.event.OptionsOpenedEvent;
+import net.centilehcf.core.profile.option.menu.button.PrivateChatOptionButton;
+import net.centilehcf.core.profile.option.menu.button.PrivateChatSoundsOptionButton;
+import net.centilehcf.core.profile.option.menu.button.PublicChatOptionButton;
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.Menu;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.centilehcf.core.util.menu.Menu;
+import org.bukkit.entity.Player;
+
+public class ProfileOptionsMenu extends Menu {
+
+ @Override
+ public String getTitle(Player player) {
+ return "&6&lOptions";
+ }
+
+ @Override
+ public Map getButtons(Player player) {
+ Map buttons = new HashMap<>();
+ buttons.put(buttons.size(), new PublicChatOptionButton());
+ buttons.put(buttons.size(), new PrivateChatOptionButton());
+ buttons.put(buttons.size(), new PrivateChatSoundsOptionButton());
+
+ OptionsOpenedEvent event = new OptionsOpenedEvent(player);
+ event.call();
+
+ if (!event.getButtons().isEmpty()) {
+ for (ProfileOptionButton button : event.getButtons()) {
+ buttons.put(buttons.size(), button);
+ }
+ }
+
+ return buttons;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/option/menu/button/PrivateChatOptionButton.java b/src/main/java/net/centilehcf/core/profile/option/menu/button/PrivateChatOptionButton.java
new file mode 100644
index 0000000..5755a76
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/option/menu/button/PrivateChatOptionButton.java
@@ -0,0 +1,56 @@
+package net.centilehcf.core.profile.option.menu.button;
+
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.option.menu.ProfileOptionButton;
+import net.centilehcf.core.util.ItemBuilder;
+import net.centilehcf.core.profile.option.menu.ProfileOptionButton;
+import net.centilehcf.core.util.ItemBuilder;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+public class PrivateChatOptionButton extends ProfileOptionButton {
+
+ @Override
+ public String getOptionName() {
+ return "&c&lPrivate Chat";
+ }
+
+ @Override
+ public ItemStack getEnabledItem(Player player) {
+ return new ItemBuilder(Material.NAME_TAG).build();
+ }
+
+ @Override
+ public ItemStack getDisabledItem(Player player) {
+ return new ItemBuilder(Material.NAME_TAG).build();
+ }
+
+ @Override
+ public String getDescription() {
+ return "If enabled, you will receive private chat messages.";
+ }
+
+ @Override
+ public String getEnabledOption() {
+ return "Receive private chat messages";
+ }
+
+ @Override
+ public String getDisabledOption() {
+ return "Do not receive private chat messages";
+ }
+
+ @Override
+ public boolean isEnabled(Player player) {
+ return Profile.getProfiles().get(player.getUniqueId()).getOptions().isPrivateChatEnabled();
+ }
+
+ @Override
+ public void clicked(Player player, ClickType clickType) {
+ Profile profile = Profile.getProfiles().get(player.getUniqueId());
+ profile.getOptions().setPrivateChatEnabled(!profile.getOptions().isPrivateChatEnabled());
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/option/menu/button/PrivateChatSoundsOptionButton.java b/src/main/java/net/centilehcf/core/profile/option/menu/button/PrivateChatSoundsOptionButton.java
new file mode 100644
index 0000000..a9f9e0b
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/option/menu/button/PrivateChatSoundsOptionButton.java
@@ -0,0 +1,56 @@
+package net.centilehcf.core.profile.option.menu.button;
+
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.option.menu.ProfileOptionButton;
+import net.centilehcf.core.util.ItemBuilder;
+import net.centilehcf.core.profile.option.menu.ProfileOptionButton;
+import net.centilehcf.core.util.ItemBuilder;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+public class PrivateChatSoundsOptionButton extends ProfileOptionButton {
+
+ @Override
+ public String getOptionName() {
+ return "&e&lPrivate Chat Sounds";
+ }
+
+ @Override
+ public ItemStack getEnabledItem(Player player) {
+ return new ItemBuilder(Material.NOTE_BLOCK).build();
+ }
+
+ @Override
+ public ItemStack getDisabledItem(Player player) {
+ return new ItemBuilder(Material.NOTE_BLOCK).build();
+ }
+
+ @Override
+ public String getDescription() {
+ return "If enabled, a sound will be played when you receive a private chat message.";
+ }
+
+ @Override
+ public String getEnabledOption() {
+ return "Play private chat message sounds";
+ }
+
+ @Override
+ public String getDisabledOption() {
+ return "Do not play private chat message sounds";
+ }
+
+ @Override
+ public boolean isEnabled(Player player) {
+ return Profile.getProfiles().get(player.getUniqueId()).getOptions().isPrivateChatSoundsEnabled();
+ }
+
+ @Override
+ public void clicked(Player player, ClickType clickType) {
+ Profile profile = Profile.getProfiles().get(player.getUniqueId());
+ profile.getOptions().setPrivateChatSoundsEnabled(!profile.getOptions().isPrivateChatSoundsEnabled());
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/option/menu/button/PublicChatOptionButton.java b/src/main/java/net/centilehcf/core/profile/option/menu/button/PublicChatOptionButton.java
new file mode 100644
index 0000000..50e9127
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/option/menu/button/PublicChatOptionButton.java
@@ -0,0 +1,56 @@
+package net.centilehcf.core.profile.option.menu.button;
+
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.option.menu.ProfileOptionButton;
+import net.centilehcf.core.util.ItemBuilder;
+import net.centilehcf.core.profile.option.menu.ProfileOptionButton;
+import net.centilehcf.core.util.ItemBuilder;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+public class PublicChatOptionButton extends ProfileOptionButton {
+
+ @Override
+ public String getOptionName() {
+ return "&a&lPublic Chat";
+ }
+
+ @Override
+ public ItemStack getEnabledItem(Player player) {
+ return new ItemBuilder(Material.BOOK_AND_QUILL).build();
+ }
+
+ @Override
+ public ItemStack getDisabledItem(Player player) {
+ return new ItemBuilder(Material.BOOK_AND_QUILL).build();
+ }
+
+ @Override
+ public String getDescription() {
+ return "If enabled, you will receive public chat messages.";
+ }
+
+ @Override
+ public String getEnabledOption() {
+ return "Receive public chat messages";
+ }
+
+ @Override
+ public String getDisabledOption() {
+ return "Do not receive public chat messages";
+ }
+
+ @Override
+ public boolean isEnabled(Player player) {
+ return Profile.getProfiles().get(player.getUniqueId()).getOptions().isPublicChatEnabled();
+ }
+
+ @Override
+ public void clicked(Player player, ClickType clickType) {
+ Profile profile = Profile.getProfiles().get(player.getUniqueId());
+ profile.getOptions().setPublicChatEnabled(!profile.getOptions().isPublicChatEnabled());
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/Punishment.java b/src/main/java/net/centilehcf/core/profile/punishment/Punishment.java
new file mode 100644
index 0000000..3309025
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/Punishment.java
@@ -0,0 +1,142 @@
+package net.centilehcf.core.profile.punishment;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.TimeUtil;
+
+import java.util.UUID;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.bukkit.Bukkit;
+
+public class Punishment {
+
+ public static PunishmentJsonSerializer SERIALIZER = new PunishmentJsonSerializer();
+ public static PunishmentJsonDeserializer DESERIALIZER = new PunishmentJsonDeserializer();
+
+ @Getter
+ private final UUID uuid;
+ @Getter
+ private final PunishmentType type;
+ @Getter
+ @Setter
+ private UUID addedBy;
+ @Getter
+ final private long addedAt;
+ @Getter
+ private final String addedReason;
+ @Getter
+ final private long duration;
+ @Getter
+ @Setter
+ private UUID pardonedBy;
+ @Getter
+ @Setter
+ private long pardonedAt;
+ @Getter
+ @Setter
+ private String pardonedReason;
+ @Getter
+ @Setter
+ private boolean pardoned;
+
+ public Punishment(UUID uuid, PunishmentType type, long addedAt, String addedReason, long duration) {
+ this.uuid = uuid;
+ this.type = type;
+ this.addedAt = addedAt;
+ this.addedReason = addedReason;
+ this.duration = duration;
+ }
+
+ public boolean isPermanent() {
+ return type == PunishmentType.BLACKLIST || duration == Integer.MAX_VALUE;
+ }
+
+ public boolean isActive() {
+ return !pardoned && (isPermanent() || getMillisRemaining() < 0);
+ }
+
+ public long getMillisRemaining() {
+ return System.currentTimeMillis() - (addedAt + duration);
+ }
+
+ public String getTimeRemaining() {
+ if (pardoned) {
+ return "Pardoned";
+ }
+
+ if (isPermanent()) {
+ return "Permanent";
+ }
+
+ if (!isActive()) {
+ return "Expired";
+ }
+
+ return TimeUtil.millisToRoundedTime((addedAt + duration) - System.currentTimeMillis());
+ }
+
+ public String getContext() {
+ if (!(type == PunishmentType.BAN || type == PunishmentType.MUTE)) {
+ return pardoned ? type.getUndoContext() : type.getContext();
+ }
+
+ if (isPermanent()) {
+ return (pardoned ? type.getUndoContext() : "permanently " + type.getContext());
+ } else {
+ return (pardoned ? type.getUndoContext() : "temporarily " + type.getContext());
+ }
+ }
+
+ public void broadcast(String sender, String target, boolean silent) {
+ if (silent) {
+ Bukkit.getOnlinePlayers().forEach(player -> {
+ if (player.hasPermission("core.staff")) {
+ player.sendMessage(Core.get().getMainConfig().getString("PUNISHMENTS.BROADCAST_SILENT")
+ .replace("{context}", getContext())
+ .replace("{target}", target)
+ .replace("{sender}", sender));
+ }
+ });
+ } else {
+ Bukkit.broadcastMessage(Core.get().getMainConfig().getString("PUNISHMENTS.BROADCAST")
+ .replace("{context}", getContext())
+ .replace("{target}", target)
+ .replace("{sender}", sender));
+ }
+ }
+
+ public String getKickMessage() {
+ String kickMessage;
+
+ if (type == PunishmentType.BAN) {
+ kickMessage = Core.get().getMainConfig().getString("PUNISHMENTS.BAN.KICK");
+ String temporary = "";
+
+ if (!isPermanent()) {
+ temporary = Core.get().getMainConfig().getString("PUNISHMENTS.BAN.TEMPORARY");
+ temporary = temporary.replace("{time-remaining}", getTimeRemaining());
+ }
+
+ kickMessage = kickMessage.replace("{context}", getContext())
+ .replace("{temporary}", temporary);
+ } else if (type == PunishmentType.KICK) {
+ kickMessage = Core.get().getMainConfig().getString("PUNISHMENTS.KICK.KICK")
+ .replace("{context}", getContext())
+ .replace("{reason}", addedReason);
+ } else if (type == PunishmentType.BLACKLIST) {
+ kickMessage = Core.get().getMainConfig().getString("PUNISHMENTS.BLACKLIST");
+ } else {
+ kickMessage = null;
+ }
+
+ return CC.translate(kickMessage);
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ return object != null && object instanceof Punishment && ((Punishment) object).uuid.equals(uuid);
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/PunishmentJsonDeserializer.java b/src/main/java/net/centilehcf/core/profile/punishment/PunishmentJsonDeserializer.java
new file mode 100644
index 0000000..60a247f
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/PunishmentJsonDeserializer.java
@@ -0,0 +1,42 @@
+package net.centilehcf.core.profile.punishment;
+
+import com.google.gson.JsonObject;
+import net.centilehcf.core.util.json.JsonDeserializer;
+import java.util.UUID;
+
+public class PunishmentJsonDeserializer implements JsonDeserializer {
+
+ @Override
+ public Punishment deserialize(JsonObject object) {
+ Punishment punishment = new Punishment(
+ UUID.fromString(object.get("uuid").getAsString()),
+ PunishmentType.valueOf(object.get("type").getAsString()),
+ object.get("addedAt").getAsLong(),
+ object.get("addedReason").getAsString(),
+ object.get("duration").getAsLong()
+ );
+
+ if (!object.get("addedBy").isJsonNull()) {
+ punishment.setAddedBy(UUID.fromString(object.get("addedBy").getAsString()));
+ }
+
+ if (!object.get("pardonedBy").isJsonNull()) {
+ punishment.setPardonedBy(UUID.fromString(object.get("pardonedBy").getAsString()));
+ }
+
+ if (!object.get("pardonedAt").isJsonNull()) {
+ punishment.setPardonedAt(object.get("pardonedAt").getAsLong());
+ }
+
+ if (!object.get("pardonedReason").isJsonNull()) {
+ punishment.setPardonedReason(object.get("pardonedReason").getAsString());
+ }
+
+ if (!object.get("pardoned").isJsonNull()) {
+ punishment.setPardoned(object.get("pardoned").getAsBoolean());
+ }
+
+ return punishment;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/PunishmentJsonSerializer.java b/src/main/java/net/centilehcf/core/profile/punishment/PunishmentJsonSerializer.java
new file mode 100644
index 0000000..69bbf4f
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/PunishmentJsonSerializer.java
@@ -0,0 +1,24 @@
+package net.centilehcf.core.profile.punishment;
+
+import com.google.gson.JsonObject;
+import net.centilehcf.core.util.json.JsonSerializer;
+
+public class PunishmentJsonSerializer implements JsonSerializer {
+
+ @Override
+ public JsonObject serialize(Punishment punishment) {
+ JsonObject object = new JsonObject();
+ object.addProperty("uuid", punishment.getUuid().toString());
+ object.addProperty("type", punishment.getType().name());
+ object.addProperty("addedBy", punishment.getAddedBy() == null ? null : punishment.getAddedBy().toString());
+ object.addProperty("addedAt", punishment.getAddedAt());
+ object.addProperty("addedReason", punishment.getAddedReason());
+ object.addProperty("duration", punishment.getDuration());
+ object.addProperty("pardonedBy", punishment.getPardonedBy() == null ? null : punishment.getPardonedBy().toString());
+ object.addProperty("pardonedAt", punishment.getPardonedAt());
+ object.addProperty("pardonedReason", punishment.getPardonedReason());
+ object.addProperty("pardoned", punishment.isPardoned());
+ return object;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/PunishmentType.java b/src/main/java/net/centilehcf/core/profile/punishment/PunishmentType.java
new file mode 100644
index 0000000..eb918b9
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/PunishmentType.java
@@ -0,0 +1,34 @@
+package net.centilehcf.core.profile.punishment;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import net.minecraft.util.org.apache.commons.lang3.StringUtils;
+import org.bukkit.ChatColor;
+
+@Getter
+@AllArgsConstructor
+public enum PunishmentType {
+
+ BLACKLIST("blacklisted", "unblacklisted", true, true, new PunishmentTypeData("Blacklists", ChatColor.DARK_RED, 14)),
+ BAN("banned", "unbanned", true, true, new PunishmentTypeData("Bans", ChatColor.GOLD, 1)),
+ MUTE("muted", "unmuted", false, true, new PunishmentTypeData("Mutes", ChatColor.YELLOW, 4)),
+ WARN("warned", null, false, false, new PunishmentTypeData("Warnings", ChatColor.GREEN, 13)),
+ KICK("kicked", null, false, false, new PunishmentTypeData("Kicks", ChatColor.GRAY, 7));
+
+ private String context;
+ private String undoContext;
+ private boolean ban;
+ private boolean canBePardoned;
+ private PunishmentTypeData typeData;
+
+ @AllArgsConstructor
+ @Getter
+ public static class PunishmentTypeData {
+
+ private String readable;
+ private ChatColor color;
+ private int durability;
+
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/command/BanCommand.java b/src/main/java/net/centilehcf/core/profile/punishment/command/BanCommand.java
new file mode 100644
index 0000000..01a43ed
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/command/BanCommand.java
@@ -0,0 +1,66 @@
+package net.centilehcf.core.profile.punishment.command;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.network.packet.PacketBroadcastPunishment;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.punishment.Punishment;
+import net.centilehcf.core.profile.punishment.PunishmentType;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.duration.Duration;
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import com.qrakn.honcho.command.CommandOption;
+import java.util.UUID;
+
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+
+@CommandMeta(label = "ban", permission = "rank.trialmod", async = true, options = "s")
+public class BanCommand {
+
+ public void execute(CommandSender sender, CommandOption option, @CPL("player") Profile profile, Duration duration, String reason) {
+ if (profile == null || !profile.isLoaded()) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ if (profile.getActiveBan() != null) {
+ sender.sendMessage(CC.RED + "That player is already banned.");
+ return;
+ }
+
+ if (duration.getValue() == -1) {
+ sender.sendMessage(CC.RED + "That duration is not valid.");
+ sender.sendMessage(CC.RED + "Example: [perm/1y1m1w1d]");
+ return;
+ }
+
+ String staffName = sender instanceof Player ? Profile.getProfiles().get(((Player) sender)
+ .getUniqueId()).getColoredUsername() : CC.DARK_RED + "Console";
+
+ Punishment punishment = new Punishment(UUID.randomUUID(), PunishmentType.BAN, System.currentTimeMillis(),
+ reason, duration.getValue());
+
+ if (sender instanceof Player) {
+ punishment.setAddedBy(((Player) sender).getUniqueId());
+ }
+
+ profile.getPunishments().add(punishment);
+ profile.save();
+
+ Core.get().getPidgin().sendPacket(new PacketBroadcastPunishment(punishment, staffName, profile.getColoredUsername(), profile.getUuid(), option != null));
+
+ Player player = profile.getPlayer();
+
+ if (player != null) {
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ player.kickPlayer(punishment.getKickMessage());
+ }
+ }.runTask(Core.get());
+ }
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/command/BlacklistCommand.java b/src/main/java/net/centilehcf/core/profile/punishment/command/BlacklistCommand.java
new file mode 100644
index 0000000..b4078ff
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/command/BlacklistCommand.java
@@ -0,0 +1,63 @@
+package net.centilehcf.core.profile.punishment.command;
+
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import com.qrakn.honcho.command.CommandOption;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.network.packet.PacketBroadcastPunishment;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.punishment.Punishment;
+import net.centilehcf.core.profile.punishment.PunishmentType;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.duration.Duration;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.util.UUID;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/21/2019.
+ */
+@CommandMeta(label = "blacklist", permission = "rank.owner", async = true, options = "s")
+public class BlacklistCommand {
+
+ public void execute(CommandSender sender, CommandOption option, @CPL("player") Profile profile, String reason) {
+ if (profile == null || !profile.isLoaded()) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ if (profile.getActiveBlacklist() != null) {
+ sender.sendMessage(CC.RED + "That player is already blacklisted!");
+ return;
+ }
+
+ String staffName = sender instanceof Player ? Profile.getProfiles().get(((Player) sender)
+ .getUniqueId()).getColoredUsername() : CC.DARK_RED + "Console";
+
+ Punishment punishment = new Punishment(UUID.randomUUID(), PunishmentType.BLACKLIST, System.currentTimeMillis(), reason, Integer.MAX_VALUE);
+
+ if (sender instanceof Player) {
+ punishment.setAddedBy(((Player) sender).getUniqueId());
+ }
+
+ profile.getPunishments().add(punishment);
+ profile.save();
+
+ Core.get().getPidgin().sendPacket(new PacketBroadcastPunishment(punishment, staffName, profile.getColoredUsername(), profile.getUuid(), option != null));
+
+ Player player = profile.getPlayer();
+
+ if (player != null) {
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ player.kickPlayer(punishment.getKickMessage());
+ }
+ }.runTask(Core.get());
+ }
+ }
+}
+
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/command/ClearPunishmentsCommand.java b/src/main/java/net/centilehcf/core/profile/punishment/command/ClearPunishmentsCommand.java
new file mode 100644
index 0000000..9dd9980
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/command/ClearPunishmentsCommand.java
@@ -0,0 +1,45 @@
+package net.centilehcf.core.profile.punishment.command;
+
+import com.mongodb.client.model.UpdateOptions;
+import com.mongodb.client.result.UpdateResult;
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.network.packet.PacketRemovePunishments;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.util.CC;
+import org.bson.Document;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.ConsoleCommandSender;
+
+@CommandMeta(label = "clearpunishments", permission = "command.clearpunishments", async = true)
+public class ClearPunishmentsCommand {
+
+ public void execute(CommandSender sender, @CPL("player") Profile profile) {
+ // If it is for a single profile only
+ if (profile != null) {
+ Command.broadcastCommandMessage(sender, CC.YELLOW + "Cleared all punishments for " + profile.getUsername() + '.');
+ profile.getPunishments().clear();
+ profile.save();
+ Core.get().getPidgin().sendPacket(new PacketRemovePunishments(profile.getUuid()));
+ }
+ }
+
+ public void execute(CommandSender sender) {
+ // For all profiles
+ if (!(sender instanceof ConsoleCommandSender)) {
+ sender.sendMessage(CC.RED + "This command can only be executed from the console!");
+ } else {
+ Core.get().debug(CC.YELLOW + "Attempting to clear all punishments. This could take a few seconds...");
+ UpdateResult result = Core.get().getMongoDatabase()
+ .getCollection("profiles")
+ .updateMany(new Document(), new Document("$set", new Document("punishments", "[]")), new UpdateOptions().upsert(false));
+ if (result.wasAcknowledged()) {
+ Core.get().debug(CC.AQUA + "Cleared all punishments. \n * Result was acknowledged \n * Modified " + result.getModifiedCount() + " documents");
+ } else {
+ Core.get().debug(CC.RED + "Couldn't clear all punishments\n" + CC.RED + CC.BOLD + "The result was not acknowledged");
+ }
+ }
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/command/HistoryCommand.java b/src/main/java/net/centilehcf/core/profile/punishment/command/HistoryCommand.java
new file mode 100644
index 0000000..0320a0b
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/command/HistoryCommand.java
@@ -0,0 +1,22 @@
+package net.centilehcf.core.profile.punishment.command;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.punishment.menu.PunishmentsMenu;
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = { "history", "punishments" }, permission = "rank.trialmod", async = true)
+public class HistoryCommand {
+
+ public void execute(Player player, @CPL("player") Profile profile) {
+ if (profile == null || !profile.isLoaded()) {
+ player.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ new PunishmentsMenu(profile).openMenu(player);
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/command/KickCommand.java b/src/main/java/net/centilehcf/core/profile/punishment/command/KickCommand.java
new file mode 100644
index 0000000..33f59c6
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/command/KickCommand.java
@@ -0,0 +1,58 @@
+package net.centilehcf.core.profile.punishment.command;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.network.packet.PacketBroadcastPunishment;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.punishment.Punishment;
+import net.centilehcf.core.profile.punishment.PunishmentType;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import com.qrakn.honcho.command.CommandOption;
+import java.util.UUID;
+
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+
+@CommandMeta(label = "kick", permission = "rank.trialmod", async = true, options = "s")
+public class KickCommand {
+
+ public void execute(CommandSender sender, CommandOption option, Player player, String reason) {
+ if (player == null) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ Profile profile = Profile.getProfiles().get(player.getUniqueId());
+
+ if (profile == null || !profile.isLoaded()) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ String staffName = sender instanceof Player ? Profile.getProfiles().get(((Player) sender)
+ .getUniqueId()).getColoredUsername() : CC.DARK_RED + "Console";
+
+ Punishment punishment = new Punishment(UUID.randomUUID(), PunishmentType.KICK, System.currentTimeMillis(),
+ reason, -1);
+
+ if (sender instanceof Player) {
+ punishment.setAddedBy(((Player) sender).getUniqueId());
+ }
+
+ profile.getPunishments().add(punishment);
+ profile.save();
+
+ Core.get().getPidgin().sendPacket(new PacketBroadcastPunishment(punishment, staffName,
+ profile.getColoredUsername(), profile.getUuid(), option != null));
+
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ player.kickPlayer(punishment.getKickMessage());
+ }
+ }.runTask(Core.get());
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/command/MuteCommand.java b/src/main/java/net/centilehcf/core/profile/punishment/command/MuteCommand.java
new file mode 100644
index 0000000..2f72ce5
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/command/MuteCommand.java
@@ -0,0 +1,67 @@
+package net.centilehcf.core.profile.punishment.command;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.network.packet.PacketBroadcastPunishment;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.punishment.Punishment;
+import net.centilehcf.core.profile.punishment.PunishmentType;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.duration.Duration;
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import com.qrakn.honcho.command.CommandOption;
+import java.util.UUID;
+
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "mute", permission = "rank.trialmod", async = true, options = "s")
+public class MuteCommand {
+
+ public void execute(CommandSender sender, CommandOption option, @CPL("player") Profile profile, Duration duration, String reason) {
+ if (profile == null || !profile.isLoaded()) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ if (profile.getActiveMute() != null) {
+ sender.sendMessage(CC.RED + "That player is already muted.");
+ return;
+ }
+
+ if (duration.getValue() == -1) {
+ sender.sendMessage(CC.RED + "That duration is not valid.");
+ sender.sendMessage(CC.RED + "Example: [perm/1y1m1w1d]");
+ return;
+ }
+
+ String staffName = sender instanceof Player ? Profile.getProfiles().get(((Player) sender)
+ .getUniqueId()).getColoredUsername() : CC.DARK_RED + "Console";
+
+ Punishment punishment = new Punishment(UUID.randomUUID(), PunishmentType.MUTE, System.currentTimeMillis(),
+ reason, duration.getValue());
+
+ if (sender instanceof Player) {
+ punishment.setAddedBy(((Player) sender).getUniqueId());
+ }
+
+ profile.getPunishments().add(punishment);
+ profile.save();
+
+ Player player = profile.getPlayer();
+
+ if (player != null) {
+ String senderName = sender instanceof Player ? Profile.getProfiles().get(((Player) sender).getUniqueId()).getColoredUsername() : CC.DARK_RED + "Console";
+ player.sendMessage(CC.RED + "You have been muted by " + senderName + CC.RED + " for: " + CC.YELLOW + reason);
+
+ if (!punishment.isPermanent()) {
+ player.sendMessage(CC.RED + "This mute will expire in " + CC.YELLOW + punishment.getTimeRemaining());
+ }
+ }
+
+ Core.get().getPidgin().sendPacket(new PacketBroadcastPunishment(punishment, staffName,
+ profile.getColoredUsername(), profile.getUuid(), option != null));
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/command/UnbanCommand.java b/src/main/java/net/centilehcf/core/profile/punishment/command/UnbanCommand.java
new file mode 100644
index 0000000..3659651
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/command/UnbanCommand.java
@@ -0,0 +1,46 @@
+package net.centilehcf.core.profile.punishment.command;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.network.packet.PacketBroadcastPunishment;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.punishment.Punishment;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import com.qrakn.honcho.command.CommandOption;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "unban", permission = "rank.seniormod", async = true, options = "s")
+public class UnbanCommand {
+
+ public void execute(CommandSender sender, CommandOption option, @CPL("player") Profile profile, String reason) {
+ if (profile == null || !profile.isLoaded()) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ if (profile.getActiveBan() == null) {
+ sender.sendMessage(CC.RED + "That player is not banned.");
+ return;
+ }
+
+ String staffName = sender instanceof Player ? Profile.getProfiles().get(((Player) sender)
+ .getUniqueId()).getColoredUsername() : CC.DARK_RED + "Console";
+
+ Punishment punishment = profile.getActiveBan();
+ punishment.setPardonedAt(System.currentTimeMillis());
+ punishment.setPardonedReason(reason);
+ punishment.setPardoned(true);
+
+ if (sender instanceof Player) {
+ punishment.setPardonedBy(((Player) sender).getUniqueId());
+ }
+
+ profile.save();
+
+ Core.get().getPidgin().sendPacket(new PacketBroadcastPunishment(punishment, staffName,
+ profile.getColoredUsername(), profile.getUuid(), option != null));
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/command/UnblacklistCommand.java b/src/main/java/net/centilehcf/core/profile/punishment/command/UnblacklistCommand.java
new file mode 100644
index 0000000..9f10ad6
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/command/UnblacklistCommand.java
@@ -0,0 +1,47 @@
+package net.centilehcf.core.profile.punishment.command;
+
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import com.qrakn.honcho.command.CommandOption;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.network.packet.PacketBroadcastPunishment;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.punishment.Punishment;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/21/2019.
+ */
+@CommandMeta(label = "unblacklist", permission = "rank.owner", async = true, options = "s")
+public class UnblacklistCommand {
+
+ public void execute(CommandSender sender, CommandOption option, @CPL("player") Profile profile, String reason) {
+ if (profile == null || !profile.isLoaded()) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ if (profile.getActiveBlacklist() == null) {
+ sender.sendMessage(CC.RED + "That player is not blacklisted!");
+ return;
+ }
+
+ String staffName = sender instanceof Player ? Profile.getProfiles().get(((Player) sender).getUniqueId()).getColoredUsername() : CC.DARK_RED + "Console";
+
+ Punishment punishment = profile.getActiveBlacklist();
+ punishment.setPardonedAt(System.currentTimeMillis());
+ punishment.setPardonedReason(reason);
+ punishment.setPardoned(true);
+
+ if (sender instanceof Player) {
+ punishment.setPardonedBy(((Player) sender).getUniqueId());
+ }
+
+ profile.save();
+
+ Core.get().getPidgin().sendPacket(new PacketBroadcastPunishment(punishment, staffName, profile.getColoredUsername(), profile.getUuid(), option != null));
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/command/UnmuteCommand.java b/src/main/java/net/centilehcf/core/profile/punishment/command/UnmuteCommand.java
new file mode 100644
index 0000000..318379d
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/command/UnmuteCommand.java
@@ -0,0 +1,47 @@
+package net.centilehcf.core.profile.punishment.command;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.network.packet.PacketBroadcastPunishment;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.punishment.Punishment;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import com.qrakn.honcho.command.CommandOption;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "unmute", permission = "rank.mod", async = true, options = "s")
+public class UnmuteCommand {
+
+ public void execute(CommandSender sender, CommandOption option, @CPL("player") Profile profile, String reason) {
+ if (profile == null || !profile.isLoaded()) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ if (profile.getActiveMute() == null) {
+ sender.sendMessage(CC.RED + "That player is not muted.");
+ return;
+ }
+
+ String staffName = sender instanceof Player ? Profile.getProfiles().get(((Player) sender)
+ .getUniqueId()).getColoredUsername() : CC.DARK_RED + "Console";
+
+ Punishment punishment = profile.getActiveMute();
+ punishment.setPardonedAt(System.currentTimeMillis());
+ punishment.setPardonedReason(reason);
+ punishment.setPardoned(true);
+
+ if (sender instanceof Player) {
+ punishment.setPardonedBy(((Player) sender).getUniqueId());
+ }
+
+ profile.save();
+
+ Core.get().getPidgin().sendPacket(new PacketBroadcastPunishment(punishment, staffName,
+ profile.getColoredUsername(), profile.getUuid(), option != null));
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/command/WarnCommand.java b/src/main/java/net/centilehcf/core/profile/punishment/command/WarnCommand.java
new file mode 100644
index 0000000..b263dad
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/command/WarnCommand.java
@@ -0,0 +1,51 @@
+package net.centilehcf.core.profile.punishment.command;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.network.packet.PacketBroadcastPunishment;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.punishment.Punishment;
+import net.centilehcf.core.profile.punishment.PunishmentType;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import com.qrakn.honcho.command.CommandOption;
+import java.util.UUID;
+
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+@CommandMeta(label = "warn", permission = "rank.trialmod", async = true, options = "s")
+public class WarnCommand {
+
+ public void execute(CommandSender sender, CommandOption option, @CPL("player") Profile profile, String reason) {
+ if (profile == null || !profile.isLoaded()) {
+ sender.sendMessage(Locale.COULD_NOT_RESOLVE_PLAYER.format());
+ return;
+ }
+
+ String staffName = sender instanceof Player ? Profile.getProfiles().get(((Player) sender)
+ .getUniqueId()).getColoredUsername() : CC.DARK_RED + "Console";
+
+ Punishment punishment = new Punishment(UUID.randomUUID(), PunishmentType.WARN, System.currentTimeMillis(),
+ reason, -1);
+
+ if (sender instanceof Player) {
+ punishment.setAddedBy(((Player) sender).getUniqueId());
+ }
+
+ profile.getPunishments().add(punishment);
+ profile.save();
+
+ Player player = profile.getPlayer();
+
+ if (player != null) {
+ String senderName = sender instanceof Player ? Profile.getProfiles().get(((Player) sender).getUniqueId()).getColoredUsername() : CC.DARK_RED + "Console";
+ player.sendMessage(CC.RED + "You have been warned by " + senderName + CC.RED + " for: " + CC.YELLOW + reason);
+ }
+
+ Core.get().getPidgin().sendPacket(new PacketBroadcastPunishment(punishment, staffName,
+ profile.getColoredUsername(), profile.getUuid(), option != null));
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/listener/PunishmentListener.java b/src/main/java/net/centilehcf/core/profile/punishment/listener/PunishmentListener.java
new file mode 100644
index 0000000..4a276da
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/listener/PunishmentListener.java
@@ -0,0 +1,67 @@
+package net.centilehcf.core.profile.punishment.listener;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.bootstrap.BootstrappedListener;
+import net.centilehcf.core.profile.punishment.procedure.PunishmentProcedure;
+import net.centilehcf.core.profile.punishment.procedure.PunishmentProcedureStage;
+import net.centilehcf.core.profile.punishment.procedure.PunishmentProcedureType;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.callback.TypeCallback;
+import net.centilehcf.core.util.menu.menus.ConfirmMenu;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.player.AsyncPlayerChatEvent;
+
+public class PunishmentListener extends BootstrappedListener {
+
+ public PunishmentListener(Core core) {
+ super(core);
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST)
+ public void onAsyncPlayerChatEvent(AsyncPlayerChatEvent event) {
+ if (!event.getPlayer().hasPermission("rank.manager")) {
+ return;
+ }
+
+ PunishmentProcedure procedure = PunishmentProcedure.getByPlayer(event.getPlayer());
+
+ if (procedure != null && procedure.getStage() == PunishmentProcedureStage.REQUIRE_TEXT) {
+ event.setCancelled(true);
+
+ if (event.getMessage().equalsIgnoreCase("cancel")) {
+ PunishmentProcedure.getProcedures().remove(procedure);
+ event.getPlayer().sendMessage(CC.RED + "You have cancelled the punishment procedure.");
+ return;
+ }
+
+ if (procedure.getType() == PunishmentProcedureType.PARDON) {
+ new ConfirmMenu(CC.YELLOW + "Pardon this punishment?", new TypeCallback() {
+ @Override
+ public void callback(Boolean data) {
+ if (data) {
+ procedure.getPunishment().setPardonedBy(event.getPlayer().getUniqueId());
+ procedure.getPunishment().setPardonedAt(System.currentTimeMillis());
+ procedure.getPunishment().setPardonedReason(event.getMessage());
+ procedure.getPunishment().setPardoned(true);
+ procedure.finish();
+
+ event.getPlayer().sendMessage(CC.GREEN + "The punishment has been pardoned.");
+ } else {
+ event.getPlayer().sendMessage(CC.RED + "You did not confirm to pardon the punishment.");
+ }
+ }
+ }, true) {
+ @Override
+ public void onClose(Player player) {
+ if (!isClosedByMenu()) {
+ event.getPlayer().sendMessage(CC.RED + "You did not confirm to pardon the punishment.");
+ }
+ }
+ }.openMenu(event.getPlayer());
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/menu/PunishmentsListMenu.java b/src/main/java/net/centilehcf/core/profile/punishment/menu/PunishmentsListMenu.java
new file mode 100644
index 0000000..5862f4a
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/menu/PunishmentsListMenu.java
@@ -0,0 +1,123 @@
+package net.centilehcf.core.profile.punishment.menu;
+
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.punishment.procedure.PunishmentProcedure;
+import net.centilehcf.core.profile.punishment.procedure.PunishmentProcedureStage;
+import net.centilehcf.core.profile.punishment.procedure.PunishmentProcedureType;
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.pagination.PaginatedMenu;
+import net.centilehcf.core.profile.punishment.Punishment;
+import net.centilehcf.core.profile.punishment.PunishmentType;
+import net.centilehcf.core.util.ItemBuilder;
+import net.centilehcf.core.util.TimeUtil;
+import net.centilehcf.core.util.CC;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.AllArgsConstructor;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.ItemBuilder;
+import net.centilehcf.core.util.TimeUtil;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+@AllArgsConstructor
+public class PunishmentsListMenu extends PaginatedMenu {
+
+ private Profile profile;
+ private PunishmentType punishmentType;
+
+ @Override
+ public String getPrePaginatedTitle(Player player) {
+ return "&6" + punishmentType.getTypeData().getReadable() + " &7- &f" + profile.getUsername();
+ }
+
+ @Override
+ public Map getAllPagesButtons(Player player) {
+ Map buttons = new HashMap<>();
+
+ for (Punishment punishment : profile.getPunishments()) {
+ if (punishment.getType() == punishmentType) {
+ buttons.put(buttons.size(), new PunishmentInfoButton(punishment));
+ }
+ }
+
+ return buttons;
+ }
+
+ @AllArgsConstructor
+ private class PunishmentInfoButton extends Button {
+
+ private Punishment punishment;
+
+ @Override
+ public ItemStack getButtonItem(Player player) {
+ String addedBy = "Console";
+
+ if (punishment.getAddedBy() != null) {
+ try {
+ Profile addedByProfile = Profile.getByUuid(punishment.getAddedBy());
+ addedBy = addedByProfile.getUsername();
+ } catch (Exception e) {
+ addedBy = "Could not fetch...";
+ }
+ }
+
+ List lore = new ArrayList<>();
+
+ lore.add(CC.MENU_BAR);
+ lore.add("&eAdded by: &c" + addedBy);
+ lore.add("&eAdded for: &c" + punishment.getAddedReason());
+
+ if (punishment.isActive() && !punishment.isPermanent() && punishment.getDuration() != -1) {
+ lore.add("&eDuration: &c" + punishment.getTimeRemaining());
+ }
+
+ if (punishment.isPardoned()) {
+ String removedBy = "Console";
+
+ if (punishment.getPardonedBy() != null) {
+ try {
+ Profile removedByProfile = Profile.getByUuid(punishment.getPardonedBy());
+ removedBy = removedByProfile.getUsername();
+ } catch (Exception e) {
+ removedBy = "Could not fetch...";
+ }
+ }
+
+ lore.add(CC.MENU_BAR);
+ lore.add("&ePardoned at: &c" + TimeUtil.dateToString(new Date(punishment.getPardonedAt())));
+ lore.add("&ePardoned by: &c" + removedBy);
+ lore.add("&ePardoned for: &c" + punishment.getPardonedReason());
+ }
+
+ lore.add(CC.MENU_BAR);
+
+ if (!punishment.isPardoned() && punishment.getType().isCanBePardoned()) {
+ lore.add("&eRight click to pardon this punishment");
+ lore.add(CC.MENU_BAR);
+ }
+
+ return new ItemBuilder(Material.PAPER)
+ .name("&6" + TimeUtil.dateToString(new Date(punishment.getAddedAt())))
+ .lore(lore)
+ .build();
+ }
+
+ @Override
+ public void clicked(Player player, ClickType clickType) {
+ if (clickType == ClickType.RIGHT && !punishment.isPardoned() && punishment.getType().isCanBePardoned()) {
+ PunishmentProcedure procedure = new PunishmentProcedure(player, profile, PunishmentProcedureType.PARDON, PunishmentProcedureStage.REQUIRE_TEXT);
+ procedure.setPunishment(punishment);
+
+ player.sendMessage(CC.GREEN + "Type a reason for pardoning this punishment in chat...");
+ player.closeInventory();
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/menu/PunishmentsMenu.java b/src/main/java/net/centilehcf/core/profile/punishment/menu/PunishmentsMenu.java
new file mode 100644
index 0000000..c729b98
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/menu/PunishmentsMenu.java
@@ -0,0 +1,70 @@
+package net.centilehcf.core.profile.punishment.menu;
+
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.punishment.PunishmentType;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.ItemBuilder;
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.Menu;
+import java.util.HashMap;
+import java.util.Map;
+import lombok.AllArgsConstructor;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.ItemBuilder;
+import net.centilehcf.core.util.menu.Menu;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+@AllArgsConstructor
+public class PunishmentsMenu extends Menu {
+
+ private Profile profile;
+
+ @Override
+ public String getTitle(Player player) {
+ return "&6Punishments of " + profile.getUsername();
+ }
+
+ @Override
+ public Map getButtons(Player player) {
+ Map buttons = new HashMap<>();
+
+ buttons.put(9, new SelectPunishmentTypeButton(profile, PunishmentType.BLACKLIST));
+ buttons.put(11, new SelectPunishmentTypeButton(profile, PunishmentType.BAN));
+ buttons.put(13, new SelectPunishmentTypeButton(profile, PunishmentType.MUTE));
+ buttons.put(15, new SelectPunishmentTypeButton(profile, PunishmentType.WARN));
+ buttons.put(17, new SelectPunishmentTypeButton(profile, PunishmentType.KICK));
+
+ return buttons;
+ }
+
+ @Override
+ public int getSize() {
+ return 27;
+ }
+
+ @AllArgsConstructor
+ private class SelectPunishmentTypeButton extends Button {
+
+ private Profile profile;
+ private PunishmentType punishmentType;
+
+ @Override
+ public ItemStack getButtonItem(Player player) {
+ return new ItemBuilder(Material.WOOL)
+ .name(punishmentType.getTypeData().getColor() + CC.BOLD + punishmentType.getTypeData().getReadable())
+ .lore(CC.GRAY + profile.getPunishmentCountByType(punishmentType) + " " + (punishmentType.getTypeData().getReadable().toLowerCase()) + " on record")
+ .durability(punishmentType.getTypeData().getDurability())
+ .build();
+ }
+
+ @Override
+ public void clicked(Player player, ClickType clickType) {
+ new PunishmentsListMenu(profile, punishmentType).openMenu(player);
+ }
+
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedure.java b/src/main/java/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedure.java
new file mode 100644
index 0000000..67cbfa4
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedure.java
@@ -0,0 +1,49 @@
+package net.centilehcf.core.profile.punishment.procedure;
+
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.punishment.Punishment;
+import java.util.HashSet;
+import java.util.Set;
+import lombok.Getter;
+import lombok.Setter;
+import net.centilehcf.core.profile.Profile;
+import net.centilehcf.core.profile.punishment.Punishment;
+import org.bukkit.entity.Player;
+
+public class PunishmentProcedure {
+
+ @Getter private static final Set procedures = new HashSet<>();
+
+ @Getter private final Player issuer;
+ @Getter private final Profile recipient;
+ @Getter private final PunishmentProcedureType type;
+ @Getter private PunishmentProcedureStage stage;
+ @Getter @Setter private Punishment punishment;
+
+ public PunishmentProcedure(Player issuer, Profile recipient, PunishmentProcedureType type, PunishmentProcedureStage stage) {
+ this.issuer = issuer;
+ this.recipient = recipient;
+ this.type = type;
+ this.stage = stage;
+
+ procedures.add(this);
+ }
+
+ public void finish() {
+ this.recipient.save();
+ procedures.remove(this);
+ }
+
+ public static PunishmentProcedure getByPlayer(Player player) {
+ for (PunishmentProcedure procedure : procedures) {
+ if (procedure.issuer.equals(player)) {
+ return procedure;
+ }
+ }
+
+ return null;
+ }
+
+
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedureStage.java b/src/main/java/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedureStage.java
new file mode 100644
index 0000000..e96fa37
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedureStage.java
@@ -0,0 +1,9 @@
+package net.centilehcf.core.profile.punishment.procedure;
+
+public enum PunishmentProcedureStage {
+
+ REQUIRE_CLICK,
+ REQUIRE_TEXT,
+ REQUIRE_CONFIRMATION,
+
+}
diff --git a/src/main/java/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedureType.java b/src/main/java/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedureType.java
new file mode 100644
index 0000000..844b5d9
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedureType.java
@@ -0,0 +1,8 @@
+package net.centilehcf.core.profile.punishment.procedure;
+
+public enum PunishmentProcedureType {
+
+ ADD,
+ PARDON
+
+}
diff --git a/src/main/java/net/centilehcf/core/rank/Rank.java b/src/main/java/net/centilehcf/core/rank/Rank.java
new file mode 100644
index 0000000..f3515a6
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/Rank.java
@@ -0,0 +1,258 @@
+package net.centilehcf.core.rank;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoCursor;
+import com.mongodb.client.model.Filters;
+import com.mongodb.client.model.ReplaceOptions;
+import lombok.Getter;
+import lombok.Setter;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.network.packet.PacketDeleteRank;
+import net.centilehcf.core.network.packet.PacketRefreshRank;
+import org.bson.Document;
+import org.bukkit.ChatColor;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class Rank {
+
+ @Getter
+ private static Map ranks = new HashMap<>();
+ private static MongoCollection collection;
+
+ @Getter
+ private final UUID uuid;
+ @Getter
+ @Setter
+ private String displayName;
+ @Getter
+ private final List permissions = new ArrayList<>();
+ @Getter
+ private final List inherited = new ArrayList<>();
+ @Getter
+ @Setter
+ private String prefix = "";
+ @Getter
+ @Setter
+ private String suffix = "";
+ @Getter
+ @Setter
+ private ChatColor color = ChatColor.WHITE;
+ @Getter
+ @Setter
+ private int weight;
+ @Setter
+ private boolean defaultRank;
+
+ public Rank(String displayName) {
+ this.uuid = UUID.randomUUID();
+ this.displayName = displayName;
+
+ ranks.put(uuid, this);
+ }
+
+ public Rank(UUID uuid, String displayName) {
+ this.uuid = uuid;
+ this.displayName = displayName;
+ }
+
+ public Rank(UUID uuid, String displayName, String prefix, String suffix, ChatColor color, int weight, boolean defaultRank) {
+ this.uuid = uuid;
+ this.displayName = displayName;
+ this.prefix = prefix;
+ this.suffix = suffix;
+ this.color = color;
+ this.weight = weight;
+ this.defaultRank = defaultRank;
+
+ ranks.put(uuid, this);
+ }
+
+ public static void init() {
+ collection = Core.get().getMongoDatabase().getCollection("ranks");
+
+ Map> inheritanceReferences = new HashMap<>();
+
+ try (MongoCursor cursor = collection.find().iterator()) {
+ while (cursor.hasNext()) {
+ Document document = cursor.next();
+
+ Rank rank = new Rank(UUID.fromString(document.getString("uuid")),
+ document.getString("displayName"));
+ rank.load(document);
+
+ Core.GSON.>fromJson(document.getString("permissions"), Core.LIST_STRING_TYPE)
+ .forEach(perm -> rank.getPermissions().add(perm));
+
+ List ranksToInherit = new ArrayList<>();
+
+ for (JsonElement element : new JsonParser().parse(document.getString("inherits")).getAsJsonArray()) {
+ ranksToInherit.add(UUID.fromString(element.getAsString()));
+ }
+
+ inheritanceReferences.put(rank, ranksToInherit);
+
+ ranks.put(rank.getUuid(), rank);
+ }
+ }
+
+ inheritanceReferences.forEach((rank, list) -> {
+ list.forEach(uuid -> {
+ Rank inherited = ranks.get(uuid);
+
+ if (inherited != null) {
+ rank.getInherited().add(inherited);
+ }
+ });
+ });
+
+ getDefaultRank();
+ }
+
+ /**
+ * Retrieves a rank by UUID if one exists.
+ *
+ * @param uuid The UUID.
+ * @return A rank that matches the given UUID if found.
+ */
+ public static Rank getRankByUuid(UUID uuid) {
+ return ranks.get(uuid);
+ }
+
+ /**
+ * Retrieves a rank by name if one exists.
+ *
+ * @param name The name.
+ * @return A rank that matches the given name if found.
+ */
+ public static Rank getRankByDisplayName(String name) {
+ for (Rank rank : ranks.values()) {
+ if (rank.getDisplayName().equalsIgnoreCase(name)) {
+ return rank;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieves the default rank or creates a new default rank if one does not already exist.
+ *
+ * @return A default rank, or a new default rank if unavailable.
+ */
+ public static Rank getDefaultRank() {
+ for (Rank rank : ranks.values()) {
+ if (rank.isDefaultRank()) {
+ return rank;
+ }
+ }
+
+ Rank defaultRank = new Rank("Default");
+ defaultRank.setDefaultRank(true);
+ defaultRank.save();
+
+ ranks.put(defaultRank.getUuid(), defaultRank);
+
+ return defaultRank;
+ }
+
+ public boolean isDefaultRank() {
+ return defaultRank;
+ }
+
+ public boolean addPermission(String permission) {
+ if (!permissions.contains(permission)) {
+ permissions.add(permission);
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean deletePermission(String permission) {
+ return permissions.remove(permission);
+ }
+
+ public boolean hasPermission(String permission) {
+ if (permissions.contains(permission)) {
+ return true;
+ }
+
+ for (Rank rank : inherited) {
+ if (rank.hasPermission(permission)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean canInherit(Rank rankToCheck) {
+ if (inherited.contains(rankToCheck) || rankToCheck.inherited.contains(this)) {
+ return false;
+ }
+
+ for (Rank rank : inherited) {
+ if (!rank.canInherit(rankToCheck)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public List getAllPermissions() {
+ List permissions = new ArrayList<>();
+ permissions.addAll(this.permissions);
+
+ for (Rank rank : inherited) {
+ permissions.addAll(rank.getAllPermissions());
+ }
+
+ return permissions;
+ }
+
+ public void load() {
+ load(collection.find(Filters.eq("uuid", uuid.toString())).first());
+ }
+
+ public void load(Document document) {
+ if (document == null) {
+ return;
+ }
+
+ prefix = ChatColor.translateAlternateColorCodes('&', document.getString("prefix"));
+ suffix = ChatColor.translateAlternateColorCodes('&', document.getString("suffix"));
+ color = ChatColor.valueOf(document.getString("color"));
+ weight = document.getInteger("weight");
+ defaultRank = document.getBoolean("defaultRank");
+ }
+
+ public void save() {
+ Document document = new Document();
+ document.put("uuid", uuid.toString());
+ document.put("displayName", displayName);
+ document.put("prefix", prefix.replace(String.valueOf(ChatColor.COLOR_CHAR), "&"));
+ document.put("suffix", suffix.replace(String.valueOf(ChatColor.COLOR_CHAR), "&"));
+ document.put("color", color.name());
+ document.put("weight", weight);
+ document.put("defaultRank", defaultRank);
+ document.put("permissions", Core.GSON.toJson(permissions));
+ document.put("inherits", Core.GSON.toJson(inherited.stream().map(Rank::getUuid).map(UUID::toString).collect(Collectors.toList())));
+
+ collection.replaceOne(Filters.eq("uuid", uuid.toString()), document, new ReplaceOptions().upsert(true));
+
+ Core.get().getPidgin().sendPacket(new PacketRefreshRank(uuid, displayName));
+ }
+
+ public void delete() {
+ ranks.remove(uuid);
+ collection.deleteOne(Filters.eq("uuid", uuid.toString()));
+
+ Core.get().getPidgin().sendPacket(new PacketDeleteRank(uuid));
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/rank/RankTypeAdapter.java b/src/main/java/net/centilehcf/core/rank/RankTypeAdapter.java
new file mode 100644
index 0000000..51392f4
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/RankTypeAdapter.java
@@ -0,0 +1,27 @@
+package net.centilehcf.core.rank;
+
+import com.qrakn.honcho.command.adapter.CommandTypeAdapter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class RankTypeAdapter implements CommandTypeAdapter {
+
+ @Override
+ public T convert(String string, Class type) {
+ return type.cast(Rank.getRankByDisplayName(string));
+ }
+
+ @Override
+ public List tabComplete(String string, Class type) {
+ List completed = new ArrayList<>();
+
+ for (Rank rank : Rank.getRanks().values()) {
+ if (rank.getDisplayName().toLowerCase().startsWith(string)) {
+ completed.add(rank.getDisplayName());
+ }
+ }
+
+ return completed;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/rank/command/RankAddPermissionCommand.java b/src/main/java/net/centilehcf/core/rank/command/RankAddPermissionCommand.java
new file mode 100644
index 0000000..7cb035b
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/command/RankAddPermissionCommand.java
@@ -0,0 +1,25 @@
+package net.centilehcf.core.rank.command;
+
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = { "rank addpermission", "rank addperm" }, permission = "rank.manager", async = true)
+public class RankAddPermissionCommand {
+
+ public void execute(CommandSender sender, Rank rank, String permission) {
+ if (rank.hasPermission(permission)) {
+ sender.sendMessage(CC.RED + "That rank already has that permission.");
+ return;
+ }
+
+ rank.getPermissions().add(permission);
+ rank.save();
+
+ sender.sendMessage(CC.GREEN + "Successfully added permission to rank.");
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/rank/command/RankAuditCommand.java b/src/main/java/net/centilehcf/core/rank/command/RankAuditCommand.java
new file mode 100644
index 0000000..e888d3f
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/command/RankAuditCommand.java
@@ -0,0 +1,67 @@
+package net.centilehcf.core.rank.command;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.Core;
+import net.centilehcf.core.profile.grant.Grant;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.HastebinUtil;
+import net.centilehcf.core.util.TimeUtil;
+import net.centilehcf.core.uuid.UUIDCache;
+import org.bson.Document;
+import org.bukkit.command.CommandSender;
+import org.fusesource.jansi.Ansi;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+@CommandMeta(label = "rank audit", permission = "rank.manager", async = true)
+public class RankAuditCommand {
+
+ public void execute(CommandSender sender, Rank rank) {
+ sender.sendMessage(CC.YELLOW + "Preforming audit on " + CC.RED + rank.getDisplayName() + CC.YELLOW + ". This may take a few seconds.");
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("----------------------\n");
+ buffer.append("Audit for ").append(rank.getDisplayName()).append('\n');
+ buffer.append("Prefix: ").append(rank.getPrefix()).append(" Suffix: ").append(rank.getSuffix()).append('\n');
+ buffer.append("Color: ").append(rank.getColor().name()).append('\n');
+ buffer.append("--\n");
+ buffer.append("Permissions:\n");
+ rank.getPermissions().forEach(s -> buffer.append(s).append('\n'));
+ buffer.append("--\n");
+ buffer.append("Users that have this rank:\n");
+ for (Document document : Core.get().getMongoDatabase().getCollection("profiles").find()) {
+ List grantList = new ArrayList<>();
+ JsonArray jsonGrants = new JsonParser().parse(document.getString("grants")).getAsJsonArray();
+ for (JsonElement jsonElement : jsonGrants) {
+ JsonObject jsonObject = jsonElement.getAsJsonObject();
+ Rank jsonRank = Rank.getRankByUuid(UUID.fromString(jsonObject.get("rank").getAsString()));
+
+ if (jsonRank != null) {
+ grantList.add(Grant.DESERIALIZER.deserialize(jsonObject));
+ }
+ }
+
+ for (Grant grant : grantList) {
+ if (!grant.isRemoved()) {
+ buffer.append(document.getString("username")).append(" (").append(document.getString("uuid")).append(")\n");
+ buffer.append(" *").append("Added by: ").append(grant.getAddedBy() != null ? Core.get().getUuidCache().getName(grant.getAddedBy()) : "Console").append(" (").append(grant.getAddedBy() != null ? grant.getAddedBy() : UUIDCache.CONSOLE_UUID.toString()).append(")\n");
+ buffer.append(" *").append("Date added: ").append(grant.getAddedAtDate()).append('\n');
+ buffer.append(" *").append("Reason: ").append(grant.getAddedReason()).append('\n');
+ buffer.append("\u001B[32m").append(" ** ACTIVE **").append("\u001B[0m");
+ }
+ }
+ }
+ buffer.append("----------------------\n");
+ buffer.append("Generated on ").append(TimeUtil.dateToString(new Date())).append(" by ").append(sender.getName()).append('\n');
+ buffer.append("----------------------");
+ sender.sendMessage(CC.GREEN + "Summary of Audit: " + HastebinUtil.paste(buffer.toString()));
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/rank/command/RankCommand.java b/src/main/java/net/centilehcf/core/rank/command/RankCommand.java
new file mode 100644
index 0000000..645370f
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/command/RankCommand.java
@@ -0,0 +1,26 @@
+package net.centilehcf.core.rank.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 3/30/2019.
+ */
+@CommandMeta(label = {"rank"}, permission = "rank.manager", async = true)
+public class RankCommand {
+ public void execute(CommandSender sender) {
+ sender.sendMessage(CC.DARK_RED + "Rank Commands");
+ sender.sendMessage(CC.CHAT_BAR);
+ sender.sendMessage(CC.DARK_RED + "/rank addpermission - Adds a permission to a Rank!");
+ sender.sendMessage(CC.RED + "Usage: /rank convert ");
+ sender.sendMessage(CC.RED + "Usage: /rank convert ");
+ sender.sendMessage(CC.RED + "Usage: /rank convert ");
+ sender.sendMessage(CC.RED + "Usage: /rank convert ");
+ sender.sendMessage(CC.RED + "Usage: /rank convert ");
+ sender.sendMessage(CC.RED + "Usage: /rank convert ");
+ sender.sendMessage(CC.RED + "Usage: /rank convert ");
+ sender.sendMessage(CC.RED + "Usage: /rank convert ");
+ sender.sendMessage(CC.RED + "Usage: /rank convert ");
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/rank/command/RankCreateCommand.java b/src/main/java/net/centilehcf/core/rank/command/RankCreateCommand.java
new file mode 100644
index 0000000..5f5e510
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/command/RankCreateCommand.java
@@ -0,0 +1,24 @@
+package net.centilehcf.core.rank.command;
+
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = "rank create", permission = "rank.manager", async = true)
+public class RankCreateCommand {
+
+ public void execute(CommandSender sender, String name) {
+ if (Rank.getRankByDisplayName(name) != null) {
+ sender.sendMessage(CC.RED + "A rank with that name already exists.");
+ return;
+ }
+
+ Rank rank = new Rank(name);
+ rank.save();
+
+ sender.sendMessage(CC.GREEN + "You created a new rank.");
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/rank/command/RankDeleteCommand.java b/src/main/java/net/centilehcf/core/rank/command/RankDeleteCommand.java
new file mode 100644
index 0000000..202fe37
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/command/RankDeleteCommand.java
@@ -0,0 +1,26 @@
+package net.centilehcf.core.rank.command;
+
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = "rank delete", permission = "rank.manager", async = true)
+public class RankDeleteCommand {
+
+ public void execute(CommandSender sender, Rank rank) {
+ if (rank == null) {
+ sender.sendMessage(Locale.RANK_NOT_FOUND.format());
+ return;
+ }
+
+ rank.delete();
+
+ sender.sendMessage(CC.GREEN + "You deleted the rank.");
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/rank/command/RankDumpCommand.java b/src/main/java/net/centilehcf/core/rank/command/RankDumpCommand.java
new file mode 100644
index 0000000..fbc8c21
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/command/RankDumpCommand.java
@@ -0,0 +1,20 @@
+package net.centilehcf.core.rank.command;
+
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import org.bukkit.entity.Player;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 4/5/2019.
+ */
+@CommandMeta(label = "rank dump", permission = "rank.manager", async = true)
+public class RankDumpCommand {
+ public void execute(Player player, Rank rank) {
+ player.sendMessage(rank.getDisplayName() + "'s Permissions:");
+
+ for (String permission : rank.getAllPermissions()) {
+ player.sendMessage(CC.GRAY + " * " + permission);
+ }
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/rank/command/RankListCommand.java b/src/main/java/net/centilehcf/core/rank/command/RankListCommand.java
new file mode 100644
index 0000000..4210d16
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/command/RankListCommand.java
@@ -0,0 +1,28 @@
+package net.centilehcf.core.rank.command;
+
+import net.centilehcf.core.rank.Rank;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.rank.comparator.RankComparator;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+@CommandMeta(label = "rank list", permission = "rank.manager", async = true)
+public class RankListCommand {
+
+ public void execute(CommandSender sender) {
+ List ranks = Rank.getRanks().values().stream().sorted(new RankComparator()).collect(Collectors.toList());
+ sender.sendMessage(CC.CHAT_BAR);
+ sender.sendMessage(CC.YELLOW + "Listing all ranks...");
+ for (Rank rank : ranks) {
+ sender.sendMessage(rank.getDisplayName() + (rank.isDefaultRank() ? " (Default)" : "") +
+ " (Weight: " + rank.getWeight() + ")");
+ }
+ sender.sendMessage(CC.CHAT_BAR);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/centilehcf/core/rank/command/RankRemovePermissionCommand.java b/src/main/java/net/centilehcf/core/rank/command/RankRemovePermissionCommand.java
new file mode 100644
index 0000000..a9f00b1
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/command/RankRemovePermissionCommand.java
@@ -0,0 +1,25 @@
+package net.centilehcf.core.rank.command;
+
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = { "rank removepermission", "rank removeperm" }, permission = "rank.manager", async = true)
+public class RankRemovePermissionCommand {
+
+ public void execute(CommandSender sender, Rank rank, String permission) {
+ if (!rank.hasPermission(permission)) {
+ sender.sendMessage(CC.RED + "That rank does not have that permission.");
+ return;
+ }
+
+ rank.getPermissions().remove(permission);
+ rank.save();
+
+ sender.sendMessage(CC.GREEN + "Successfully removed permission from rank.");
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/rank/command/RankRenameCommand.java b/src/main/java/net/centilehcf/core/rank/command/RankRenameCommand.java
new file mode 100644
index 0000000..ed24cef
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/command/RankRenameCommand.java
@@ -0,0 +1,23 @@
+package net.centilehcf.core.rank.command;
+
+import com.qrakn.honcho.command.CPL;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.Locale;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = "rank rename", permission = "rank.owner", async = true)
+public class RankRenameCommand {
+
+ public void execute(CommandSender sender, @CPL(value = "rank") Rank rank, @CPL(value = "newName") String newName) {
+ if (rank == null){
+ sender.sendMessage(CC.RED + Locale.RANK_NOT_FOUND);
+ return;
+ }
+
+ rank.setDisplayName(newName);
+ rank.save();
+ sender.sendMessage(CC.GREEN + "Set rank name to " + newName + '.');
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/rank/command/RankSetColorCommand.java b/src/main/java/net/centilehcf/core/rank/command/RankSetColorCommand.java
new file mode 100644
index 0000000..4b14b94
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/command/RankSetColorCommand.java
@@ -0,0 +1,31 @@
+package net.centilehcf.core.rank.command;
+
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.ChatColor;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = "rank setcolor", permission = "rank.manager", async = true)
+public class RankSetColorCommand {
+
+ public void execute(CommandSender sender, Rank rank, String color) {
+ if (rank == null) {
+ sender.sendMessage(CC.RED + "A rank with that name does not exist.");
+ return;
+ }
+
+ try {
+ rank.setColor(ChatColor.valueOf(color));
+ rank.save();
+ } catch (Exception e) {
+ e.printStackTrace();
+ sender.sendMessage(CC.RED + "That color is not valid.");
+ return;
+ }
+
+ sender.sendMessage(CC.GREEN + "You updated the rank's color.");
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/rank/command/RankSetPrefixCommand.java b/src/main/java/net/centilehcf/core/rank/command/RankSetPrefixCommand.java
new file mode 100644
index 0000000..523290c
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/command/RankSetPrefixCommand.java
@@ -0,0 +1,24 @@
+package net.centilehcf.core.rank.command;
+
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = "rank setprefix", permission = "rank.manager", async = true)
+public class RankSetPrefixCommand {
+
+ public void execute(CommandSender sender, Rank rank, String prefix) {
+ if (rank == null) {
+ sender.sendMessage(CC.RED + "A rank with that name does not exist.");
+ return;
+ }
+
+ rank.setPrefix(CC.translate(prefix));
+ rank.save();
+
+ sender.sendMessage(CC.GREEN + "You updated the rank's prefix.");
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/rank/command/RankSetSuffixCommand.java b/src/main/java/net/centilehcf/core/rank/command/RankSetSuffixCommand.java
new file mode 100644
index 0000000..d9b9f0c
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/command/RankSetSuffixCommand.java
@@ -0,0 +1,25 @@
+package net.centilehcf.core.rank.command;
+
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = "rank setsuffix", permission = "rank.manager", async = true)
+public class RankSetSuffixCommand {
+
+ public void execute(CommandSender sender, Rank rank, String suffix) {
+ if (rank == null) {
+ sender.sendMessage(CC.RED + "A rank with that name does not exist.");
+ return;
+ }
+
+ rank.setSuffix(CC.translate(suffix));
+ rank.save();
+
+ sender.sendMessage(CC.GREEN + "You updated the rank's suffix.");
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/rank/command/RankSetWeightCommand.java b/src/main/java/net/centilehcf/core/rank/command/RankSetWeightCommand.java
new file mode 100644
index 0000000..4d6b2a5
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/command/RankSetWeightCommand.java
@@ -0,0 +1,32 @@
+package net.centilehcf.core.rank.command;
+
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import com.qrakn.honcho.command.CommandMeta;
+import net.centilehcf.core.rank.Rank;
+import net.centilehcf.core.util.CC;
+import org.bukkit.command.CommandSender;
+
+@CommandMeta(label = "rank setweight", permission = "rank.manager", async = true)
+public class RankSetWeightCommand {
+
+ public void execute(CommandSender sender, Rank rank, String weight) {
+ if (rank == null) {
+ sender.sendMessage(CC.RED + "A rank with that name does not exist.");
+ return;
+ }
+
+ try {
+ Integer.parseInt(weight);
+ } catch (Exception e) {
+ sender.sendMessage(CC.RED + "Invalid number.");
+ return;
+ }
+
+ rank.setWeight(Integer.parseInt(weight));
+ rank.save();
+
+ sender.sendMessage(CC.GREEN + "You updated the rank's weight.");
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/rank/comparator/RankComparator.java b/src/main/java/net/centilehcf/core/rank/comparator/RankComparator.java
new file mode 100644
index 0000000..cfa99be
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/rank/comparator/RankComparator.java
@@ -0,0 +1,13 @@
+package net.centilehcf.core.rank.comparator;
+
+import net.centilehcf.core.rank.Rank;
+import java.util.Comparator;
+
+public class RankComparator implements Comparator {
+
+ @Override
+ public int compare(Rank rank1, Rank rank2) {
+ return rank2.getWeight() - rank1.getWeight();
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/BaseEvent.java b/src/main/java/net/centilehcf/core/util/BaseEvent.java
new file mode 100644
index 0000000..60186cf
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/BaseEvent.java
@@ -0,0 +1,23 @@
+package net.centilehcf.core.util;
+
+import net.centilehcf.core.Core;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+public class BaseEvent extends Event {
+
+ private static final HandlerList handlers = new HandlerList();
+
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ public void call() {
+ Core.get().getServer().getPluginManager().callEvent(this);
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/BukkitReflection.java b/src/main/java/net/centilehcf/core/util/BukkitReflection.java
new file mode 100644
index 0000000..91aff83
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/BukkitReflection.java
@@ -0,0 +1,112 @@
+package net.centilehcf.core.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import org.bukkit.Bukkit;
+import org.bukkit.Server;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+
+public class BukkitReflection {
+
+ private static final String CRAFT_BUKKIT_PACKAGE;
+ private static final String NET_MINECRAFT_SERVER_PACKAGE;
+
+ private static final Class CRAFT_SERVER_CLASS;
+ private static final Method CRAFT_SERVER_GET_HANDLE_METHOD;
+
+ private static final Class PLAYER_LIST_CLASS;
+ private static final Field PLAYER_LIST_MAX_PLAYERS_FIELD;
+
+ private static final Class CRAFT_PLAYER_CLASS;
+ private static final Method CRAFT_PLAYER_GET_HANDLE_METHOD;
+
+ private static final Class ENTITY_PLAYER_CLASS;
+ private static final Field ENTITY_PLAYER_PING_FIELD;
+
+ private static final Class CRAFT_ITEM_STACK_CLASS;
+ private static final Method CRAFT_ITEM_STACK_AS_NMS_COPY_METHOD;
+ private static final Class ENTITY_ITEM_STACK_CLASS;
+ private static final Method ENTITY_ITEM_STACK_GET_NAME;
+
+ private static final Class SPIGOT_CONFIG_CLASS;
+ private static final Field SPIGOT_CONFIG_BUNGEE_FIELD;
+
+ static {
+ try {
+ String version = Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3];
+
+ CRAFT_BUKKIT_PACKAGE = "org.bukkit.craftbukkit." + version + ".";
+ NET_MINECRAFT_SERVER_PACKAGE = "net.minecraft.server." + version + ".";
+
+ CRAFT_SERVER_CLASS = Class.forName(CRAFT_BUKKIT_PACKAGE + "CraftServer");
+ CRAFT_SERVER_GET_HANDLE_METHOD = CRAFT_SERVER_CLASS.getDeclaredMethod("getHandle");
+ CRAFT_SERVER_GET_HANDLE_METHOD.setAccessible(true);
+
+ PLAYER_LIST_CLASS = Class.forName(NET_MINECRAFT_SERVER_PACKAGE + "PlayerList");
+ PLAYER_LIST_MAX_PLAYERS_FIELD = PLAYER_LIST_CLASS.getDeclaredField("maxPlayers");
+ PLAYER_LIST_MAX_PLAYERS_FIELD.setAccessible(true);
+
+ CRAFT_PLAYER_CLASS = Class.forName(CRAFT_BUKKIT_PACKAGE + "entity.CraftPlayer");
+ CRAFT_PLAYER_GET_HANDLE_METHOD = CRAFT_PLAYER_CLASS.getDeclaredMethod("getHandle");
+ CRAFT_PLAYER_GET_HANDLE_METHOD.setAccessible(true);
+
+ ENTITY_PLAYER_CLASS = Class.forName(NET_MINECRAFT_SERVER_PACKAGE + "EntityPlayer");
+ ENTITY_PLAYER_PING_FIELD = ENTITY_PLAYER_CLASS.getDeclaredField("ping");
+ ENTITY_PLAYER_PING_FIELD.setAccessible(true);
+
+ CRAFT_ITEM_STACK_CLASS = Class.forName(CRAFT_BUKKIT_PACKAGE + "inventory.CraftItemStack");
+ CRAFT_ITEM_STACK_AS_NMS_COPY_METHOD =
+ CRAFT_ITEM_STACK_CLASS.getDeclaredMethod("asNMSCopy", ItemStack.class);
+ CRAFT_ITEM_STACK_AS_NMS_COPY_METHOD.setAccessible(true);
+
+ ENTITY_ITEM_STACK_CLASS = Class.forName(NET_MINECRAFT_SERVER_PACKAGE + "ItemStack");
+ ENTITY_ITEM_STACK_GET_NAME = ENTITY_ITEM_STACK_CLASS.getDeclaredMethod("getName");
+
+ SPIGOT_CONFIG_CLASS = Class.forName("org.spigotmc.SpigotConfig");
+ SPIGOT_CONFIG_BUNGEE_FIELD = SPIGOT_CONFIG_CLASS.getDeclaredField("bungee");
+ SPIGOT_CONFIG_BUNGEE_FIELD.setAccessible(true);
+ } catch (Exception e) {
+ e.printStackTrace();
+
+ throw new RuntimeException("Failed to initialize Bukkit/NMS Reflection");
+ }
+ }
+
+ public static int getPing(Player player) {
+ try {
+ int ping = ENTITY_PLAYER_PING_FIELD.getInt(CRAFT_PLAYER_GET_HANDLE_METHOD.invoke(player));
+
+ return ping > 0 ? ping : 0;
+ } catch (Exception e) {
+ return 1;
+ }
+ }
+
+ public static void setMaxPlayers(Server server, int slots) {
+ try {
+ PLAYER_LIST_MAX_PLAYERS_FIELD.set(CRAFT_SERVER_GET_HANDLE_METHOD.invoke(server), slots);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static String getItemStackName(ItemStack itemStack) {
+ try {
+ return (String) ENTITY_ITEM_STACK_GET_NAME.invoke(CRAFT_ITEM_STACK_AS_NMS_COPY_METHOD.invoke(itemStack, itemStack));
+ } catch (Exception e) {
+ e.printStackTrace();
+ return "";
+ }
+ }
+
+ public static boolean isBungeeServer() {
+ try {
+ return (boolean) SPIGOT_CONFIG_BUNGEE_FIELD.get(null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/BukkitUtils.java b/src/main/java/net/centilehcf/core/util/BukkitUtils.java
new file mode 100644
index 0000000..826e204
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/BukkitUtils.java
@@ -0,0 +1,73 @@
+package net.centilehcf.core.util;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import org.bukkit.ChatColor;
+import org.bukkit.DyeColor;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Projectile;
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
+import org.bukkit.potion.PotionEffectType;
+
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 7/20/2019.
+ */
+public class BukkitUtils {
+
+ public static final UUID CONSOLE_UUID = UUID.fromString("f78a4d8d-d51b-4b39-98a3-230f2de0c670");
+
+ private static final ImmutableMap CHAT_DYE_COLOUR_MAP = Maps.immutableEnumMap((Map) ImmutableMap.builder()
+ .put(ChatColor.AQUA, DyeColor.LIGHT_BLUE)
+ .put(ChatColor.BLACK, DyeColor.BLACK)
+ .put(ChatColor.BLUE, DyeColor.LIGHT_BLUE)
+ .put(ChatColor.DARK_AQUA,DyeColor.CYAN)
+ .put(ChatColor.DARK_BLUE,DyeColor.BLUE)
+ .put(ChatColor.DARK_GRAY,DyeColor.GRAY)
+ .put(ChatColor.DARK_GREEN,DyeColor.GREEN)
+ .put(ChatColor.DARK_PURPLE,DyeColor.PURPLE)
+ .put(ChatColor.DARK_RED,DyeColor.RED)
+ .put(ChatColor.GOLD,DyeColor.ORANGE)
+ .put(ChatColor.GRAY,DyeColor.SILVER)
+ .put(ChatColor.GREEN,DyeColor.LIME)
+ .put(ChatColor.LIGHT_PURPLE, DyeColor.MAGENTA)
+ .put(ChatColor.RED, DyeColor.RED)
+ .put(ChatColor.WHITE, DyeColor.WHITE)
+ .put(ChatColor.YELLOW, DyeColor.YELLOW)
+ .build()
+ );
+
+ public static String getName(PotionEffectType potionEffectType) {
+ if (potionEffectType.getName().equalsIgnoreCase("fire_resistance")) {
+ return "Fire Resistance";
+ } else if (potionEffectType.getName().equalsIgnoreCase("speed")) {
+ return "Speed";
+ } else if (potionEffectType.getName().equalsIgnoreCase("weakness")) {
+ return "Weakness";
+ } else if (potionEffectType.getName().equalsIgnoreCase("slowness")) {
+ return "Slowness";
+ } else {
+ return "Unknown";
+ }
+ }
+
+ public static Player getDamager(EntityDamageByEntityEvent event) {
+ if (event.getDamager() instanceof Player) {
+ return (Player) event.getDamager();
+ } else if (event.getDamager() instanceof Projectile) {
+ if (((Projectile) event.getDamager()).getShooter() instanceof Player) {
+ return (Player) ((Projectile) event.getDamager()).getShooter();
+ }
+ }
+
+ return null;
+ }
+
+ public static DyeColor toDyeColor(ChatColor color) {
+ return CHAT_DYE_COLOUR_MAP.get(color);
+ }
+
+}
+
diff --git a/src/main/java/net/centilehcf/core/util/CC.java b/src/main/java/net/centilehcf/core/util/CC.java
new file mode 100644
index 0000000..df7a164
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/CC.java
@@ -0,0 +1,70 @@
+package net.centilehcf.core.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.bukkit.ChatColor;
+
+public class CC {
+
+ public static final String BLUE = ChatColor.BLUE.toString();
+ public static final String AQUA = ChatColor.AQUA.toString();
+ public static final String YELLOW = ChatColor.YELLOW.toString();
+ public static final String RED = ChatColor.RED.toString();
+ public static final String GRAY = ChatColor.GRAY.toString();
+ public static final String GOLD = ChatColor.GOLD.toString();
+ public static final String GREEN = ChatColor.GREEN.toString();
+ public static final String WHITE = ChatColor.WHITE.toString();
+ public static final String BLACK = ChatColor.BLACK.toString();
+ public static final String BOLD = ChatColor.BOLD.toString();
+ public static final String ITALIC = ChatColor.ITALIC.toString();
+ public static final String UNDER_LINE = ChatColor.UNDERLINE.toString();
+ public static final String STRIKE_THROUGH = ChatColor.STRIKETHROUGH.toString();
+ public static final String RESET = ChatColor.RESET.toString();
+ public static final String MAGIC = ChatColor.MAGIC.toString();
+ public static final String DARK_BLUE = ChatColor.DARK_BLUE.toString();
+ public static final String DARK_AQUA = ChatColor.DARK_AQUA.toString();
+ public static final String DARK_GRAY = ChatColor.DARK_GRAY.toString();
+ public static final String DARK_GREEN = ChatColor.DARK_GREEN.toString();
+ public static final String DARK_PURPLE = ChatColor.DARK_PURPLE.toString();
+ public static final String DARK_RED = ChatColor.DARK_RED.toString();
+ public static final String PINK = ChatColor.LIGHT_PURPLE.toString();
+ public static final String UNICODE_VERTICAL_BAR = CC.GRAY + StringEscapeUtils.unescapeJava("\u2503");
+ public static final String UNICODE_CAUTION = StringEscapeUtils.unescapeJava("\u26a0");
+ public static final String UNICODE_ARROW_LEFT = StringEscapeUtils.unescapeJava("\u25C0");
+ public static final String UNICODE_ARROW_RIGHT = StringEscapeUtils.unescapeJava("\u25B6");
+ public static final String UNICODE_ARROWS_LEFT = StringEscapeUtils.unescapeJava("\u00AB");
+ public static final String UNICODE_ARROWS_RIGHT = StringEscapeUtils.unescapeJava("\u00BB");
+ public static final String UNICODE_HEART = StringEscapeUtils.unescapeJava("\u2764");
+ public static final String MENU_BAR = ChatColor.GRAY.toString() + ChatColor.STRIKETHROUGH.toString() + "------------------------";
+ public static final String CHAT_BAR = ChatColor.GRAY.toString() + ChatColor.STRIKETHROUGH.toString() + "------------------------------------------------";
+ public static final String SB_BAR = ChatColor.GRAY.toString() + ChatColor.STRIKETHROUGH.toString() + "----------------------";
+
+ public static String translate(String in) {
+ return ChatColor.translateAlternateColorCodes('&', in);
+ }
+
+ public static List translate(List lines) {
+ List toReturn = new ArrayList<>();
+
+ for (String line : lines) {
+ toReturn.add(ChatColor.translateAlternateColorCodes('&', line));
+ }
+
+ return toReturn;
+ }
+
+ public static List translate(String[] lines) {
+ List toReturn = new ArrayList<>();
+
+ for (String line : lines) {
+ if (line != null) {
+ toReturn.add(ChatColor.translateAlternateColorCodes('&', line));
+ }
+ }
+
+ return toReturn;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/CollectionUtil.java b/src/main/java/net/centilehcf/core/util/CollectionUtil.java
new file mode 100644
index 0000000..65b11c7
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/CollectionUtil.java
@@ -0,0 +1,18 @@
+package net.centilehcf.core.util;
+
+import lombok.experimental.UtilityClass;
+
+import java.util.Collection;
+
+@UtilityClass
+public class CollectionUtil {
+
+
+ // Taken from Peter Lawrey on StackOverflow
+ // https://stackoverflow.com/questions/21092086/get-random-element-from-collection
+ public T random(Collection coll) {
+ int num = (int) (Math.random() * coll.size());
+ for (T t : coll) if (--num < 0) return t;
+ throw new AssertionError();
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/util/Cooldown.java b/src/main/java/net/centilehcf/core/util/Cooldown.java
new file mode 100644
index 0000000..1aae03d
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/Cooldown.java
@@ -0,0 +1,40 @@
+package net.centilehcf.core.util;
+
+import lombok.Data;
+
+@Data
+public class Cooldown {
+
+ private long start = System.currentTimeMillis();
+ private long expire;
+ private boolean notified;
+
+ public Cooldown(long duration) {
+ this.expire = this.start + duration;
+
+ if (duration == 0) {
+ this.notified = true;
+ }
+ }
+
+ public long getPassed() {
+ return System.currentTimeMillis() - this.start;
+ }
+
+ public long getRemaining() {
+ return this.expire - System.currentTimeMillis();
+ }
+
+ public boolean hasExpired() {
+ return System.currentTimeMillis() - this.expire >= 0;
+ }
+
+ public String getTimeLeft() {
+ if (this.getRemaining() >= 60_000) {
+ return TimeUtil.millisToRoundedTime(this.getRemaining());
+ } else {
+ return TimeUtil.millisToSeconds(this.getRemaining());
+ }
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/HastebinUtil.java b/src/main/java/net/centilehcf/core/util/HastebinUtil.java
new file mode 100644
index 0000000..9e0e196
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/HastebinUtil.java
@@ -0,0 +1,47 @@
+package net.centilehcf.core.util;
+
+import lombok.experimental.UtilityClass;
+import net.minecraft.util.com.google.gson.JsonParser;
+
+import javax.net.ssl.HttpsURLConnection;
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+
+@UtilityClass
+public class HastebinUtil {
+
+ private String HASTEBIN_POST_URL = "https://hastebin.com/documents";
+
+ private String HASTEBIN_URL = "https://hastebin.com/";
+
+ public String paste(String content) {
+ try {
+ byte[] data = content.getBytes(StandardCharsets.UTF_8);
+ URL hasteURL = new URL(HASTEBIN_POST_URL);
+ HttpsURLConnection connection = (HttpsURLConnection) hasteURL.openConnection();
+
+ connection.setRequestMethod("POST"); // We want to POST the paste data
+ connection.setDoOutput(true);
+ connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+ connection.setRequestProperty("Content-Length", data.length + "");
+
+ DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());
+ dataOutputStream.write(data); // Write the content to the output stream
+ // Cleanup
+ dataOutputStream.flush();
+ dataOutputStream.close();
+
+ // Receive the response from hastebin
+ BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ String responseUrl = new JsonParser().parse(reader.readLine()).getAsJsonObject().get("key").getAsString();
+ reader.close();
+ return HASTEBIN_URL + responseUrl;
+ } catch (Exception ex) { // Broad catch statement to handle anything that could've gone wrong
+ ex.printStackTrace();
+ return "Couldn't paste! Error: " + ex.getCause() + '!';
+ }
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/util/ItemBuilder.java b/src/main/java/net/centilehcf/core/util/ItemBuilder.java
new file mode 100644
index 0000000..5bf8c7c
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/ItemBuilder.java
@@ -0,0 +1,107 @@
+package net.centilehcf.core.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.bukkit.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.enchantments.Enchantment;
+import org.bukkit.event.Listener;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+public class ItemBuilder implements Listener {
+
+ private ItemStack is;
+
+ public ItemBuilder(Material mat) {
+ is = new ItemStack(mat);
+ }
+
+ public ItemBuilder(ItemStack is) {
+ this.is = is;
+ }
+
+ public ItemBuilder amount(int amount) {
+ is.setAmount(amount);
+ return this;
+ }
+
+ public ItemBuilder name(String name) {
+ ItemMeta meta = is.getItemMeta();
+ meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', name));
+ is.setItemMeta(meta);
+ return this;
+ }
+
+ public ItemBuilder lore(String name) {
+ ItemMeta meta = is.getItemMeta();
+ List lore = meta.getLore();
+
+ if (lore == null) {
+ lore = new ArrayList<>();
+ }
+
+ lore.add(ChatColor.translateAlternateColorCodes('&', name));
+ meta.setLore(lore);
+
+ is.setItemMeta(meta);
+
+ return this;
+ }
+
+ public ItemBuilder lore(List lore) {
+ List toSet = new ArrayList<>();
+ ItemMeta meta = is.getItemMeta();
+
+ for (String string : lore) {
+ toSet.add(ChatColor.translateAlternateColorCodes('&', string));
+ }
+
+ meta.setLore(toSet);
+ is.setItemMeta(meta);
+
+ return this;
+ }
+
+ public ItemBuilder durability(int durability) {
+ is.setDurability((short) durability);
+ return this;
+ }
+
+ public ItemBuilder enchantment(Enchantment enchantment, int level) {
+ is.addUnsafeEnchantment(enchantment, level);
+ return this;
+ }
+
+ public ItemBuilder enchantment(Enchantment enchantment) {
+ is.addUnsafeEnchantment(enchantment, 1);
+ return this;
+ }
+
+ public ItemBuilder type(Material material) {
+ is.setType(material);
+ return this;
+ }
+
+ public ItemBuilder clearLore() {
+ ItemMeta meta = is.getItemMeta();
+
+ meta.setLore(new ArrayList<>());
+ is.setItemMeta(meta);
+
+ return this;
+ }
+
+ public ItemBuilder clearEnchantments() {
+ for (Enchantment e : is.getEnchantments().keySet()) {
+ is.removeEnchantment(e);
+ }
+
+ return this;
+ }
+
+ public ItemStack build() {
+ return is;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/centilehcf/core/util/LocationUtil.java b/src/main/java/net/centilehcf/core/util/LocationUtil.java
new file mode 100644
index 0000000..373c946
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/LocationUtil.java
@@ -0,0 +1,38 @@
+package net.centilehcf.core.util;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+
+public class LocationUtil {
+
+ public static Location[] getFaces(Location start) {
+ Location[] faces = new Location[4];
+ faces[0] = new Location(start.getWorld(), start.getX() + 1, start.getY(), start.getZ());
+ faces[1] = new Location(start.getWorld(), start.getX() - 1, start.getY(), start.getZ());
+ faces[2] = new Location(start.getWorld(), start.getX(), start.getY() + 1, start.getZ());
+ faces[3] = new Location(start.getWorld(), start.getX(), start.getY() - 1, start.getZ());
+ return faces;
+ }
+
+ public static String serialize(Location location) {
+ return location.getWorld().getName() + ":" + location.getX() + ":" + location.getY() + ":" + location.getZ() +
+ ":" + location.getYaw() + ":" + location.getPitch();
+ }
+
+ public static Location deserialize(String source) {
+ if (source == null) {
+ return null;
+ }
+
+ String[] split = source.split(":");
+ World world = Bukkit.getServer().getWorld(split[0]);
+
+ if (world == null) {
+ return null;
+ }
+
+ return new Location(world, Double.parseDouble(split[1]), Double.parseDouble(split[2]), Double.parseDouble(split[3]), Float.parseFloat(split[4]), Float.parseFloat(split[5]));
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/PotionUtil.java b/src/main/java/net/centilehcf/core/util/PotionUtil.java
new file mode 100644
index 0000000..6e30d22
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/PotionUtil.java
@@ -0,0 +1,21 @@
+package net.centilehcf.core.util;
+
+import org.bukkit.potion.PotionEffectType;
+
+public class PotionUtil {
+
+ public static String getName(PotionEffectType potionEffectType) {
+ if (potionEffectType.getName().equalsIgnoreCase("fire_resistance")) {
+ return "Fire Resistance";
+ } else if (potionEffectType.getName().equalsIgnoreCase("speed")) {
+ return "Speed";
+ } else if (potionEffectType.getName().equalsIgnoreCase("weakness")) {
+ return "Weakness";
+ } else if (potionEffectType.getName().equalsIgnoreCase("slowness")) {
+ return "Slowness";
+ } else {
+ return "Unknown";
+ }
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/TaskUtil.java b/src/main/java/net/centilehcf/core/util/TaskUtil.java
new file mode 100644
index 0000000..4062215
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/TaskUtil.java
@@ -0,0 +1,40 @@
+package net.centilehcf.core.util;
+
+import net.centilehcf.core.Core;
+import org.bukkit.scheduler.BukkitRunnable;
+
+/**
+ * Created by DaddyDombo daddydombo@gmail.com on 3/30/2019.
+ */
+public class TaskUtil {
+ public TaskUtil() {
+ }
+
+ public static void run(Runnable runnable) {
+ Core.get().getServer().getScheduler().runTask(Core.get(), runnable);
+ }
+
+ public static void runTimer(Runnable runnable, long delay, long timer) {
+ Core.get().getServer().getScheduler().runTaskTimer(Core.get(), runnable, delay, timer);
+ }
+
+ public static void runTimer(BukkitRunnable runnable, long delay, long timer) {
+ runnable.runTaskTimer(Core.get(), delay, timer);
+ }
+
+ public static void runLater(Runnable runnable, long delay) {
+ Core.get().getServer().getScheduler().runTaskLater(Core.get(), runnable, delay);
+ }
+
+ public static void runAsync(Runnable runnable) {
+ Core.get().getServer().getScheduler().runTaskAsynchronously(Core.get(), runnable);
+ }
+
+ public static void runTimerAsync(Runnable runnable, long delay, long timer) {
+ Core.get().getServer().getScheduler().runTaskTimerAsynchronously(Core.get(), runnable, delay, timer);
+ }
+
+ public static Thread runAsyncReturn(Runnable runnable) {
+ return new Thread(runnable);
+ }
+}
diff --git a/src/main/java/net/centilehcf/core/util/TextSplitter.java b/src/main/java/net/centilehcf/core/util/TextSplitter.java
new file mode 100644
index 0000000..16e771f
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/TextSplitter.java
@@ -0,0 +1,35 @@
+package net.centilehcf.core.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class TextSplitter {
+
+ public static List split(int length, String text, String prefix) {
+ if (text.length() <= length) {
+ return Collections.singletonList(prefix + text);
+ }
+
+ List lines = new ArrayList<>();
+ String[] split = text.split(" ");
+ StringBuilder builder = new StringBuilder(prefix);
+
+ for (int i = 0; i < split.length; ++i) {
+ if (builder.length() + split[i].length() >= length) {
+ lines.add(builder.toString());
+ builder = new StringBuilder(prefix);
+ }
+
+ builder.append(split[i]);
+ builder.append(" ");
+ }
+
+ if (builder.length() != 0) {
+ lines.add(builder.toString());
+ }
+
+ return lines;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/TimeUtil.java b/src/main/java/net/centilehcf/core/util/TimeUtil.java
new file mode 100644
index 0000000..3806074
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/TimeUtil.java
@@ -0,0 +1,144 @@
+package net.centilehcf.core.util;
+
+import java.sql.Timestamp;
+import java.text.DecimalFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class TimeUtil {
+
+ private static final String HOUR_FORMAT = "%02d:%02d:%02d";
+ private static final String MINUTE_FORMAT = "%02d:%02d";
+
+ private TimeUtil() {
+ throw new RuntimeException("Cannot instantiate a utility class.");
+ }
+
+ public static String millisToTimer(long millis) {
+ long seconds = millis / 1000L;
+
+ if (seconds > 3600L) {
+ return String.format(HOUR_FORMAT, seconds / 3600L, seconds % 3600L / 60L, seconds % 60L);
+ } else {
+ return String.format(MINUTE_FORMAT, seconds / 60L, seconds % 60L);
+ }
+ }
+
+ /**
+ * Return the amount of seconds from milliseconds.
+ * Note: We explicitly use 1000.0F (float) instead of 1000L (long).
+ *
+ * @param millis the amount of time in milliseconds
+ * @return the seconds
+ */
+ public static String millisToSeconds(long millis) {
+ return new DecimalFormat("#0.0").format(millis / 1000.0F);
+ }
+
+ public static String dateToString(Date date) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(date);
+
+ return calendar.getTime().toString();
+ }
+
+ public static Timestamp addDuration(long duration) {
+ return truncateTimestamp(new Timestamp(System.currentTimeMillis() + duration));
+ }
+
+ public static Timestamp truncateTimestamp(Timestamp timestamp) {
+ if (timestamp.toLocalDateTime().getYear() > 2037) {
+ timestamp.setYear(2037);
+ }
+
+ return timestamp;
+ }
+
+ public static Timestamp addDuration(Timestamp timestamp) {
+ return truncateTimestamp(new Timestamp(System.currentTimeMillis() + timestamp.getTime()));
+ }
+
+ public static Timestamp fromMillis(long millis) {
+ return new Timestamp(millis);
+ }
+
+ public static Timestamp getCurrentTimestamp() {
+ return new Timestamp(System.currentTimeMillis());
+ }
+
+ public static String millisToRoundedTime(long millis) {
+ millis += 1L;
+
+ long seconds = millis / 1000L;
+ long minutes = seconds / 60L;
+ long hours = minutes / 60L;
+ long days = hours / 24L;
+ long weeks = days / 7L;
+ long months = weeks / 4L;
+ long years = months / 12L;
+
+ if (years > 0) {
+ return years + " year" + (years == 1 ? "" : "s");
+ } else if (months > 0) {
+ return months + " month" + (months == 1 ? "" : "s");
+ } else if (weeks > 0) {
+ return weeks + " week" + (weeks == 1 ? "" : "s");
+ } else if (days > 0) {
+ return days + " day" + (days == 1 ? "" : "s");
+ } else if (hours > 0) {
+ return hours + " hour" + (hours == 1 ? "" : "s");
+ } else if (minutes > 0) {
+ return minutes + " minute" + (minutes == 1 ? "" : "s");
+ } else {
+ return seconds + " second" + (seconds == 1 ? "" : "s");
+ }
+ }
+
+ public static long parseTime(String time) {
+ long totalTime = 0L;
+ boolean found = false;
+ Matcher matcher = Pattern.compile("\\d+\\D+").matcher(time);
+
+ while (matcher.find()) {
+ String s = matcher.group();
+ Long value = Long.parseLong(s.split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)")[0]);
+ String type = s.split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)")[1];
+
+ switch (type) {
+ case "s":
+ totalTime += value;
+ found = true;
+ break;
+ case "m":
+ totalTime += value * 60;
+ found = true;
+ break;
+ case "h":
+ totalTime += value * 60 * 60;
+ found = true;
+ break;
+ case "d":
+ totalTime += value * 60 * 60 * 24;
+ found = true;
+ break;
+ case "w":
+ totalTime += value * 60 * 60 * 24 * 7;
+ found = true;
+ break;
+ case "M":
+ totalTime += value * 60 * 60 * 24 * 30;
+ found = true;
+ break;
+ case "y":
+ totalTime += value * 60 * 60 * 24 * 365;
+ found = true;
+ break;
+ }
+ }
+
+ return !found ? -1 : totalTime * 1000;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/callback/Callback.java b/src/main/java/net/centilehcf/core/util/callback/Callback.java
new file mode 100644
index 0000000..e043d66
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/callback/Callback.java
@@ -0,0 +1,12 @@
+package net.centilehcf.core.util.callback;
+
+import java.io.Serializable;
+
+public interface Callback extends Serializable {
+
+ /**
+ * A callback for running a task on a set of data.
+ */
+ void callback();
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/callback/ReturnableTypeCallback.java b/src/main/java/net/centilehcf/core/util/callback/ReturnableTypeCallback.java
new file mode 100644
index 0000000..b2cc725
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/callback/ReturnableTypeCallback.java
@@ -0,0 +1,7 @@
+package net.centilehcf.core.util.callback;
+
+public interface ReturnableTypeCallback {
+
+ T call();
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/callback/TypeCallback.java b/src/main/java/net/centilehcf/core/util/callback/TypeCallback.java
new file mode 100644
index 0000000..84b4217
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/callback/TypeCallback.java
@@ -0,0 +1,14 @@
+package net.centilehcf.core.util.callback;
+
+import java.io.Serializable;
+
+public interface TypeCallback extends Serializable {
+
+ /**
+ * A callback for running a task on a set of data.
+ *
+ * @param data the data needed to run the task.
+ */
+ void callback(T data);
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/duration/Duration.java b/src/main/java/net/centilehcf/core/util/duration/Duration.java
new file mode 100644
index 0000000..0f6a109
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/duration/Duration.java
@@ -0,0 +1,64 @@
+package net.centilehcf.core.util.duration;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import lombok.Getter;
+
+public class Duration {
+
+ @Getter private long value;
+
+ public Duration(long value) {
+ this.value = value;
+ }
+
+ public static Duration fromString(String source) {
+ if (source.equalsIgnoreCase("perm") || source.equalsIgnoreCase("permanent")) {
+ return new Duration(Integer.MAX_VALUE);
+ }
+
+ long totalTime = 0L;
+ boolean found = false;
+ Matcher matcher = Pattern.compile("\\d+\\D+").matcher(source);
+
+ while (matcher.find()) {
+ String s = matcher.group();
+ Long value = Long.parseLong(s.split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)")[0]);
+ String type = s.split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)")[1];
+
+ switch (type) {
+ case "s":
+ totalTime += value;
+ found = true;
+ break;
+ case "m":
+ totalTime += value * 60;
+ found = true;
+ break;
+ case "h":
+ totalTime += value * 60 * 60;
+ found = true;
+ break;
+ case "d":
+ totalTime += value * 60 * 60 * 24;
+ found = true;
+ break;
+ case "w":
+ totalTime += value * 60 * 60 * 24 * 7;
+ found = true;
+ break;
+ case "M":
+ totalTime += value * 60 * 60 * 24 * 30;
+ found = true;
+ break;
+ case "y":
+ totalTime += value * 60 * 60 * 24 * 365;
+ found = true;
+ break;
+ }
+ }
+
+ return new Duration(!found ? -1 : totalTime * 1000);
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/duration/DurationTypeAdapter.java b/src/main/java/net/centilehcf/core/util/duration/DurationTypeAdapter.java
new file mode 100644
index 0000000..6acffdc
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/duration/DurationTypeAdapter.java
@@ -0,0 +1,13 @@
+package net.centilehcf.core.util.duration;
+
+import com.qrakn.honcho.command.adapter.CommandTypeAdapter;
+
+public class DurationTypeAdapter implements CommandTypeAdapter {
+
+ @Override
+ public T convert(String string, Class type) {
+ return type.cast(Duration.fromString(string));
+ }
+
+}
+
diff --git a/src/main/java/net/centilehcf/core/util/json/JsonChain.java b/src/main/java/net/centilehcf/core/util/json/JsonChain.java
new file mode 100644
index 0000000..2a465fe
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/json/JsonChain.java
@@ -0,0 +1,39 @@
+package net.centilehcf.core.util.json;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+public class JsonChain {
+
+ private JsonObject json = new JsonObject();
+
+ public JsonChain addProperty(String property, String value) {
+ this.json.addProperty(property, value);
+ return this;
+ }
+
+ public JsonChain addProperty(String property, Number value) {
+ this.json.addProperty(property, value);
+ return this;
+ }
+
+ public JsonChain addProperty(String property, Boolean value) {
+ this.json.addProperty(property, value);
+ return this;
+ }
+
+ public JsonChain addProperty(String property, Character value) {
+ this.json.addProperty(property, value);
+ return this;
+ }
+
+ public JsonChain add(String property, JsonElement element) {
+ this.json.add(property, element);
+ return this;
+ }
+
+ public JsonObject get() {
+ return this.json;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/json/JsonDeserializer.java b/src/main/java/net/centilehcf/core/util/json/JsonDeserializer.java
new file mode 100644
index 0000000..8f39fec
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/json/JsonDeserializer.java
@@ -0,0 +1,9 @@
+package net.centilehcf.core.util.json;
+
+import com.google.gson.JsonObject;
+
+public interface JsonDeserializer {
+
+ T deserialize(JsonObject object);
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/json/JsonSerializer.java b/src/main/java/net/centilehcf/core/util/json/JsonSerializer.java
new file mode 100644
index 0000000..b8b0873
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/json/JsonSerializer.java
@@ -0,0 +1,9 @@
+package net.centilehcf.core.util.json;
+
+import com.google.gson.JsonObject;
+
+public interface JsonSerializer {
+
+ JsonObject serialize(T t);
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/menu/Button.java b/src/main/java/net/centilehcf/core/util/menu/Button.java
new file mode 100644
index 0000000..588efab
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/menu/Button.java
@@ -0,0 +1,54 @@
+package net.centilehcf.core.util.menu;
+
+import org.apache.commons.lang.StringUtils;
+import org.bukkit.Material;
+import org.bukkit.Sound;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+public abstract class Button {
+
+ public static Button placeholder(final Material material, final byte data, String... title) {
+ return (new Button() {
+ public ItemStack getButtonItem(Player player) {
+ ItemStack it = new ItemStack(material, 1, data);
+ ItemMeta meta = it.getItemMeta();
+
+ meta.setDisplayName(StringUtils.join(title));
+ it.setItemMeta(meta);
+
+ return it;
+ }
+ });
+ }
+
+ public static void playFail(Player player) {
+ player.playSound(player.getLocation(), Sound.DIG_GRASS, 20F, 0.1F);
+
+ }
+
+ public static void playSuccess(Player player) {
+ player.playSound(player.getLocation(), Sound.NOTE_PIANO, 20F, 15F);
+ }
+
+ public static void playNeutral(Player player) {
+ player.playSound(player.getLocation(), Sound.CLICK, 20F, 1F);
+ }
+
+ public abstract ItemStack getButtonItem(Player player);
+
+ public void clicked(Player player, ClickType clickType) {}
+
+ public void clicked(Player player, int slot, ClickType clickType, int hotbarSlot) {}
+
+ public boolean shouldCancel(Player player, ClickType clickType) {
+ return true;
+ }
+
+ public boolean shouldUpdate(Player player, ClickType clickType) {
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/centilehcf/core/util/menu/Menu.java b/src/main/java/net/centilehcf/core/util/menu/Menu.java
new file mode 100644
index 0000000..0299324
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/menu/Menu.java
@@ -0,0 +1,138 @@
+package net.centilehcf.core.util.menu;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.util.CC;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+@Getter
+@Setter
+public abstract class Menu {
+
+ public static Map currentlyOpenedMenus = new HashMap<>();
+
+ @Getter protected Core core = Core.get();
+ private Map buttons = new HashMap<>();
+ private boolean autoUpdate = false;
+ private boolean updateAfterClick = true;
+ private boolean closedByMenu = false;
+ private boolean placeholder = false;
+ private Button placeholderButton = Button.placeholder(Material.STAINED_GLASS_PANE, (byte) 15, " ");
+
+ private ItemStack createItemStack(Player player, Button button) {
+ ItemStack item = button.getButtonItem(player);
+
+ if (item.getType() != Material.SKULL_ITEM) {
+ ItemMeta meta = item.getItemMeta();
+
+ if (meta != null && meta.hasDisplayName()) {
+ meta.setDisplayName(meta.getDisplayName() + "§b§c§d§e");
+ }
+
+ item.setItemMeta(meta);
+ }
+
+ return item;
+ }
+
+ public void openMenu(final Player player) {
+ this.buttons = this.getButtons(player);
+
+ Menu previousMenu = Menu.currentlyOpenedMenus.get(player.getUniqueId());
+ Inventory inventory = null;
+ int size = this.getSize() == -1 ? this.size(this.buttons) : this.getSize();
+ boolean update = false;
+ String title = CC.translate(this.getTitle(player));
+
+ if (title.length() > 32) {
+ title = title.substring(0, 32);
+ }
+
+ if (player.getOpenInventory() != null) {
+ if (previousMenu == null) {
+ player.closeInventory();
+ } else {
+ int previousSize = player.getOpenInventory().getTopInventory().getSize();
+
+ if (previousSize == size && player.getOpenInventory().getTopInventory().getTitle().equals(title)) {
+ inventory = player.getOpenInventory().getTopInventory();
+ update = true;
+ } else {
+ previousMenu.setClosedByMenu(true);
+ player.closeInventory();
+ }
+ }
+ }
+
+ if (inventory == null) {
+ inventory = Bukkit.createInventory(player, size, title);
+ }
+
+ inventory.setContents(new ItemStack[inventory.getSize()]);
+
+ currentlyOpenedMenus.put(player.getUniqueId(), this);
+
+ for (Map.Entry buttonEntry : this.buttons.entrySet()) {
+ inventory.setItem(buttonEntry.getKey(), createItemStack(player, buttonEntry.getValue()));
+ }
+
+ if (this.isPlaceholder()) {
+ for (int index = 0; index < size; index++) {
+ if (this.buttons.get(index) == null) {
+ this.buttons.put(index, this.placeholderButton);
+ inventory.setItem(index, this.placeholderButton.getButtonItem(player));
+ }
+ }
+ }
+
+ if (update) {
+ player.updateInventory();
+ } else {
+ player.openInventory(inventory);
+ }
+
+ this.onOpen(player);
+ this.setClosedByMenu(false);
+ }
+
+ public int size(Map buttons) {
+ int highest = 0;
+
+ for (int buttonValue : buttons.keySet()) {
+ if (buttonValue > highest) {
+ highest = buttonValue;
+ }
+ }
+
+ return (int) (Math.ceil((highest + 1) / 9D) * 9D);
+ }
+
+ public int getSize() {
+ return -1;
+ }
+
+ public int getSlot(int x, int y) {
+ return ((9 * y) + x);
+ }
+
+ public abstract String getTitle(Player player);
+
+ public abstract Map getButtons(Player player);
+
+ public void onOpen(Player player) {
+ }
+
+ public void onClose(Player player) {
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/menu/MenuListener.java b/src/main/java/net/centilehcf/core/util/menu/MenuListener.java
new file mode 100644
index 0000000..9daece1
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/menu/MenuListener.java
@@ -0,0 +1,93 @@
+package net.centilehcf.core.util.menu;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.bootstrap.BootstrappedListener;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+
+public class MenuListener extends BootstrappedListener {
+
+ public MenuListener(Core core) {
+ super(core);
+ }
+
+ @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
+ public void onButtonPress(InventoryClickEvent event) {
+ Player player = (Player) event.getWhoClicked();
+ Menu openMenu = Menu.currentlyOpenedMenus.get(player.getUniqueId());
+
+ if (openMenu != null) {
+ if (event.getSlot() != event.getRawSlot()) {
+ if ((event.getClick() == ClickType.SHIFT_LEFT || event.getClick() == ClickType.SHIFT_RIGHT)) {
+ event.setCancelled(true);
+ }
+
+ return;
+ }
+
+ if (openMenu.getButtons().containsKey(event.getSlot())) {
+ Button button = openMenu.getButtons().get(event.getSlot());
+ boolean cancel = button.shouldCancel(player, event.getClick());
+
+ if (!cancel && (event.getClick() == ClickType.SHIFT_LEFT || event.getClick() == ClickType.SHIFT_RIGHT)) {
+ event.setCancelled(true);
+
+ if (event.getCurrentItem() != null) {
+ player.getInventory().addItem(event.getCurrentItem());
+ }
+ } else {
+ event.setCancelled(cancel);
+ }
+
+ button.clicked(player, event.getClick());
+ button.clicked(player, event.getSlot(), event.getClick(), event.getHotbarButton());
+
+ if (Menu.currentlyOpenedMenus.containsKey(player.getUniqueId())) {
+ Menu newMenu = Menu.currentlyOpenedMenus.get(player.getUniqueId());
+
+ if (newMenu == openMenu) {
+ boolean buttonUpdate = button.shouldUpdate(player, event.getClick());
+
+ if (buttonUpdate) {
+ openMenu.setClosedByMenu(true);
+ newMenu.openMenu(player);
+ }
+ }
+ } else if (button.shouldUpdate(player, event.getClick())) {
+ openMenu.setClosedByMenu(true);
+ openMenu.openMenu(player);
+ }
+
+ if (event.isCancelled()) {
+ Bukkit.getScheduler().runTaskLater(core, player::updateInventory, 1L);
+ }
+ } else {
+ if (event.getCurrentItem() != null) {
+ event.setCancelled(true);
+ }
+
+ if ((event.getClick() == ClickType.SHIFT_LEFT || event.getClick() == ClickType.SHIFT_RIGHT)) {
+ event.setCancelled(true);
+ }
+ }
+ }
+ }
+
+ @EventHandler(priority = EventPriority.HIGH)
+ public void onInventoryClose(InventoryCloseEvent event) {
+ Player player = (Player) event.getPlayer();
+ Menu openMenu = Menu.currentlyOpenedMenus.get(player.getUniqueId());
+
+ if (openMenu != null) {
+ openMenu.onClose(player);
+
+ Menu.currentlyOpenedMenus.remove(player.getUniqueId());
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/centilehcf/core/util/menu/button/BackButton.java b/src/main/java/net/centilehcf/core/util/menu/button/BackButton.java
new file mode 100644
index 0000000..77aa324
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/menu/button/BackButton.java
@@ -0,0 +1,40 @@
+package net.centilehcf.core.util.menu.button;
+
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.ItemBuilder;
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.Menu;
+import java.util.Arrays;
+import lombok.AllArgsConstructor;
+import net.centilehcf.core.util.CC;
+import net.centilehcf.core.util.ItemBuilder;
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.Menu;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+@AllArgsConstructor
+public class BackButton extends Button {
+
+ private Menu back;
+
+ @Override
+ public ItemStack getButtonItem(Player player) {
+ return new ItemBuilder(Material.REDSTONE)
+ .name(CC.RED + CC.BOLD + "Back")
+ .lore(Arrays.asList(
+ CC.RED + "Click here to return to",
+ CC.RED + "the previous menu.")
+ )
+ .build();
+ }
+
+ @Override
+ public void clicked(Player player, ClickType clickType) {
+ Button.playNeutral(player);
+ back.openMenu(player);
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/menu/button/ConfirmationButton.java b/src/main/java/net/centilehcf/core/util/menu/button/ConfirmationButton.java
new file mode 100644
index 0000000..0fe35e2
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/menu/button/ConfirmationButton.java
@@ -0,0 +1,57 @@
+package net.centilehcf.core.util.menu.button;
+
+import net.centilehcf.core.util.callback.TypeCallback;
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.Menu;
+import lombok.AllArgsConstructor;
+import net.centilehcf.core.util.callback.TypeCallback;
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.Menu;
+import org.bukkit.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.Sound;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+@AllArgsConstructor
+public class ConfirmationButton extends Button {
+
+ private boolean confirm;
+ private TypeCallback callback;
+ private boolean closeAfterResponse;
+
+ @Override
+ public ItemStack getButtonItem(Player player) {
+ ItemStack itemStack = new ItemStack(Material.WOOL, 1, this.confirm ? ((byte) 5) : ((byte) 14));
+ ItemMeta itemMeta = itemStack.getItemMeta();
+
+ itemMeta.setDisplayName(this.confirm ? ChatColor.GREEN + "Confirm" : ChatColor.RED + "Cancel");
+ itemStack.setItemMeta(itemMeta);
+
+ return itemStack;
+ }
+
+ @Override
+ public void clicked(Player player, ClickType clickType) {
+ if (this.confirm) {
+ player.playSound(player.getLocation(), Sound.NOTE_PIANO, 20f, 0.1f);
+ } else {
+ player.playSound(player.getLocation(), Sound.DIG_GRAVEL, 20f, 0.1F);
+ }
+
+ if (this.closeAfterResponse) {
+ Menu menu = Menu.currentlyOpenedMenus.get(player.getUniqueId());
+
+ if (menu != null) {
+ menu.setClosedByMenu(true);
+ }
+
+ player.closeInventory();
+ }
+
+ this.callback.callback(this.confirm);
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/menu/button/DisplayButton.java b/src/main/java/net/centilehcf/core/util/menu/button/DisplayButton.java
new file mode 100644
index 0000000..42cb839
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/menu/button/DisplayButton.java
@@ -0,0 +1,34 @@
+package net.centilehcf.core.util.menu.button;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+import net.centilehcf.core.util.menu.Button;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+@AllArgsConstructor
+@Getter
+@Setter
+public class DisplayButton extends Button {
+
+ private ItemStack itemStack;
+ private boolean cancel;
+
+ @Override
+ public ItemStack getButtonItem(Player player) {
+ if (this.itemStack == null) {
+ return new ItemStack(Material.AIR);
+ } else {
+ return this.itemStack;
+ }
+ }
+
+ @Override
+ public boolean shouldCancel(Player player, ClickType clickType) {
+ return this.cancel;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/menu/button/JumpToMenuButton.java b/src/main/java/net/centilehcf/core/util/menu/button/JumpToMenuButton.java
new file mode 100644
index 0000000..82442ed
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/menu/button/JumpToMenuButton.java
@@ -0,0 +1,31 @@
+package net.centilehcf.core.util.menu.button;
+
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.Menu;
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.Menu;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+public class JumpToMenuButton extends Button {
+
+ private Menu menu;
+ private ItemStack itemStack;
+
+ public JumpToMenuButton(Menu menu, ItemStack itemStack) {
+ this.menu = menu;
+ this.itemStack = itemStack;
+ }
+
+ @Override
+ public ItemStack getButtonItem(Player player) {
+ return itemStack;
+ }
+
+ @Override
+ public void clicked(Player player, ClickType clickType) {
+ menu.openMenu(player);
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/menu/menus/ConfirmMenu.java b/src/main/java/net/centilehcf/core/util/menu/menus/ConfirmMenu.java
new file mode 100644
index 0000000..f00e19a
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/menu/menus/ConfirmMenu.java
@@ -0,0 +1,55 @@
+package net.centilehcf.core.util.menu.menus;
+
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.Menu;
+import net.centilehcf.core.util.menu.button.ConfirmationButton;
+import net.centilehcf.core.util.callback.TypeCallback;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.centilehcf.core.util.callback.TypeCallback;
+import net.centilehcf.core.util.menu.Menu;
+import org.bukkit.entity.Player;
+
+public class ConfirmMenu extends Menu {
+
+ private String title;
+ private TypeCallback response;
+ private boolean closeAfterResponse;
+ private Button[] centerButtons;
+
+ public ConfirmMenu(String title, TypeCallback response, boolean closeAfter, Button... centerButtons) {
+ this.title = title;
+ this.response = response;
+ this.closeAfterResponse = closeAfter;
+ this.centerButtons = centerButtons;
+ }
+
+ @Override
+ public Map getButtons(Player player) {
+ HashMap buttons = new HashMap<>();
+
+ for (int x = 0; x < 3; x++) {
+ for (int y = 0; y < 3; y++) {
+ buttons.put(getSlot(x, y), new ConfirmationButton(true, response, closeAfterResponse));
+ buttons.put(getSlot(8 - x, y), new ConfirmationButton(false, response, closeAfterResponse));
+ }
+ }
+
+ if (centerButtons != null) {
+ for (int i = 0; i < centerButtons.length; i++) {
+ if (centerButtons[i] != null) {
+ buttons.put(getSlot(4, i), centerButtons[i]);
+ }
+ }
+ }
+
+ return buttons;
+ }
+
+ @Override
+ public String getTitle(Player player) {
+ return title;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/menu/pagination/JumpToPageButton.java b/src/main/java/net/centilehcf/core/util/menu/pagination/JumpToPageButton.java
new file mode 100644
index 0000000..6006422
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/menu/pagination/JumpToPageButton.java
@@ -0,0 +1,45 @@
+package net.centilehcf.core.util.menu.pagination;
+
+import net.centilehcf.core.util.menu.Button;
+import java.util.Arrays;
+import lombok.AllArgsConstructor;
+import org.bukkit.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+@AllArgsConstructor
+public class JumpToPageButton extends Button {
+
+ private int page;
+ private PaginatedMenu menu;
+ private boolean current;
+
+ @Override
+ public ItemStack getButtonItem(Player player) {
+ ItemStack itemStack = new ItemStack(this.current ? Material.ENCHANTED_BOOK : Material.BOOK, this.page);
+ ItemMeta itemMeta = itemStack.getItemMeta();
+
+ itemMeta.setDisplayName(ChatColor.YELLOW + "Page " + this.page);
+
+ if (this.current) {
+ itemMeta.setLore(Arrays.asList(
+ "",
+ ChatColor.GREEN + "Current page"
+ ));
+ }
+
+ itemStack.setItemMeta(itemMeta);
+
+ return itemStack;
+ }
+
+ @Override
+ public void clicked(Player player, ClickType clickType) {
+ this.menu.modPage(player, this.page - this.menu.getPage());
+ Button.playNeutral(player);
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/menu/pagination/PageButton.java b/src/main/java/net/centilehcf/core/util/menu/pagination/PageButton.java
new file mode 100644
index 0000000..bf169c7
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/menu/pagination/PageButton.java
@@ -0,0 +1,61 @@
+package net.centilehcf.core.util.menu.pagination;
+
+import java.util.Arrays;
+import lombok.AllArgsConstructor;
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.Button;
+import org.bukkit.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+@AllArgsConstructor
+public class PageButton extends Button {
+
+ private int mod;
+ private PaginatedMenu menu;
+
+ @Override
+ public ItemStack getButtonItem(Player player) {
+ ItemStack itemStack = new ItemStack(Material.ARROW);
+ ItemMeta itemMeta = itemStack.getItemMeta();
+
+ if (this.hasNext(player)) {
+ itemMeta.setDisplayName(this.mod > 0 ? ChatColor.GREEN + "Next page" : ChatColor.RED + "Previous page");
+ } else {
+ itemMeta.setDisplayName(ChatColor.YELLOW + (this.mod > 0 ? "Last page" : "First page"));
+ }
+
+ itemMeta.setLore(Arrays.asList(
+ ChatColor.GRAY + "Click here to",
+ ChatColor.GRAY + "jump to a page"
+ ));
+
+ itemStack.setItemMeta(itemMeta);
+
+ return itemStack;
+ }
+
+ @Override
+ public void clicked(Player player, ClickType clickType) {
+ if (clickType == ClickType.RIGHT) {
+ new ViewAllPagesMenu(this.menu).openMenu(player);
+ playNeutral(player);
+ } else {
+ if (hasNext(player)) {
+ this.menu.modPage(player, this.mod);
+ Button.playNeutral(player);
+ } else {
+ Button.playFail(player);
+ }
+ }
+ }
+
+ private boolean hasNext(Player player) {
+ int pg = this.menu.getPage() + this.mod;
+ return pg > 0 && this.menu.getPages(player) >= pg;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/menu/pagination/PaginatedMenu.java b/src/main/java/net/centilehcf/core/util/menu/pagination/PaginatedMenu.java
new file mode 100644
index 0000000..33881e2
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/menu/pagination/PaginatedMenu.java
@@ -0,0 +1,107 @@
+package net.centilehcf.core.util.menu.pagination;
+
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.Menu;
+import java.util.HashMap;
+import java.util.Map;
+import lombok.Getter;
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.Menu;
+import org.bukkit.entity.Player;
+
+public abstract class PaginatedMenu extends Menu {
+
+ @Getter private int page = 1;
+
+ {
+ setUpdateAfterClick(false);
+ }
+
+ @Override
+ public String getTitle(Player player) {
+ return getPrePaginatedTitle(player) + " &7(page " + page + ")";
+ }
+
+ /**
+ * Changes the page number
+ *
+ * @param player player viewing the inventory
+ * @param mod delta to modify the page number by
+ */
+ public final void modPage(Player player, int mod) {
+ page += mod;
+ getButtons().clear();
+ openMenu(player);
+ }
+
+ /**
+ * @param player player viewing the inventory
+ */
+ public final int getPages(Player player) {
+ int buttonAmount = getAllPagesButtons(player).size();
+
+ if (buttonAmount == 0) {
+ return 1;
+ }
+
+ return (int) Math.ceil(buttonAmount / (double) getMaxItemsPerPage(player));
+ }
+
+ @Override
+ public final Map getButtons(Player player) {
+ int minIndex = (int) ((double) (page - 1) * getMaxItemsPerPage(player));
+ int maxIndex = (int) ((double) (page) * getMaxItemsPerPage(player));
+
+ HashMap buttons = new HashMap<>();
+
+ buttons.put(0, new PageButton(-1, this));
+ buttons.put(8, new PageButton(1, this));
+
+ for (Map.Entry entry : getAllPagesButtons(player).entrySet()) {
+ int ind = entry.getKey();
+
+ if (ind >= minIndex && ind < maxIndex) {
+ ind -= (int) ((double) (getMaxItemsPerPage(player)) * (page - 1)) - 9;
+ buttons.put(ind, entry.getValue());
+ }
+ }
+
+ Map global = getGlobalButtons(player);
+
+ if (global != null) {
+ for (Map.Entry gent : global.entrySet()) {
+ buttons.put(gent.getKey(), gent.getValue());
+ }
+ }
+
+ return buttons;
+ }
+
+ public int getMaxItemsPerPage(Player player) {
+ return 18;
+ }
+
+ /**
+ * @param player player viewing the inventory
+ *
+ * @return a Map of button that returns items which will be present on all pages
+ */
+ public Map getGlobalButtons(Player player) {
+ return null;
+ }
+
+ /**
+ * @param player player viewing the inventory
+ *
+ * @return title of the inventory before the page number is added
+ */
+ public abstract String getPrePaginatedTitle(Player player);
+
+ /**
+ * @param player player viewing the inventory
+ *
+ * @return a map of button that will be paginated and spread across pages
+ */
+ public abstract Map getAllPagesButtons(Player player);
+
+}
diff --git a/src/main/java/net/centilehcf/core/util/menu/pagination/ViewAllPagesMenu.java b/src/main/java/net/centilehcf/core/util/menu/pagination/ViewAllPagesMenu.java
new file mode 100644
index 0000000..d3bf57f
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/util/menu/pagination/ViewAllPagesMenu.java
@@ -0,0 +1,52 @@
+package net.centilehcf.core.util.menu.pagination;
+
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.Menu;
+import net.centilehcf.core.util.menu.button.BackButton;
+import java.util.HashMap;
+import java.util.Map;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import net.centilehcf.core.util.menu.Button;
+import net.centilehcf.core.util.menu.Menu;
+import net.centilehcf.core.util.menu.button.BackButton;
+import org.bukkit.entity.Player;
+
+@RequiredArgsConstructor
+public class ViewAllPagesMenu extends Menu {
+
+ @NonNull
+ @Getter
+ PaginatedMenu menu;
+
+ @Override
+ public String getTitle(Player player) {
+ return "Jump to page";
+ }
+
+ @Override
+ public Map getButtons(Player player) {
+ HashMap buttons = new HashMap<>();
+
+ buttons.put(0, new BackButton(menu));
+
+ int index = 10;
+
+ for (int i = 1; i <= menu.getPages(player); i++) {
+ buttons.put(index++, new JumpToPageButton(i, menu, menu.getPage() == i));
+
+ if ((index - 8) % 9 == 0) {
+ index += 2;
+ }
+ }
+
+ return buttons;
+ }
+
+ @Override
+ public boolean isAutoUpdate() {
+ return true;
+ }
+
+}
diff --git a/src/main/java/net/centilehcf/core/uuid/UUIDCache.java b/src/main/java/net/centilehcf/core/uuid/UUIDCache.java
new file mode 100644
index 0000000..a5c5f4f
--- /dev/null
+++ b/src/main/java/net/centilehcf/core/uuid/UUIDCache.java
@@ -0,0 +1,138 @@
+package net.centilehcf.core.uuid;
+
+import net.centilehcf.core.Core;
+import net.centilehcf.core.bootstrap.Bootstrapped;
+import net.minecraft.util.com.google.gson.JsonObject;
+import net.minecraft.util.com.google.gson.JsonParser;
+import org.json.simple.parser.ParseException;
+import redis.clients.jedis.Jedis;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.UUID;
+import java.util.logging.Level;
+
+public class UUIDCache extends Bootstrapped {
+
+ public static final UUID CONSOLE_UUID = UUID.fromString("f78a4d8d-d51b-4b39-98a3-230f2de0c670");
+
+ public UUIDCache(Core core) {
+ super(core);
+ // Ensure the CONSOLE name & uuid are in the cache
+ core.getServer().getScheduler().runTaskAsynchronously(core, () -> update("CONSOLE", CONSOLE_UUID));
+ }
+
+ private static UUID getUUIDFromNameMojang(String name) throws IOException, ParseException {
+ URL url = new URL("https://api.mojang.com/users/profiles/minecraft/" + name);
+ URLConnection conn = url.openConnection();
+ conn.setDoOutput(true);
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+ String line = reader.readLine();
+
+ if (line == null) {
+ return null;
+ }
+
+ String[] id = line.split(",");
+
+ String part = id[0];
+ part = part.substring(7, 39);
+
+ return UUID.fromString(part.replaceAll("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})",
+ "$1-$2-$3-$4-$5"));
+ }
+
+ private static String getNameFromUUIDMojang(UUID uuid) throws IOException {
+ URL url = new URL("https://api.mojang.com/users/profiles/minecraft/" + uuid.toString().replace("-", ""));
+ URLConnection conn = url.openConnection();
+ conn.setDoOutput(true);
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+ String line = reader.readLine();
+
+ if (line == null) {
+ return null;
+ }
+
+ JsonObject mojangQuery = new JsonParser().parse(line).getAsJsonObject();
+
+ return mojangQuery.get("name").getAsString();
+ }
+
+ public UUID getUuid(String name) {
+ if (name == null || name.isEmpty()) {
+ throw new IllegalArgumentException("The parameter 'name' cannot be null or empty (UUID cache)");
+ }
+
+ if (core.getServer().isPrimaryThread()) {
+ throw new IllegalStateException("Cannot query on main thread (Redis profile cache)");
+ }
+
+ try (Jedis jedis = core.getJedisPool().getResource()) {
+ String uuid = jedis.hget("uuid-cache:name-to-uuid", name.toLowerCase());
+
+ if (uuid != null) {
+ return UUID.fromString(uuid);
+ }
+ } catch (Exception e) {
+ core.debug(Level.WARNING, "Could not connect to redis", e);
+ }
+
+ try {
+ UUID uuid = getUUIDFromNameMojang(name);
+
+ if (uuid != null) {
+ update(name, uuid);
+ return uuid;
+ }
+ } catch (Exception e) {
+ core.debug(Level.WARNING, "Could not fetch from Mojang API", e);
+ }
+
+ return null;
+ }
+
+ public String getName(UUID uuid) {
+ if (uuid == null) {
+ throw new IllegalArgumentException("The parameter 'uuid' cannot be null (UUID cache)");
+ }
+
+ if (core.getServer().isPrimaryThread()) {
+ throw new IllegalStateException("Cannot query on main thread (Redis profile cache)");
+ }
+
+ try (Jedis jedis = core.getJedisPool().getResource()) {
+ String name = jedis.hget("uuid-cache:uuid-to-name", uuid.toString());
+
+ if (name != null) {
+ return name;
+ }
+ } catch (Exception e) {
+ core.debug(Level.WARNING, "Could not connect to redis", e);
+ }
+
+ try {
+ String name = getNameFromUUIDMojang(uuid);
+
+ if (name != null) {
+ update(name, uuid);
+ return name;
+ }
+ } catch (Exception e) {
+ core.debug(Level.WARNING, "Could not fetch from Mojang API", e);
+ }
+ return null;
+ }
+
+ public void update(String name, UUID uuid) {
+ try (Jedis jedis = core.getJedisPool().getResource()) {
+ jedis.hset("uuid-cache:name-to-uuid", name.toLowerCase(), uuid.toString());
+ jedis.hset("uuid-cache:uuid-to-name", uuid.toString(), name);
+ }
+ }
+
+}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
new file mode 100644
index 0000000..be3d3ad
--- /dev/null
+++ b/src/main/resources/config.yml
@@ -0,0 +1,44 @@
+SERVER_NAME: "Development"
+MONGO:
+ HOST: "127.0.0.1"
+ PORT: 27017
+ AUTHENTICATION:
+ ENABLED: false
+ USERNAME: ""
+ PASSWORD: ""
+REDIS:
+ HOST: "127.0.0.1"
+ PORT: 6379
+ AUTHENTICATION:
+ ENABLED: false
+ PASSWORD: ""
+TAB: true
+PUNISHMENTS:
+ BROADCAST: "&r{target} &ahas been {context} by &r{sender}"
+ BROADCAST_SILENT: "&7[Silent] &r{target} &ahas been {context} by &r{sender}"
+ BLACKLIST: "&cYour account is Blacklisted from CentileHCF.\n\n&cThis Punishment cant not be undone."
+ BAN:
+ KICK: "&cYour account is {context} from CentileHCF.{temporary}\n\n&cIf you feel this punishment is unjust, you may appeal at:\n&ehttps://www.centilehcf.net"
+ TEMPORARY: "\n&cThis punishment expires in &e{time-remaining}&c."
+ KICK:
+ KICK: "&cYou were kicked by a staff member.\nReason: &e{reason}"
+COMMON_ERRORS:
+ FAILED_TO_LOAD_PROFILE: "&cFailed to load your profile. Try again later."
+ COULD_NOT_RESOLVE_PLAYER: "&cCould not resolve player information..."
+ PLAYER_NOT_FOUND: "&cA player with that name could not be found."
+ RANK_NOT_FOUND: "&cA rank with that name could not be found."
+STAFF:
+ CHAT: "{0}&r{1}&b ({2}): {3}"
+ REQUEST: "&b({0}) {1}: &b({2})"
+ BROADCAST_PREFIX: "&9&l[Staff] &r"
+ REQUEST_PREFIX: "&9&l[Request] &r"
+ JOIN_NETWORK: "{0}{1} &ajoined &2({2})"
+ LEAVE_NETWORK: "{0}{1} &aleft &2({2})"
+ SWITCH_SERVER: "{0}({1} &ajoined {2} &2(from {3})"
+SERVER:
+ STATUS: "&b({0}) -> {1}"
+ PREFIX: "&9&l[Status] &r"
+NETWORK:
+ BROADCAST_PREFIX: "&8[&eNetwork&8] &f"
+ RANK_REFRESH: "{0} Refreshed rank \"{1}\""
+ RANK_DELETE: "{0} Deleted rank \"{1}\""
\ No newline at end of file
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
new file mode 100644
index 0000000..63c9443
--- /dev/null
+++ b/src/main/resources/plugin.yml
@@ -0,0 +1,6 @@
+main: net.centilehcf.core.Core
+name: Core
+author: CentileHCF Development Team
+version: ${git.commit.id.abbrev}-${git.branch}
+description: Plugin used to add all core features to a bukkit server.
+depend: [ProtocolLib, Vault]
\ No newline at end of file
diff --git a/src/test/java/HastebinTest.java b/src/test/java/HastebinTest.java
new file mode 100644
index 0000000..b06cc9d
--- /dev/null
+++ b/src/test/java/HastebinTest.java
@@ -0,0 +1,11 @@
+import net.centilehcf.core.util.HastebinUtil;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class HastebinTest {
+
+ public static void main(String[] args) {
+ String test = "Hello " + ThreadLocalRandom.current().nextInt();
+ System.out.println(HastebinUtil.paste(test));
+ }
+}
diff --git a/target/classes/META-INF/core.kotlin_module b/target/classes/META-INF/core.kotlin_module
new file mode 100644
index 0000000..2983af7
Binary files /dev/null and b/target/classes/META-INF/core.kotlin_module differ
diff --git a/target/classes/config.yml b/target/classes/config.yml
new file mode 100644
index 0000000..be3d3ad
--- /dev/null
+++ b/target/classes/config.yml
@@ -0,0 +1,44 @@
+SERVER_NAME: "Development"
+MONGO:
+ HOST: "127.0.0.1"
+ PORT: 27017
+ AUTHENTICATION:
+ ENABLED: false
+ USERNAME: ""
+ PASSWORD: ""
+REDIS:
+ HOST: "127.0.0.1"
+ PORT: 6379
+ AUTHENTICATION:
+ ENABLED: false
+ PASSWORD: ""
+TAB: true
+PUNISHMENTS:
+ BROADCAST: "&r{target} &ahas been {context} by &r{sender}"
+ BROADCAST_SILENT: "&7[Silent] &r{target} &ahas been {context} by &r{sender}"
+ BLACKLIST: "&cYour account is Blacklisted from CentileHCF.\n\n&cThis Punishment cant not be undone."
+ BAN:
+ KICK: "&cYour account is {context} from CentileHCF.{temporary}\n\n&cIf you feel this punishment is unjust, you may appeal at:\n&ehttps://www.centilehcf.net"
+ TEMPORARY: "\n&cThis punishment expires in &e{time-remaining}&c."
+ KICK:
+ KICK: "&cYou were kicked by a staff member.\nReason: &e{reason}"
+COMMON_ERRORS:
+ FAILED_TO_LOAD_PROFILE: "&cFailed to load your profile. Try again later."
+ COULD_NOT_RESOLVE_PLAYER: "&cCould not resolve player information..."
+ PLAYER_NOT_FOUND: "&cA player with that name could not be found."
+ RANK_NOT_FOUND: "&cA rank with that name could not be found."
+STAFF:
+ CHAT: "{0}&r{1}&b ({2}): {3}"
+ REQUEST: "&b({0}) {1}: &b({2})"
+ BROADCAST_PREFIX: "&9&l[Staff] &r"
+ REQUEST_PREFIX: "&9&l[Request] &r"
+ JOIN_NETWORK: "{0}{1} &ajoined &2({2})"
+ LEAVE_NETWORK: "{0}{1} &aleft &2({2})"
+ SWITCH_SERVER: "{0}({1} &ajoined {2} &2(from {3})"
+SERVER:
+ STATUS: "&b({0}) -> {1}"
+ PREFIX: "&9&l[Status] &r"
+NETWORK:
+ BROADCAST_PREFIX: "&8[&eNetwork&8] &f"
+ RANK_REFRESH: "{0} Refreshed rank \"{1}\""
+ RANK_DELETE: "{0} Deleted rank \"{1}\""
\ No newline at end of file
diff --git a/target/classes/net/centilehcf/core/Core$1.class b/target/classes/net/centilehcf/core/Core$1.class
new file mode 100644
index 0000000..7c3759b
Binary files /dev/null and b/target/classes/net/centilehcf/core/Core$1.class differ
diff --git a/target/classes/net/centilehcf/core/Core$2.class b/target/classes/net/centilehcf/core/Core$2.class
new file mode 100644
index 0000000..e918609
Binary files /dev/null and b/target/classes/net/centilehcf/core/Core$2.class differ
diff --git a/target/classes/net/centilehcf/core/Core.class b/target/classes/net/centilehcf/core/Core.class
new file mode 100644
index 0000000..2dae3a2
Binary files /dev/null and b/target/classes/net/centilehcf/core/Core.class differ
diff --git a/target/classes/net/centilehcf/core/Locale.class b/target/classes/net/centilehcf/core/Locale.class
new file mode 100644
index 0000000..28c24a3
Binary files /dev/null and b/target/classes/net/centilehcf/core/Locale.class differ
diff --git a/target/classes/net/centilehcf/core/board/Board.class b/target/classes/net/centilehcf/core/board/Board.class
new file mode 100644
index 0000000..e6d0a48
Binary files /dev/null and b/target/classes/net/centilehcf/core/board/Board.class differ
diff --git a/target/classes/net/centilehcf/core/board/BoardAdapter.class b/target/classes/net/centilehcf/core/board/BoardAdapter.class
new file mode 100644
index 0000000..99e6e54
Binary files /dev/null and b/target/classes/net/centilehcf/core/board/BoardAdapter.class differ
diff --git a/target/classes/net/centilehcf/core/board/BoardEntry.class b/target/classes/net/centilehcf/core/board/BoardEntry.class
new file mode 100644
index 0000000..bb6c568
Binary files /dev/null and b/target/classes/net/centilehcf/core/board/BoardEntry.class differ
diff --git a/target/classes/net/centilehcf/core/board/BoardListener.class b/target/classes/net/centilehcf/core/board/BoardListener.class
new file mode 100644
index 0000000..1d78845
Binary files /dev/null and b/target/classes/net/centilehcf/core/board/BoardListener.class differ
diff --git a/target/classes/net/centilehcf/core/board/BoardStyle.class b/target/classes/net/centilehcf/core/board/BoardStyle.class
new file mode 100644
index 0000000..a879d88
Binary files /dev/null and b/target/classes/net/centilehcf/core/board/BoardStyle.class differ
diff --git a/target/classes/net/centilehcf/core/board/BoardThread.class b/target/classes/net/centilehcf/core/board/BoardThread.class
new file mode 100644
index 0000000..7d3d26e
Binary files /dev/null and b/target/classes/net/centilehcf/core/board/BoardThread.class differ
diff --git a/target/classes/net/centilehcf/core/board/MainBoard.class b/target/classes/net/centilehcf/core/board/MainBoard.class
new file mode 100644
index 0000000..1f48a4d
Binary files /dev/null and b/target/classes/net/centilehcf/core/board/MainBoard.class differ
diff --git a/target/classes/net/centilehcf/core/board/events/BoardCreateEvent.class b/target/classes/net/centilehcf/core/board/events/BoardCreateEvent.class
new file mode 100644
index 0000000..3333908
Binary files /dev/null and b/target/classes/net/centilehcf/core/board/events/BoardCreateEvent.class differ
diff --git a/target/classes/net/centilehcf/core/board/events/BoardDestroyEvent.class b/target/classes/net/centilehcf/core/board/events/BoardDestroyEvent.class
new file mode 100644
index 0000000..629e85f
Binary files /dev/null and b/target/classes/net/centilehcf/core/board/events/BoardDestroyEvent.class differ
diff --git a/target/classes/net/centilehcf/core/bootstrap/Bootstrapped.class b/target/classes/net/centilehcf/core/bootstrap/Bootstrapped.class
new file mode 100644
index 0000000..983b447
Binary files /dev/null and b/target/classes/net/centilehcf/core/bootstrap/Bootstrapped.class differ
diff --git a/target/classes/net/centilehcf/core/bootstrap/BootstrappedListener.class b/target/classes/net/centilehcf/core/bootstrap/BootstrappedListener.class
new file mode 100644
index 0000000..dc15367
Binary files /dev/null and b/target/classes/net/centilehcf/core/bootstrap/BootstrappedListener.class differ
diff --git a/target/classes/net/centilehcf/core/chat/Chat.class b/target/classes/net/centilehcf/core/chat/Chat.class
new file mode 100644
index 0000000..b60321b
Binary files /dev/null and b/target/classes/net/centilehcf/core/chat/Chat.class differ
diff --git a/target/classes/net/centilehcf/core/chat/ChatAttempt$Response.class b/target/classes/net/centilehcf/core/chat/ChatAttempt$Response.class
new file mode 100644
index 0000000..d645464
Binary files /dev/null and b/target/classes/net/centilehcf/core/chat/ChatAttempt$Response.class differ
diff --git a/target/classes/net/centilehcf/core/chat/ChatAttempt.class b/target/classes/net/centilehcf/core/chat/ChatAttempt.class
new file mode 100644
index 0000000..eab874e
Binary files /dev/null and b/target/classes/net/centilehcf/core/chat/ChatAttempt.class differ
diff --git a/target/classes/net/centilehcf/core/chat/command/MuteChatCommand.class b/target/classes/net/centilehcf/core/chat/command/MuteChatCommand.class
new file mode 100644
index 0000000..a90b9f4
Binary files /dev/null and b/target/classes/net/centilehcf/core/chat/command/MuteChatCommand.class differ
diff --git a/target/classes/net/centilehcf/core/chat/event/ChatAttemptEvent.class b/target/classes/net/centilehcf/core/chat/event/ChatAttemptEvent.class
new file mode 100644
index 0000000..91ea2d2
Binary files /dev/null and b/target/classes/net/centilehcf/core/chat/event/ChatAttemptEvent.class differ
diff --git a/target/classes/net/centilehcf/core/chat/filter/ChatFilter.class b/target/classes/net/centilehcf/core/chat/filter/ChatFilter.class
new file mode 100644
index 0000000..6c8a58c
Binary files /dev/null and b/target/classes/net/centilehcf/core/chat/filter/ChatFilter.class differ
diff --git a/target/classes/net/centilehcf/core/chat/filter/impl/ContainsFilter.class b/target/classes/net/centilehcf/core/chat/filter/impl/ContainsFilter.class
new file mode 100644
index 0000000..6d8f0d9
Binary files /dev/null and b/target/classes/net/centilehcf/core/chat/filter/impl/ContainsFilter.class differ
diff --git a/target/classes/net/centilehcf/core/chat/filter/impl/LinkFilter.class b/target/classes/net/centilehcf/core/chat/filter/impl/LinkFilter.class
new file mode 100644
index 0000000..eefba0e
Binary files /dev/null and b/target/classes/net/centilehcf/core/chat/filter/impl/LinkFilter.class differ
diff --git a/target/classes/net/centilehcf/core/chat/listener/ChatListener$1.class b/target/classes/net/centilehcf/core/chat/listener/ChatListener$1.class
new file mode 100644
index 0000000..c5d0aa0
Binary files /dev/null and b/target/classes/net/centilehcf/core/chat/listener/ChatListener$1.class differ
diff --git a/target/classes/net/centilehcf/core/chat/listener/ChatListener.class b/target/classes/net/centilehcf/core/chat/listener/ChatListener.class
new file mode 100644
index 0000000..cb823e5
Binary files /dev/null and b/target/classes/net/centilehcf/core/chat/listener/ChatListener.class differ
diff --git a/target/classes/net/centilehcf/core/chat/util/ChatComponentBuilder.class b/target/classes/net/centilehcf/core/chat/util/ChatComponentBuilder.class
new file mode 100644
index 0000000..dab1ba2
Binary files /dev/null and b/target/classes/net/centilehcf/core/chat/util/ChatComponentBuilder.class differ
diff --git a/target/classes/net/centilehcf/core/chat/util/ChatComponentExtras.class b/target/classes/net/centilehcf/core/chat/util/ChatComponentExtras.class
new file mode 100644
index 0000000..cc5044e
Binary files /dev/null and b/target/classes/net/centilehcf/core/chat/util/ChatComponentExtras.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/Essentials.class b/target/classes/net/centilehcf/core/essentials/Essentials.class
new file mode 100644
index 0000000..aa8a5ed
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/Essentials.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/BroadcastCommand.class b/target/classes/net/centilehcf/core/essentials/command/BroadcastCommand.class
new file mode 100644
index 0000000..2c165c0
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/BroadcastCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/ClearChatCommand.class b/target/classes/net/centilehcf/core/essentials/command/ClearChatCommand.class
new file mode 100644
index 0000000..019e1ea
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/ClearChatCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/ClearCommand.class b/target/classes/net/centilehcf/core/essentials/command/ClearCommand.class
new file mode 100644
index 0000000..6d07302
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/ClearCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/CraftCommand.class b/target/classes/net/centilehcf/core/essentials/command/CraftCommand.class
new file mode 100644
index 0000000..96bddce
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/CraftCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/DayCommand.class b/target/classes/net/centilehcf/core/essentials/command/DayCommand.class
new file mode 100644
index 0000000..e7298bf
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/DayCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/GameModeCommand.class b/target/classes/net/centilehcf/core/essentials/command/GameModeCommand.class
new file mode 100644
index 0000000..65c0ff3
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/GameModeCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/HealCommand.class b/target/classes/net/centilehcf/core/essentials/command/HealCommand.class
new file mode 100644
index 0000000..4501be1
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/HealCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/HidePlayerCommand.class b/target/classes/net/centilehcf/core/essentials/command/HidePlayerCommand.class
new file mode 100644
index 0000000..d306f67
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/HidePlayerCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/InvseeCommand.class b/target/classes/net/centilehcf/core/essentials/command/InvseeCommand.class
new file mode 100644
index 0000000..ec2d35b
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/InvseeCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/ListCommand.class b/target/classes/net/centilehcf/core/essentials/command/ListCommand.class
new file mode 100644
index 0000000..edf2a0b
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/ListCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/MasssayCommand.class b/target/classes/net/centilehcf/core/essentials/command/MasssayCommand.class
new file mode 100644
index 0000000..288f609
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/MasssayCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/MessageCommand.class b/target/classes/net/centilehcf/core/essentials/command/MessageCommand.class
new file mode 100644
index 0000000..bed5668
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/MessageCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/MoreCommand.class b/target/classes/net/centilehcf/core/essentials/command/MoreCommand.class
new file mode 100644
index 0000000..eaa6444
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/MoreCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/NightCommand.class b/target/classes/net/centilehcf/core/essentials/command/NightCommand.class
new file mode 100644
index 0000000..3599cb9
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/NightCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/PingCommand.class b/target/classes/net/centilehcf/core/essentials/command/PingCommand.class
new file mode 100644
index 0000000..145f320
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/PingCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/PrefixCommand.class b/target/classes/net/centilehcf/core/essentials/command/PrefixCommand.class
new file mode 100644
index 0000000..23f130e
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/PrefixCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/RawCommand.class b/target/classes/net/centilehcf/core/essentials/command/RawCommand.class
new file mode 100644
index 0000000..865ab3f
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/RawCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/RenameCommand.class b/target/classes/net/centilehcf/core/essentials/command/RenameCommand.class
new file mode 100644
index 0000000..74cd56c
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/RenameCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/ReplyCommand.class b/target/classes/net/centilehcf/core/essentials/command/ReplyCommand.class
new file mode 100644
index 0000000..77267b7
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/ReplyCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/RequestCommand.class b/target/classes/net/centilehcf/core/essentials/command/RequestCommand.class
new file mode 100644
index 0000000..6c487a7
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/RequestCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/SetSlotsCommand.class b/target/classes/net/centilehcf/core/essentials/command/SetSlotsCommand.class
new file mode 100644
index 0000000..52b3bd2
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/SetSlotsCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/SetSpawnCommand.class b/target/classes/net/centilehcf/core/essentials/command/SetSpawnCommand.class
new file mode 100644
index 0000000..eb3d9ad
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/SetSpawnCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/ShowPlayerCommand.class b/target/classes/net/centilehcf/core/essentials/command/ShowPlayerCommand.class
new file mode 100644
index 0000000..7df1447
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/ShowPlayerCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/SpawnCommand.class b/target/classes/net/centilehcf/core/essentials/command/SpawnCommand.class
new file mode 100644
index 0000000..353c84d
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/SpawnCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/SpawnerCommand.class b/target/classes/net/centilehcf/core/essentials/command/SpawnerCommand.class
new file mode 100644
index 0000000..7b31e68
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/SpawnerCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/StreamingCommand.class b/target/classes/net/centilehcf/core/essentials/command/StreamingCommand.class
new file mode 100644
index 0000000..5a6a5c2
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/StreamingCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/SunsetCommand.class b/target/classes/net/centilehcf/core/essentials/command/SunsetCommand.class
new file mode 100644
index 0000000..d01a76c
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/SunsetCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/TeleportAllCommand.class b/target/classes/net/centilehcf/core/essentials/command/TeleportAllCommand.class
new file mode 100644
index 0000000..f6480ff
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/TeleportAllCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/TeleportCommand.class b/target/classes/net/centilehcf/core/essentials/command/TeleportCommand.class
new file mode 100644
index 0000000..eb3eed1
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/TeleportCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/TeleportHereCommand.class b/target/classes/net/centilehcf/core/essentials/command/TeleportHereCommand.class
new file mode 100644
index 0000000..9566bf3
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/TeleportHereCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/TeleportPositionCommand.class b/target/classes/net/centilehcf/core/essentials/command/TeleportPositionCommand.class
new file mode 100644
index 0000000..bce9f81
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/TeleportPositionCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/command/WorldCommand.class b/target/classes/net/centilehcf/core/essentials/command/WorldCommand.class
new file mode 100644
index 0000000..eda6bc2
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/command/WorldCommand.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/event/SpawnTeleportEvent.class b/target/classes/net/centilehcf/core/essentials/event/SpawnTeleportEvent.class
new file mode 100644
index 0000000..6ea21f2
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/event/SpawnTeleportEvent.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/listener/DurabilityListener.class b/target/classes/net/centilehcf/core/essentials/listener/DurabilityListener.class
new file mode 100644
index 0000000..75e5e4a
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/listener/DurabilityListener.class differ
diff --git a/target/classes/net/centilehcf/core/essentials/listener/EssentialsListener.class b/target/classes/net/centilehcf/core/essentials/listener/EssentialsListener.class
new file mode 100644
index 0000000..192066a
Binary files /dev/null and b/target/classes/net/centilehcf/core/essentials/listener/EssentialsListener.class differ
diff --git a/target/classes/net/centilehcf/core/hook/VaultProvider.class b/target/classes/net/centilehcf/core/hook/VaultProvider.class
new file mode 100644
index 0000000..82c03b8
Binary files /dev/null and b/target/classes/net/centilehcf/core/hook/VaultProvider.class differ
diff --git a/target/classes/net/centilehcf/core/network/NetworkPacketListener$1.class b/target/classes/net/centilehcf/core/network/NetworkPacketListener$1.class
new file mode 100644
index 0000000..c5770a7
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/NetworkPacketListener$1.class differ
diff --git a/target/classes/net/centilehcf/core/network/NetworkPacketListener$2.class b/target/classes/net/centilehcf/core/network/NetworkPacketListener$2.class
new file mode 100644
index 0000000..ad0ba9a
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/NetworkPacketListener$2.class differ
diff --git a/target/classes/net/centilehcf/core/network/NetworkPacketListener.class b/target/classes/net/centilehcf/core/network/NetworkPacketListener.class
new file mode 100644
index 0000000..a0db380
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/NetworkPacketListener.class differ
diff --git a/target/classes/net/centilehcf/core/network/event/ReceiveRequestCommandEvent.class b/target/classes/net/centilehcf/core/network/event/ReceiveRequestCommandEvent.class
new file mode 100644
index 0000000..2bba283
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/event/ReceiveRequestCommandEvent.class differ
diff --git a/target/classes/net/centilehcf/core/network/event/ReceiveStaffChatEvent.class b/target/classes/net/centilehcf/core/network/event/ReceiveStaffChatEvent.class
new file mode 100644
index 0000000..07eed0b
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/event/ReceiveStaffChatEvent.class differ
diff --git a/target/classes/net/centilehcf/core/network/packet/PacketAddGrant.class b/target/classes/net/centilehcf/core/network/packet/PacketAddGrant.class
new file mode 100644
index 0000000..6d9c08d
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/packet/PacketAddGrant.class differ
diff --git a/target/classes/net/centilehcf/core/network/packet/PacketBroadcastPunishment.class b/target/classes/net/centilehcf/core/network/packet/PacketBroadcastPunishment.class
new file mode 100644
index 0000000..6db2b31
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/packet/PacketBroadcastPunishment.class differ
diff --git a/target/classes/net/centilehcf/core/network/packet/PacketClearGrants.class b/target/classes/net/centilehcf/core/network/packet/PacketClearGrants.class
new file mode 100644
index 0000000..f8e346b
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/packet/PacketClearGrants.class differ
diff --git a/target/classes/net/centilehcf/core/network/packet/PacketDeleteGrant.class b/target/classes/net/centilehcf/core/network/packet/PacketDeleteGrant.class
new file mode 100644
index 0000000..9da4e03
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/packet/PacketDeleteGrant.class differ
diff --git a/target/classes/net/centilehcf/core/network/packet/PacketDeletePrefix.class b/target/classes/net/centilehcf/core/network/packet/PacketDeletePrefix.class
new file mode 100644
index 0000000..b73d484
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/packet/PacketDeletePrefix.class differ
diff --git a/target/classes/net/centilehcf/core/network/packet/PacketDeleteRank.class b/target/classes/net/centilehcf/core/network/packet/PacketDeleteRank.class
new file mode 100644
index 0000000..a6c8f40
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/packet/PacketDeleteRank.class differ
diff --git a/target/classes/net/centilehcf/core/network/packet/PacketRefreshRank.class b/target/classes/net/centilehcf/core/network/packet/PacketRefreshRank.class
new file mode 100644
index 0000000..7bd78b1
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/packet/PacketRefreshRank.class differ
diff --git a/target/classes/net/centilehcf/core/network/packet/PacketRemovePunishments.class b/target/classes/net/centilehcf/core/network/packet/PacketRemovePunishments.class
new file mode 100644
index 0000000..1f57eef
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/packet/PacketRemovePunishments.class differ
diff --git a/target/classes/net/centilehcf/core/network/packet/PacketRequestCommand.class b/target/classes/net/centilehcf/core/network/packet/PacketRequestCommand.class
new file mode 100644
index 0000000..5d445bf
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/packet/PacketRequestCommand.class differ
diff --git a/target/classes/net/centilehcf/core/network/packet/PacketServerRestart.class b/target/classes/net/centilehcf/core/network/packet/PacketServerRestart.class
new file mode 100644
index 0000000..f1b2572
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/packet/PacketServerRestart.class differ
diff --git a/target/classes/net/centilehcf/core/network/packet/PacketStaffChat.class b/target/classes/net/centilehcf/core/network/packet/PacketStaffChat.class
new file mode 100644
index 0000000..e80f5b9
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/packet/PacketStaffChat.class differ
diff --git a/target/classes/net/centilehcf/core/network/packet/PacketStaffJoinNetwork.class b/target/classes/net/centilehcf/core/network/packet/PacketStaffJoinNetwork.class
new file mode 100644
index 0000000..30d66e5
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/packet/PacketStaffJoinNetwork.class differ
diff --git a/target/classes/net/centilehcf/core/network/packet/PacketStaffLeaveNetwork.class b/target/classes/net/centilehcf/core/network/packet/PacketStaffLeaveNetwork.class
new file mode 100644
index 0000000..05aba57
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/packet/PacketStaffLeaveNetwork.class differ
diff --git a/target/classes/net/centilehcf/core/network/packet/PacketStaffSwitchServer.class b/target/classes/net/centilehcf/core/network/packet/PacketStaffSwitchServer.class
new file mode 100644
index 0000000..577c8a4
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/packet/PacketStaffSwitchServer.class differ
diff --git a/target/classes/net/centilehcf/core/network/packet/PacketUpdatePrefix.class b/target/classes/net/centilehcf/core/network/packet/PacketUpdatePrefix.class
new file mode 100644
index 0000000..7c9b134
Binary files /dev/null and b/target/classes/net/centilehcf/core/network/packet/PacketUpdatePrefix.class differ
diff --git a/target/classes/net/centilehcf/core/prefix/Prefix.class b/target/classes/net/centilehcf/core/prefix/Prefix.class
new file mode 100644
index 0000000..8362bde
Binary files /dev/null and b/target/classes/net/centilehcf/core/prefix/Prefix.class differ
diff --git a/target/classes/net/centilehcf/core/prefix/PrefixHandler.class b/target/classes/net/centilehcf/core/prefix/PrefixHandler.class
new file mode 100644
index 0000000..063979e
Binary files /dev/null and b/target/classes/net/centilehcf/core/prefix/PrefixHandler.class differ
diff --git a/target/classes/net/centilehcf/core/prefix/command/AddPrefixCommand.class b/target/classes/net/centilehcf/core/prefix/command/AddPrefixCommand.class
new file mode 100644
index 0000000..cf0080c
Binary files /dev/null and b/target/classes/net/centilehcf/core/prefix/command/AddPrefixCommand.class differ
diff --git a/target/classes/net/centilehcf/core/prefix/command/DeletePrefixCommand.class b/target/classes/net/centilehcf/core/prefix/command/DeletePrefixCommand.class
new file mode 100644
index 0000000..f83cdb5
Binary files /dev/null and b/target/classes/net/centilehcf/core/prefix/command/DeletePrefixCommand.class differ
diff --git a/target/classes/net/centilehcf/core/prefix/command/ListPrefixCommand.class b/target/classes/net/centilehcf/core/prefix/command/ListPrefixCommand.class
new file mode 100644
index 0000000..76e3f05
Binary files /dev/null and b/target/classes/net/centilehcf/core/prefix/command/ListPrefixCommand.class differ
diff --git a/target/classes/net/centilehcf/core/prefix/command/PrefixTypeAdapter.class b/target/classes/net/centilehcf/core/prefix/command/PrefixTypeAdapter.class
new file mode 100644
index 0000000..598dd8a
Binary files /dev/null and b/target/classes/net/centilehcf/core/prefix/command/PrefixTypeAdapter.class differ
diff --git a/target/classes/net/centilehcf/core/prefix/command/SetPrefixCommand.class b/target/classes/net/centilehcf/core/prefix/command/SetPrefixCommand.class
new file mode 100644
index 0000000..192bec9
Binary files /dev/null and b/target/classes/net/centilehcf/core/prefix/command/SetPrefixCommand.class differ
diff --git a/target/classes/net/centilehcf/core/prefix/command/SetPrefixWeightCommand.class b/target/classes/net/centilehcf/core/prefix/command/SetPrefixWeightCommand.class
new file mode 100644
index 0000000..5971429
Binary files /dev/null and b/target/classes/net/centilehcf/core/prefix/command/SetPrefixWeightCommand.class differ
diff --git a/target/classes/net/centilehcf/core/prefix/menu/PrefixSelectionMenu$1.class b/target/classes/net/centilehcf/core/prefix/menu/PrefixSelectionMenu$1.class
new file mode 100644
index 0000000..6c44fdf
Binary files /dev/null and b/target/classes/net/centilehcf/core/prefix/menu/PrefixSelectionMenu$1.class differ
diff --git a/target/classes/net/centilehcf/core/prefix/menu/PrefixSelectionMenu$PrefixSelectionButton.class b/target/classes/net/centilehcf/core/prefix/menu/PrefixSelectionMenu$PrefixSelectionButton.class
new file mode 100644
index 0000000..79f3e28
Binary files /dev/null and b/target/classes/net/centilehcf/core/prefix/menu/PrefixSelectionMenu$PrefixSelectionButton.class differ
diff --git a/target/classes/net/centilehcf/core/prefix/menu/PrefixSelectionMenu.class b/target/classes/net/centilehcf/core/prefix/menu/PrefixSelectionMenu.class
new file mode 100644
index 0000000..445769d
Binary files /dev/null and b/target/classes/net/centilehcf/core/prefix/menu/PrefixSelectionMenu.class differ
diff --git a/target/classes/net/centilehcf/core/profile/Profile.class b/target/classes/net/centilehcf/core/profile/Profile.class
new file mode 100644
index 0000000..0038284
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/Profile.class differ
diff --git a/target/classes/net/centilehcf/core/profile/ProfileInfo.class b/target/classes/net/centilehcf/core/profile/ProfileInfo.class
new file mode 100644
index 0000000..e712ddf
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/ProfileInfo.class differ
diff --git a/target/classes/net/centilehcf/core/profile/ProfileListener$1.class b/target/classes/net/centilehcf/core/profile/ProfileListener$1.class
new file mode 100644
index 0000000..160d7f1
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/ProfileListener$1.class differ
diff --git a/target/classes/net/centilehcf/core/profile/ProfileListener.class b/target/classes/net/centilehcf/core/profile/ProfileListener.class
new file mode 100644
index 0000000..0fef1fd
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/ProfileListener.class differ
diff --git a/target/classes/net/centilehcf/core/profile/ProfileTypeAdapter.class b/target/classes/net/centilehcf/core/profile/ProfileTypeAdapter.class
new file mode 100644
index 0000000..27599a5
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/ProfileTypeAdapter.class differ
diff --git a/target/classes/net/centilehcf/core/profile/command/AltsCommand.class b/target/classes/net/centilehcf/core/profile/command/AltsCommand.class
new file mode 100644
index 0000000..f4725ec
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/command/AltsCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/command/OptionsCommand.class b/target/classes/net/centilehcf/core/profile/command/OptionsCommand.class
new file mode 100644
index 0000000..75094af
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/command/OptionsCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/command/StaffChatCommand.class b/target/classes/net/centilehcf/core/profile/command/StaffChatCommand.class
new file mode 100644
index 0000000..0437175
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/command/StaffChatCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/command/StaffChatToggleCommand.class b/target/classes/net/centilehcf/core/profile/command/StaffChatToggleCommand.class
new file mode 100644
index 0000000..ff80b22
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/command/StaffChatToggleCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/command/individualperms/AddIndividualPermissionCommand.class b/target/classes/net/centilehcf/core/profile/command/individualperms/AddIndividualPermissionCommand.class
new file mode 100644
index 0000000..45d417b
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/command/individualperms/AddIndividualPermissionCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/command/individualperms/ListIndividualPermissionsCommand.class b/target/classes/net/centilehcf/core/profile/command/individualperms/ListIndividualPermissionsCommand.class
new file mode 100644
index 0000000..4a660a6
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/command/individualperms/ListIndividualPermissionsCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/command/individualperms/RemoveIndividualPermissionCommand.class b/target/classes/net/centilehcf/core/profile/command/individualperms/RemoveIndividualPermissionCommand.class
new file mode 100644
index 0000000..79ee4af
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/command/individualperms/RemoveIndividualPermissionCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/Grant.class b/target/classes/net/centilehcf/core/profile/grant/Grant.class
new file mode 100644
index 0000000..0bf7e1a
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/Grant.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/GrantJsonDeserializer.class b/target/classes/net/centilehcf/core/profile/grant/GrantJsonDeserializer.class
new file mode 100644
index 0000000..cb1487f
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/GrantJsonDeserializer.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/GrantJsonSerializer.class b/target/classes/net/centilehcf/core/profile/grant/GrantJsonSerializer.class
new file mode 100644
index 0000000..f8843a5
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/GrantJsonSerializer.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/command/ClearGrantsCommand.class b/target/classes/net/centilehcf/core/profile/grant/command/ClearGrantsCommand.class
new file mode 100644
index 0000000..5ce3069
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/command/ClearGrantsCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/command/GrantCommand.class b/target/classes/net/centilehcf/core/profile/grant/command/GrantCommand.class
new file mode 100644
index 0000000..f8b0861
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/command/GrantCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/command/GrantsCommand.class b/target/classes/net/centilehcf/core/profile/grant/command/GrantsCommand.class
new file mode 100644
index 0000000..99637d0
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/command/GrantsCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/command/SetRankCommand.class b/target/classes/net/centilehcf/core/profile/grant/command/SetRankCommand.class
new file mode 100644
index 0000000..fd95ad4
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/command/SetRankCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/event/GrantAppliedEvent.class b/target/classes/net/centilehcf/core/profile/grant/event/GrantAppliedEvent.class
new file mode 100644
index 0000000..2f00abd
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/event/GrantAppliedEvent.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/event/GrantExpireEvent.class b/target/classes/net/centilehcf/core/profile/grant/event/GrantExpireEvent.class
new file mode 100644
index 0000000..d54d2e7
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/event/GrantExpireEvent.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/listener/GrantListener$1.class b/target/classes/net/centilehcf/core/profile/grant/listener/GrantListener$1.class
new file mode 100644
index 0000000..461b0dd
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/listener/GrantListener$1.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/listener/GrantListener$2.class b/target/classes/net/centilehcf/core/profile/grant/listener/GrantListener$2.class
new file mode 100644
index 0000000..4539477
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/listener/GrantListener$2.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/listener/GrantListener.class b/target/classes/net/centilehcf/core/profile/grant/listener/GrantListener.class
new file mode 100644
index 0000000..b8bd113
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/listener/GrantListener.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/menu/GrantsMenu$GrantInfoButton.class b/target/classes/net/centilehcf/core/profile/grant/menu/GrantsMenu$GrantInfoButton.class
new file mode 100644
index 0000000..b20650c
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/menu/GrantsMenu$GrantInfoButton.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/menu/GrantsMenu.class b/target/classes/net/centilehcf/core/profile/grant/menu/GrantsMenu.class
new file mode 100644
index 0000000..95b71b2
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/menu/GrantsMenu.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/menu/RankSelectionMenu$RankDisplayButton$1.class b/target/classes/net/centilehcf/core/profile/grant/menu/RankSelectionMenu$RankDisplayButton$1.class
new file mode 100644
index 0000000..91e06dd
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/menu/RankSelectionMenu$RankDisplayButton$1.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/menu/RankSelectionMenu$RankDisplayButton.class b/target/classes/net/centilehcf/core/profile/grant/menu/RankSelectionMenu$RankDisplayButton.class
new file mode 100644
index 0000000..63555dd
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/menu/RankSelectionMenu$RankDisplayButton.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/menu/RankSelectionMenu.class b/target/classes/net/centilehcf/core/profile/grant/menu/RankSelectionMenu.class
new file mode 100644
index 0000000..87066c8
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/menu/RankSelectionMenu.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/procedure/GrantProcedure.class b/target/classes/net/centilehcf/core/profile/grant/procedure/GrantProcedure.class
new file mode 100644
index 0000000..4342889
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/procedure/GrantProcedure.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/procedure/GrantProcedureStage.class b/target/classes/net/centilehcf/core/profile/grant/procedure/GrantProcedureStage.class
new file mode 100644
index 0000000..eb6f37e
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/procedure/GrantProcedureStage.class differ
diff --git a/target/classes/net/centilehcf/core/profile/grant/procedure/GrantProcedureType.class b/target/classes/net/centilehcf/core/profile/grant/procedure/GrantProcedureType.class
new file mode 100644
index 0000000..8dde8ad
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/grant/procedure/GrantProcedureType.class differ
diff --git a/target/classes/net/centilehcf/core/profile/option/ProfileOptions.class b/target/classes/net/centilehcf/core/profile/option/ProfileOptions.class
new file mode 100644
index 0000000..e069704
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/option/ProfileOptions.class differ
diff --git a/target/classes/net/centilehcf/core/profile/option/ProfileStaffOptions.class b/target/classes/net/centilehcf/core/profile/option/ProfileStaffOptions.class
new file mode 100644
index 0000000..d7ff77c
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/option/ProfileStaffOptions.class differ
diff --git a/target/classes/net/centilehcf/core/profile/option/commands/TogglePrivateMessagesCommand.class b/target/classes/net/centilehcf/core/profile/option/commands/TogglePrivateMessagesCommand.class
new file mode 100644
index 0000000..bce2721
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/option/commands/TogglePrivateMessagesCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/option/event/OptionsOpenedEvent.class b/target/classes/net/centilehcf/core/profile/option/event/OptionsOpenedEvent.class
new file mode 100644
index 0000000..3f61000
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/option/event/OptionsOpenedEvent.class differ
diff --git a/target/classes/net/centilehcf/core/profile/option/menu/ProfileOptionButton.class b/target/classes/net/centilehcf/core/profile/option/menu/ProfileOptionButton.class
new file mode 100644
index 0000000..f64fc31
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/option/menu/ProfileOptionButton.class differ
diff --git a/target/classes/net/centilehcf/core/profile/option/menu/ProfileOptionsMenu.class b/target/classes/net/centilehcf/core/profile/option/menu/ProfileOptionsMenu.class
new file mode 100644
index 0000000..90dafef
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/option/menu/ProfileOptionsMenu.class differ
diff --git a/target/classes/net/centilehcf/core/profile/option/menu/button/PrivateChatOptionButton.class b/target/classes/net/centilehcf/core/profile/option/menu/button/PrivateChatOptionButton.class
new file mode 100644
index 0000000..97c99a1
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/option/menu/button/PrivateChatOptionButton.class differ
diff --git a/target/classes/net/centilehcf/core/profile/option/menu/button/PrivateChatSoundsOptionButton.class b/target/classes/net/centilehcf/core/profile/option/menu/button/PrivateChatSoundsOptionButton.class
new file mode 100644
index 0000000..c3a9425
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/option/menu/button/PrivateChatSoundsOptionButton.class differ
diff --git a/target/classes/net/centilehcf/core/profile/option/menu/button/PublicChatOptionButton.class b/target/classes/net/centilehcf/core/profile/option/menu/button/PublicChatOptionButton.class
new file mode 100644
index 0000000..167a660
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/option/menu/button/PublicChatOptionButton.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/Punishment.class b/target/classes/net/centilehcf/core/profile/punishment/Punishment.class
new file mode 100644
index 0000000..dfd52f8
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/Punishment.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/PunishmentJsonDeserializer.class b/target/classes/net/centilehcf/core/profile/punishment/PunishmentJsonDeserializer.class
new file mode 100644
index 0000000..b18da68
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/PunishmentJsonDeserializer.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/PunishmentJsonSerializer.class b/target/classes/net/centilehcf/core/profile/punishment/PunishmentJsonSerializer.class
new file mode 100644
index 0000000..ae6841d
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/PunishmentJsonSerializer.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/PunishmentType$PunishmentTypeData.class b/target/classes/net/centilehcf/core/profile/punishment/PunishmentType$PunishmentTypeData.class
new file mode 100644
index 0000000..c247cd8
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/PunishmentType$PunishmentTypeData.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/PunishmentType.class b/target/classes/net/centilehcf/core/profile/punishment/PunishmentType.class
new file mode 100644
index 0000000..4a6fe17
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/PunishmentType.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/command/BanCommand$1.class b/target/classes/net/centilehcf/core/profile/punishment/command/BanCommand$1.class
new file mode 100644
index 0000000..3214ced
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/command/BanCommand$1.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/command/BanCommand.class b/target/classes/net/centilehcf/core/profile/punishment/command/BanCommand.class
new file mode 100644
index 0000000..03e44b0
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/command/BanCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/command/BlacklistCommand$1.class b/target/classes/net/centilehcf/core/profile/punishment/command/BlacklistCommand$1.class
new file mode 100644
index 0000000..15a39f9
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/command/BlacklistCommand$1.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/command/BlacklistCommand.class b/target/classes/net/centilehcf/core/profile/punishment/command/BlacklistCommand.class
new file mode 100644
index 0000000..01d1aa1
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/command/BlacklistCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/command/ClearPunishmentsCommand.class b/target/classes/net/centilehcf/core/profile/punishment/command/ClearPunishmentsCommand.class
new file mode 100644
index 0000000..dada564
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/command/ClearPunishmentsCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/command/HistoryCommand.class b/target/classes/net/centilehcf/core/profile/punishment/command/HistoryCommand.class
new file mode 100644
index 0000000..5ed2bf8
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/command/HistoryCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/command/KickCommand$1.class b/target/classes/net/centilehcf/core/profile/punishment/command/KickCommand$1.class
new file mode 100644
index 0000000..da80b45
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/command/KickCommand$1.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/command/KickCommand.class b/target/classes/net/centilehcf/core/profile/punishment/command/KickCommand.class
new file mode 100644
index 0000000..0c9e355
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/command/KickCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/command/MuteCommand.class b/target/classes/net/centilehcf/core/profile/punishment/command/MuteCommand.class
new file mode 100644
index 0000000..8a43352
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/command/MuteCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/command/UnbanCommand.class b/target/classes/net/centilehcf/core/profile/punishment/command/UnbanCommand.class
new file mode 100644
index 0000000..524318a
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/command/UnbanCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/command/UnblacklistCommand.class b/target/classes/net/centilehcf/core/profile/punishment/command/UnblacklistCommand.class
new file mode 100644
index 0000000..91259de
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/command/UnblacklistCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/command/UnmuteCommand.class b/target/classes/net/centilehcf/core/profile/punishment/command/UnmuteCommand.class
new file mode 100644
index 0000000..e26da74
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/command/UnmuteCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/command/WarnCommand.class b/target/classes/net/centilehcf/core/profile/punishment/command/WarnCommand.class
new file mode 100644
index 0000000..104d00c
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/command/WarnCommand.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/listener/PunishmentListener$1.class b/target/classes/net/centilehcf/core/profile/punishment/listener/PunishmentListener$1.class
new file mode 100644
index 0000000..30f7a4a
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/listener/PunishmentListener$1.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/listener/PunishmentListener$2.class b/target/classes/net/centilehcf/core/profile/punishment/listener/PunishmentListener$2.class
new file mode 100644
index 0000000..b26df17
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/listener/PunishmentListener$2.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/listener/PunishmentListener.class b/target/classes/net/centilehcf/core/profile/punishment/listener/PunishmentListener.class
new file mode 100644
index 0000000..0d724af
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/listener/PunishmentListener.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/menu/PunishmentsListMenu$PunishmentInfoButton.class b/target/classes/net/centilehcf/core/profile/punishment/menu/PunishmentsListMenu$PunishmentInfoButton.class
new file mode 100644
index 0000000..1eba771
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/menu/PunishmentsListMenu$PunishmentInfoButton.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/menu/PunishmentsListMenu.class b/target/classes/net/centilehcf/core/profile/punishment/menu/PunishmentsListMenu.class
new file mode 100644
index 0000000..7b96f09
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/menu/PunishmentsListMenu.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/menu/PunishmentsMenu$SelectPunishmentTypeButton.class b/target/classes/net/centilehcf/core/profile/punishment/menu/PunishmentsMenu$SelectPunishmentTypeButton.class
new file mode 100644
index 0000000..e65edb9
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/menu/PunishmentsMenu$SelectPunishmentTypeButton.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/menu/PunishmentsMenu.class b/target/classes/net/centilehcf/core/profile/punishment/menu/PunishmentsMenu.class
new file mode 100644
index 0000000..f6c6d0d
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/menu/PunishmentsMenu.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedure.class b/target/classes/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedure.class
new file mode 100644
index 0000000..a51b35b
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedure.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedureStage.class b/target/classes/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedureStage.class
new file mode 100644
index 0000000..47b74c1
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedureStage.class differ
diff --git a/target/classes/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedureType.class b/target/classes/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedureType.class
new file mode 100644
index 0000000..1e9ea5b
Binary files /dev/null and b/target/classes/net/centilehcf/core/profile/punishment/procedure/PunishmentProcedureType.class differ
diff --git a/target/classes/net/centilehcf/core/rank/Rank.class b/target/classes/net/centilehcf/core/rank/Rank.class
new file mode 100644
index 0000000..7013c32
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/Rank.class differ
diff --git a/target/classes/net/centilehcf/core/rank/RankTypeAdapter.class b/target/classes/net/centilehcf/core/rank/RankTypeAdapter.class
new file mode 100644
index 0000000..24657fe
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/RankTypeAdapter.class differ
diff --git a/target/classes/net/centilehcf/core/rank/command/RankAddPermissionCommand.class b/target/classes/net/centilehcf/core/rank/command/RankAddPermissionCommand.class
new file mode 100644
index 0000000..92cb023
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/command/RankAddPermissionCommand.class differ
diff --git a/target/classes/net/centilehcf/core/rank/command/RankAuditCommand.class b/target/classes/net/centilehcf/core/rank/command/RankAuditCommand.class
new file mode 100644
index 0000000..6c3ecd7
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/command/RankAuditCommand.class differ
diff --git a/target/classes/net/centilehcf/core/rank/command/RankCommand.class b/target/classes/net/centilehcf/core/rank/command/RankCommand.class
new file mode 100644
index 0000000..ec2c6e2
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/command/RankCommand.class differ
diff --git a/target/classes/net/centilehcf/core/rank/command/RankCreateCommand.class b/target/classes/net/centilehcf/core/rank/command/RankCreateCommand.class
new file mode 100644
index 0000000..0506467
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/command/RankCreateCommand.class differ
diff --git a/target/classes/net/centilehcf/core/rank/command/RankDeleteCommand.class b/target/classes/net/centilehcf/core/rank/command/RankDeleteCommand.class
new file mode 100644
index 0000000..b437501
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/command/RankDeleteCommand.class differ
diff --git a/target/classes/net/centilehcf/core/rank/command/RankDumpCommand.class b/target/classes/net/centilehcf/core/rank/command/RankDumpCommand.class
new file mode 100644
index 0000000..792abb6
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/command/RankDumpCommand.class differ
diff --git a/target/classes/net/centilehcf/core/rank/command/RankListCommand.class b/target/classes/net/centilehcf/core/rank/command/RankListCommand.class
new file mode 100644
index 0000000..45be1db
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/command/RankListCommand.class differ
diff --git a/target/classes/net/centilehcf/core/rank/command/RankRemovePermissionCommand.class b/target/classes/net/centilehcf/core/rank/command/RankRemovePermissionCommand.class
new file mode 100644
index 0000000..f31491d
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/command/RankRemovePermissionCommand.class differ
diff --git a/target/classes/net/centilehcf/core/rank/command/RankRenameCommand.class b/target/classes/net/centilehcf/core/rank/command/RankRenameCommand.class
new file mode 100644
index 0000000..67b67db
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/command/RankRenameCommand.class differ
diff --git a/target/classes/net/centilehcf/core/rank/command/RankSetColorCommand.class b/target/classes/net/centilehcf/core/rank/command/RankSetColorCommand.class
new file mode 100644
index 0000000..e98e3c0
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/command/RankSetColorCommand.class differ
diff --git a/target/classes/net/centilehcf/core/rank/command/RankSetPrefixCommand.class b/target/classes/net/centilehcf/core/rank/command/RankSetPrefixCommand.class
new file mode 100644
index 0000000..beca4a1
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/command/RankSetPrefixCommand.class differ
diff --git a/target/classes/net/centilehcf/core/rank/command/RankSetSuffixCommand.class b/target/classes/net/centilehcf/core/rank/command/RankSetSuffixCommand.class
new file mode 100644
index 0000000..80f1c80
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/command/RankSetSuffixCommand.class differ
diff --git a/target/classes/net/centilehcf/core/rank/command/RankSetWeightCommand.class b/target/classes/net/centilehcf/core/rank/command/RankSetWeightCommand.class
new file mode 100644
index 0000000..e3ac557
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/command/RankSetWeightCommand.class differ
diff --git a/target/classes/net/centilehcf/core/rank/comparator/RankComparator.class b/target/classes/net/centilehcf/core/rank/comparator/RankComparator.class
new file mode 100644
index 0000000..3872565
Binary files /dev/null and b/target/classes/net/centilehcf/core/rank/comparator/RankComparator.class differ
diff --git a/target/classes/net/centilehcf/core/tab/LayoutProvider.class b/target/classes/net/centilehcf/core/tab/LayoutProvider.class
new file mode 100644
index 0000000..af6791c
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/LayoutProvider.class differ
diff --git a/target/classes/net/centilehcf/core/tab/PlayerInfoPacketMod.class b/target/classes/net/centilehcf/core/tab/PlayerInfoPacketMod.class
new file mode 100644
index 0000000..64da9d8
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/PlayerInfoPacketMod.class differ
diff --git a/target/classes/net/centilehcf/core/tab/ScoreboardTeamPacketMod$Companion.class b/target/classes/net/centilehcf/core/tab/ScoreboardTeamPacketMod$Companion.class
new file mode 100644
index 0000000..a8810ad
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/ScoreboardTeamPacketMod$Companion.class differ
diff --git a/target/classes/net/centilehcf/core/tab/ScoreboardTeamPacketMod.class b/target/classes/net/centilehcf/core/tab/ScoreboardTeamPacketMod.class
new file mode 100644
index 0000000..0b57286
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/ScoreboardTeamPacketMod.class differ
diff --git a/target/classes/net/centilehcf/core/tab/Tab.class b/target/classes/net/centilehcf/core/tab/Tab.class
new file mode 100644
index 0000000..53254e9
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/Tab.class differ
diff --git a/target/classes/net/centilehcf/core/tab/TabAdapter$Companion.class b/target/classes/net/centilehcf/core/tab/TabAdapter$Companion.class
new file mode 100644
index 0000000..e78d96b
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/TabAdapter$Companion.class differ
diff --git a/target/classes/net/centilehcf/core/tab/TabAdapter$onPacketSending$1.class b/target/classes/net/centilehcf/core/tab/TabAdapter$onPacketSending$1.class
new file mode 100644
index 0000000..fc61f2f
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/TabAdapter$onPacketSending$1.class differ
diff --git a/target/classes/net/centilehcf/core/tab/TabAdapter.class b/target/classes/net/centilehcf/core/tab/TabAdapter.class
new file mode 100644
index 0000000..d3b1801
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/TabAdapter.class differ
diff --git a/target/classes/net/centilehcf/core/tab/TabEngine$Companion.class b/target/classes/net/centilehcf/core/tab/TabEngine$Companion.class
new file mode 100644
index 0000000..0673ad9
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/TabEngine$Companion.class differ
diff --git a/target/classes/net/centilehcf/core/tab/TabEngine.class b/target/classes/net/centilehcf/core/tab/TabEngine.class
new file mode 100644
index 0000000..c0c6e84
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/TabEngine.class differ
diff --git a/target/classes/net/centilehcf/core/tab/TabLayout$Companion.class b/target/classes/net/centilehcf/core/tab/TabLayout$Companion.class
new file mode 100644
index 0000000..c43f49f
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/TabLayout$Companion.class differ
diff --git a/target/classes/net/centilehcf/core/tab/TabLayout.class b/target/classes/net/centilehcf/core/tab/TabLayout.class
new file mode 100644
index 0000000..85ab636
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/TabLayout.class differ
diff --git a/target/classes/net/centilehcf/core/tab/TabListener$onPlayerJoin$1.class b/target/classes/net/centilehcf/core/tab/TabListener$onPlayerJoin$1.class
new file mode 100644
index 0000000..fa1ce67
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/TabListener$onPlayerJoin$1.class differ
diff --git a/target/classes/net/centilehcf/core/tab/TabListener.class b/target/classes/net/centilehcf/core/tab/TabListener.class
new file mode 100644
index 0000000..4a0e1ae
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/TabListener.class differ
diff --git a/target/classes/net/centilehcf/core/tab/TabThread.class b/target/classes/net/centilehcf/core/tab/TabThread.class
new file mode 100644
index 0000000..08724f9
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/TabThread.class differ
diff --git a/target/classes/net/centilehcf/core/tab/TabUtils$Companion.class b/target/classes/net/centilehcf/core/tab/TabUtils$Companion.class
new file mode 100644
index 0000000..a8f0a57
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/TabUtils$Companion.class differ
diff --git a/target/classes/net/centilehcf/core/tab/TabUtils.class b/target/classes/net/centilehcf/core/tab/TabUtils.class
new file mode 100644
index 0000000..0b432c9
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/TabUtils.class differ
diff --git a/target/classes/net/centilehcf/core/tab/test/TestLayoutProvider.class b/target/classes/net/centilehcf/core/tab/test/TestLayoutProvider.class
new file mode 100644
index 0000000..144afa4
Binary files /dev/null and b/target/classes/net/centilehcf/core/tab/test/TestLayoutProvider.class differ
diff --git a/target/classes/net/centilehcf/core/util/BaseEvent.class b/target/classes/net/centilehcf/core/util/BaseEvent.class
new file mode 100644
index 0000000..3f7e12a
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/BaseEvent.class differ
diff --git a/target/classes/net/centilehcf/core/util/BukkitReflection.class b/target/classes/net/centilehcf/core/util/BukkitReflection.class
new file mode 100644
index 0000000..7c54872
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/BukkitReflection.class differ
diff --git a/target/classes/net/centilehcf/core/util/BukkitUtils.class b/target/classes/net/centilehcf/core/util/BukkitUtils.class
new file mode 100644
index 0000000..d3ff662
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/BukkitUtils.class differ
diff --git a/target/classes/net/centilehcf/core/util/CC.class b/target/classes/net/centilehcf/core/util/CC.class
new file mode 100644
index 0000000..1422f7a
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/CC.class differ
diff --git a/target/classes/net/centilehcf/core/util/CollectionUtil.class b/target/classes/net/centilehcf/core/util/CollectionUtil.class
new file mode 100644
index 0000000..3fb69fd
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/CollectionUtil.class differ
diff --git a/target/classes/net/centilehcf/core/util/Cooldown.class b/target/classes/net/centilehcf/core/util/Cooldown.class
new file mode 100644
index 0000000..9c6edf7
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/Cooldown.class differ
diff --git a/target/classes/net/centilehcf/core/util/HastebinUtil.class b/target/classes/net/centilehcf/core/util/HastebinUtil.class
new file mode 100644
index 0000000..dc0648a
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/HastebinUtil.class differ
diff --git a/target/classes/net/centilehcf/core/util/ItemBuilder.class b/target/classes/net/centilehcf/core/util/ItemBuilder.class
new file mode 100644
index 0000000..9863392
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/ItemBuilder.class differ
diff --git a/target/classes/net/centilehcf/core/util/LocationUtil.class b/target/classes/net/centilehcf/core/util/LocationUtil.class
new file mode 100644
index 0000000..39afaf5
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/LocationUtil.class differ
diff --git a/target/classes/net/centilehcf/core/util/PotionUtil.class b/target/classes/net/centilehcf/core/util/PotionUtil.class
new file mode 100644
index 0000000..1c1af10
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/PotionUtil.class differ
diff --git a/target/classes/net/centilehcf/core/util/TaskUtil.class b/target/classes/net/centilehcf/core/util/TaskUtil.class
new file mode 100644
index 0000000..0bcda5b
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/TaskUtil.class differ
diff --git a/target/classes/net/centilehcf/core/util/TextSplitter.class b/target/classes/net/centilehcf/core/util/TextSplitter.class
new file mode 100644
index 0000000..6fb95a3
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/TextSplitter.class differ
diff --git a/target/classes/net/centilehcf/core/util/TimeUtil.class b/target/classes/net/centilehcf/core/util/TimeUtil.class
new file mode 100644
index 0000000..6afc59d
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/TimeUtil.class differ
diff --git a/target/classes/net/centilehcf/core/util/callback/Callback.class b/target/classes/net/centilehcf/core/util/callback/Callback.class
new file mode 100644
index 0000000..e532053
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/callback/Callback.class differ
diff --git a/target/classes/net/centilehcf/core/util/callback/ReturnableTypeCallback.class b/target/classes/net/centilehcf/core/util/callback/ReturnableTypeCallback.class
new file mode 100644
index 0000000..6120732
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/callback/ReturnableTypeCallback.class differ
diff --git a/target/classes/net/centilehcf/core/util/callback/TypeCallback.class b/target/classes/net/centilehcf/core/util/callback/TypeCallback.class
new file mode 100644
index 0000000..ffcf6ef
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/callback/TypeCallback.class differ
diff --git a/target/classes/net/centilehcf/core/util/duration/Duration.class b/target/classes/net/centilehcf/core/util/duration/Duration.class
new file mode 100644
index 0000000..dfbab19
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/duration/Duration.class differ
diff --git a/target/classes/net/centilehcf/core/util/duration/DurationTypeAdapter.class b/target/classes/net/centilehcf/core/util/duration/DurationTypeAdapter.class
new file mode 100644
index 0000000..620ba5a
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/duration/DurationTypeAdapter.class differ
diff --git a/target/classes/net/centilehcf/core/util/json/JsonChain.class b/target/classes/net/centilehcf/core/util/json/JsonChain.class
new file mode 100644
index 0000000..496246d
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/json/JsonChain.class differ
diff --git a/target/classes/net/centilehcf/core/util/json/JsonDeserializer.class b/target/classes/net/centilehcf/core/util/json/JsonDeserializer.class
new file mode 100644
index 0000000..dbb8219
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/json/JsonDeserializer.class differ
diff --git a/target/classes/net/centilehcf/core/util/json/JsonSerializer.class b/target/classes/net/centilehcf/core/util/json/JsonSerializer.class
new file mode 100644
index 0000000..e4a601e
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/json/JsonSerializer.class differ
diff --git a/target/classes/net/centilehcf/core/util/menu/Button$1.class b/target/classes/net/centilehcf/core/util/menu/Button$1.class
new file mode 100644
index 0000000..6e7854f
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/menu/Button$1.class differ
diff --git a/target/classes/net/centilehcf/core/util/menu/Button.class b/target/classes/net/centilehcf/core/util/menu/Button.class
new file mode 100644
index 0000000..359da58
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/menu/Button.class differ
diff --git a/target/classes/net/centilehcf/core/util/menu/Menu.class b/target/classes/net/centilehcf/core/util/menu/Menu.class
new file mode 100644
index 0000000..8643eb1
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/menu/Menu.class differ
diff --git a/target/classes/net/centilehcf/core/util/menu/MenuListener.class b/target/classes/net/centilehcf/core/util/menu/MenuListener.class
new file mode 100644
index 0000000..79c397a
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/menu/MenuListener.class differ
diff --git a/target/classes/net/centilehcf/core/util/menu/button/BackButton.class b/target/classes/net/centilehcf/core/util/menu/button/BackButton.class
new file mode 100644
index 0000000..c04bcc6
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/menu/button/BackButton.class differ
diff --git a/target/classes/net/centilehcf/core/util/menu/button/ConfirmationButton.class b/target/classes/net/centilehcf/core/util/menu/button/ConfirmationButton.class
new file mode 100644
index 0000000..ea87086
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/menu/button/ConfirmationButton.class differ
diff --git a/target/classes/net/centilehcf/core/util/menu/button/DisplayButton.class b/target/classes/net/centilehcf/core/util/menu/button/DisplayButton.class
new file mode 100644
index 0000000..45a61cc
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/menu/button/DisplayButton.class differ
diff --git a/target/classes/net/centilehcf/core/util/menu/button/JumpToMenuButton.class b/target/classes/net/centilehcf/core/util/menu/button/JumpToMenuButton.class
new file mode 100644
index 0000000..e82c93a
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/menu/button/JumpToMenuButton.class differ
diff --git a/target/classes/net/centilehcf/core/util/menu/menus/ConfirmMenu.class b/target/classes/net/centilehcf/core/util/menu/menus/ConfirmMenu.class
new file mode 100644
index 0000000..0a561ef
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/menu/menus/ConfirmMenu.class differ
diff --git a/target/classes/net/centilehcf/core/util/menu/pagination/JumpToPageButton.class b/target/classes/net/centilehcf/core/util/menu/pagination/JumpToPageButton.class
new file mode 100644
index 0000000..476e897
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/menu/pagination/JumpToPageButton.class differ
diff --git a/target/classes/net/centilehcf/core/util/menu/pagination/PageButton.class b/target/classes/net/centilehcf/core/util/menu/pagination/PageButton.class
new file mode 100644
index 0000000..649f589
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/menu/pagination/PageButton.class differ
diff --git a/target/classes/net/centilehcf/core/util/menu/pagination/PaginatedMenu.class b/target/classes/net/centilehcf/core/util/menu/pagination/PaginatedMenu.class
new file mode 100644
index 0000000..d7a51cb
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/menu/pagination/PaginatedMenu.class differ
diff --git a/target/classes/net/centilehcf/core/util/menu/pagination/ViewAllPagesMenu.class b/target/classes/net/centilehcf/core/util/menu/pagination/ViewAllPagesMenu.class
new file mode 100644
index 0000000..0ea8b89
Binary files /dev/null and b/target/classes/net/centilehcf/core/util/menu/pagination/ViewAllPagesMenu.class differ
diff --git a/target/classes/net/centilehcf/core/uuid/UUIDCache.class b/target/classes/net/centilehcf/core/uuid/UUIDCache.class
new file mode 100644
index 0000000..199a648
Binary files /dev/null and b/target/classes/net/centilehcf/core/uuid/UUIDCache.class differ
diff --git a/target/classes/plugin.yml b/target/classes/plugin.yml
new file mode 100644
index 0000000..63c9443
--- /dev/null
+++ b/target/classes/plugin.yml
@@ -0,0 +1,6 @@
+main: net.centilehcf.core.Core
+name: Core
+author: CentileHCF Development Team
+version: ${git.commit.id.abbrev}-${git.branch}
+description: Plugin used to add all core features to a bukkit server.
+depend: [ProtocolLib, Vault]
\ No newline at end of file
diff --git a/target/core-1.2-SNAPSHOT.jar b/target/core-1.2-SNAPSHOT.jar
new file mode 100644
index 0000000..2ae46e5
Binary files /dev/null and b/target/core-1.2-SNAPSHOT.jar differ
diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties
new file mode 100644
index 0000000..6b8b8f8
--- /dev/null
+++ b/target/maven-archiver/pom.properties
@@ -0,0 +1,5 @@
+#Generated by Maven
+#Sun Jul 21 21:32:21 EDT 2019
+version=1.2-SNAPSHOT
+groupId=net.centilehcf
+artifactId=core
diff --git a/target/maven-status/maven-compiler-plugin/compile/java-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/java-compile/createdFiles.lst
new file mode 100644
index 0000000..a18dc11
--- /dev/null
+++ b/target/maven-status/maven-compiler-plugin/compile/java-compile/createdFiles.lst
@@ -0,0 +1,215 @@
+net\centilehcf\core\rank\command\RankSetSuffixCommand.class
+net\centilehcf\core\profile\grant\listener\GrantListener$2.class
+net\centilehcf\core\essentials\command\SpawnCommand.class
+net\centilehcf\core\profile\grant\command\GrantCommand.class
+net\centilehcf\core\profile\grant\command\ClearGrantsCommand.class
+net\centilehcf\core\board\BoardListener.class
+net\centilehcf\core\chat\filter\ChatFilter.class
+net\centilehcf\core\essentials\command\ListCommand.class
+net\centilehcf\core\profile\grant\procedure\GrantProcedureType.class
+net\centilehcf\core\essentials\command\WorldCommand.class
+net\centilehcf\core\profile\punishment\menu\PunishmentsMenu$SelectPunishmentTypeButton.class
+net\centilehcf\core\essentials\command\BroadcastCommand.class
+net\centilehcf\core\rank\Rank.class
+net\centilehcf\core\Locale.class
+net\centilehcf\core\profile\Profile.class
+net\centilehcf\core\network\packet\PacketUpdatePrefix.class
+net\centilehcf\core\prefix\command\AddPrefixCommand.class
+net\centilehcf\core\rank\command\RankCreateCommand.class
+net\centilehcf\core\essentials\command\SunsetCommand.class
+net\centilehcf\core\profile\grant\procedure\GrantProcedure.class
+net\centilehcf\core\chat\listener\ChatListener$1.class
+net\centilehcf\core\essentials\command\SetSpawnCommand.class
+net\centilehcf\core\util\json\JsonSerializer.class
+net\centilehcf\core\profile\grant\command\SetRankCommand.class
+net\centilehcf\core\profile\punishment\command\BlacklistCommand.class
+net\centilehcf\core\rank\command\RankSetColorCommand.class
+net\centilehcf\core\util\menu\Menu.class
+net\centilehcf\core\board\Board.class
+net\centilehcf\core\rank\command\RankSetPrefixCommand.class
+net\centilehcf\core\profile\punishment\command\BlacklistCommand$1.class
+net\centilehcf\core\profile\punishment\listener\PunishmentListener.class
+net\centilehcf\core\essentials\command\RawCommand.class
+net\centilehcf\core\network\packet\PacketRequestCommand.class
+net\centilehcf\core\util\json\JsonDeserializer.class
+net\centilehcf\core\bootstrap\Bootstrapped.class
+net\centilehcf\core\essentials\listener\EssentialsListener.class
+net\centilehcf\core\util\TaskUtil.class
+net\centilehcf\core\profile\punishment\PunishmentJsonSerializer.class
+net\centilehcf\core\util\menu\pagination\PageButton.class
+net\centilehcf\core\essentials\command\RequestCommand.class
+net\centilehcf\core\network\event\ReceiveRequestCommandEvent.class
+net\centilehcf\core\profile\punishment\command\HistoryCommand.class
+net\centilehcf\core\network\packet\PacketAddGrant.class
+net\centilehcf\core\util\menu\button\DisplayButton.class
+net\centilehcf\core\profile\grant\listener\GrantListener$1.class
+net\centilehcf\core\util\ItemBuilder.class
+net\centilehcf\core\profile\grant\procedure\GrantProcedureStage.class
+net\centilehcf\core\network\NetworkPacketListener$2.class
+net\centilehcf\core\util\duration\DurationTypeAdapter.class
+net\centilehcf\core\util\BukkitUtils.class
+net\centilehcf\core\prefix\command\SetPrefixCommand.class
+net\centilehcf\core\essentials\command\HealCommand.class
+net\centilehcf\core\essentials\command\PrefixCommand.class
+net\centilehcf\core\hook\VaultProvider.class
+net\centilehcf\core\rank\command\RankSetWeightCommand.class
+net\centilehcf\core\profile\option\menu\button\PrivateChatOptionButton.class
+net\centilehcf\core\util\json\JsonChain.class
+net\centilehcf\core\profile\option\commands\TogglePrivateMessagesCommand.class
+net\centilehcf\core\profile\option\menu\ProfileOptionsMenu.class
+net\centilehcf\core\rank\command\RankRemovePermissionCommand.class
+net\centilehcf\core\essentials\command\PingCommand.class
+net\centilehcf\core\rank\RankTypeAdapter.class
+net\centilehcf\core\essentials\command\HidePlayerCommand.class
+net\centilehcf\core\network\packet\PacketDeleteGrant.class
+net\centilehcf\core\board\BoardThread.class
+net\centilehcf\core\network\NetworkPacketListener$1.class
+net\centilehcf\core\chat\util\ChatComponentExtras.class
+net\centilehcf\core\profile\command\OptionsCommand.class
+net\centilehcf\core\network\NetworkPacketListener.class
+net\centilehcf\core\network\packet\PacketDeleteRank.class
+net\centilehcf\core\profile\punishment\command\KickCommand$1.class
+net\centilehcf\core\profile\punishment\command\BanCommand$1.class
+net\centilehcf\core\profile\option\menu\ProfileOptionButton.class
+net\centilehcf\core\rank\command\RankRenameCommand.class
+net\centilehcf\core\board\events\BoardCreateEvent.class
+net\centilehcf\core\essentials\command\CraftCommand.class
+net\centilehcf\core\prefix\command\ListPrefixCommand.class
+net\centilehcf\core\essentials\command\MoreCommand.class
+net\centilehcf\core\chat\filter\impl\ContainsFilter.class
+net\centilehcf\core\essentials\command\ShowPlayerCommand.class
+net\centilehcf\core\essentials\Essentials.class
+net\centilehcf\core\network\packet\PacketBroadcastPunishment.class
+net\centilehcf\core\profile\punishment\listener\PunishmentListener$2.class
+net\centilehcf\core\Core.class
+net\centilehcf\core\essentials\command\SpawnerCommand.class
+net\centilehcf\core\util\Cooldown.class
+net\centilehcf\core\board\BoardStyle.class
+net\centilehcf\core\util\TextSplitter.class
+net\centilehcf\core\util\TimeUtil.class
+net\centilehcf\core\network\packet\PacketStaffSwitchServer.class
+net\centilehcf\core\profile\command\individualperms\ListIndividualPermissionsCommand.class
+net\centilehcf\core\chat\listener\ChatListener.class
+net\centilehcf\core\util\menu\MenuListener.class
+net\centilehcf\core\prefix\command\DeletePrefixCommand.class
+net\centilehcf\core\util\HastebinUtil.class
+net\centilehcf\core\essentials\command\InvseeCommand.class
+net\centilehcf\core\profile\grant\event\GrantAppliedEvent.class
+net\centilehcf\core\essentials\command\NightCommand.class
+net\centilehcf\core\essentials\command\ClearCommand.class
+net\centilehcf\core\rank\command\RankDumpCommand.class
+net\centilehcf\core\profile\ProfileListener.class
+net\centilehcf\core\profile\ProfileInfo.class
+net\centilehcf\core\network\packet\PacketRefreshRank.class
+net\centilehcf\core\profile\grant\event\GrantExpireEvent.class
+net\centilehcf\core\profile\punishment\command\UnbanCommand.class
+net\centilehcf\core\network\event\ReceiveStaffChatEvent.class
+net\centilehcf\core\essentials\command\TeleportHereCommand.class
+net\centilehcf\core\essentials\command\StreamingCommand.class
+net\centilehcf\core\profile\punishment\PunishmentType$PunishmentTypeData.class
+net\centilehcf\core\util\menu\Button$1.class
+net\centilehcf\core\profile\command\StaffChatToggleCommand.class
+net\centilehcf\core\profile\grant\GrantJsonSerializer.class
+net\centilehcf\core\profile\punishment\command\WarnCommand.class
+net\centilehcf\core\board\events\BoardDestroyEvent.class
+net\centilehcf\core\profile\grant\command\GrantsCommand.class
+net\centilehcf\core\essentials\command\MessageCommand.class
+net\centilehcf\core\util\menu\button\ConfirmationButton.class
+net\centilehcf\core\essentials\command\TeleportPositionCommand.class
+net\centilehcf\core\board\BoardEntry.class
+net\centilehcf\core\util\menu\Button.class
+net\centilehcf\core\network\packet\PacketDeletePrefix.class
+net\centilehcf\core\util\CollectionUtil.class
+net\centilehcf\core\profile\punishment\command\UnblacklistCommand.class
+net\centilehcf\core\profile\punishment\listener\PunishmentListener$1.class
+net\centilehcf\core\profile\punishment\command\ClearPunishmentsCommand.class
+net\centilehcf\core\essentials\command\TeleportCommand.class
+net\centilehcf\core\essentials\command\ClearChatCommand.class
+net\centilehcf\core\profile\option\menu\button\PrivateChatSoundsOptionButton.class
+net\centilehcf\core\chat\event\ChatAttemptEvent.class
+net\centilehcf\core\util\callback\Callback.class
+net\centilehcf\core\profile\command\StaffChatCommand.class
+net\centilehcf\core\profile\ProfileTypeAdapter.class
+net\centilehcf\core\Core$2.class
+net\centilehcf\core\essentials\command\ReplyCommand.class
+net\centilehcf\core\profile\option\event\OptionsOpenedEvent.class
+net\centilehcf\core\profile\punishment\menu\PunishmentsListMenu.class
+net\centilehcf\core\profile\command\individualperms\AddIndividualPermissionCommand.class
+net\centilehcf\core\rank\command\RankAuditCommand.class
+net\centilehcf\core\network\packet\PacketStaffJoinNetwork.class
+net\centilehcf\core\prefix\Prefix.class
+net\centilehcf\core\profile\grant\menu\GrantsMenu$GrantInfoButton.class
+net\centilehcf\core\profile\grant\GrantJsonDeserializer.class
+net\centilehcf\core\profile\grant\menu\RankSelectionMenu.class
+net\centilehcf\core\network\packet\PacketRemovePunishments.class
+net\centilehcf\core\profile\punishment\command\BanCommand.class
+net\centilehcf\core\util\CC.class
+net\centilehcf\core\rank\command\RankListCommand.class
+net\centilehcf\core\profile\grant\Grant.class
+net\centilehcf\core\profile\punishment\procedure\PunishmentProcedure.class
+net\centilehcf\core\profile\option\ProfileOptions.class
+net\centilehcf\core\profile\punishment\command\KickCommand.class
+net\centilehcf\core\board\MainBoard.class
+net\centilehcf\core\prefix\command\PrefixTypeAdapter.class
+net\centilehcf\core\util\menu\pagination\JumpToPageButton.class
+net\centilehcf\core\util\menu\button\JumpToMenuButton.class
+net\centilehcf\core\essentials\command\RenameCommand.class
+net\centilehcf\core\profile\option\menu\button\PublicChatOptionButton.class
+net\centilehcf\core\network\packet\PacketStaffChat.class
+net\centilehcf\core\prefix\menu\PrefixSelectionMenu.class
+net\centilehcf\core\profile\punishment\Punishment.class
+net\centilehcf\core\rank\command\RankCommand.class
+net\centilehcf\core\prefix\menu\PrefixSelectionMenu$1.class
+net\centilehcf\core\util\menu\pagination\ViewAllPagesMenu.class
+net\centilehcf\core\Core$1.class
+net\centilehcf\core\prefix\PrefixHandler.class
+net\centilehcf\core\util\menu\pagination\PaginatedMenu.class
+net\centilehcf\core\util\PotionUtil.class
+net\centilehcf\core\profile\punishment\menu\PunishmentsMenu.class
+net\centilehcf\core\util\callback\TypeCallback.class
+net\centilehcf\core\prefix\menu\PrefixSelectionMenu$PrefixSelectionButton.class
+net\centilehcf\core\chat\ChatAttempt.class
+net\centilehcf\core\profile\command\AltsCommand.class
+net\centilehcf\core\chat\Chat.class
+net\centilehcf\core\prefix\command\SetPrefixWeightCommand.class
+net\centilehcf\core\chat\util\ChatComponentBuilder.class
+net\centilehcf\core\profile\grant\listener\GrantListener.class
+net\centilehcf\core\essentials\command\SetSlotsCommand.class
+net\centilehcf\core\essentials\command\TeleportAllCommand.class
+net\centilehcf\core\rank\command\RankAddPermissionCommand.class
+net\centilehcf\core\essentials\command\GameModeCommand.class
+net\centilehcf\core\profile\ProfileListener$1.class
+net\centilehcf\core\essentials\event\SpawnTeleportEvent.class
+net\centilehcf\core\essentials\command\MasssayCommand.class
+net\centilehcf\core\profile\punishment\menu\PunishmentsListMenu$PunishmentInfoButton.class
+net\centilehcf\core\util\menu\button\BackButton.class
+net\centilehcf\core\essentials\command\DayCommand.class
+net\centilehcf\core\util\BukkitReflection.class
+net\centilehcf\core\board\BoardAdapter.class
+net\centilehcf\core\util\menu\menus\ConfirmMenu.class
+net\centilehcf\core\network\packet\PacketServerRestart.class
+net\centilehcf\core\rank\command\RankDeleteCommand.class
+net\centilehcf\core\essentials\listener\DurabilityListener.class
+net\centilehcf\core\util\BaseEvent.class
+net\centilehcf\core\util\duration\Duration.class
+net\centilehcf\core\rank\comparator\RankComparator.class
+net\centilehcf\core\profile\punishment\PunishmentJsonDeserializer.class
+net\centilehcf\core\profile\punishment\procedure\PunishmentProcedureType.class
+net\centilehcf\core\uuid\UUIDCache.class
+net\centilehcf\core\util\LocationUtil.class
+net\centilehcf\core\profile\grant\menu\GrantsMenu.class
+net\centilehcf\core\network\packet\PacketStaffLeaveNetwork.class
+net\centilehcf\core\profile\grant\menu\RankSelectionMenu$RankDisplayButton$1.class
+net\centilehcf\core\profile\command\individualperms\RemoveIndividualPermissionCommand.class
+net\centilehcf\core\chat\command\MuteChatCommand.class
+net\centilehcf\core\bootstrap\BootstrappedListener.class
+net\centilehcf\core\chat\filter\impl\LinkFilter.class
+net\centilehcf\core\chat\ChatAttempt$Response.class
+net\centilehcf\core\profile\punishment\procedure\PunishmentProcedureStage.class
+net\centilehcf\core\profile\grant\menu\RankSelectionMenu$RankDisplayButton.class
+net\centilehcf\core\profile\option\ProfileStaffOptions.class
+net\centilehcf\core\profile\punishment\command\MuteCommand.class
+net\centilehcf\core\util\callback\ReturnableTypeCallback.class
+net\centilehcf\core\network\packet\PacketClearGrants.class
+net\centilehcf\core\profile\punishment\command\UnmuteCommand.class
+net\centilehcf\core\profile\punishment\PunishmentType.class
diff --git a/target/maven-status/maven-compiler-plugin/compile/java-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/java-compile/inputFiles.lst
new file mode 100644
index 0000000..42d74c0
--- /dev/null
+++ b/target/maven-status/maven-compiler-plugin/compile/java-compile/inputFiles.lst
@@ -0,0 +1,192 @@
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\StreamingCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\chat\Chat.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\ClearCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\PrefixCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\command\RankAddPermissionCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\command\RankAuditCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\Core.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\chat\filter\impl\LinkFilter.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\option\menu\ProfileOptionButton.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\menu\pagination\ViewAllPagesMenu.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\command\UnmuteCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\RankTypeAdapter.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\callback\ReturnableTypeCallback.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\TaskUtil.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\HealCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\RequestCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\board\BoardStyle.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\CraftCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\SetSpawnCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\listener\DurabilityListener.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\grant\procedure\GrantProcedure.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\packet\PacketDeleteRank.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\prefix\command\ListPrefixCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\duration\Duration.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\prefix\PrefixHandler.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\Rank.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\CollectionUtil.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\ProfileListener.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\board\BoardThread.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\grant\command\SetRankCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\event\ReceiveRequestCommandEvent.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\packet\PacketStaffJoinNetwork.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\NightCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\TeleportHereCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\command\RankSetWeightCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\DayCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\prefix\Prefix.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\board\BoardAdapter.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\menu\menus\ConfirmMenu.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\command\BanCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\menu\pagination\PageButton.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\MessageCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\menu\pagination\JumpToPageButton.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\SetSlotsCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\BroadcastCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\menu\button\BackButton.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\Locale.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\command\individualperms\AddIndividualPermissionCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\Cooldown.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\packet\PacketServerRestart.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\packet\PacketDeleteGrant.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\PingCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\command\RankDumpCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\chat\util\ChatComponentExtras.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\HastebinUtil.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\Essentials.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\BukkitUtils.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\packet\PacketRemovePunishments.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\command\individualperms\ListIndividualPermissionsCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\grant\command\GrantsCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\grant\menu\GrantsMenu.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\menu\button\JumpToMenuButton.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\packet\PacketBroadcastPunishment.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\command\RankSetPrefixCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\option\menu\ProfileOptionsMenu.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\MoreCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\procedure\PunishmentProcedureStage.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\BaseEvent.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\ItemBuilder.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\command\KickCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\BukkitReflection.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\command\WarnCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\command\RankDeleteCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\callback\TypeCallback.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\menu\pagination\PaginatedMenu.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\CC.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\grant\menu\RankSelectionMenu.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\PunishmentJsonSerializer.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\prefix\command\PrefixTypeAdapter.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\command\BlacklistCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\command\RankListCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\command\UnbanCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\grant\command\ClearGrantsCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\ClearChatCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\InvseeCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\procedure\PunishmentProcedure.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\board\events\BoardDestroyEvent.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\board\Board.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\option\menu\button\PrivateChatOptionButton.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\menu\PunishmentsListMenu.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\board\MainBoard.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\chat\command\MuteChatCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\prefix\command\SetPrefixCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\command\HistoryCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\menu\MenuListener.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\packet\PacketDeletePrefix.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\menu\Button.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\chat\filter\ChatFilter.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\packet\PacketStaffChat.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\grant\GrantJsonDeserializer.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\RawCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\json\JsonSerializer.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\SunsetCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\prefix\command\DeletePrefixCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\menu\button\DisplayButton.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\MasssayCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\ShowPlayerCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\packet\PacketAddGrant.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\grant\GrantJsonSerializer.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\grant\listener\GrantListener.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\option\ProfileStaffOptions.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\command\RankRenameCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\json\JsonDeserializer.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\ProfileInfo.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\PunishmentJsonDeserializer.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\board\BoardListener.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\RenameCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\packet\PacketRequestCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\option\menu\button\PrivateChatSoundsOptionButton.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\option\menu\button\PublicChatOptionButton.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\grant\procedure\GrantProcedureStage.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\HidePlayerCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\option\event\OptionsOpenedEvent.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\event\SpawnTeleportEvent.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\grant\event\GrantAppliedEvent.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\GameModeCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\PotionUtil.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\Profile.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\packet\PacketRefreshRank.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\grant\event\GrantExpireEvent.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\menu\PunishmentsMenu.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\TeleportCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\WorldCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\command\UnblacklistCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\TeleportPositionCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\comparator\RankComparator.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\hook\VaultProvider.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\grant\procedure\GrantProcedureType.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\command\RankCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\SpawnerCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\grant\command\GrantCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\command\AltsCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\listener\EssentialsListener.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\grant\Grant.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\callback\Callback.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\command\individualperms\RemoveIndividualPermissionCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\chat\ChatAttempt.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\ListCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\packet\PacketStaffSwitchServer.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\board\events\BoardCreateEvent.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\chat\util\ChatComponentBuilder.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\json\JsonChain.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\duration\DurationTypeAdapter.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\command\RankSetSuffixCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\command\RankCreateCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\bootstrap\BootstrappedListener.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\SpawnCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\command\StaffChatCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\procedure\PunishmentProcedureType.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\option\commands\TogglePrivateMessagesCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\prefix\command\SetPrefixWeightCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\menu\button\ConfirmationButton.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\command\RankSetColorCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\TextSplitter.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\command\MuteCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\ReplyCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\Punishment.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\essentials\command\TeleportAllCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\command\OptionsCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\prefix\menu\PrefixSelectionMenu.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\event\ReceiveStaffChatEvent.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\bootstrap\Bootstrapped.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\ProfileTypeAdapter.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\prefix\command\AddPrefixCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\command\StaffChatToggleCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\packet\PacketUpdatePrefix.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\menu\Menu.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\command\ClearPunishmentsCommand.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\listener\PunishmentListener.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\chat\event\ChatAttemptEvent.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\packet\PacketStaffLeaveNetwork.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\TimeUtil.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\NetworkPacketListener.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\option\ProfileOptions.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\board\BoardEntry.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\chat\filter\impl\ContainsFilter.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\profile\punishment\PunishmentType.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\util\LocationUtil.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\chat\listener\ChatListener.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\network\packet\PacketClearGrants.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\uuid\UUIDCache.java
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\main\java\net\centilehcf\core\rank\command\RankRemovePermissionCommand.java
diff --git a/target/maven-status/maven-compiler-plugin/testCompile/java-test-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/testCompile/java-test-compile/createdFiles.lst
new file mode 100644
index 0000000..b2048ec
--- /dev/null
+++ b/target/maven-status/maven-compiler-plugin/testCompile/java-test-compile/createdFiles.lst
@@ -0,0 +1 @@
+HastebinTest.class
diff --git a/target/maven-status/maven-compiler-plugin/testCompile/java-test-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/testCompile/java-test-compile/inputFiles.lst
new file mode 100644
index 0000000..a811469
--- /dev/null
+++ b/target/maven-status/maven-compiler-plugin/testCompile/java-test-compile/inputFiles.lst
@@ -0,0 +1 @@
+C:\Users\William\Desktop\Centile Dev Folder\Core\src\test\java\HastebinTest.java
diff --git a/target/test-classes/HastebinTest.class b/target/test-classes/HastebinTest.class
new file mode 100644
index 0000000..665c4ea
Binary files /dev/null and b/target/test-classes/HastebinTest.class differ