diff --git a/stark/.gitignore b/stark/.gitignore new file mode 100644 index 0000000..f157c1d --- /dev/null +++ b/stark/.gitignore @@ -0,0 +1,18 @@ +# IntelliJ +.idea/ +*.iml +*.iws + +# Mac +.DS_Store + +# Maven +log/ +target/ +dependency-reduced-pom.xml + +# Java +*.jar + +# JRebel +rebel.xml diff --git a/stark/bukkit/pom.xml b/stark/bukkit/pom.xml new file mode 100644 index 0000000..462a434 --- /dev/null +++ b/stark/bukkit/pom.xml @@ -0,0 +1,167 @@ + + + + net.evilblock.stark + stark-parent + 1.0-SNAPSHOT + + 4.0.0 + + bukkit + + + + dmulloy2-repo + http://repo.dmulloy2.net/nexus/repository/public/ + + + jitpack.io + https://jitpack.io + + + + + + net.hylist + spigot-server + 1.7.10-R0.1-SNAPSHOT + provided + + + net.hylist + spigot-api + 1.7.10-R0.1-SNAPSHOT + provided + + + net.evilblock.stark + core + 1.0-SNAPSHOT + compile + + + mkremins + fanciful + 1.0-SNAPSHOT + system + C:/Users/sindr/Desktop/1v1club/fancy boy/target/fanciful-0.4.0-SNAPSHOT.jar + + + com.comphenix.protocol + ProtocolLib-API + 4.3.0 + provided + + + com.github.MilkBowl + VaultAPI + 1.7 + provided + + + org.reflections + reflections + 0.9.11 + compile + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + 1.3.41 + + + org.jetbrains.kotlin + kotlin-reflect + 1.3.41 + compile + + + + + + Stark + src/main/kotlin + + + net.md-5 + scriptus + 0.2 + + + initialize + + git-Stark-%s + bukkit.desc + + + describe + + + + + + kotlin-maven-plugin + org.jetbrains.kotlin + 1.3.41 + + + compile + + compile + + + + ${project.basedir}/src/main/kotlin + + + + + + + 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 + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.0.0 + + + package + + shade + + + + + + + + \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/Stark.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/Stark.kt new file mode 100644 index 0000000..077cf5b --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/Stark.kt @@ -0,0 +1,342 @@ +package net.evilblock.stark + +import com.comphenix.protocol.ProtocolLibrary +import com.google.common.util.concurrent.ThreadFactoryBuilder +import com.google.gson.GsonBuilder +import mkremins.fanciful.FancyMessage +import net.evilblock.stark.availability.AvailabilityHeartbeatRunnable +import net.evilblock.stark.availability.AvailabilityListeners +import net.evilblock.stark.config.ConfigMemory +import net.evilblock.stark.core.StarkCore +import net.evilblock.stark.core.mongo.MongoCredentials +import net.evilblock.stark.core.profile.ProfileHandler +import net.evilblock.stark.core.rank.Rank +import net.evilblock.stark.core.rank.runnable.RankLoadRunnable +import net.evilblock.stark.core.redis.RedisCredentials +import net.evilblock.stark.core.uuid.UUIDCacheLoadRunnable +import net.evilblock.stark.engine.command.CommandHandler +import net.evilblock.stark.engine.command.defaults.* +import net.evilblock.stark.engine.menu.ButtonListeners +import net.evilblock.stark.engine.protocol.InventoryAdapter +import net.evilblock.stark.engine.protocol.LagCheck +import net.evilblock.stark.engine.protocol.PingAdapter +import net.evilblock.stark.messaging.MessagingManager +import net.evilblock.stark.messaging.command.* +import net.evilblock.stark.modsuite.ModSuiteMessageListeners +import net.evilblock.stark.modsuite.command.* +import net.evilblock.stark.modsuite.options.ModOptionsListeners +import net.evilblock.stark.profile.* +import net.evilblock.stark.profile.grant.ProfileGrantListeners +import net.evilblock.stark.profile.grant.command.GrantCommand +import net.evilblock.stark.profile.grant.command.GrantsCommand +import net.evilblock.stark.profile.grant.command.RankCommand +import net.evilblock.stark.profile.punishment.command.create.BanCommand +import net.evilblock.stark.profile.punishment.command.create.BlacklistCommand +import net.evilblock.stark.profile.punishment.command.create.MuteCommand +import net.evilblock.stark.profile.punishment.command.create.TempBanCommand +import net.evilblock.stark.profile.punishment.command.remove.UnbanCommand +import net.evilblock.stark.profile.punishment.command.remove.UnblacklistCommand +import net.evilblock.stark.profile.punishment.command.remove.UnmuteCommand +import net.evilblock.stark.profile.runnable.ProfileHeartbeatRunnable +import net.evilblock.stark.rank.RankParameterType +import net.evilblock.stark.rank.RankCommands +import net.evilblock.stark.reboot.RebootCommands +import net.evilblock.stark.reboot.RebootHandler +import net.evilblock.stark.reboot.RebootListener +import net.evilblock.stark.server.ServerHandler +import net.evilblock.stark.server.ServerSyncRunnable +import net.evilblock.stark.server.command.* +import net.evilblock.stark.server.listener.* +import net.evilblock.stark.util.event.HourEvent +import net.evilblock.stark.util.serialization.* +import net.evilblock.stark.uuid.UUIDListeners +import org.bukkit.Bukkit +import org.bukkit.Location +import org.bukkit.inventory.ItemStack +import org.bukkit.plugin.java.JavaPlugin +import org.bukkit.potion.PotionEffect +import org.bukkit.util.BlockVector +import org.bukkit.util.Vector +import java.util.* +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit + +class Stark : JavaPlugin() { + + lateinit var core: StarkCore + + val serverHandler: ServerHandler = ServerHandler() + val messagingManager: MessagingManager = MessagingManager() + val rebootHandler: RebootHandler = RebootHandler() + val commandHandler: CommandHandler = CommandHandler() + + var enabledAt: Long = -1 + + override fun onEnable() { + + instance = this + enabledAt = System.currentTimeMillis() + + ConfigMemory.load() + + try { + saveDefaultConfig() + + core = object : StarkCore(logger) { + val profileHandler = object : ProfileHandler() { + override fun createProfileInstance(uuid: UUID): BukkitProfile { + return BukkitProfile(uuid) + } + } + + override fun isPrimaryThread(): Boolean { + return Bukkit.isPrimaryThread() + } + + override fun getProfileHandler(): ProfileHandler { + return profileHandler + } + } + + core.load(config.getString("TimeZone"), + getRedisCredentials("Local"), + getRedisCredentials("Backbone"), + getMongoCredentials()) + + if (!core.servers.getServerByName(Bukkit.getServerName()).isPresent) { + core.servers.loadOrCreateServer(Bukkit.getServerName(), Bukkit.getPort()) + } + + checkForServerMismatch() + + core.globalMessageChannel.registerListener(ModSuiteMessageListeners()) + core.globalMessageChannel.registerListener(ProfileMessageListeners()) + + loadEngine() + + registerListeners() + registerCommands() + + setupHourEvents() + + server.scheduler.runTaskTimerAsynchronously(this, ProfileHeartbeatRunnable(), 20L * 30, 20L * 30) + server.scheduler.runTaskTimerAsynchronously(this, UUIDCacheLoadRunnable(), 20L * 120 * 2, 20L * 120) + server.scheduler.runTaskTimerAsynchronously(this, RankLoadRunnable(), 20L * 60, 20L * 60) + server.scheduler.runTaskTimerAsynchronously(this, AvailabilityHeartbeatRunnable(), 20L * 3, 20L * 3) + server.scheduler.runTaskTimerAsynchronously(this, ServerSyncRunnable(), 40L, 40L) + + server.messenger.registerOutgoingPluginChannel(this, "BungeeCord") + + logger.info("Finished loading stark in ${((System.currentTimeMillis() - enabledAt) / 1000)}ms") + } catch (e: Exception) { + logger.severe("An error occurred on startup") + e.printStackTrace() + server.shutdown() + } + + // hijack logging filter to suppress the server's complaints about commands being dispatched async + Bukkit.getLogger().setFilter { + return@setFilter !it.message.contains("Command Dispatched Async") + } + + // hook into vault + if (server.pluginManager.getPlugin("Vault") != null) { + Class.forName(this::class.java.`package`.name + ".hook.VaultHook").getMethod("hook").invoke(null) + } + } + + override fun onDisable() { + core.redis.close() + core.mongo.close() + } + + private fun loadEngine() { + serverHandler.load() + rebootHandler.load() + commandHandler.load() + + val pingAdapter = PingAdapter() + + ProtocolLibrary.getProtocolManager().addPacketListener(pingAdapter) + ProtocolLibrary.getProtocolManager().addPacketListener(InventoryAdapter()) + + server.pluginManager.registerEvents(pingAdapter, this) + + LagCheck().runTaskTimerAsynchronously(this, 100L, 100L) + } + + private fun registerListeners() { + val pm = Bukkit.getPluginManager() + pm.registerEvents(ButtonListeners(), this) + pm.registerEvents(UUIDListeners(), this) + pm.registerEvents(AvailabilityListeners(), this) + pm.registerEvents(ProfileListeners(), this) + pm.registerEvents(ProfileGrantListeners(), this) + pm.registerEvents(RebootListener(), this) + pm.registerEvents(DisallowedCommandsListeners(), this) + pm.registerEvents(FreezeListeners(), this) + pm.registerEvents(FrozenServerListeners(), this) + pm.registerEvents(FrozenPlayerListeners(), this) + pm.registerEvents(HeadNameListeners(), this) + pm.registerEvents(ColoredSignListeners(), this) + pm.registerEvents(TeleportationListeners(), this) + pm.registerEvents(ModOptionsListeners(), this) + pm.registerEvents(ChatFilterListeners(), this) + } + + private fun registerCommands() { + commandHandler.registerParameterType(BukkitProfile::class.java, ProfileParameterType()) + commandHandler.registerParameterType(Rank::class.java, RankParameterType()) + + // punishment commands + commandHandler.registerClass(BanCommand::class.java) + commandHandler.registerClass(TempBanCommand::class.java) + commandHandler.registerClass(UnbanCommand::class.java) + commandHandler.registerClass(BlacklistCommand::class.java) + commandHandler.registerClass(UnblacklistCommand::class.java) + commandHandler.registerClass(MuteCommand::class.java) + commandHandler.registerClass(UnmuteCommand::class.java) + commandHandler.registerClass(KickCommand::class.java) + + // essential commands + commandHandler.registerClass(BroadcastCommand::class.java) + commandHandler.registerClass(BuildCommand::class.java) + commandHandler.registerClass(ClearCacheCommand::class.java) + commandHandler.registerClass(ClearCommand::class.java) + commandHandler.registerClass(CraftCommand::class.java) + commandHandler.registerClass(EnchantCommand::class.java) + commandHandler.registerClass(FeedCommand::class.java) + commandHandler.registerClass(FlyCommand::class.java) + commandHandler.registerClass(FreezeCommand::class.java) + commandHandler.registerClass(GamemodeCommands::class.java) + commandHandler.registerClass(HeadCommand::class.java) + commandHandler.registerClass(HealCommand::class.java) + commandHandler.registerClass(KillCommand::class.java) + commandHandler.registerClass(ListCommand::class.java) + commandHandler.registerClass(MoreCommand::class.java) + commandHandler.registerClass(RenameCommand::class.java) + commandHandler.registerClass(RepairCommand::class.java) + commandHandler.registerClass(SetSlotsCommand::class.java) + commandHandler.registerClass(SetSpawnCommand::class.java) + commandHandler.registerClass(SpawnerCommand::class.java) + commandHandler.registerClass(SpeedCommand::class.java) + commandHandler.registerClass(SudoCommands::class.java) + commandHandler.registerClass(TeleportationCommands::class.java) + commandHandler.registerClass(UptimeCommand::class.java) + commandHandler.registerClass(WorldCommand::class.java) + + // messaging commands + commandHandler.registerClass(IgnoreCommand::class.java) + commandHandler.registerClass(IgnoreListClearCommand::class.java) + commandHandler.registerClass(IgnoreListCommand::class.java) + commandHandler.registerClass(IgnoreRemoveCommand::class.java) + commandHandler.registerClass(MessageCommand::class.java) + commandHandler.registerClass(ReplyCommand::class.java) + commandHandler.registerClass(SpyCommand::class.java) + commandHandler.registerClass(ToggleMessagesCommand::class.java) + commandHandler.registerClass(ToggleSoundsCommand::class.java) + + // staff commands + commandHandler.registerClass(AltsCommand::class.java) + commandHandler.registerClass(ReportCommand::class.java) + commandHandler.registerClass(RequestCommand::class.java) + commandHandler.registerClass(StaffChatCommand::class.java) + commandHandler.registerClass(ToggleStaffChatCommand::class.java) + commandHandler.registerClass(ToggleRequestsCommand::class.java) + + // profile commands + commandHandler.registerClass(ProfileCommands::class.java) + commandHandler.registerClass(GrantCommand::class.java) + commandHandler.registerClass(GrantsCommand::class.java) + commandHandler.registerClass(RankCommand::class.java) + + // server management commands + commandHandler.registerClass(RankCommands::class.java) + commandHandler.registerClass(RebootCommands::class.java) + commandHandler.registerClass(ClearChatCommand::class.java) + commandHandler.registerClass(FreezeServerCommand::class.java) + commandHandler.registerClass(MuteChatCommand::class.java) + commandHandler.registerClass(SlowChatCommand::class.java) + commandHandler.registerClass(WhitelistCommands::class.java) + } + + private fun checkForServerMismatch() { + if (!core.servers.checkNamePortMatch(Bukkit.getServerName(), Bukkit.getPort())) { + logger.severe("********************************************************") + logger.severe("Can't start server because server.properties config doesn't match the bungee config.yml.") + logger.severe("Make sure the `server-id` value in `server.properties` matches the server-id assigned to") + logger.severe("this server's port in your bungee config.yml.") + logger.severe("********************************************************") + server.pluginManager.disablePlugin(this) + server.shutdown() + } + } + + private fun setupHourEvents() { + val executor = Executors.newSingleThreadScheduledExecutor(ThreadFactoryBuilder().setNameFormat("stark - Hour Event Thread").setDaemon(true).build()) + val minOfHour = Calendar.getInstance().get(12) + val minToHour = 60 - minOfHour + executor.scheduleAtFixedRate({ instance.server.scheduler.runTask(instance) { Bukkit.getPluginManager().callEvent(HourEvent(Calendar.getInstance().get(11))) } }, minToHour.toLong(), 60L, TimeUnit.MINUTES) + } + + private fun getRedisCredentials(prefix: String): RedisCredentials { + val builder = RedisCredentials.Builder() + .host(config.getString("${prefix}Redis.Host")) + .port(config.getInt("${prefix}Redis.Port")) + + if (config.contains("${prefix}Redis.Password")) { + builder.password(config.getString("${prefix}Redis.Password")) + } + + if (config.contains("${prefix}Redis.DbId")) { + builder.dbId(config.getInt("${prefix}Redis.DbId")) + } + + return builder.build() + } + + private fun getMongoCredentials(): MongoCredentials { + val builder = MongoCredentials.Builder() + .host(config.getString("Mongo.Host")) + .port(config.getInt("Mongo.Port")) + + if (config.contains("Mongo.Username")) { + builder.username(config.getString("Mongo.Username")) + } + + if (config.contains("Mongo.Password")) { + builder.password(config.getString("Mongo.Password")) + } + + return builder.build() + } + + companion object { + + @JvmStatic + lateinit var instance: Stark + + @JvmStatic + val gson = GsonBuilder() + .registerTypeHierarchyAdapter(PotionEffect::class.java, PotionEffectAdapter()) + .registerTypeHierarchyAdapter(ItemStack::class.java, ItemStackAdapter()) + .registerTypeHierarchyAdapter(Location::class.java, LocationAdapter()) + .registerTypeHierarchyAdapter(org.bukkit.util.Vector::class.java, VectorAdapter()) + .registerTypeAdapter(BlockVector::class.java, BlockVectorAdapter()) + .setPrettyPrinting() + .serializeNulls() + .create() + + @JvmStatic + val plainGson = GsonBuilder() + .registerTypeHierarchyAdapter(PotionEffect::class.java, PotionEffectAdapter()) + .registerTypeHierarchyAdapter(ItemStack::class.java, ItemStackAdapter()) + .registerTypeHierarchyAdapter(Location::class.java, LocationAdapter()) + .registerTypeHierarchyAdapter(Vector::class.java, VectorAdapter()) + .registerTypeAdapter(BlockVector::class.java, BlockVectorAdapter()) + .serializeNulls() + .create() + + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/availability/AvailabilityHeartbeatRunnable.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/availability/AvailabilityHeartbeatRunnable.kt new file mode 100644 index 0000000..2353d58 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/availability/AvailabilityHeartbeatRunnable.kt @@ -0,0 +1,14 @@ +package net.evilblock.stark.availability + +import net.evilblock.stark.Stark +import org.bukkit.Bukkit + +class AvailabilityHeartbeatRunnable : Runnable { + + override fun run() { + Stark.instance.server.onlinePlayers.forEach { player -> + Stark.instance.core.availabilityHandler.update(player.uniqueId, true, null, Bukkit.getServerId()) + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/availability/AvailabilityListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/availability/AvailabilityListeners.kt new file mode 100644 index 0000000..13ea318 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/availability/AvailabilityListeners.kt @@ -0,0 +1,22 @@ +package net.evilblock.stark.availability + +import net.evilblock.stark.Stark +import org.bukkit.Bukkit +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.player.AsyncPlayerPreLoginEvent +import org.bukkit.event.player.PlayerQuitEvent + +class AvailabilityListeners : Listener { + + @EventHandler + fun onAsyncPlayerPreLoginEvent(event: AsyncPlayerPreLoginEvent) { + Stark.instance.core.availabilityHandler.update(event.uniqueId, true, null, Bukkit.getServerId()) + } + + @EventHandler + fun onPlayerQuitEvent(event: PlayerQuitEvent) { + Stark.instance.core.availabilityHandler.update(event.player.uniqueId, false, null, Bukkit.getServerId()) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/config/ConfigMemory.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/config/ConfigMemory.kt new file mode 100644 index 0000000..b1b1726 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/config/ConfigMemory.kt @@ -0,0 +1,25 @@ +package net.evilblock.stark.config + +import net.evilblock.stark.Stark +import net.evilblock.stark.core.config.ConfigReader +import java.io.File + +object ConfigMemory { + + var langConfig: Map? = null + + fun load() { + langConfig = ConfigReader.readJsonToMap(ensureFileExists("lang.json")) + } + + private fun ensureFileExists(fileName: String): File { + val file = File(Stark.instance.dataFolder, fileName) + + if (!file.exists()) { + Stark.instance.saveResource(fileName, false) + } + + return file + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/config/LangConfigEntry.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/config/LangConfigEntry.kt new file mode 100644 index 0000000..ffa57f0 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/config/LangConfigEntry.kt @@ -0,0 +1,16 @@ +package net.evilblock.stark.config + +import net.evilblock.stark.core.config.ConfigEntry +import net.evilblock.stark.core.config.ConfigEntryTransformer + +class LangConfigEntry(transformer: ConfigEntryTransformer, key: String) : ConfigEntry(transformer, key) { + + override fun getCachedObject(): Map { + return ConfigMemory.langConfig!! + } + + companion object { + val REBOOT_LINES: ConfigEntry> = LangConfigEntry(ConfigEntryTransformer.STRING_LIST, "reboot.broadcast-lines") + } + +} diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/Command.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/Command.kt new file mode 100644 index 0000000..a80fb4f --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/Command.kt @@ -0,0 +1,10 @@ +package net.evilblock.stark.engine.command + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class Command(val names: Array, + val permission: String = "", + val hidden: Boolean = false, + val async: Boolean = false, + val description: String = "", + val logToConsole: Boolean = true) diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/CommandHandler.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/CommandHandler.kt new file mode 100644 index 0000000..0593ff5 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/CommandHandler.kt @@ -0,0 +1,195 @@ +package net.evilblock.stark.engine.command + +import net.evilblock.stark.core.util.mojanguser.MojangUser +import net.evilblock.stark.engine.command.bukkit.ExtendedCommand +import net.evilblock.stark.engine.command.bukkit.ExtendedCommandMap +import net.evilblock.stark.engine.command.bukkit.ExtendedHelpTopic +import net.evilblock.stark.engine.command.data.method.MethodProcessor +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import net.evilblock.stark.engine.command.data.parameter.impl.* +import net.evilblock.stark.engine.command.data.parameter.impl.filter.NormalFilter +import net.evilblock.stark.engine.command.data.parameter.impl.filter.StrictFilter +import net.evilblock.stark.engine.command.data.parameter.impl.offlineplayer.OfflinePlayerWrapper +import net.evilblock.stark.engine.command.data.parameter.impl.offlineplayer.OfflinePlayerWrapperParameterType +import net.evilblock.stark.engine.command.defaults.* +import net.evilblock.stark.engine.command.data.parameter.impl.* +import net.evilblock.stark.engine.command.defaults.* +import net.evilblock.stark.util.ClassUtils +import org.bukkit.Bukkit +import org.bukkit.GameMode +import org.bukkit.OfflinePlayer +import org.bukkit.World +import org.bukkit.command.Command +import org.bukkit.command.CommandMap +import org.bukkit.command.SimpleCommandMap +import org.bukkit.enchantments.Enchantment +import org.bukkit.entity.EntityType +import org.bukkit.entity.Player +import org.bukkit.inventory.ItemStack +import org.bukkit.plugin.Plugin +import org.bukkit.plugin.java.JavaPlugin +import java.lang.reflect.Field +import java.lang.reflect.Method +import java.util.* +import kotlin.collections.HashMap + +class CommandHandler { + + companion object { + val rootNode: CommandNode = CommandNode() + } + + val parameterTypeMap: MutableMap, ParameterType<*>> = HashMap() + private val commandMap: CommandMap + private val knownCommands: MutableMap + + init { + val commandMapField = Bukkit.getServer().javaClass.getDeclaredField("commandMap") + commandMapField.setAccessible(true) + + commandMap = commandMapField.get(Bukkit.getServer()) as CommandMap + + val knownCommandsField = SimpleCommandMap::class.java.getDeclaredField("knownCommands") + knownCommandsField.setAccessible(true) + + knownCommands = knownCommandsField.get(commandMap) as MutableMap + } + + fun load() { + registerParameterType(Boolean::class.java, BooleanParameterType()) + registerParameterType(Integer::class.java, IntegerParameterType()) + registerParameterType(Int::class.java, IntegerParameterType()) + registerParameterType(Double::class.java, DoubleParameterType()) + registerParameterType(Float::class.java, FloatParameterType()) + registerParameterType(String::class.java, StringParameterType()) + registerParameterType(GameMode::class.java, GameModeParameterType()) + registerParameterType(EntityType::class.java, EntityTypeParameterType()) + registerParameterType(Player::class.java, PlayerParameterType()) + registerParameterType(World::class.java, WorldParameterType()) + registerParameterType(ItemStack::class.java, ItemStackParameterType()) + registerParameterType(Enchantment::class.java, EnchantmentParameterType()) + registerParameterType(OfflinePlayer::class.java, OfflinePlayerParameterType()) + registerParameterType(OfflinePlayerWrapper::class.java, OfflinePlayerWrapperParameterType()) + registerParameterType(MojangUser::class.java, MojangUserParameterType()) + registerParameterType(UUID::class.java, UUIDParameterType()) + registerParameterType(NormalFilter::class.java, NormalFilter()) + registerParameterType(StrictFilter::class.java, StrictFilter()) + + registerClass(BroadcastCommand::class.java) + registerClass(BuildCommand::class.java) + registerClass(ClearCommand::class.java) + registerClass(CraftCommand::class.java) + registerClass(EnchantCommand::class.java) + registerClass(FeedCommand::class.java) + registerClass(FlyCommand::class.java) + registerClass(FreezeCommand::class.java) + registerClass(GamemodeCommands::class.java) + registerClass(HeadCommand::class.java) + registerClass(HealCommand::class.java) + registerClass(KickCommand::class.java) + registerClass(KillCommand::class.java) + registerClass(ListCommand::class.java) + registerClass(MoreCommand::class.java) + registerClass(RenameCommand::class.java) + registerClass(RepairCommand::class.java) + registerClass(SetSlotsCommand::class.java) + registerClass(SetSpawnCommand::class.java) + registerClass(SpawnerCommand::class.java) + registerClass(SpeedCommand::class.java) + registerClass(SudoCommands::class.java) + registerClass(TeleportationCommands::class.java) + registerClass(UptimeCommand::class.java) + registerClass(WorldCommand::class.java) + + swapCommandMap() + } + + fun registerParameterType(clazz: Class<*>, parameterType: ParameterType<*>) { + parameterTypeMap[clazz] = parameterType + } + + fun getParameterType(clazz: Class<*>): ParameterType<*>? { + return parameterTypeMap[clazz] + } + + private fun swapCommandMap() { + val commandMapField = Bukkit.getServer().javaClass.getDeclaredField("commandMap") + commandMapField.setAccessible(true) + + val oldCommandMap = commandMapField.get(Bukkit.getServer()) + val newCommandMap = ExtendedCommandMap(Bukkit.getServer()) + + val knownCommandsField = SimpleCommandMap::class.java.getDeclaredField("knownCommands") + knownCommandsField.setAccessible(true) + + val modifiersField = Field::class.java.getDeclaredField("modifiers") + modifiersField.setAccessible(true) + modifiersField.setInt(knownCommandsField, knownCommandsField.modifiers and -0x11) + + knownCommandsField.set(newCommandMap, knownCommandsField.get(oldCommandMap)) + commandMapField.set(Bukkit.getServer(), newCommandMap) + } + + fun registerClass(clazz: Class<*>) { + for (method in clazz.methods) { + registerMethod(method) + } + } + + private fun registerMethod(method: Method) { + method.setAccessible(true) + + val nodes = MethodProcessor().process(method) + if (nodes != null) { + nodes.forEach { node -> + val command = ExtendedCommand(node, JavaPlugin.getProvidingPlugin(method.declaringClass)) + + register(command) + + node.children.values.forEach { child -> + registerHelpTopic(child, node.aliases) + } + } + } + } + + private fun register(command: ExtendedCommand) { + val iterator = knownCommands.iterator() + + while (iterator.hasNext()) { + val entry = iterator.next() + if (entry.value.name.equals(command.name, true)) { + entry.value.unregister(commandMap) + iterator.remove() + } + } + + command.aliases.forEach { alias -> + knownCommands[alias] = command + } + + command.register(commandMap) + knownCommands[command.name] = command + } + + private fun registerHelpTopic(node: CommandNode, aliases: Set?) { + if (node.method != null) { + Bukkit.getHelpMap().addTopic(ExtendedHelpTopic(node, aliases)) + } + + if (node.hasCommands()) { + node.children.values.forEach { child -> + registerHelpTopic(child, null) + } + } + } + + fun registerPackage(plugin: Plugin, packageName: String) { + ClassUtils.getClassesInPackage(plugin, packageName).forEach(this::registerClass) + } + + fun registerAll(plugin: Plugin) { + registerPackage(plugin, plugin::class.java.`package`.name) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/CommandNode.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/CommandNode.kt new file mode 100644 index 0000000..83fbcc6 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/CommandNode.kt @@ -0,0 +1,366 @@ +package net.evilblock.stark.engine.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.data.Data +import net.evilblock.stark.engine.command.data.argument.Arguments +import net.evilblock.stark.engine.command.data.flag.FlagData +import net.evilblock.stark.engine.command.data.parameter.ParameterData +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import com.google.common.base.Strings +import mkremins.fanciful.FancyMessage +import org.apache.commons.lang.StringUtils +import org.bukkit.ChatColor +import org.bukkit.command.CommandException +import org.bukkit.command.CommandSender +import org.bukkit.command.ConsoleCommandSender +import org.bukkit.entity.Player +import org.spigotmc.SpigotConfig +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method +import java.util.* +import java.util.stream.Collectors +import kotlin.collections.ArrayList + +class CommandNode(var name: String?, + var permission: String?, + var description: String?) { + + var async: Boolean = false + var hidden: Boolean = false + val aliases: MutableSet = HashSet() + val children: MutableMap = TreeMap() + var validFlags: MutableList = mutableListOf() + var parameters: MutableList = mutableListOf() + var parent: CommandNode? = null + var logToConsole: Boolean = false + var method: Method? = null + var owningClass: Class<*>? = null + + constructor(clazz: Class<*>) : this(null, null, "ROOT NODE") { + owningClass = clazz + } + + constructor() : this(null, null, null) + constructor(name: String) : this(name, null, "") + constructor(name: String, permission: String?) : this(name, permission, "") + + fun registerCommand(commandNode: CommandNode) { + if (commandNode.name != null) { + commandNode.parent = this + children[commandNode.name!!] = commandNode + } else { + throw IllegalArgumentException("Cannot register command without a name to root node") + } + } + + fun hasCommand(name: String): Boolean = children.containsKey(name.toLowerCase()) + fun getCommand(name: String): CommandNode? = children[name.toLowerCase()] + fun hasCommands(): Boolean = children.isNotEmpty() + + fun findCommand(arguments: Arguments): CommandNode { + if (arguments.arguments.isNotEmpty()) { + val trySub = arguments.arguments[0] + + if (hasCommand(trySub)) { + arguments.arguments.removeAt(0) + + val returnNode = getCommand(trySub) as CommandNode + return returnNode.findCommand(arguments) + } + } + + return this + } + + fun isValidFlag(test: String): Boolean { + return if (test.length == 1) { + validFlags.contains(test) + } else { + validFlags.contains(test.toLowerCase()) + } + } + + fun canUse(sender: CommandSender): Boolean { + if (permission == null) { + return true + } + + return when (permission) { + "console" -> { + sender is ConsoleCommandSender + } + "op" -> { + sender.isOp + } + "" -> { + true + } + else -> { + sender.hasPermission(this.permission) + } + } + } + + fun getUsage(realLabel: String): FancyMessage { + val usage = FancyMessage("Usage: /$realLabel").color(ChatColor.RED) + usage.tooltip("${ChatColor.YELLOW}$description") + + val flags = mutableListOf() + flags.addAll(this.parameters.stream().filter { data -> data is FlagData }.map { data -> data as FlagData }.collect(Collectors.toList())) + + val parameters = mutableListOf() + parameters.addAll(this.parameters.stream().filter { data -> data is ParameterData }.map { data -> data as ParameterData }.collect(Collectors.toList())) + + var flagFirst = true + + if (flags.isNotEmpty()) { + usage.then("(").color(ChatColor.RED) + usage.tooltip("${ChatColor.YELLOW}$description") + + for (flag in flags) { + val name = flag.names[0] + + if (!flagFirst) { + usage.then(" | ").color(ChatColor.RED) + usage.tooltip("${ChatColor.YELLOW}$description") + } + + flagFirst = false + + usage.then("-$name").color(ChatColor.AQUA) + usage.tooltip("${ChatColor.GRAY}${flag.description}") + } + + usage.then(") ").color(ChatColor.RED) + usage.tooltip("${ChatColor.YELLOW}$description") + } + + if (parameters.isNotEmpty()) { + for (index in parameters.indices) { + val parameter = parameters[index] + val required = parameter.defaultValue.isEmpty() + + usage.then((if (required) "<" else "[") + parameter.name + (if (parameter.wildcard) "..." else "") + (if (required) ">" else "]") + if (index != parameters.size - 1) " " else "").color(ChatColor.RED) + usage.tooltip("${ChatColor.YELLOW}$description") + } + } + + return usage + } + + fun getUsage(): FancyMessage { + val usage = FancyMessage("") + val flags = mutableListOf() + flags.addAll(parameters.stream().filter { data -> data is FlagData }.map { data -> data as FlagData }.collect(Collectors.toList())) + + val parameters = mutableListOf() + parameters.addAll(parameters.stream().filter { data -> data is ParameterData }.map { data -> data as ParameterData }.collect(Collectors.toList())) + + var flagFirst = true + + if (flags.isNotEmpty()) { + usage.then("(").color(ChatColor.RED) + usage.tooltip("${ChatColor.YELLOW}$description") + + for (flag in flags) { + val name = flag.names[0] + + if (!flagFirst) { + usage.then(" | ").color(ChatColor.RED) + usage.tooltip("${ChatColor.YELLOW}$description") + } + + flagFirst = false + + usage.then("-$name").color(ChatColor.AQUA) + usage.tooltip("${ChatColor.GRAY}${flag.description}") + } + + usage.then(") ").color(ChatColor.RED) + usage.tooltip("${ChatColor.YELLOW}$description") + } + + if (parameters.isNotEmpty()) { + for (index in parameters.indices) { + val parameter = parameters[index] + val required = parameter.defaultValue.isEmpty() + + usage.then((if (required) "<" else "[") + parameter.name + (if (parameter.wildcard) "..." else "") + (if (required) ">" else "]") + if (index != parameters.size - 1) " " else "").color(ChatColor.RED) + usage.tooltip("${ChatColor.YELLOW}$description") + } + } + + return usage + } + + @Throws(CommandException::class) + operator fun invoke(sender: CommandSender, arguments: Arguments): Boolean { + if (method == null) { + if (hasCommands()) { + if (getSubCommands(sender, true).isEmpty()) { + if (hidden) { + sender.sendMessage(SpigotConfig.unknownCommandMessage) + } else { + sender.sendMessage("${ChatColor.RED}No permission.") + } + } + } else { + sender.sendMessage(SpigotConfig.unknownCommandMessage) + } + + return true + } + + val methodParamCount = method!!.parameterCount + val objects = ArrayList(methodParamCount) + objects.add(sender) + + var index = 0 + for (data in this.parameters) { + if (data is FlagData) { + var value = data.defaultValue + + for (s in data.names) { + if (arguments.hasFlag(s)) { + value = !value + break + } + } + + objects.add(data.methodIndex, value) + } else { + if (data !is ParameterData) { + continue + } + + var argument: String? + argument = if (index < arguments.arguments.size) { + arguments.arguments[index] + } else { + if (data.defaultValue.isEmpty()) { + return false + } else { + data.defaultValue + } + } + + if (data.wildcard && (argument.isEmpty() || argument != data.defaultValue)) { + argument = arguments.join(index) + } + + var type: ParameterType<*>? = Stark.instance.commandHandler.getParameterType(data.type) + + if (data.parameterType != null) { + try { + type = data.parameterType.newInstance() as ParameterType<*> + } catch (e1: InstantiationException) { + throw CommandException("Failed to create ParameterType instance: " + data.parameterType.name, e1) + } catch (e2: IllegalAccessException) { + throw CommandException("Failed to create ParameterType instance: " + data.parameterType.name, e2) + } + } + + if (type == null) { + sender.sendMessage("${ChatColor.RED}No data type found: ${(data.parameterType + ?: data.type).simpleName}") + return true + } + + val result = type.transform(sender, argument) ?: return true + objects.add(data.methodIndex, result) + + ++index + } + } + + try { + val start = System.currentTimeMillis() + + if (method!!.declaringClass.kotlin.isCompanion) { + method!!.invoke(method!!.declaringClass.kotlin.objectInstance, *objects.toTypedArray()) + } else { + method!!.invoke(null, *objects.toTypedArray()) + } + + val stop = System.currentTimeMillis() + + val executionThreshold = 10 // command threshold + + if (!async && logToConsole && stop - start >= executionThreshold) { + Stark.instance.logger.warning("Command '/" + getFullLabel() + "' took " + (stop - start) + "ms!") + } + + return true + } catch (e1: IllegalAccessException) { + throw CommandException("An error occurred while executing the command", e1) + } catch (e2: InvocationTargetException) { + throw CommandException("An error occurred while executing the command", e2) + } + } + + fun getSubCommands(sender: CommandSender, print: Boolean): List { + val commands = ArrayList() + if (canUse(sender)) { + val command = (if (sender is Player) "/" else "") + this.getFullLabel() + " " + getUsage().toOldMessageFormat() + if (Strings.isNullOrEmpty(this.description)) "" else "${ChatColor.GRAY} - $description" + + if (parent == null) { + commands.add(command) + } else if (parent?.name != null && CommandHandler.rootNode.getCommand(this.parent!!.name!!) !== this.parent) { + commands.add(command) + } + + if (this.hasCommands()) { + for (n in children.values) { + commands.addAll(n.getSubCommands(sender, false)) + } + } + } + + if (commands.isNotEmpty() && print) { + sender.sendMessage(ChatColor.BLUE.toString() + ChatColor.STRIKETHROUGH + StringUtils.repeat("-", 35)) + + for (command2 in commands) { + sender.sendMessage("${ChatColor.RED} $command2") + } + + sender.sendMessage(ChatColor.BLUE.toString() + ChatColor.STRIKETHROUGH + StringUtils.repeat("-", 35)) + } + + return commands + } + + fun getRealAliases(): Set { + aliases.remove(name) + return aliases + } + + fun getFullLabel(): String { + val labels = ArrayList() + var node: CommandNode? = this + + while (node != null) { + val name = node.name + + if (name != null) { + labels.add(name) + } + + node = node.parent + } + + labels.reverse() + labels.removeAt(0) + + val builder = StringBuilder() + labels.forEach { s -> builder.append(s).append(' ') } + + return builder.toString().trim { it <= ' ' } + } + + fun getUsageForHelpTopic(): String { + return if (method != null) { + "/" + getFullLabel() + " " + ChatColor.stripColor(getUsage().toOldMessageFormat()) + } else "" + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/bukkit/ExtendedCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/bukkit/ExtendedCommand.kt new file mode 100644 index 0000000..e47df73 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/bukkit/ExtendedCommand.kt @@ -0,0 +1,242 @@ +package net.evilblock.stark.engine.command.bukkit + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.CommandNode +import net.evilblock.stark.engine.command.data.argument.ArgumentProcessor +import net.evilblock.stark.engine.command.data.flag.Flag +import net.evilblock.stark.engine.command.data.parameter.ParameterData +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import org.apache.commons.lang.StringUtils +import org.apache.commons.lang.exception.ExceptionUtils +import org.bukkit.ChatColor +import org.bukkit.command.Command +import org.bukkit.command.CommandException +import org.bukkit.command.CommandSender +import org.bukkit.command.PluginIdentifiableCommand +import org.bukkit.entity.Player +import org.bukkit.plugin.Plugin +import org.bukkit.plugin.java.JavaPlugin +import org.spigotmc.SpigotConfig +import java.lang.StringBuilder +import java.util.* +import java.util.stream.Collectors +import kotlin.collections.ArrayList + +class ExtendedCommand(val node: CommandNode, private val plugin: JavaPlugin) : Command(node.name, "", "/", node.getRealAliases().toList()), PluginIdentifiableCommand { + + override fun getPlugin(): Plugin { + return plugin + } + + override fun execute(sender: CommandSender, label: String, args: Array): Boolean { + val label = label.replace("${plugin.name.toLowerCase()}:", "") + val newArgs = concat(label, args) + val arguments = ArgumentProcessor().process(newArgs) + val executionNode = node.findCommand(arguments) + val realLabel = getFullLabel(executionNode) + + if (executionNode.canUse(sender)) { + if (executionNode.async) { + plugin.server.scheduler.runTaskAsynchronously(plugin) { + try { + if (!executionNode.invoke(sender, arguments)) { + executionNode.getUsage(realLabel).send(sender) + } + } catch (e: CommandException) { + e.printStackTrace() + + executionNode.getUsage(realLabel).send(sender) + sender.sendMessage("${ChatColor.RED}An error occurred while processing your command.") + + if (sender.isOp) { + sendStackTrace(sender, e) + } + } + } + } else { + try { + if (!executionNode.invoke(sender, arguments)) { + executionNode.getUsage(realLabel).send(sender) + } + } catch (e: CommandException) { + e.printStackTrace() + + executionNode.getUsage(realLabel).send(sender) + sender.sendMessage("${ChatColor.RED}An error occurred while processing your command.") + + if (sender.isOp) { + sendStackTrace(sender, e) + } + } + } + } else if (executionNode.hidden) { + sender.sendMessage(SpigotConfig.unknownCommandMessage) + } else { + sender.sendMessage("${ChatColor.RED}No permission.") + } + + return true + } + + fun tabComplete(sender: CommandSender, cmdLine: String): List { + if (sender !is Player) { + return listOf() + } + + val rawArgs = cmdLine.replace("${plugin.name.toLowerCase()}:", "").split(" ") + + if (rawArgs.isEmpty()) { + if (!node.canUse(sender)) { + return listOf() + } + + return listOf() + } else { + val arguments = ArgumentProcessor().process(rawArgs.toTypedArray()) + val realNode = node.findCommand(arguments) + + if (!realNode.canUse(sender)) { + return listOf() + } + + val realArgs = arguments.arguments + var currentIndex = realArgs.size - 1 + + if (currentIndex < 0) { + currentIndex = 0 + } + + if (cmdLine.endsWith(" ") && realArgs.size >= 1) { + ++currentIndex; + } + + if (currentIndex < 0) { + return listOf() + } + + val completions: ArrayList = arrayListOf() + + if (realNode.hasCommands()) { + val name = if (realArgs.size == 0) "" else realArgs[realArgs.size - 1] + completions.addAll(realNode.children.values.stream().filter { node -> node.name != null && node.canUse(sender) && (StringUtils.startsWithIgnoreCase(node.name, name) || StringUtils.isEmpty(name)) }.map { node -> node.name }.collect(Collectors.toList())) + + if (completions.isNotEmpty()) { + return completions + } + } + + if (rawArgs[rawArgs.size - 1].equals(realNode.name, true) && !cmdLine.endsWith(" ")) { + return listOf() + } + + if (realNode.validFlags.isNotEmpty()) { + for (flag in realNode.validFlags) { + val arg = rawArgs[rawArgs.size - 1] + + if (Flag.FLAG_PATTERN.matcher(arg).matches() || arg == "-" && (StringUtils.startsWithIgnoreCase(flag, arg.substring(1, arg.length))) || arg == "-") { + completions.add("-$flag") + } + } + + if (completions.isNotEmpty()) { + return completions + } + } + + try { + val params = realNode.parameters.stream().filter { param -> param is ParameterData }.map { param -> param as ParameterData }.collect(Collectors.toList()) + val fixed = Math.max(0, currentIndex - 1) + + if (params.isEmpty()) { + return emptyList() + } + + val data: ParameterData = params[fixed]!! + val parameterType: ParameterType<*>? = Stark.instance.commandHandler.getParameterType(data.type) + + if (parameterType != null) { + if (currentIndex < realArgs.size && realArgs[currentIndex].equals(realNode.name, true)) { + realArgs.add("") + ++currentIndex + } + + val argumentBeingCompleted = (if (currentIndex >= realArgs.size || realArgs.size == 0) "" else realArgs[currentIndex]).trim() + val suggested = parameterType.tabComplete(sender, data.tabCompleteFlags, argumentBeingCompleted) + + completions.addAll(suggested.stream().filter { s -> StringUtils.startsWithIgnoreCase(s, argumentBeingCompleted) }.collect(Collectors.toList())) + } + } catch (e: java.lang.Exception) { + e.printStackTrace() + } + + return completions + } + } + + private fun concat(label: String, args: Array): Array { + val list = arrayListOf(label) + list.addAll(args.toList().stream().filter { it != null }.collect(Collectors.toList())) + return list.toTypedArray() + } + + private fun getFullLabel(node: CommandNode?): String { + var workingNode = node + val labels = arrayListOf() + + while (workingNode != null) { + val name = workingNode.name + + if (name != null) { + labels.add(name) + } + + workingNode = workingNode.parent + } + + labels.reverse() + labels.removeAt(0) + + val builder = StringBuilder() + + labels.forEach { s -> + builder.append(s).append(' ') + } + + return builder.toString() + } + + private fun sendStackTrace(sender: CommandSender, exception: Exception) { + val rootCauseMessage = ExceptionUtils.getRootCauseMessage(exception) + + sender.sendMessage("${ChatColor.RED}Message: $rootCauseMessage") + + val cause = ExceptionUtils.getStackTrace(exception) + val tokenizer = StringTokenizer(cause) + var exceptionType = "" + var details = "" + var parsingNeeded = false + + while (tokenizer.hasMoreTokens()) { + val token = tokenizer.nextToken() + + if (token.equals("Caused", true)) { + tokenizer.nextToken() + + parsingNeeded = true + exceptionType = tokenizer.nextToken() + } else { + if (token.equals("at", true) && parsingNeeded) { + details = tokenizer.nextToken() + break + } + + continue + } + } + + sender.sendMessage("${ChatColor.RED}Exception: ${exceptionType.replace(":", "")}") + sender.sendMessage("${ChatColor.RED}Details:") + sender.sendMessage("${ChatColor.RED}$details") + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/bukkit/ExtendedCommandMap.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/bukkit/ExtendedCommandMap.kt new file mode 100644 index 0000000..d6942b3 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/bukkit/ExtendedCommandMap.kt @@ -0,0 +1,95 @@ +package net.evilblock.stark.engine.command.bukkit + +import java.util.Collections +import net.evilblock.stark.engine.command.CommandNode +import org.bukkit.Server +import org.bukkit.command.Command +import org.bukkit.command.CommandException +import org.bukkit.entity.Player +import java.util.ArrayList +import org.bukkit.command.CommandSender +import org.bukkit.command.SimpleCommandMap +import org.bukkit.util.StringUtil + +class ExtendedCommandMap(server: Server) : SimpleCommandMap(server) { + + override fun tabComplete(sender: CommandSender, cmdLine: String): List? { + checkNotNull(sender as Any) { "Sender cannot be null" } + checkNotNull(cmdLine as Any) { "Command line cannot be null" } + + val spaceIndex = cmdLine.indexOf(' ') + + if (spaceIndex == -1) { + val completions = ArrayList() + val knownCommands = knownCommands as Map + val prefix = if (sender is Player) "/" else "" + + for ((name, command) in knownCommands) { + if (StringUtil.startsWithIgnoreCase(name, cmdLine)) { + if (command is ExtendedCommand) { + var executionNode = command.node.getCommand(name) + + if (executionNode == null) { + executionNode = command.node + } + + if (!executionNode.hasCommands()) { + var testNode: CommandNode? = executionNode.getCommand(name) + + if (testNode == null) { + testNode = command.node.getCommand(name) + } + + if (!testNode!!.canUse(sender)) { + continue + } + + completions.add(prefix + name) + } else { + if (executionNode.getSubCommands(sender, false).isEmpty()) { + continue + } + + completions.add(prefix + name) + } + } else { + if (!command.testPermissionSilent(sender)) { + continue + } + + completions.add(prefix + name) + } + } + } + + Collections.sort(completions, String.CASE_INSENSITIVE_ORDER) + return completions + } + + val commandName = cmdLine.substring(0, spaceIndex) + val target = getCommand(commandName) ?: return null + + if (!target.testPermissionSilent(sender)) { + return null + } + + val argLine = cmdLine.substring(spaceIndex + 1, cmdLine.length) + val args = argLine.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + + try { + val completions = if (target is ExtendedCommand) target.tabComplete(sender, cmdLine) else target.tabComplete(sender, commandName, args) + + if (completions != null) { + Collections.sort(completions, String.CASE_INSENSITIVE_ORDER) + } + + return completions + } catch (ex: CommandException) { + throw ex + } catch (ex2: Throwable) { + throw CommandException("Unhandled exception executing tab-completer for '$cmdLine' in $target", ex2) + } + + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/bukkit/ExtendedHelpTopic.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/bukkit/ExtendedHelpTopic.kt new file mode 100644 index 0000000..02df1b7 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/bukkit/ExtendedHelpTopic.kt @@ -0,0 +1,48 @@ +package net.evilblock.stark.engine.command.bukkit + +import org.bukkit.help.HelpTopic +import org.bukkit.command.CommandSender +import net.evilblock.stark.engine.command.CommandNode +import org.apache.commons.lang.StringUtils +import org.bukkit.ChatColor + +class ExtendedHelpTopic(private val node: CommandNode, aliases: Set?) : HelpTopic() { + + init { + name = "/" + node.name!! + + val description = node.description + + shortText = if (description!!.length < 32) { + description + } else { + description.substring(0, 32) + } + + val sb = StringBuilder() + sb.append(ChatColor.GOLD) + sb.append("Description: ") + sb.append(ChatColor.WHITE) + sb.append(node.description) + sb.append("\n") + sb.append(ChatColor.GOLD) + sb.append("Usage: ") + sb.append(ChatColor.WHITE) + sb.append(node.getUsageForHelpTopic()) + + if (aliases != null && aliases.isNotEmpty()) { + sb.append("\n") + sb.append(ChatColor.GOLD) + sb.append("Aliases: ") + sb.append(ChatColor.WHITE) + sb.append(StringUtils.join(aliases as Collection<*>?, ", ")) + } + + fullText = sb.toString() + } + + override fun canSee(commandSender: CommandSender): Boolean { + return node.canUse(commandSender) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/Data.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/Data.kt new file mode 100644 index 0000000..035b9e0 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/Data.kt @@ -0,0 +1,3 @@ +package net.evilblock.stark.engine.command.data + +interface Data \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/Type.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/Type.kt new file mode 100644 index 0000000..96c3e21 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/Type.kt @@ -0,0 +1,7 @@ +package net.evilblock.stark.engine.command.data + +import kotlin.reflect.KClass + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.VALUE_PARAMETER) +annotation class Type(val value: KClass<*>) \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/argument/ArgumentProcessor.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/argument/ArgumentProcessor.kt new file mode 100644 index 0000000..44cc585 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/argument/ArgumentProcessor.kt @@ -0,0 +1,44 @@ +package net.evilblock.stark.engine.command.data.argument + +import net.evilblock.stark.engine.command.data.flag.Flag +import net.evilblock.stark.engine.command.data.processor.Processor +import java.util.ArrayList + +class ArgumentProcessor : Processor, Arguments> { + + override fun process(type: Array): Arguments { + val flags = ArrayList() + val arguments = ArrayList() + + for (s in type) { + if (s.isNotEmpty()) { + if (s[0] == '-' && s != "-" && matches(s)) { + val flag = getFlagName(s) + if (flag != null) { + flags.add(flag) + } + } else { + arguments.add(s) + } + } + } + + return Arguments(arguments, flags) + } + + private fun getFlagName(flag: String): String? { + val matcher = Flag.FLAG_PATTERN.matcher(flag) + + if (matcher.matches()) { + val name = matcher.replaceAll("$2$3") + return if (name.length == 1) name else name.toLowerCase() + } + + return null + } + + private fun matches(flag: String): Boolean { + return Flag.FLAG_PATTERN.matcher(flag).matches() + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/argument/Arguments.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/argument/Arguments.kt new file mode 100644 index 0000000..efb2b81 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/argument/Arguments.kt @@ -0,0 +1,42 @@ +package net.evilblock.stark.engine.command.data.argument + +class Arguments(val arguments: MutableList, val flags: ArrayList) { + + fun hasFlag(flag: String): Boolean { + return this.flags.contains(flag.toLowerCase()) + } + + fun join(from: Int, to: Int, delimiter: Char): String { + var to = to + if (to > this.arguments.size - 1 || to < 1) { + to = this.arguments.size - 1 + } + + val builder = StringBuilder() + for (i in from..to) { + builder.append(this.arguments[i]) + if (i != to) { + builder.append(delimiter) + } + } + + return builder.toString() + } + + fun join(from: Int, delimiter: Char): String { + return this.join(from, -1, delimiter) + } + + fun join(from: Int): String { + return this.join(from, ' ') + } + + fun join(delimiter: Char): String { + return this.join(0, delimiter) + } + + fun join(): String { + return this.join(' ') + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/flag/Flag.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/flag/Flag.kt new file mode 100644 index 0000000..76835ea --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/flag/Flag.kt @@ -0,0 +1,13 @@ +package net.evilblock.stark.engine.command.data.flag + +import java.util.regex.Pattern + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.VALUE_PARAMETER) +annotation class Flag(vararg val value: String, val defaultValue: Boolean = false, val description: String = "") { + + companion object { + val FLAG_PATTERN: Pattern = Pattern.compile("(-)([a-zA-Z])([\\w]*)?") + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/flag/FlagData.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/flag/FlagData.kt new file mode 100644 index 0000000..7f0c556 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/flag/FlagData.kt @@ -0,0 +1,8 @@ +package net.evilblock.stark.engine.command.data.flag + +import net.evilblock.stark.engine.command.data.Data + +data class FlagData(val names: List, + val description: String, + val defaultValue: Boolean, + val methodIndex: Int) : Data \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/method/MethodProcessor.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/method/MethodProcessor.kt new file mode 100644 index 0000000..a1dcff6 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/method/MethodProcessor.kt @@ -0,0 +1,139 @@ +package net.evilblock.stark.engine.command.data.method + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.CommandHandler +import net.evilblock.stark.engine.command.CommandNode +import net.evilblock.stark.engine.command.data.Data +import net.evilblock.stark.engine.command.data.flag.Flag +import net.evilblock.stark.engine.command.data.flag.FlagData +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.engine.command.data.parameter.ParameterData +import net.evilblock.stark.engine.command.data.processor.Processor +import org.bukkit.command.CommandSender +import java.lang.IllegalArgumentException +import java.lang.reflect.Method +import kotlin.collections.HashSet + +class MethodProcessor : Processor?> { + + override fun process(type: Method): Set? { + if (type.isAnnotationPresent(Command::class.java)) { + if (type.parameterCount >= 1 && CommandSender::class.java.isAssignableFrom(type.parameterTypes[0])) { + val command = type.getAnnotation(Command::class.java) + val owningClass = type.declaringClass + val flagNames = mutableListOf() + val allParams = mutableListOf() + + if (type.parameterCount > 1) { + for (i in 1 until type.parameterCount) { + val parameter = type.parameters[i] + + if (parameter.isAnnotationPresent(Param::class.java)) { + val param: Param = parameter.getAnnotation(Param::class.java) + val hash = setOf(*param.tabCompleteFlags) + val data = ParameterData(param.name, param.defaultValue, parameter.type, param.wildcard, i, hash, Stark.instance.commandHandler.parameterTypeMap[parameter.type]?.javaClass) + allParams.add(data) + } else { + if (!parameter.isAnnotationPresent(Flag::class.java)) { + throw IllegalArgumentException("Every data, other than the sender, must be annotated with Param") + } + + val flag: Flag = parameter.getAnnotation(Flag::class.java) + val flagData = FlagData(flag.value.toList(), flag.description, flag.defaultValue, i) + + allParams.add(flagData) + flagNames.addAll(listOf(*flag.value)) + } + } + } + + val registered = HashSet() + + for (name in command.names) { + val qualifiedName = name.toLowerCase().trim() + var hadChild = false + var cmdNames: Array = arrayOf(qualifiedName) + + if (qualifiedName.contains(" ")) { + cmdNames = qualifiedName.split(" ").toTypedArray() + } + + val primaryName = cmdNames[0] + var workingNode = CommandNode(owningClass) + + if (CommandHandler.rootNode.hasCommand(primaryName)) { + workingNode = CommandHandler.rootNode.getCommand(primaryName)!! + workingNode.aliases.add(primaryName) + } else { + workingNode.name = primaryName + } + + var parentNode = CommandNode(owningClass) + + if (workingNode.hasCommand(primaryName)) { + parentNode = workingNode.getCommand(primaryName)!! + } else { + parentNode.name = primaryName + parentNode.permission = "" + } + + if (cmdNames.size > 1) { + hadChild = true + + workingNode.registerCommand(parentNode) + + var childNode = CommandNode(owningClass) + + for (i in 1 until cmdNames.size) { + val subName = cmdNames[i] + + childNode.name = subName + + if (parentNode.hasCommand(subName)) { + childNode = parentNode.getCommand(subName)!! + } + + parentNode.registerCommand(childNode) + + if (i == cmdNames.size - 1) { + childNode.method = type + childNode.async = command.async + childNode.hidden = command.hidden + childNode.permission = command.permission + childNode.description = command.description + childNode.validFlags = flagNames + childNode.parameters = allParams + childNode.logToConsole = command.logToConsole + } else { + parentNode = childNode + childNode = CommandNode(owningClass) + } + } + } + + if (!hadChild) { + parentNode.method = type + parentNode.async = command.async + parentNode.hidden = command.hidden + parentNode.permission = command.permission + parentNode.description = command.description + parentNode.validFlags = flagNames + parentNode.parameters = allParams + parentNode.logToConsole = command.logToConsole + + workingNode.registerCommand(parentNode) + } + + CommandHandler.rootNode.registerCommand(workingNode) + registered.add(workingNode) + } + + return registered + } + } + + return null + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/Param.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/Param.kt new file mode 100644 index 0000000..e25a018 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/Param.kt @@ -0,0 +1,8 @@ +package net.evilblock.stark.engine.command.data.parameter + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.VALUE_PARAMETER) +annotation class Param(val name: String, + val defaultValue: String = "", + val tabCompleteFlags: Array = [], + val wildcard: Boolean = false) \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/ParameterData.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/ParameterData.kt new file mode 100644 index 0000000..6c7413d --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/ParameterData.kt @@ -0,0 +1,11 @@ +package net.evilblock.stark.engine.command.data.parameter + +import net.evilblock.stark.engine.command.data.Data + +data class ParameterData(val name: String, + val defaultValue: String, + val type: Class<*>, + val wildcard: Boolean, + val methodIndex: Int, + val tabCompleteFlags: Set, + val parameterType: Class<*>?) : Data \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/ParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/ParameterType.kt new file mode 100644 index 0000000..c0591a8 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/ParameterType.kt @@ -0,0 +1,12 @@ +package net.evilblock.stark.engine.command.data.parameter + +import org.bukkit.entity.Player +import org.bukkit.command.CommandSender + +interface ParameterType { + + fun transform(sender: CommandSender, source: String): T + + fun tabComplete(player: Player, flags: Set, source: String): List + +} diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/BooleanParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/BooleanParameterType.kt new file mode 100644 index 0000000..c3eee27 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/BooleanParameterType.kt @@ -0,0 +1,34 @@ +package net.evilblock.stark.engine.command.data.parameter.impl + +import java.util.ArrayList +import org.bukkit.entity.Player +import org.bukkit.command.CommandSender +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import org.bukkit.ChatColor + +class BooleanParameterType : ParameterType { + + private val map: MutableMap = mutableMapOf() + + init { + map["on"] = true + map["yes"] = true + map["false"] = false + map["off"] = false + map["no"] = false + } + + override fun transform(sender: CommandSender, source: String): Boolean? { + if (!this.map.containsKey(source.toLowerCase())) { + sender.sendMessage("${ChatColor.RED} $source is not a valid boolean.") + return null + } + + return this.map[source.toLowerCase()] + } + + override fun tabComplete(sender: Player, flags: Set, source: String): List { + return ArrayList(this.map.keys) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/DoubleParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/DoubleParameterType.kt new file mode 100644 index 0000000..14a71af --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/DoubleParameterType.kt @@ -0,0 +1,36 @@ +package net.evilblock.stark.engine.command.data.parameter.impl + +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import org.bukkit.ChatColor +import org.bukkit.entity.Player +import org.bukkit.command.CommandSender + +class DoubleParameterType : ParameterType { + + override fun transform(sender: CommandSender, value: String): Double? { + if (value.toLowerCase().contains("e")) { + sender.sendMessage(ChatColor.RED.toString() + value + " is not a valid number.") + return null + } + + try { + val parsed = java.lang.Double.parseDouble(value) + + if (java.lang.Double.isNaN(parsed) || !java.lang.Double.isFinite(parsed)) { + sender.sendMessage(ChatColor.RED.toString() + value + " is not a valid number.") + return null + } + + return parsed + } catch (exception: NumberFormatException) { + sender.sendMessage(ChatColor.RED.toString() + value + " is not a valid number.") + return null + } + + } + + override fun tabComplete(sender: Player, flags: Set, prefix: String): List { + return listOf() + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/EnchantmentParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/EnchantmentParameterType.kt new file mode 100644 index 0000000..5315cf5 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/EnchantmentParameterType.kt @@ -0,0 +1,50 @@ +package net.evilblock.stark.engine.command.data.parameter.impl + +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import net.evilblock.stark.util.enchantment.EnchantmentWrapper +import org.apache.commons.lang.StringUtils +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.enchantments.Enchantment +import org.bukkit.entity.Player + +class EnchantmentParameterType : ParameterType { + + override fun transform(sender: CommandSender, source: String): Enchantment? { + val enchantment = EnchantmentWrapper.parse(source) + + if (enchantment == null) { + sender.sendMessage("${ChatColor.RED}No enchantment with the name " + source + " found.") + return null + } + + return enchantment.bukkitEnchantment + } + + override fun tabComplete(sender: Player, flags: Set, source: String): List { + val completions = ArrayList() + + outer@ for (enchantment in EnchantmentWrapper.values()) { + for (str in enchantment.parse) { + if (StringUtils.startsWithIgnoreCase(str, source)) { + completions.add(str) + continue@outer + } + } + + if (StringUtils.startsWithIgnoreCase(enchantment.friendlyName, source)) { + completions.add(enchantment.friendlyName.toLowerCase()) + continue + } + + if (StringUtils.startsWithIgnoreCase(enchantment.bukkitEnchantment.name, source)) { + completions.add(enchantment.friendlyName.toLowerCase()) + } + + continue + } + + return completions + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/EntityTypeParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/EntityTypeParameterType.kt new file mode 100644 index 0000000..d6bfbcf --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/EntityTypeParameterType.kt @@ -0,0 +1,50 @@ +package net.evilblock.stark.engine.command.data.parameter.impl + +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import net.evilblock.stark.util.EntityUtils +import org.apache.commons.lang.StringUtils +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.EntityType +import org.bukkit.entity.Player +import java.util.* + +class EntityTypeParameterType : ParameterType { + + override fun transform(sender: CommandSender, source: String): EntityType? { + val type = EntityUtils.parse(source) + if (type == null) { + for (possibleType in EntityType.values()) { + if (possibleType.name.equals(source, ignoreCase = true)) { + return possibleType + } + + if (possibleType.typeId.toString().equals(source, ignoreCase = true)) { + return possibleType + } + + if (StringUtils.startsWithIgnoreCase(possibleType.name, source)) { + return possibleType + } + } + } + + if (type == null) { + sender.sendMessage("${ChatColor.RED}No entity type with the name " + source + " found.") + return null + } + + return type + } + + override fun tabComplete(sender: Player, flags: Set, source: String): List { + val completions = ArrayList() + for (mode in EntityType.values()) { + if (StringUtils.startsWithIgnoreCase(mode.name, source)) { + completions.add(mode.name) + } + } + return completions + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/FloatParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/FloatParameterType.kt new file mode 100644 index 0000000..ca2f78e --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/FloatParameterType.kt @@ -0,0 +1,35 @@ +package net.evilblock.stark.engine.command.data.parameter.impl + +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import org.bukkit.ChatColor +import org.bukkit.entity.Player +import org.bukkit.command.CommandSender + +class FloatParameterType : ParameterType { + + override fun transform(sender: CommandSender, value: String): Float? { + if (value.toLowerCase().contains("e")) { + sender.sendMessage(ChatColor.RED.toString() + value + " is not a valid number.") + return null + } + + try { + val parsed = java.lang.Float.parseFloat(value) + + if (java.lang.Float.isNaN(parsed) || !java.lang.Float.isFinite(parsed)) { + sender.sendMessage(ChatColor.RED.toString() + value + " is not a valid number.") + return null + } + + return parsed + } catch (exception: NumberFormatException) { + sender.sendMessage(ChatColor.RED.toString() + value + " is not a valid number.") + return null + } + } + + override fun tabComplete(sender: Player, flags: Set, prefix: String): List { + return listOf() + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/GameModeParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/GameModeParameterType.kt new file mode 100644 index 0000000..cb433c9 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/GameModeParameterType.kt @@ -0,0 +1,45 @@ +package net.evilblock.stark.engine.command.data.parameter.impl + +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import org.apache.commons.lang.StringUtils +import org.bukkit.ChatColor +import org.bukkit.GameMode +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import java.util.* + +class GameModeParameterType : ParameterType { + + override fun transform(sender: CommandSender, source: String): GameMode? { + if (source != "-0*toggle*0-" || sender !is Player) { + for (mode in GameMode.values()) { + if (mode.name.equals(source, ignoreCase = true)) { + return mode + } + if (mode.value.toString().equals(source, ignoreCase = true)) { + return mode + } + if (StringUtils.startsWithIgnoreCase(mode.name, source)) { + return mode + } + } + sender.sendMessage("${ChatColor.RED}No gamemode with the name " + source + " found.") + return null + } + + return if (sender.gameMode != GameMode.CREATIVE) { + GameMode.CREATIVE + } else GameMode.SURVIVAL + } + + override fun tabComplete(sender: Player, flags: Set, source: String): List { + val completions = ArrayList() + for (mode in GameMode.values()) { + if (StringUtils.startsWithIgnoreCase(mode.name, source)) { + completions.add(mode.name) + } + } + return completions + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/IntegerParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/IntegerParameterType.kt new file mode 100644 index 0000000..0ef72fb --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/IntegerParameterType.kt @@ -0,0 +1,23 @@ +package net.evilblock.stark.engine.command.data.parameter.impl + +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import org.bukkit.ChatColor +import org.bukkit.entity.Player +import org.bukkit.command.CommandSender + +class IntegerParameterType : ParameterType { + + override fun transform(sender: CommandSender, value: String): Int? { + return try { + Integer.parseInt(value) + } catch (exception: NumberFormatException) { + sender.sendMessage("${ChatColor.RED} $value is not a valid number.") + null + } + } + + override fun tabComplete(sender: Player, flags: Set, prefix: String): List { + return listOf() + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/ItemStackParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/ItemStackParameterType.kt new file mode 100644 index 0000000..7170152 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/ItemStackParameterType.kt @@ -0,0 +1,27 @@ +package net.evilblock.stark.engine.command.data.parameter.impl + +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import net.evilblock.stark.util.ItemUtils +import org.bukkit.ChatColor +import org.bukkit.inventory.ItemStack +import org.bukkit.entity.Player +import org.bukkit.command.CommandSender + +class ItemStackParameterType : ParameterType { + + override fun transform(sender: CommandSender, source: String): ItemStack? { + val item = ItemUtils[source] + + if (item == null) { + sender.sendMessage("${ChatColor.RED}No item with the name $source found.") + return null + } + + return item + } + + override fun tabComplete(player: Player, flags: Set, source: String): List { + return listOf() + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/MojangUserParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/MojangUserParameterType.kt new file mode 100644 index 0000000..f5103e0 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/MojangUserParameterType.kt @@ -0,0 +1,33 @@ +package net.evilblock.stark.engine.command.data.parameter.impl + +import net.evilblock.stark.Stark +import net.evilblock.stark.core.util.mojanguser.MojangUser +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +class MojangUserParameterType : ParameterType { + + override fun transform(sender: CommandSender, source: String): MojangUser? { + val mojangUser = Stark.instance.core.getProfileHandler().fetchMojangUser(source) + + if (mojangUser == null) { + sender.sendMessage("${ChatColor.RED}A player by that name doesn't exist.") + } + + return mojangUser + } + + override fun tabComplete(sender: Player, flags: Set, source: String): List { + val completions = ArrayList() + + Bukkit.getOnlinePlayers().forEach { target -> + completions.add(target.name) + } + + return completions + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/OfflinePlayerParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/OfflinePlayerParameterType.kt new file mode 100644 index 0000000..3228458 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/OfflinePlayerParameterType.kt @@ -0,0 +1,29 @@ +package net.evilblock.stark.engine.command.data.parameter.impl + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import org.bukkit.OfflinePlayer +import org.bukkit.Bukkit +import org.bukkit.entity.Player +import java.util.ArrayList +import org.bukkit.command.CommandSender + +class OfflinePlayerParameterType : ParameterType { + + override fun transform(sender: CommandSender, source: String): OfflinePlayer { + return if (sender is Player && (source.equals("self", ignoreCase = true) || source == "")) { + sender + } else Stark.instance.server.getOfflinePlayer(source) + } + + override fun tabComplete(sender: Player, flags: Set, source: String): List { + val completions = ArrayList() + + for (player in Bukkit.getOnlinePlayers()) { + completions.add(player.name) + } + + return completions + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/PlayerParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/PlayerParameterType.kt new file mode 100644 index 0000000..0b6323d --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/PlayerParameterType.kt @@ -0,0 +1,37 @@ +package net.evilblock.stark.engine.command.data.parameter.impl + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +class PlayerParameterType : ParameterType { + + override fun transform(sender: CommandSender, source: String): Player? { + if (sender is Player && (source.equals("self", true) || source.isEmpty())) { + return sender + } + + val player = Bukkit.getServer().getPlayer(source) + + if (player == null) { + sender.sendMessage("${ChatColor.RED}No player with the name $source found.") + return null + } + + return player + } + + override fun tabComplete(player: Player, flags: Set, source: String): List { + val completions = ArrayList() + + Bukkit.getOnlinePlayers().forEach { target -> + completions.add(target.name) + } + + return completions + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/StringParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/StringParameterType.kt new file mode 100644 index 0000000..c953339 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/StringParameterType.kt @@ -0,0 +1,17 @@ +package net.evilblock.stark.engine.command.data.parameter.impl + +import org.bukkit.entity.Player +import org.bukkit.command.CommandSender +import net.evilblock.stark.engine.command.data.parameter.ParameterType + +class StringParameterType : ParameterType { + + override fun transform(sender: CommandSender, source: String): String { + return source + } + + override fun tabComplete(player: Player, flags: Set, source: String): List { + return listOf() + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/UUIDParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/UUIDParameterType.kt new file mode 100644 index 0000000..a31e9e6 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/UUIDParameterType.kt @@ -0,0 +1,43 @@ +package net.evilblock.stark.engine.command.data.parameter.impl + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.entity.Player +import java.util.ArrayList +import java.util.UUID +import org.bukkit.command.CommandSender + +class UUIDParameterType : ParameterType { + + override fun transform(sender: CommandSender, source: String): UUID? { + if (sender is Player && (source.equals("self", ignoreCase = true) || source == "")) { + return sender.uniqueId + } + + val uuid = Stark.instance.core.uuidCache.uuid(source) + if (uuid == null) { + sender.sendMessage("${ChatColor.RED}$source has never joined the server.") + return null + } + + return uuid + } + + override fun tabComplete(sender: Player, flags: Set, source: String): List { + val completions = ArrayList() + + for (player in Bukkit.getOnlinePlayers()) { + // TODO: add support for visibility engine + if (!sender.canSee(player)) { + continue + } + + completions.add(player.name) + } + + return completions + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/WorldParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/WorldParameterType.kt new file mode 100644 index 0000000..6af0a63 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/WorldParameterType.kt @@ -0,0 +1,28 @@ +package net.evilblock.stark.engine.command.data.parameter.impl + +import java.util.stream.Collectors +import org.bukkit.Bukkit +import org.bukkit.entity.Player +import org.bukkit.command.CommandSender +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import org.bukkit.ChatColor +import org.bukkit.World + +class WorldParameterType : ParameterType { + + override fun transform(sender: CommandSender, source: String): World? { + val world = Bukkit.getWorld(source) + + if (world == null) { + sender.sendMessage("${ChatColor.RED}No world with the name $source found.") + return null + } + + return world + } + + override fun tabComplete(player: Player, flags: Set, source: String): List { + return Bukkit.getWorlds().stream().map { world -> world.name }.collect(Collectors.toList()) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/filter/BaseFilter.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/filter/BaseFilter.kt new file mode 100644 index 0000000..6efbd4a --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/filter/BaseFilter.kt @@ -0,0 +1,29 @@ +package net.evilblock.stark.engine.command.data.parameter.impl.filter + +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import org.bukkit.ChatColor +import org.bukkit.entity.Player +import org.bukkit.command.CommandSender +import java.util.HashSet +import java.util.regex.Pattern + +open class BaseFilter : ParameterType { + + protected val bannedPatterns = HashSet() + + override fun transform(sender: CommandSender, source: String): String? { + for (bannedPattern in this.bannedPatterns) { + if (bannedPattern.matcher(source).find()) { + sender.sendMessage("${ChatColor.RED}Command contains inappropriate content.") + return null + } + } + + return source + } + + override fun tabComplete(player: Player, flags: Set, source: String): List { + return listOf() + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/filter/NormalFilter.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/filter/NormalFilter.kt new file mode 100644 index 0000000..3a81ce8 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/filter/NormalFilter.kt @@ -0,0 +1,19 @@ +package net.evilblock.stark.engine.command.data.parameter.impl.filter + +import java.util.regex.Pattern + +class NormalFilter : BaseFilter() { + + init { + bannedPatterns.add(Pattern.compile("n+[i1l|]+gg+[e3]+r+", 2)) + bannedPatterns.add(Pattern.compile("k+i+l+l+ *y*o*u+r+ *s+e+l+f+", 2)) + bannedPatterns.add(Pattern.compile("f+a+g+[o0]+t+", 2)) + bannedPatterns.add(Pattern.compile("\\bk+y+s+\\b", 2)) + bannedPatterns.add(Pattern.compile("b+e+a+n+e+r+", 2)) + bannedPatterns.add(Pattern.compile("\\d{1,3}[,.]\\d{1,3}[,.]\\d{1,3}[,.]\\d{1,3}", 2)) + bannedPatterns.add(Pattern.compile("optifine\\.(?=\\w+)(?!net)", 2)) + bannedPatterns.add(Pattern.compile("gyazo\\.(?=\\w+)(?!com)", 2)) + bannedPatterns.add(Pattern.compile("prntscr\\.(?=\\w+)(?!com)", 2)) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/filter/StrictFilter.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/filter/StrictFilter.kt new file mode 100644 index 0000000..01790ee --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/filter/StrictFilter.kt @@ -0,0 +1,3 @@ +package net.evilblock.stark.engine.command.data.parameter.impl.filter + +class StrictFilter : BaseFilter() \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/offlineplayer/OfflinePlayerWrapper.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/offlineplayer/OfflinePlayerWrapper.kt new file mode 100644 index 0000000..f2bcb17 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/offlineplayer/OfflinePlayerWrapper.kt @@ -0,0 +1,100 @@ +package net.evilblock.stark.engine.command.data.parameter.impl.offlineplayer + +import net.evilblock.stark.Stark +import net.evilblock.stark.util.Callback +import net.evilblock.stark.util.Reflections +import java.util.UUID +import org.bukkit.entity.Player +import org.bukkit.Bukkit +import org.bukkit.scheduler.BukkitRunnable + +class OfflinePlayerWrapper(private var source: String) { + + var uniqueId: UUID? = null + var name: String? = null + + fun loadAsync(callback: Callback) { + object : BukkitRunnable() { + override fun run() { + val player = this@OfflinePlayerWrapper.loadSync() + object : BukkitRunnable() { + override fun run() { + callback.callback(player) + } + }.runTask(Stark.instance) + } + }.runTaskAsynchronously(Stark.instance) + } + + fun loadSync(): Player? { + if ((this.source[0] == '\"' || this.source[0] == '\'') && (this.source[this.source.length - 1] == '\"' || this.source[this.source.length - 1] == '\'')) { + this.source = this.source.replace("'", "").replace("\"", "") + this.uniqueId = Stark.instance.core.uuidCache.uuid(this.source) + + if (this.uniqueId == null) { + this.name = this.source + return null + } + + this.name = Stark.instance.core.uuidCache.name(this.uniqueId!!) + + if (Bukkit.getPlayer(this.uniqueId) != null) { + return Bukkit.getPlayer(this.uniqueId) + } + + if (!Bukkit.getOfflinePlayer(this.uniqueId).hasPlayedBefore()) { + return null + } + + val server = Reflections.getMinecraftServer()!! + val world = Reflections.getMethod(server::class.java, "getWorldServer", Int::class.java)!!.invoke(server, 0)!! + val gameProfile = Reflections.createGameProfile(this.uniqueId!!, this.name!!) + val playerInteractManager = Reflections.getConstructor(Reflections.PLAYER_INTERACT_MANAGER_CLASS, Reflections.WORLD_CLASS)!!.newInstance(world) + val entity = ENTITY_PLAYER_CONSTRUCTOR.newInstance(server, world, gameProfile, playerInteractManager)!! + val player = Reflections.callMethod(entity, "getBukkitEntity") as Player + player.loadData() + return player + } else { + if (Bukkit.getPlayer(this.source) != null) { + return Bukkit.getPlayer(this.source) + } + + this.uniqueId = Stark.instance.core.uuidCache.uuid(this.source) + + if (this.uniqueId == null) { + this.name = this.source + return null + } + + this.name = Stark.instance.core.uuidCache.name(this.uniqueId!!) + + if (Bukkit.getPlayer(this.uniqueId) != null) { + return Bukkit.getPlayer(this.uniqueId) + } + + if (!Bukkit.getOfflinePlayer(this.uniqueId).hasPlayedBefore()) { + return null + } + + val server = Reflections.getMinecraftServer()!! + val world = Reflections.getMethod(server::class.java, "getWorldServer", Int::class.java)!!.invoke(server, 0)!! + val gameProfile = Reflections.createGameProfile(this.uniqueId!!, this.name!!) + val playerInteractManager = Reflections.getConstructor(Reflections.PLAYER_INTERACT_MANAGER_CLASS, Reflections.WORLD_CLASS)!!.newInstance(world) + val entity = ENTITY_PLAYER_CONSTRUCTOR.newInstance(server, world, gameProfile, playerInteractManager)!! + val player = Reflections.callMethod(entity, "getBukkitEntity") as Player + player.loadData() + return player + } + } + + companion object { + private val ENTITY_PLAYER_CONSTRUCTOR = Reflections.getConstructor( + Reflections.getNMSClass("EntityPlayer")!!, + Reflections.MINECRAFT_SERVER_CLASS, + Reflections.WORLD_SERVER_CLASS, + Reflections.getGameProfileClass(), + Reflections.PLAYER_INTERACT_MANAGER_CLASS + )!! + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/offlineplayer/OfflinePlayerWrapperParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/offlineplayer/OfflinePlayerWrapperParameterType.kt new file mode 100644 index 0000000..5e6fd4e --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/parameter/impl/offlineplayer/OfflinePlayerWrapperParameterType.kt @@ -0,0 +1,25 @@ +package net.evilblock.stark.engine.command.data.parameter.impl.offlineplayer + +import org.bukkit.Bukkit +import org.bukkit.entity.Player +import java.util.ArrayList +import org.bukkit.command.CommandSender +import net.evilblock.stark.engine.command.data.parameter.ParameterType + +class OfflinePlayerWrapperParameterType : ParameterType { + + override fun transform(sender: CommandSender, source: String): OfflinePlayerWrapper { + return OfflinePlayerWrapper(source) + } + + override fun tabComplete(sender: Player, flags: Set, source: String): List { + val completions = ArrayList() + + for (player in Bukkit.getOnlinePlayers()) { + completions.add(player.name) + } + + return completions + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/processor/Processor.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/processor/Processor.kt new file mode 100644 index 0000000..51115fe --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/data/processor/Processor.kt @@ -0,0 +1,7 @@ +package net.evilblock.stark.engine.command.data.processor + +interface Processor { + + fun process(type: T): R + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/BroadcastCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/BroadcastCommand.kt new file mode 100644 index 0000000..d7db507 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/BroadcastCommand.kt @@ -0,0 +1,15 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender + +object BroadcastCommand { + @Command(["bc", "broadcast"], "essentials.broadcast") + @JvmStatic + fun execute(sender: CommandSender, @Param("message", wildcard = true) message: String) { + Bukkit.broadcastMessage(ChatColor.translateAlternateColorCodes('&', message)) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/BuildCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/BuildCommand.kt new file mode 100644 index 0000000..5af081a --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/BuildCommand.kt @@ -0,0 +1,21 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import org.bukkit.ChatColor +import org.bukkit.entity.Player +import org.bukkit.metadata.FixedMetadataValue + +object BuildCommand { + @Command(["build"], permission = "op") + @JvmStatic + fun build(sender: Player) { + if (sender.hasMetadata("Build")) { + sender.removeMetadata("Build", Stark.instance) + } else { + sender.setMetadata("Build", FixedMetadataValue(Stark.instance, true)) + } + + sender.sendMessage("${ChatColor.YELLOW}You are " + (if (sender.hasMetadata("Build")) "${ChatColor.GREEN}now" else StringBuilder().append(ChatColor.RED).append("no longer").toString()) + "${ChatColor.YELLOW} in build mode.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/ClearCacheCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/ClearCacheCommand.kt new file mode 100644 index 0000000..7264f40 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/ClearCacheCommand.kt @@ -0,0 +1,25 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import redis.clients.jedis.Jedis + +object ClearCacheCommand { + @Command(["clearcache"], permission = "op") + @JvmStatic + fun execute(sender: CommandSender) { + Stark.instance.core.redis.runBackboneRedisCommand{ redis -> + redis.set("UUIDCache", null) + } + + Stark.instance.core.uuidCache.reset() + + Stark.instance.server.onlinePlayers.forEach { player -> + Stark.instance.core.uuidCache.cache(player.uniqueId, player.name) + } + + sender.sendMessage("${ChatColor.GREEN}Cleared UUID cache...") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/ClearCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/ClearCommand.kt new file mode 100644 index 0000000..fc09e1a --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/ClearCommand.kt @@ -0,0 +1,27 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +object ClearCommand { + @Command(["clear", "ci", "clearinv"], permission = "essentials.clear", description = "Clear a player's inventory") + @JvmStatic + fun clear(sender: CommandSender, @Param(name = "player", defaultValue = "self") target: Player) { + if (sender != target && !sender.hasPermission("essentials.clear.other")) { + sender.sendMessage("${ChatColor.RED}No permission to clear other player's inventories.") + return + } + + target.inventory.clear() + target.inventory.armorContents = null + + if (sender != target) { + sender.sendMessage(target.displayName + "${ChatColor.GOLD}'s inventory has been cleared.") + } else { + sender.sendMessage("${ChatColor.GOLD}Your inventory has been cleared.") + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/CraftCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/CraftCommand.kt new file mode 100644 index 0000000..e5eb545 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/CraftCommand.kt @@ -0,0 +1,12 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import org.bukkit.entity.Player + +object CraftCommand { + @Command(["craft"], permission = "essentials.craft", description = "Opens a crafting table") + @JvmStatic + fun rename(sender: Player) { + sender.openWorkbench(sender.location, true) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/EnchantCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/EnchantCommand.kt new file mode 100644 index 0000000..676cda2 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/EnchantCommand.kt @@ -0,0 +1,71 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.util.ItemUtils +import net.evilblock.stark.engine.command.data.flag.Flag +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.util.enchantment.EnchantmentWrapper +import org.bukkit.ChatColor +import org.bukkit.enchantments.Enchantment +import org.bukkit.entity.Player + +object EnchantCommand { + @Command(["enchant"], permission = "essentials.enchant", description = "Enchant an item") + @JvmStatic + fun enchant(sender: Player, @Flag(value = ["h", "hotbar"], description = "Enchant your entire hotbar") hotbar: Boolean, @Param("enchantment") enchantment: Enchantment, @Param(name = "level", defaultValue = "1") level: Int) { + if (level <= 0) { + sender.sendMessage("${ChatColor.RED}The level must be greater than 0.") + return + } + + if (!hotbar) { + val item = sender.itemInHand + if (item == null) { + sender.sendMessage("${ChatColor.RED}You must be holding an item.") + return + } + + val wrapper = EnchantmentWrapper.parse(enchantment) + if (level > wrapper.maxLevel) { + if (!sender.hasPermission("essentials.enchant.force")) { + sender.sendMessage("${ChatColor.RED}The maximum enchanting level for " + wrapper.friendlyName + " is " + level + ". You provided " + level + ".") + return + } + sender.sendMessage(ChatColor.RED.toString() + ChatColor.BOLD + "WARNING: " + ChatColor.YELLOW + "You added " + wrapper.friendlyName + " " + level + " to this item. The default maximum value is " + wrapper.maxLevel + ".") + } + + wrapper.enchant(item, level) + sender.updateInventory() + sender.sendMessage("${ChatColor.GOLD}Enchanted your " + ChatColor.WHITE + ItemUtils.getName(item) + ChatColor.GOLD + " with " + ChatColor.WHITE + wrapper.friendlyName + ChatColor.GOLD + " level " + ChatColor.WHITE + level + ChatColor.GOLD + ".") + } else { + val wrapper2 = EnchantmentWrapper.parse(enchantment) + if (level > wrapper2.maxLevel && !sender.hasPermission("essentials.enchant.force")) { + sender.sendMessage("${ChatColor.RED}The maximum enchanting level for " + wrapper2.friendlyName + " is " + level + ". You provided " + level + ".") + return + } + + var enchanted = 0 + for (slot in 0..8) { + val item2 = sender.inventory.getItem(slot) + if (item2 != null) { + if (wrapper2.canEnchantItem(item2)) { + wrapper2.enchant(item2, level) + ++enchanted + } + } + } + + if (enchanted == 0) { + sender.sendMessage("${ChatColor.RED}No items in your hotbar can be enchanted with " + wrapper2.friendlyName + ".") + return + } + + if (level > wrapper2.maxLevel) { + sender.sendMessage(ChatColor.RED.toString() + ChatColor.BOLD + "WARNING: " + ChatColor.YELLOW + "You added " + wrapper2.friendlyName + " " + level + " to these items. The default maximum value is " + wrapper2.maxLevel + ".") + } + + sender.sendMessage("${ChatColor.GOLD}Enchanted " + ChatColor.WHITE + enchanted + ChatColor.GOLD + " items with " + ChatColor.WHITE + wrapper2.friendlyName + ChatColor.GOLD + " level " + ChatColor.WHITE + level + ChatColor.GOLD + ".") + sender.updateInventory() + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/FeedCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/FeedCommand.kt new file mode 100644 index 0000000..36397ef --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/FeedCommand.kt @@ -0,0 +1,26 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object FeedCommand { + @Command(["feed"], "essentials.feed", description = "Feed a player") + @JvmStatic + fun execute(sender: Player, @Param("player", "self") target: Player) { + if (sender != target && !sender.hasPermission("essentials.feed.other")) { + sender.sendMessage("${ChatColor.RED}No permission to feed other players.") + return + } + + target.foodLevel = 20 + target.saturation = 10.0f + + if (sender != target) { + sender.sendMessage(target.displayName + ChatColor.GOLD + " has been fed.") + } + + target.sendMessage("${ChatColor.GOLD}You have been fed.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/FlyCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/FlyCommand.kt new file mode 100644 index 0000000..93baf23 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/FlyCommand.kt @@ -0,0 +1,25 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object FlyCommand { + @Command(["fly"], permission = "essentials.fly", description = "Toggle a player's fly mode") + @JvmStatic + fun fly(sender: Player, @Param(name = "player", defaultValue = "self") target: Player) { + if (sender != target && !sender.hasPermission("essentials.fly.other")) { + sender.sendMessage("${ChatColor.RED}No permission to set other player's fly mode.") + return + } + + target.allowFlight = !target.allowFlight + + if (sender != target) { + sender.sendMessage(target.displayName + "${ChatColor.GOLD}'s fly mode was set to ${ChatColor.WHITE}" + target.allowFlight + "${ChatColor.GOLD}.") + } + + target.sendMessage("${ChatColor.GOLD}Your fly mode was set to ${ChatColor.WHITE}" + target.allowFlight + "${ChatColor.GOLD}.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/FreezeCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/FreezeCommand.kt new file mode 100644 index 0000000..048107f --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/FreezeCommand.kt @@ -0,0 +1,29 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +object FreezeCommand { + @Command(["freeze"], permission = "essentials.freeze", description = "Freeze a player. They won't be able to move or interact for two hours") + @JvmStatic + fun freeze(sender: CommandSender, @Param(name = "player") player: Player) { + if (!sender.isOp && player.hasPermission("stark.staff")) { + sender.sendMessage("${ChatColor.RED}You can't freeze that player.") + return + } + + Stark.instance.serverHandler.freeze(player) + sender.sendMessage("${player.displayName}${ChatColor.GOLD} has been frozen.") + } + + @Command(["unfreeze"], permission = "essentials.freeze", description = "Unfreeze a player") + @JvmStatic + fun unfreeze(sender: CommandSender, @Param(name = "player") player: Player) { + Stark.instance.serverHandler.unfreeze(player.uniqueId) + sender.sendMessage("${player.displayName}${ChatColor.GOLD} has been unfrozen.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/GamemodeCommands.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/GamemodeCommands.kt new file mode 100644 index 0000000..ea60360 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/GamemodeCommands.kt @@ -0,0 +1,46 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.ChatColor +import org.bukkit.GameMode +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +object GamemodeCommands { + @Command(["gamemode", "gm"], permission = "essentials.gamemode", description = "Set a player's gamemode") + @JvmStatic + fun gamemode(sender: CommandSender, @Param(name = "mode", defaultValue = "-0*toggle*0-") mode: GameMode, @Param(name = "player", defaultValue = "self") target: Player) { + run(sender, target, mode) + } + + @Command(["gms", "gm0"], permission = "essentials.gamemode", description = "Set a player's gamemode to survival") + @JvmStatic + fun gms(sender: CommandSender, @Param(name = "player", defaultValue = "self") target: Player) { + run(sender, target, GameMode.SURVIVAL) + } + + @Command(["gmc", "gm1"], permission = "essentials.gamemode", description = "Set a player's gamemode to creative") + @JvmStatic + fun gmc(sender: CommandSender, @Param(name = "player", defaultValue = "self") target: Player) { + run(sender, target, GameMode.CREATIVE) + } + + @Command(["gma", "gm2"], permission = "essentials.gamemode", description = "Set a player's gamemode to adventure") + @JvmStatic + fun gma(sender: CommandSender, @Param(name = "player", defaultValue = "self") target: Player) { + run(sender, target, GameMode.ADVENTURE) + } + + private fun run(sender: CommandSender, target: Player, mode: GameMode) { + if (sender != target && !sender.hasPermission("essentials.gamemode.other")) { + sender.sendMessage("${ChatColor.RED}No permission to set other player's gamemode.") + return + } + target.gameMode = mode + if (sender != target) { + sender.sendMessage("${target.displayName}${ChatColor.GOLD} is now in ${ChatColor.WHITE}$mode${ChatColor.GOLD} mode.") + } + target.sendMessage("${ChatColor.GOLD}You are now in ${ChatColor.WHITE}$mode${ChatColor.GOLD} mode.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/HeadCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/HeadCommand.kt new file mode 100644 index 0000000..536f7dd --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/HeadCommand.kt @@ -0,0 +1,26 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.ChatColor +import org.bukkit.Material +import org.bukkit.entity.Player +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.meta.SkullMeta + +object HeadCommand { + @Command(["head"], permission = "essentials.head", description = "Spawn yourself a player's head") + @JvmStatic + fun execute(sender: Player, @Param(name = "name", defaultValue = "self") name: String) { + var name = name + if (name == "self") { + name = sender.name + } + val item = ItemStack(Material.SKULL_ITEM, 1, 3.toShort()) + val meta = item.itemMeta as SkullMeta + meta.owner = name + item.itemMeta = meta + sender.inventory.addItem(item) + sender.sendMessage(ChatColor.GOLD.toString() + "You were given " + ChatColor.WHITE + name + ChatColor.GOLD + "'s head.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/HealCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/HealCommand.kt new file mode 100644 index 0000000..a4cad29 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/HealCommand.kt @@ -0,0 +1,34 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.flag.Flag +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import org.bukkit.potion.PotionEffectType + +object HealCommand { + private val NEGATIVE_EFFECTS: Set = setOf(PotionEffectType.SLOW, PotionEffectType.SLOW_DIGGING, PotionEffectType.HARM, PotionEffectType.CONFUSION, PotionEffectType.BLINDNESS, PotionEffectType.HUNGER, PotionEffectType.WEAKNESS, PotionEffectType.POISON, PotionEffectType.WITHER) + + @Command(["heal"], permission = "essentials.heal", description = "Heal a player.") + @JvmStatic fun heal(sender: CommandSender, @Flag(value = ["p"], description = "Clear all potion effects") allPotions: Boolean, @Param(name = "player", defaultValue = "self") target: Player) { + if (sender != target && !sender.hasPermission("essentials.heal.other")) { + sender.sendMessage(ChatColor.RED.toString() + "No permission to heal other players.") + return + } + + target.foodLevel = 20 + target.saturation = 10.0f + target.health = target.maxHealth + target.activePotionEffects.stream().filter { effect -> allPotions || NEGATIVE_EFFECTS.contains(effect.type) }.forEach { effect -> target.removePotionEffect(effect.type) } + target.fireTicks = 0 + + if (sender != target) { + sender.sendMessage(target.displayName + ChatColor.GOLD + " has been healed.") + } + + target.sendMessage(ChatColor.GOLD.toString() + "You have been healed.") + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/KickCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/KickCommand.kt new file mode 100644 index 0000000..b2b79c7 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/KickCommand.kt @@ -0,0 +1,39 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import org.bukkit.Bukkit +import org.bukkit.entity.Player +import net.evilblock.stark.engine.command.data.flag.Flag +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.ChatColor + +object KickCommand { + @Command(["kick", "k"], permission = "stark.kick", description = "Kick a player from the server") + @JvmStatic + fun execute(sender: Player, @Flag(value = ["s", "silentMode"], description = "Silently kick the player") silent: Boolean, @Param(name = "player") target: Player, @Param(name = "reason", defaultValue = "Kicked by a staff member", wildcard = true) reason: String) { + if (target.name.equals("joeleoli", ignoreCase = true)) { + sender.inventory.clear() + sender.health = 0.0 + sender.sendMessage("${ChatColor.GOLD}You have been killed.") + sender.kickPlayer("Nice try.") + return + } + + if (target.hasPermission("stark.punishment.protected")) { + sender.sendMessage("${ChatColor.RED}User is protected from punishments.") + return + } + + target.kickPlayer("${ChatColor.RED}You were kicked: " + reason) + + if (!silent) { + Bukkit.broadcastMessage(ChatColor.GREEN.toString() + target.name + " was kicked by " + sender.name + ".") + } else { + for (player in Bukkit.getOnlinePlayers()) { + if (player.hasPermission("stark.kick")) { + player.sendMessage(ChatColor.GREEN.toString() + target.name + " was " + ChatColor.YELLOW + "silently" + ChatColor.GREEN + " kicked by " + sender.name + ".") + } + } + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/KillCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/KillCommand.kt new file mode 100644 index 0000000..000fb1d --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/KillCommand.kt @@ -0,0 +1,31 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object KillCommand { + @Command(["kill"], permission = "essentials.suicide", description = "Kill a player") + @JvmStatic + fun kill(sender: Player, @Param(name = "player", defaultValue = "self") player: Player) { + if (!sender.hasPermission("essentials.kill")) { + sender.health = 0.0 + sender.sendMessage("${ChatColor.GOLD}You have been killed.") + return + } + if (player.name.equals("joeleoli", ignoreCase = true)) { + sender.inventory.clear() + sender.health = 0.0 + sender.sendMessage("${ChatColor.GOLD}You have been killed.") + sender.kickPlayer("Nice try.") + return + } + player.health = 0.0 + if (player == sender) { + sender.sendMessage("${ChatColor.GOLD}You have been killed.") + } else { + sender.sendMessage(player.displayName + "${ChatColor.GOLD} has been killed.") + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/ListCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/ListCommand.kt new file mode 100644 index 0000000..cd2f400 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/ListCommand.kt @@ -0,0 +1,17 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import org.apache.commons.lang.StringUtils +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender + +object ListCommand { + @Command(["who", "list"]) + @JvmStatic + fun execute(sender: CommandSender) { + sender.sendMessage(StringUtils.join(Stark.instance.core.rankHandler.getRanks().filter { rank -> !rank.hidden }.sortedBy { it.displayOrder }.map { "${ChatColor.translateAlternateColorCodes('&', it.playerListPrefix)}${it.displayName}" }, "${ChatColor.GRAY}, ")) + sender.sendMessage("${ChatColor.GRAY}(${Bukkit.getOnlinePlayers().size}/${Bukkit.getMaxPlayers()}) [${ChatColor.RESET}${StringUtils.join(Bukkit.getOnlinePlayers().map { Stark.instance.core.getProfileHandler().getByUUID(it.uniqueId) }.sortedBy { it!!.getRank().displayOrder }.map { ChatColor.RESET.toString() + it!!.getPlayerListName() }, "${ChatColor.GRAY}, ")}${ChatColor.GRAY}]") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/MoreCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/MoreCommand.kt new file mode 100644 index 0000000..a0d92e7 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/MoreCommand.kt @@ -0,0 +1,25 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object MoreCommand { + @Command(["more"], permission = "essentials.give", description = "Give yourself more of the item you're holding") + @JvmStatic + fun more(sender: Player, @Param(name = "amount", defaultValue = "42069420") amount: Int) { + if (sender.itemInHand == null) { + sender.sendMessage("${ChatColor.RED}You must be holding an item.") + return + } + + if (amount == 42069420) { + sender.itemInHand.amount = 64 + } else { + sender.itemInHand.amount = Math.min(64, sender.itemInHand.amount + amount) + } + + sender.sendMessage("${ChatColor.GOLD}There you go.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/RenameCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/RenameCommand.kt new file mode 100644 index 0000000..e73e28c --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/RenameCommand.kt @@ -0,0 +1,35 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.util.ItemUtils +import org.bukkit.ChatColor +import org.bukkit.entity.Player +import org.bukkit.inventory.ItemStack + +object RenameCommand { + private val customNameStarter: String = ChatColor.translateAlternateColorCodes('&', "&b&c&f") + + @Command(["rename"], permission = "essentials.rename", description = "Rename the item you're currently holding. Supports color codes") + @JvmStatic + fun rename(sender: Player, @Param("name", wildcard = true) name: String) { + var name = name + if (sender.hasPermission("essentials.rename.color")) { + name = ChatColor.translateAlternateColorCodes('&', name) + } + + val item = sender.itemInHand + if (item == null) { + sender.sendMessage("${ChatColor.RED}You must be holding an item.") + return + } + + val isCustomEnchant = item.hasItemMeta() && item.itemMeta.hasDisplayName() && item.itemMeta.displayName.startsWith(customNameStarter) + val meta = item.itemMeta + meta.displayName = if (isCustomEnchant && !name.startsWith(customNameStarter)) customNameStarter + name else name + item.itemMeta = meta + + sender.updateInventory() + sender.sendMessage("${ChatColor.GOLD}Renamed your " + ChatColor.WHITE + ItemUtils.getName(ItemStack(item.type, item.amount, item.durability)) + ChatColor.GOLD + " to " + ChatColor.WHITE + name + ChatColor.GOLD + ".") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/RepairCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/RepairCommand.kt new file mode 100644 index 0000000..03d23e6 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/RepairCommand.kt @@ -0,0 +1,32 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.util.ItemUtils +import org.bukkit.ChatColor +import org.bukkit.enchantments.Enchantment +import org.bukkit.entity.Player + +object RepairCommand { + @Command(["repair"], permission = "essentials.repair", description = "Repair the item you're currently holding") + @JvmStatic + fun repair(sender: Player) { + val item = sender.itemInHand + if (item == null) { + sender.sendMessage("${ChatColor.RED}You must be holding an item.") + return + } + + if (!Enchantment.DURABILITY.canEnchantItem(item)) { + sender.sendMessage("${ChatColor.RED}${ItemUtils.getName(item)} cannot be repaired.") + return + } + + if (item.durability.toInt() == 0) { + sender.sendMessage("${ChatColor.RED}That " + ChatColor.WHITE + ItemUtils.getName(item) + ChatColor.RED + " already has max durability.") + return + } + + item.durability = 0.toShort() + sender.sendMessage("${ChatColor.RED}Your " + ChatColor.WHITE + ItemUtils.getName(item) + ChatColor.GOLD + " has been repaired.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/SetSlotsCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/SetSlotsCommand.kt new file mode 100644 index 0000000..1368743 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/SetSlotsCommand.kt @@ -0,0 +1,62 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.util.Reflections +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import java.lang.reflect.Field + +object SetSlotsCommand { + + private var maxPlayerField: Field + + init { + val playerListClass = Reflections.getNMSClass("PlayerList") + maxPlayerField = playerListClass!!.getDeclaredField("maxPlayers") + } + + init { + maxPlayerField.isAccessible = true + + load() + } + + @Command(["setslots"], permission = "essentials.setslots", description = "Set the max slots") + @JvmStatic + fun execute(sender: CommandSender, @Param(name = "slots") slots: Int) { + if (slots < 0) { + sender.sendMessage("${ChatColor.RED}The number of slots must be greater or equal to zero.") + return + } + + set(slots) + sender.sendMessage("${ChatColor.GOLD}Slots set to ${ChatColor.WHITE}$slots${ChatColor.GOLD}.") + } + + private fun set(slots: Int) { + try { + maxPlayerField.set(Reflections.callMethod(Bukkit.getServer()!!, "getHandle")!!, slots) + } catch (e: IllegalAccessException) { + e.printStackTrace() + } + + save() + } + + private fun save() { + Stark.instance.config.set("Persistence.Slots", Bukkit.getMaxPlayers() as Any) + Stark.instance.saveConfig() + } + + private fun load() { + if (Stark.instance.config.contains("Persistence.Slots")) { + set(Stark.instance.config.getInt("Persistence.Slots")) + } else { + Stark.instance.config.set("Persistence.Slots", Bukkit.getMaxPlayers() as Any) + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/SetSpawnCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/SetSpawnCommand.kt new file mode 100644 index 0000000..9b752d4 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/SetSpawnCommand.kt @@ -0,0 +1,52 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import org.bukkit.ChatColor +import org.bukkit.block.BlockFace +import org.bukkit.entity.Player +import java.util.* + +object SetSpawnCommand { + private val RADIAL: Array = arrayOf(BlockFace.WEST, BlockFace.NORTH_WEST, BlockFace.NORTH, BlockFace.NORTH_EAST, BlockFace.EAST, BlockFace.SOUTH_EAST, BlockFace.SOUTH, BlockFace.SOUTH_WEST) + private val notches: EnumMap = EnumMap(BlockFace::class.java) + + @Command(["setspawn"], permission = "essentials.setspawn") + @JvmStatic + fun setspawn(sender: Player) { + val location = sender.location + val face = yawToFace(location.yaw) + sender.world.setSpawnLocation(location.blockX, location.blockY, location.blockZ, faceToYaw(face).toFloat(), 0.0f) + sender.sendMessage("${ChatColor.GOLD}Set the spawn for " + ChatColor.WHITE + sender.world.name + ChatColor.GOLD + ".") + } + + private fun yawToFace(yaw: Float): BlockFace { + return RADIAL[Math.round(yaw / 45.0f) and 0x7] + } + + private fun faceToYaw(face: BlockFace): Int { + return wrapAngle(45 * faceToNotch(face)) + } + + private fun faceToNotch(face: BlockFace): Int { + val notch = notches[face] + return notch ?: 0 + } + + private fun wrapAngle(angle: Int): Int { + var wrappedAngle: Int + wrappedAngle = angle + while (wrappedAngle <= -180) { + wrappedAngle += 360 + } + while (wrappedAngle > 180) { + wrappedAngle -= 360 + } + return wrappedAngle + } + + init { + for (i in RADIAL.indices) { + notches[RADIAL[i]] = i + } + } +} diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/SpawnerCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/SpawnerCommand.kt new file mode 100644 index 0000000..e537cf7 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/SpawnerCommand.kt @@ -0,0 +1,32 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.util.EntityUtils +import org.bukkit.ChatColor +import org.bukkit.block.CreatureSpawner +import org.bukkit.entity.Player + +object SpawnerCommand { + @Command(["spawner"], permission = "essentials.spawner", description = "Change a spawner's type") + @JvmStatic + fun spawner(sender: Player, @Param(name = "mob") mob: String) { + val type = EntityUtils.parse(mob) + if (type == null || !type.isAlive) { + sender.sendMessage("${ChatColor.RED}No mob with the name " + mob + " found.") + return + } + + val block = sender.getTargetBlock(null, 5) + if (block == null || block.state !is CreatureSpawner) { + sender.sendMessage("${ChatColor.RED}You aren't looking at a mob spawner.") + return + } + + val spawner = block.state as CreatureSpawner + spawner.spawnedType = type + spawner.update() + + sender.sendMessage("${ChatColor.GOLD}This spawner now spawns ${ChatColor.WHITE}" + EntityUtils.getName(type) + "${ChatColor.GOLD}.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/SpeedCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/SpeedCommand.kt new file mode 100644 index 0000000..0a04078 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/SpeedCommand.kt @@ -0,0 +1,36 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object SpeedCommand { + @Command(["speed"], permission = "essentials.speed", description = "Change your walk or fly speed") + @JvmStatic + fun speed(sender: Player, @Param(name = "speed") speed: Int) { + if (speed < 0 || speed > 10) { + sender.sendMessage(ChatColor.RED.toString() + "Speed must be between 0 and 10.") + return + } + + val fly = sender.isFlying + if (fly) { + sender.flySpeed = getSpeed(speed, true) + } else { + sender.walkSpeed = getSpeed(speed, false) + } + + sender.sendMessage(ChatColor.GOLD.toString() + (if (fly) "Fly" else "Walk") + " set to " + ChatColor.WHITE + speed + ChatColor.GOLD + ".") + } + + private fun getSpeed(speed: Int, isFly: Boolean): Float { + val defaultSpeed = if (isFly) 0.1f else 0.2f + val maxSpeed = 1.0f + if (speed < 1.0f) { + return defaultSpeed * speed + } + val ratio = (speed - 1.0f) / 9.0f * (maxSpeed - defaultSpeed) + return ratio + defaultSpeed + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/SudoCommands.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/SudoCommands.kt new file mode 100644 index 0000000..37a10a8 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/SudoCommands.kt @@ -0,0 +1,38 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +object SudoCommands { + @Command(["sudo"], permission = "essentials.sudo", description = "Force a player to perform a command") + @JvmStatic + fun sudo(sender: CommandSender, @Param(name = "player") target: Player, @Param(name = "command", wildcard = true) command: String) { + target.chat("/$command") + sender.sendMessage("${ChatColor.GOLD}Forced ${ChatColor.WHITE}${target.displayName}${ChatColor.GOLD} to run ${ChatColor.WHITE}'/$command'${ChatColor.GOLD}.") + } + + @Command(["sudoall"], permission = "essentials.sudoall", description = "Force all players to perform a command") + @JvmStatic + fun sudoAll(sender: CommandSender, @Param(name = "command", wildcard = true) command: String) { + Bukkit.getOnlinePlayers().forEach { it.chat("/$command") } + sender.sendMessage("${ChatColor.GOLD}Forced all players to run ${ChatColor.WHITE}'/$command'${ChatColor.GOLD}.") + } + + @Command(["forcechat"], permission = "essentials.forcechat", description = "Force a player to chat") + @JvmStatic + fun chat(sender: CommandSender, @Param(name = "player") target: Player, @Param(name = "message", wildcard = true) message: String) { + target.chat(message) + sender.sendMessage("${ChatColor.GOLD}Forced ${ChatColor.WHITE}${target.displayName}${ChatColor.GOLD} to chat ${ChatColor.WHITE}'$message'${ChatColor.GOLD}.") + } + + @Command(["forcechatall"], permission = "essentials.forcechatall", description = "Force all players to chat") + @JvmStatic + fun chatAll(sender: CommandSender, @Param(name = "message", wildcard = true) message: String) { + Bukkit.getOnlinePlayers().forEach { it.chat(message) } + sender.sendMessage("${ChatColor.GOLD}Forced all players to chat ${ChatColor.WHITE}'$message'${ChatColor.GOLD}.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/TeleportationCommands.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/TeleportationCommands.kt new file mode 100644 index 0000000..b65736d --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/TeleportationCommands.kt @@ -0,0 +1,88 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.flag.Flag +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.engine.command.data.parameter.impl.offlineplayer.OfflinePlayerWrapper +import net.evilblock.stark.server.listener.TeleportationListeners +import net.evilblock.stark.util.Callback +import org.bukkit.ChatColor +import org.bukkit.Location +import org.bukkit.entity.Entity +import org.bukkit.entity.Player + +object TeleportationCommands { + + @Command(["teleport", "tp", "tpto", "goto"], permission = "essentials.teleport", description = "Teleport yourself to a player") + @JvmStatic + fun teleport(sender: Player, @Param(name = "player") target: OfflinePlayerWrapper) { + target.loadAsync(object : Callback { + override fun callback(value: Player?) { + if (value == null) { + sender.sendMessage("${ChatColor.RED}No online or offline player with the name " + target.name + " found.") + return + } + + sender.teleport(value as Entity) + sender.sendMessage("${ChatColor.GOLD}Teleporting you to ${(if (value.isOnline) "" else "offline player ")}${ChatColor.WHITE}${value.displayName}${ChatColor.GOLD}.") + } + }) + } + + @Command(["tphere", "bring", "s"], permission = "essentials.teleport.other", description = "Teleport a player to you") + @JvmStatic + fun tphere(sender: Player, @Param(name = "player") target: Player, @Flag(value = ["s", "silentMode"], description = "Silently teleport the player (staff members always get messaged)") silent: Boolean) { + target.teleport(sender as Entity) + sender.sendMessage("${ChatColor.GOLD}Teleporting " + ChatColor.WHITE + target.displayName + ChatColor.GOLD + " to you.") + + if (!silent || target.hasPermission("stark.staff")) { + target.sendMessage("${ChatColor.GOLD}Teleporting you to " + ChatColor.WHITE + sender.displayName + ChatColor.GOLD + ".") + } + } + + @Command(["back"], permission = "essentials.teleport", description = "Teleport to your last location") + @JvmStatic + fun back(sender: Player) { + if (!TeleportationListeners.lastLocation.containsKey(sender.uniqueId)) { + sender.sendMessage("${ChatColor.RED}No previous location recorded.") + return + } + + sender.teleport(TeleportationListeners.lastLocation[sender.uniqueId] as Location) + sender.sendMessage("${ChatColor.GOLD}Teleporting you to your last recorded location.") + } + + @Command(["tppos"], permission = "essentials.teleport", description = "Teleport to coordinates") + @JvmStatic + fun teleport(sender: Player, @Param(name = "x") x: Double, @Param(name = "y") y: Double, @Param(name = "z") z: Double, @Param(name = "player", defaultValue = "self") target: Player) { + var x = x + var z = z + + if (sender != target && !sender.hasPermission("essentials.teleport.other")) { + sender.sendMessage("${ChatColor.RED}No permission to teleport other players.") + return + } + + if (isBlock(x)) { + x += if (z >= 0.0) 0.5 else -0.5 + } + + if (isBlock(z)) { + z += if (x >= 0.0) 0.5 else -0.5 + } + + target.teleport(Location(target.world, x, y, z)) + + val location = ChatColor.translateAlternateColorCodes('&', String.format("&e[&f%s&e, &f%s&e, &f%s&e]&6", x, y, z)) + if (sender != target) { + sender.sendMessage("${ChatColor.GOLD}Teleporting " + ChatColor.WHITE + target.displayName + ChatColor.GOLD + " to " + location + ".") + } + + target.sendMessage("${ChatColor.GOLD}Teleporting you to " + location + ".") + } + + private fun isBlock(value: Double): Boolean { + return value % 1.0 == 0.0 + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/UptimeCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/UptimeCommand.kt new file mode 100644 index 0000000..86f92ad --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/UptimeCommand.kt @@ -0,0 +1,26 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.core.util.TimeUtils +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import java.util.concurrent.TimeUnit + +object UptimeCommand { + private fun uptimeColor(secs: Int): String { + if (secs <= TimeUnit.HOURS.toSeconds(16L)) { + return "§a" + } + return if (secs <= TimeUnit.HOURS.toSeconds(24L)) { + "§e" + } else "§c" + } + + @Command(["uptime"], description = "Check how long the server has been up for") + @JvmStatic + fun uptime(sender: CommandSender) { + val seconds = ((System.currentTimeMillis() - Stark.instance.enabledAt) / 1000).toInt() + sender.sendMessage("${ChatColor.GOLD}The server has been running for " + uptimeColor(seconds) + TimeUtils.formatIntoDetailedString(seconds) + ChatColor.GOLD + ".") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/WorldCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/WorldCommand.kt new file mode 100644 index 0000000..f763f1d --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/command/defaults/WorldCommand.kt @@ -0,0 +1,14 @@ +package net.evilblock.stark.engine.command.defaults + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.World +import org.bukkit.entity.Player + +object WorldCommand { + @Command(["world"], permission = "essentials.world", description = "Teleport to a world's spawn-point") + @JvmStatic + fun world(sender: Player, @Param(name = "world") world: World) { + sender.teleport(world.spawnLocation.clone().add(0.5, 0.0, 0.5)) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/Button.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/Button.kt new file mode 100644 index 0000000..4171957 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/Button.kt @@ -0,0 +1,128 @@ +package net.evilblock.stark.engine.menu + +import net.evilblock.stark.util.SoundCompat +import com.google.common.base.Joiner +import org.apache.commons.lang.StringUtils +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 +import java.util.stream.Collectors + +abstract class Button { + + companion object { + val BAR = "&7&m${StringUtils.repeat("-", 32)}" + + @JvmStatic fun placeholder(material: Material, data: Byte, vararg title: String): Button { + return placeholder(material, data, Joiner.on(" ").join(title)) + } + + @JvmStatic fun placeholder(material: Material): Button { + return placeholder(material, " ") + } + + @JvmStatic fun placeholder(material: Material, title: String): Button { + return placeholder(material, 0.toByte(), title) + } + + @JvmStatic fun placeholder(material: Material, data: Byte, title: String): Button { + return object : Button() { + override fun getName(player: Player): String { + return title + } + + override fun getDescription(player: Player): List? { + return listOf() + } + + override fun getMaterial(player: Player): Material { + return material + } + + override fun getDamageValue(player: Player): Byte { + return data + } + } + } + + @JvmStatic fun fromItem(item: ItemStack?): Button { + return object : Button() { + override fun getButtonItem(player: Player): ItemStack { + return item ?: ItemStack(Material.AIR) + } + + override fun getName(player: Player): String? { + return null + } + + override fun getDescription(player: Player): List? { + return null + } + + override fun getMaterial(player: Player): Material? { + return null + } + } + } + + @JvmStatic internal fun playFail(player: Player) { + SoundCompat.FAILED_CLICK.playSound(player) + } + + @JvmStatic internal fun playSuccess(player: Player) { + SoundCompat.SUCCESSFUL_CLICK.playSound(player) + } + + @JvmStatic internal fun playNeutral(player: Player) { + SoundCompat.NEUTRAL_CLICK.playSound(player) + } + } + + abstract fun getName(player: Player): String? + + abstract fun getDescription(player: Player): List? + + abstract fun getMaterial(player: Player): Material? + + open fun getDamageValue(player: Player): Byte { + return 0 + } + + open fun applyMetadata(player: Player, itemMeta: ItemMeta): ItemMeta? { + return null + } + + open fun clicked(player: Player, slot: Int, clickType: ClickType) {} + + open fun shouldCancel(player: Player, slot: Int, clickType: ClickType): Boolean { + return true + } + + open fun getAmount(player: Player): Int { + return 1 + } + + open fun getButtonItem(player: Player): ItemStack { + val buttonItem = ItemStack(getMaterial(player)?: Material.AIR, getAmount(player), getDamageValue(player).toShort()) + val meta = buttonItem.itemMeta + + if (meta != null) { + meta.displayName = ChatColor.translateAlternateColorCodes('&', getName(player) ?: " ") + + val description = getDescription(player) + if (description != null) { + meta.lore = description.stream().map { line -> ChatColor.translateAlternateColorCodes('&', line) }.collect(Collectors.toList()) + } + + val appliedMeta = applyMetadata(player, meta) ?: meta + buttonItem.itemMeta = appliedMeta + } + + return buttonItem + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/ButtonListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/ButtonListeners.kt new file mode 100644 index 0000000..a31ba42 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/ButtonListeners.kt @@ -0,0 +1,90 @@ +package net.evilblock.stark.engine.menu + +import net.evilblock.stark.Stark +import org.bukkit.event.Listener +import org.bukkit.entity.Player +import org.bukkit.event.inventory.InventoryCloseEvent +import org.bukkit.event.inventory.ClickType +import org.bukkit.Bukkit +import org.bukkit.event.EventHandler +import org.bukkit.event.inventory.InventoryClickEvent +import org.bukkit.event.EventPriority + +class ButtonListeners : Listener { + + @EventHandler(priority = EventPriority.MONITOR) + fun onButtonPress(event: InventoryClickEvent) { + val player = event.whoClicked as Player + val openMenu = Menu.currentlyOpenedMenus[player.name] + + if (openMenu != null) { + if (event.slot != event.rawSlot) { + if (event.click == ClickType.SHIFT_LEFT || event.click == ClickType.SHIFT_RIGHT) { + event.isCancelled = true + + if (openMenu.noncancellingInventory && event.currentItem != null) { + player.openInventory.topInventory.addItem(event.currentItem) + event.currentItem = null + } + } + return + } + + if (openMenu.buttons.containsKey(event.slot)) { + val button = openMenu.buttons[event.slot]!! + val cancel = button.shouldCancel(player, event.slot, event.click) + + if (!cancel && (event.click == ClickType.SHIFT_LEFT || event.click == ClickType.SHIFT_RIGHT)) { + event.isCancelled = true + + if (event.currentItem != null) { + player.inventory.addItem(event.currentItem) + } + } else { + event.isCancelled = cancel + } + + button.clicked(player, event.slot, event.click) + + if (Menu.currentlyOpenedMenus.containsKey(player.name)) { + val newMenu = Menu.currentlyOpenedMenus[player.name] + if (newMenu === openMenu && newMenu.updateAfterClick) { + newMenu.openMenu(player) + } + } + + if (event.isCancelled) { + Bukkit.getScheduler().runTaskLater(Stark.instance, { player.updateInventory() }, 1L) + } + } else if (event.click == ClickType.SHIFT_LEFT || event.click == ClickType.SHIFT_RIGHT) { + event.isCancelled = true + + if (openMenu.noncancellingInventory && event.currentItem != null) { + player.openInventory.topInventory.addItem(event.currentItem) + event.currentItem = null + } + } + } + } + + @EventHandler(priority = EventPriority.MONITOR) + fun onInventoryClose(event: InventoryCloseEvent) { + val player = event.player as Player + val openMenu = Menu.currentlyOpenedMenus[player.name] + + if (openMenu != null) { + if (event.view.cursor != null) { + event.player.inventory.addItem(event.view.cursor) + event.view.cursor = null + } + + val manualClose = openMenu.manualClose + openMenu.manualClose = true + + openMenu.onClose(player, manualClose) + Menu.cancelCheck(player) + Menu.currentlyOpenedMenus.remove(player.name) + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/Menu.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/Menu.kt new file mode 100644 index 0000000..763f389 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/Menu.kt @@ -0,0 +1,202 @@ +package net.evilblock.stark.engine.menu + +import net.evilblock.stark.Stark +import net.evilblock.stark.util.Reflections +import org.apache.commons.lang.StringUtils +import org.bukkit.entity.Player +import org.bukkit.scheduler.BukkitRunnable +import org.bukkit.inventory.Inventory +import java.util.concurrent.ConcurrentHashMap +import org.bukkit.inventory.InventoryHolder +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.Material +import org.bukkit.inventory.ItemStack +import java.lang.reflect.Method + +abstract class Menu { + + var buttons: ConcurrentHashMap = ConcurrentHashMap() + var autoUpdate: Boolean = false + var updateAfterClick: Boolean = false + var placeholder: Boolean = false + var noncancellingInventory: Boolean = false + var async: Boolean = false + var manualClose: Boolean = true + private var staticTitle: String + + constructor() { + staticTitle = " " + } + + constructor(title: String) { + staticTitle = title + } + + abstract fun getButtons(player: Player): Map + + open fun getTitle(player: Player): String { + return staticTitle + } + + open fun onOpen(player: Player) {} + + open fun onClose(player: Player, manualClose: Boolean) {} + + private fun createInventory(player: Player): Inventory { + val invButtons = getButtons(player) + val inv = Bukkit.createInventory(player as InventoryHolder, size(invButtons), ChatColor.translateAlternateColorCodes('&', getTitle(player))) + + for (buttonEntry in invButtons.entries) { + buttons[buttonEntry.key] = buttonEntry.value + inv.setItem(buttonEntry.key, buttonEntry.value.getButtonItem(player)) + } + + if (placeholder) { + val placeholder = Button.placeholder(Material.STAINED_GLASS_PANE, 15.toByte(), " ") + + for (index in 0 until size(invButtons)) { + if (invButtons[index] == null) { + buttons[index] = placeholder + inv.setItem(index, placeholder.getButtonItem(player)) + } + } + } + + return inv + } + + fun openMenu(player: Player) { + if (async) { + Stark.instance.server.scheduler.runTaskAsynchronously(Stark.instance) { + try { + asyncLoadResources { successfulLoad -> + if (successfulLoad) { + val inv = createInventory(player) + + try { + openCustomInventory(player, inv) + } catch (ex: Exception) { + ex.printStackTrace() + } + } else { + player.sendMessage("${ChatColor.RED}Couldn't load menu...") + } + } + } catch (e: Exception) { + e.printStackTrace() + player.sendMessage("${ChatColor.RED}Couldn't load menu...") + } + } + } else { + val inv = createInventory(player) + + try { + openCustomInventory(player, inv) + } catch (ex: Exception) { + ex.printStackTrace() + } + } + } + + private fun openCustomInventory(player: Player, inv: Inventory) { + if (player.openInventory != null) { + manualClose = false + player.closeInventory() + } + + val entityPlayer = Reflections.getHandle(player) + + if (Bukkit.isPrimaryThread()) { + if (OPEN_CUSTOM_INVENTORY_METHOD_LEGACY != null) { + OPEN_CUSTOM_INVENTORY_METHOD_LEGACY.invoke(player, inv, entityPlayer, 0) + } else { + OPEN_CUSTOM_INVENTORY_METHOD!!.invoke(player, inv, entityPlayer, getWindowType(inv.size)) + } + + update(player) + } else { + Stark.instance.server.scheduler.runTaskLater(Stark.instance, { + if (OPEN_CUSTOM_INVENTORY_METHOD_LEGACY != null) { + OPEN_CUSTOM_INVENTORY_METHOD_LEGACY.invoke(player, inv, entityPlayer, 0) + } else { + OPEN_CUSTOM_INVENTORY_METHOD!!.invoke(player, inv, entityPlayer, getWindowType(inv.size)) + } + + update(player) + }, 1L) + } + } + + private fun getWindowType(size: Int): String { + return when (val rows = Math.ceil((size + 1) / 9.0).toInt()) { + 0 -> "minecraft:generic_9x1" + else -> "minecraft:generic_9x$rows" + } + } + + fun update(player: Player) { + // cancel check + cancelCheck(player) + + // set open menu reference to this menu + currentlyOpenedMenus[player.name] = this + + // call abstract onOpen + onOpen(player) + + val runnable = object : BukkitRunnable() { + override fun run() { + if (!player.isOnline) { + cancelCheck(player) + currentlyOpenedMenus.remove(player.name) + } + + if (this@Menu.autoUpdate) { + player.openInventory.topInventory.contents = this@Menu.createInventory(player).contents + } + } + } + + runnable.runTaskTimer(Stark.instance, 6L, 6L) + + checkTasks[player.name] = runnable + } + + open fun size(buttons: Map): Int { + var highest = 0 + for (buttonValue in buttons.keys) { + if (buttonValue > highest) { + highest = buttonValue + } + } + return (Math.ceil((highest + 1) / 9.0) * 9.0).toInt() + } + + fun getSlot(x: Int, y: Int): Int { + return 9 * y + x + } + + open fun asyncLoadResources(callback: (Boolean) -> Unit) {} + + open fun acceptsShiftClickedItem(player: Player, itemStack: ItemStack): Boolean { + return true + } + + companion object { + private val OPEN_CUSTOM_INVENTORY_METHOD_LEGACY: Method? = Reflections.getDeclaredMethod(true, Reflections.CRAFT_HUMAN_ENTITY_CLASS, "openCustomInventory", Inventory::class.java, Reflections.ENTITY_PLAYER_CLASS, Integer.TYPE) + private val OPEN_CUSTOM_INVENTORY_METHOD: Method? = Reflections.getDeclaredMethod(true, Reflections.CRAFT_HUMAN_ENTITY_CLASS, "openCustomInventory", Inventory::class.java, Reflections.ENTITY_PLAYER_CLASS, String::class.java) + + @JvmStatic var currentlyOpenedMenus: HashMap = hashMapOf() + @JvmStatic var checkTasks: HashMap = hashMapOf() + + fun cancelCheck(player: Player) { + if (checkTasks.containsKey(player.name)) { + checkTasks.remove(player.name)!!.cancel() + } + } + + val BAR = "&7&m${StringUtils.repeat("-", 32)}" + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/buttons/BackButton.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/buttons/BackButton.kt new file mode 100644 index 0000000..f748972 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/buttons/BackButton.kt @@ -0,0 +1,31 @@ +package net.evilblock.stark.engine.menu.buttons + +import net.evilblock.stark.engine.menu.Button +import net.evilblock.stark.util.Callback +import org.bukkit.Material +import org.bukkit.event.inventory.ClickType +import org.bukkit.entity.Player +import java.util.ArrayList + +class BackButton(private val callback: Callback) : Button() { + + override fun getMaterial(player: Player): Material? { + return Material.BED + } + + override fun getName(player: Player): String? { + return "§cGo back" + } + + override fun getDescription(player: Player): List? { + return ArrayList() + } + + override fun clicked(player: Player, i: Int, clickType: ClickType) { + playNeutral(player) + player.closeInventory() + + callback.callback(player) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/buttons/BooleanButton.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/buttons/BooleanButton.kt new file mode 100644 index 0000000..baccc3f --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/buttons/BooleanButton.kt @@ -0,0 +1,40 @@ +package net.evilblock.stark.engine.menu.buttons + +import net.evilblock.stark.engine.menu.Button +import net.evilblock.stark.util.Callback +import org.bukkit.Material +import org.bukkit.entity.Player +import java.util.ArrayList +import org.bukkit.Sound +import org.bukkit.event.inventory.ClickType + +class BooleanButton(private val confirm: Boolean, private val callback: Callback) : Button() { + + override fun getName(player: Player): String? { + return if (confirm) "§aConfirm" else "§cCancel" + } + + override fun getDescription(player: Player): List? { + return ArrayList() + } + + override fun getDamageValue(player: Player): Byte { + return (if (this.confirm) 5 else 14).toByte() + } + + override fun getMaterial(player: Player): Material? { + return Material.WOOL + } + + override fun clicked(player: Player, i: Int, clickType: ClickType) { + if (confirm) { + playSuccess(player) + } else { + playFail(player) + } + + player.closeInventory() + callback.callback(confirm) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/menus/ConfirmMenu.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/menus/ConfirmMenu.kt new file mode 100644 index 0000000..f384a73 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/menus/ConfirmMenu.kt @@ -0,0 +1,31 @@ +package net.evilblock.stark.engine.menu.menus + +import net.evilblock.stark.engine.menu.Button +import net.evilblock.stark.engine.menu.Menu +import net.evilblock.stark.util.Callback +import org.bukkit.entity.Player +import net.evilblock.stark.engine.menu.buttons.BooleanButton +import org.bukkit.Material +import java.util.HashMap + +class ConfirmMenu(private val title: String, private val callback: Callback) : Menu() { + + override fun getButtons(player: Player): Map { + val buttons = HashMap() + + for (i in 0..8) { + when (i) { + 3 -> buttons[i] = BooleanButton(true, callback) + 5 -> buttons[i] = BooleanButton(false, callback) + else -> buttons[i] = Button.placeholder(Material.STAINED_GLASS_PANE, 14.toByte()) + } + } + + return buttons + } + + override fun getTitle(player: Player): String { + return title + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/pagination/JumpToPageButton.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/pagination/JumpToPageButton.kt new file mode 100644 index 0000000..b499238 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/pagination/JumpToPageButton.kt @@ -0,0 +1,31 @@ +package net.evilblock.stark.engine.menu.pagination + +import net.evilblock.stark.engine.menu.Button +import org.bukkit.Material +import org.bukkit.event.inventory.ClickType +import org.bukkit.entity.Player + +class JumpToPageButton(private val page: Int, private val menu: PaginatedMenu) : Button() { + + override fun getName(player: Player): String { + return "§ePage " + this.page + } + + override fun getDescription(player: Player): List? { + return null + } + + override fun getMaterial(player: Player): Material { + return Material.BOOK + } + + override fun getAmount(player: Player): Int { + return this.page + } + + override fun clicked(player: Player, i: Int, clickType: ClickType) { + menu.modPage(player, page - menu.page) + playNeutral(player) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/pagination/PageButton.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/pagination/PageButton.kt new file mode 100644 index 0000000..e098c8c --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/pagination/PageButton.kt @@ -0,0 +1,51 @@ +package net.evilblock.stark.engine.menu.pagination + +import net.evilblock.stark.engine.menu.Button +import org.bukkit.Material +import org.bukkit.entity.Player +import java.util.ArrayList +import org.bukkit.event.inventory.ClickType + +class PageButton(private val mod: Int, private val menu: PaginatedMenu) : Button() { + + override fun clicked(player: Player, i: Int, clickType: ClickType) { + when { + clickType == ClickType.RIGHT -> { + ViewAllPagesMenu(menu).openMenu(player) + playNeutral(player) + } + hasNext(player) -> { + menu.modPage(player, mod) + playNeutral(player) + } + else -> playFail(player) + } + } + + private fun hasNext(player: Player): Boolean { + val pg = menu.page + mod + return pg > 0 && menu.getPages(player) >= pg + } + + override fun getName(player: Player): String? { + if (!this.hasNext(player)) { + return if (this.mod > 0) "§7Last page" else "§7First page" + } + + val str = "(§e" + (menu.page + mod) + "/§e" + menu.getPages(player) + "§a)" + return (if (this.mod > 0) "§a\u27f6" else "§c\u27f5") + str + } + + override fun getDescription(player: Player): List? { + return ArrayList() + } + + override fun getDamageValue(player: Player): Byte { + return (if (this.hasNext(player)) 11 else 7).toByte() + } + + override fun getMaterial(player: Player): Material { + return Material.CARPET + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/pagination/PaginatedMenu.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/pagination/PaginatedMenu.kt new file mode 100644 index 0000000..fbc74ef --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/pagination/PaginatedMenu.kt @@ -0,0 +1,66 @@ +package net.evilblock.stark.engine.menu.pagination + +import net.evilblock.stark.engine.menu.Button +import net.evilblock.stark.engine.menu.Menu +import org.bukkit.entity.Player +import java.util.HashMap + +abstract class PaginatedMenu : Menu() { + + internal var page: Int = 1 + + override fun getTitle(player: Player): String { + return getPrePaginatedTitle(player) + " - " + page + "/" + getPages(player) + } + + fun modPage(player: Player, mod: Int) { + page += mod + buttons.clear() + openMenu(player) + } + + internal fun getPages(player: Player): Int { + val buttonAmount = getAllPagesButtons(player).size + return if (buttonAmount == 0) { + 1 + } else Math.ceil(buttonAmount / getMaxItemsPerPage(player).toDouble()).toInt() + } + + override fun getButtons(player: Player): MutableMap { + val minIndex = ((page - 1) * getMaxItemsPerPage(player).toDouble()).toInt() + val maxIndex = (page * getMaxItemsPerPage(player).toDouble()).toInt() + val buttons = HashMap() + buttons[0] = PageButton(-1, this) + buttons[8] = PageButton(1, this) + + for (entry in getAllPagesButtons(player).entries) { + var ind = entry.key + if (ind in minIndex until maxIndex) { + ind -= (getMaxItemsPerPage(player) * (page - 1).toDouble()).toInt() - 9 + buttons[ind] = entry.value + } + } + + val global = getGlobalButtons(player) + if (global != null) { + for ((key, value) in global) { + buttons[key] = value + } + } + + return buttons + } + + open fun getMaxItemsPerPage(player: Player): Int { + return 18 + } + + open fun getGlobalButtons(player: Player): Map? { + return null + } + + abstract fun getPrePaginatedTitle(player: Player): String + + abstract fun getAllPagesButtons(player: Player): Map + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/pagination/ViewAllPagesMenu.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/pagination/ViewAllPagesMenu.kt new file mode 100644 index 0000000..ab12287 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/menu/pagination/ViewAllPagesMenu.kt @@ -0,0 +1,40 @@ +package net.evilblock.stark.engine.menu.pagination + +import net.evilblock.stark.engine.menu.Button +import net.evilblock.stark.engine.menu.Menu +import net.evilblock.stark.engine.menu.buttons.BackButton +import net.evilblock.stark.util.Callback +import java.util.HashMap +import org.bukkit.entity.Player + +class ViewAllPagesMenu(private val menu: PaginatedMenu) : Menu() { + + init { + autoUpdate = true + } + + override fun getTitle(player: Player): String { + return "Jump to page" + } + + override fun getButtons(player: Player): Map { + val buttons = HashMap() + + buttons[0] = BackButton(object : Callback { + override fun callback(value: Player) { + menu.openMenu(player) + } + }) + + var index = 10 + for (i in 1..menu.getPages(player)) { + buttons[index++] = JumpToPageButton(i, menu) + if ((index - 8) % 9 == 0) { + index += 2 + } + } + + return buttons + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/InventoryAdapter.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/InventoryAdapter.kt new file mode 100644 index 0000000..ad3159d --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/InventoryAdapter.kt @@ -0,0 +1,37 @@ +package net.evilblock.stark.engine.protocol + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.protocol.event.PlayerCloseInventoryEvent +import net.evilblock.stark.engine.protocol.event.PlayerOpenInventoryEvent +import java.util.UUID +import java.util.HashSet +import org.bukkit.Bukkit +import com.comphenix.protocol.PacketType +import com.comphenix.protocol.wrappers.EnumWrappers +import com.comphenix.protocol.events.PacketEvent +import com.comphenix.protocol.events.PacketAdapter + +class InventoryAdapter : PacketAdapter(Stark.instance, PacketType.Play.Client.CLIENT_COMMAND, PacketType.Play.Client.CLOSE_WINDOW) { + + override fun onPacketReceiving(event: PacketEvent?) { + val player = event!!.player + val packet = event.packet + + if (packet.type === PacketType.Play.Client.CLIENT_COMMAND && packet.clientCommands.size() != 0 && packet.clientCommands.read(0) == EnumWrappers.ClientCommand.OPEN_INVENTORY_ACHIEVEMENT) { + if (!currentlyOpen.contains(player.uniqueId)) { + Bukkit.getScheduler().scheduleSyncDelayedTask(Stark.instance) { Bukkit.getPluginManager().callEvent(PlayerOpenInventoryEvent(player)) } + } + currentlyOpen.add(player.uniqueId) + } else if (packet.type === PacketType.Play.Client.CLOSE_WINDOW) { + if (currentlyOpen.contains(player.uniqueId)) { + Bukkit.getScheduler().scheduleSyncDelayedTask(Stark.instance) { Bukkit.getPluginManager().callEvent(PlayerCloseInventoryEvent(player)) } + } + currentlyOpen.remove(player.uniqueId) + } + } + + companion object { + var currentlyOpen: HashSet = HashSet() + } + +} diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/LagCheck.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/LagCheck.kt new file mode 100644 index 0000000..f33061c --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/LagCheck.kt @@ -0,0 +1,27 @@ +package net.evilblock.stark.engine.protocol + +import net.evilblock.stark.engine.protocol.event.ServerLaggedOutEvent +import net.evilblock.stark.util.PlayerUtils +import org.bukkit.Bukkit +import org.bukkit.scheduler.BukkitRunnable + +class LagCheck : BukkitRunnable() { + + override fun run() { + val players = Bukkit.getOnlinePlayers() + if (players.size >= 100) { + var playersLagging = 0 + for (player in players) { + if (PlayerUtils.isLagging(player)) { + ++playersLagging + } + } + + val percentage = playersLagging * 100 / players.size + if (Math.abs(percentage) >= 30.0) { + Bukkit.getPluginManager().callEvent(ServerLaggedOutEvent(PingAdapter.averagePing())) + } + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/PingAdapter.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/PingAdapter.kt new file mode 100644 index 0000000..bb25e5b --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/PingAdapter.kt @@ -0,0 +1,82 @@ +package net.evilblock.stark.engine.protocol + +import net.evilblock.stark.Stark +import net.evilblock.stark.util.Reflections +import com.comphenix.protocol.PacketType +import com.comphenix.protocol.events.PacketAdapter +import com.comphenix.protocol.events.PacketEvent +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerQuitEvent +import java.lang.Exception +import java.util.* +import java.util.concurrent.ConcurrentHashMap + +class PingAdapter : PacketAdapter(Stark.instance, PacketType.Play.Server.KEEP_ALIVE, PacketType.Play.Client.KEEP_ALIVE), Listener { + + override fun onPacketSending(event: PacketEvent) { + val id = try { + event.packet.integers.read(0) as Int + } catch (e: Exception) { + (event.packet.longs.read(0) as Long).toInt() + } + + callbacks[event.player.uniqueId] = object : PingCallback(id) { + override fun call() { + val ping = (System.currentTimeMillis() - this.sendTime).toInt() + Companion.ping[event.player.uniqueId] = ping + lastReply[event.player.uniqueId] = Reflections.getFieldValue(Reflections.getMinecraftServer()!!, "currentTick") as Int + } + } + } + + override fun onPacketReceiving(event: PacketEvent) { + val iterator = callbacks.entries.iterator() + while (iterator.hasNext()) { + val entry = iterator.next() + + val id = try { + event.packet.integers.read(0) as Int + } catch (e: Exception) { + (event.packet.longs.read(0) as Long).toInt() + } + + if (entry.value.id == id) { + entry.value.call() + iterator.remove() + break + } + } + } + + @EventHandler + fun onQuit(event: PlayerQuitEvent) { + ping.remove(event.player.uniqueId) + lastReply.remove(event.player.uniqueId) + callbacks.remove(event.player.uniqueId) + } + + private abstract class PingCallback + constructor(val id: Int) { + val sendTime: Long = System.currentTimeMillis() + + abstract fun call() + } + + companion object { + private val callbacks: ConcurrentHashMap = ConcurrentHashMap() + val ping: ConcurrentHashMap = ConcurrentHashMap() + val lastReply: ConcurrentHashMap = ConcurrentHashMap() + + fun averagePing(): Int { + if (ping.isEmpty()) { + return 0 + } + var x = 0 + for (p in ping.values) { + x += p + } + return x / ping.size + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/event/PlayerCloseInventoryEvent.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/event/PlayerCloseInventoryEvent.kt new file mode 100644 index 0000000..3a2c130 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/event/PlayerCloseInventoryEvent.kt @@ -0,0 +1,19 @@ +package net.evilblock.stark.engine.protocol.event + +import org.bukkit.event.HandlerList +import org.bukkit.entity.Player +import org.bukkit.event.player.PlayerEvent + +class PlayerCloseInventoryEvent(player: Player) : PlayerEvent(player) { + + private val instanceHandlers: HandlerList = ServerLaggedOutEvent.handlerList + + override fun getHandlers(): HandlerList { + return instanceHandlers + } + + companion object { + var handlerList: HandlerList = HandlerList() + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/event/PlayerOpenInventoryEvent.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/event/PlayerOpenInventoryEvent.kt new file mode 100644 index 0000000..d41ffa9 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/event/PlayerOpenInventoryEvent.kt @@ -0,0 +1,19 @@ +package net.evilblock.stark.engine.protocol.event + +import org.bukkit.event.HandlerList +import org.bukkit.entity.Player +import org.bukkit.event.player.PlayerEvent + +class PlayerOpenInventoryEvent(player: Player) : PlayerEvent(player) { + + private val instanceHandlers: HandlerList = ServerLaggedOutEvent.handlerList + + override fun getHandlers(): HandlerList { + return instanceHandlers + } + + companion object { + var handlerList: HandlerList = HandlerList() + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/event/ServerLaggedOutEvent.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/event/ServerLaggedOutEvent.kt new file mode 100644 index 0000000..8d3f5e2 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/engine/protocol/event/ServerLaggedOutEvent.kt @@ -0,0 +1,18 @@ +package net.evilblock.stark.engine.protocol.event + +import org.bukkit.event.Event +import org.bukkit.event.HandlerList + +class ServerLaggedOutEvent(private val averagePing: Int) : Event(true) { + + private val instanceHandlers: HandlerList = handlerList + + override fun getHandlers(): HandlerList { + return instanceHandlers + } + + companion object { + val handlerList: HandlerList = HandlerList() + } + +} diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/hook/VaultHook.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/hook/VaultHook.kt new file mode 100644 index 0000000..8a0b97f --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/hook/VaultHook.kt @@ -0,0 +1,18 @@ +package net.evilblock.stark.hook + +import net.evilblock.stark.Stark +import net.milkbowl.vault.permission.Permission +import org.bukkit.Bukkit +import org.bukkit.plugin.ServicePriority + +object VaultHook { + + fun hook() { + if (Bukkit.getServer().pluginManager.getPlugin("Vault") == null) { + return + } + + Bukkit.getServer().servicesManager.register(Permission::class.java, VaultPermissionImpl(), Stark.instance, ServicePriority.Highest) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/hook/VaultPermissionImpl.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/hook/VaultPermissionImpl.kt new file mode 100644 index 0000000..6ad7c8e --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/hook/VaultPermissionImpl.kt @@ -0,0 +1,220 @@ +package net.evilblock.stark.hook + +import net.evilblock.stark.Stark +import net.evilblock.stark.core.profile.grant.ProfileGrant +import net.milkbowl.vault.permission.Permission +import org.bukkit.OfflinePlayer +import org.bukkit.World +import org.bukkit.entity.Player + +class VaultPermissionImpl : Permission() { + + override fun hasSuperPermsCompat(): Boolean { + return true + } + + override fun getPlayerGroups(player: Player): Array { + val groups = arrayListOf() + val profile = Stark.instance.core.getProfileHandler().getByUUID(player.uniqueId) + + profile?.rankGrants?.filter { grant -> grant.isActive() }?.forEach { + groups.add(it.rank.id) + } + + return groups.toTypedArray() + } + + // TODO: ASYNC OFFLINE SUPPORT + override fun getPlayerGroups(world: String, player: OfflinePlayer): Array { + return emptyArray() + } + + override fun getPrimaryGroup(player: Player): String { + val profile = Stark.instance.core.getProfileHandler().getByUUID(player.uniqueId) + return profile?.getRank()?.id ?: Stark.instance.core.rankHandler.getDefaultRank().id + } + + // TODO: ASYNC OFFLINE SUPPORT + override fun getPrimaryGroup(world: String, player: OfflinePlayer): String { + return Stark.instance.core.rankHandler.getDefaultRank().id + } + + override fun groupAdd(world: World, group: String, permission: String): Boolean { + val rank = Stark.instance.core.rankHandler.getById(group) + + return if (rank != null) { + val success = rank.permissions.add(permission) + + if (success) { + Stark.instance.server.scheduler.runTaskAsynchronously(Stark.instance) { + Stark.instance.core.rankHandler.saveRank(rank) + } + } + + return success + } else { + false + } + } + + override fun groupHas(world: World, group: String, permission: String): Boolean { + val rank = Stark.instance.core.rankHandler.getById(group) + return rank?.permissions?.contains(permission) ?: false + } + + override fun groupRemove(world: World, group: String, permission: String): Boolean { + val rank = Stark.instance.core.rankHandler.getById(group) + + return if (rank != null) { + val success = rank.permissions.remove(permission) + + if (success) { + Stark.instance.server.scheduler.runTaskAsynchronously(Stark.instance) { + Stark.instance.core.rankHandler.saveRank(rank) + } + } + + return success + } else { + false + } + } + + override fun playerAddGroup(player: Player, group: String): Boolean { + val rank = Stark.instance.core.rankHandler.getById(group) ?: return false + + val profile = Stark.instance.core.getProfileHandler().getByUUID(player.uniqueId) + + if (profile != null) { + profile.rankGrants.filter { it.isActive() }.forEach { grant -> + if (grant.rank.id.equals(group, ignoreCase = true)) { + return@forEach + } + } + + // TODO: MAKE VAULT GRANT'S DEFAULT DURATION CONFIGURABLE + val grant = ProfileGrant() + grant.rank = rank + grant.reason = "VaultAPI Hook" + grant.issuedAt = System.currentTimeMillis() + + profile.rankGrants.add(grant) + + Stark.instance.server.scheduler.runTaskAsynchronously(Stark.instance) { + Stark.instance.core.getProfileHandler().saveProfile(profile) + } + + return true + } + + return false + } + + // TODO: ASYNC OFFLINE SUPPORT + override fun playerAddGroup(world: String, player: OfflinePlayer, group: String): Boolean { + return false + } + + override fun playerInGroup(player: Player, group: String): Boolean { + val profile = Stark.instance.core.getProfileHandler().getByUUID(player.uniqueId) + + profile?.rankGrants?.filter { it.isActive() }?.forEach { grant -> + if (grant.rank.id.equals(group, ignoreCase = true)) { + return true + } + } + + return false + } + + // TODO: ASYNC OFFLINE SUPPORT + override fun playerInGroup(world: String, player: OfflinePlayer, group: String): Boolean { + return super.playerInGroup(world, player, group) + } + + override fun playerRemoveGroup(player: Player, group: String): Boolean { + val profile = Stark.instance.core.getProfileHandler().getByUUID(player.uniqueId) + + if (profile != null) { + profile.rankGrants.filter { it.isActive() }.forEach { grant -> + if (grant.rank.id.equals(group, ignoreCase = true)) { + grant.removedAt = System.currentTimeMillis() + grant.removalReason = "VaultAPI Hook" + return true + } + } + + Stark.instance.server.scheduler.runTaskAsynchronously(Stark.instance) { + Stark.instance.core.getProfileHandler().saveProfile(profile) + } + } + + return false + } + + // TODO: ASYNC OFFLINE SUPPORT + override fun playerRemoveGroup(world: String, player: OfflinePlayer, group: String): Boolean { + return super.playerRemoveGroup(world, player, group) + } + + override fun playerHas(world: String, player: String, permission: String): Boolean { + throw UnsupportedOperationException("Stark does not support deprecated VaultAPI methods") + } + + override fun playerAdd(p0: String?, p1: String?, p2: String?): Boolean { + throw UnsupportedOperationException("Stark does not support deprecated VaultAPI methods") + } + + override fun playerRemove(p0: String?, p1: String?, p2: String?): Boolean { + throw UnsupportedOperationException("Stark does not support deprecated VaultAPI methods") + } + + override fun getPlayerGroups(p0: String?, p1: String?): Array { + throw UnsupportedOperationException("Stark does not support deprecated VaultAPI methods") + } + + override fun getPrimaryGroup(p0: String?, p1: String?): String { + throw UnsupportedOperationException("Stark does not support deprecated VaultAPI methods") + } + + override fun playerAddGroup(p0: String?, p1: String?, p2: String?): Boolean { + throw UnsupportedOperationException("Stark does not support deprecated VaultAPI methods") + } + + override fun playerInGroup(p0: String?, p1: String?, p2: String?): Boolean { + throw UnsupportedOperationException("Stark does not support deprecated VaultAPI methods") + } + + override fun playerRemoveGroup(p0: String?, p1: String?, p2: String?): Boolean { + throw UnsupportedOperationException("Stark does not support deprecated VaultAPI methods") + } + + override fun getGroups(): Array { + throw UnsupportedOperationException("Stark does not support deprecated VaultAPI methods") + } + + override fun getName(): String { + throw UnsupportedOperationException("Stark does not support deprecated VaultAPI methods") + } + + override fun groupAdd(p0: String?, p1: String?, p2: String?): Boolean { + throw UnsupportedOperationException("Stark does not support deprecated VaultAPI methods") + } + + override fun groupHas(p0: String?, p1: String?, p2: String?): Boolean { + throw UnsupportedOperationException("Stark does not support deprecated VaultAPI methods") + } + + override fun groupRemove(p0: String?, p1: String?, p2: String?): Boolean { + throw UnsupportedOperationException("Stark does not support deprecated VaultAPI methods") + } + + override fun hasGroupSupport(): Boolean { + throw UnsupportedOperationException("Stark does not support deprecated VaultAPI methods") + } + + override fun isEnabled(): Boolean { + throw UnsupportedOperationException("Stark does not support deprecated VaultAPI methods") + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/MessagingManager.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/MessagingManager.kt new file mode 100644 index 0000000..b72fc57 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/MessagingManager.kt @@ -0,0 +1,231 @@ +package net.evilblock.stark.messaging + +import net.evilblock.stark.Stark +import net.evilblock.stark.messaging.event.PlayerMessageEvent +import net.evilblock.stark.util.SoundCompat +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.entity.Player +import java.util.* + +class MessagingManager { + + private val key = "stark:messageSettings" + val globalSpy: MutableSet = hashSetOf() + + /** + * Gets the last messaged player for a player. + * + * @param player the player + * + * @return the last player's uuid that [player] has messaged + */ + fun getLastMessaged(player: UUID): UUID? { + return Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.hget("$key:$player", "lastMessaged") + }?.run { UUID.fromString(this) } + } + /** + * Sets the last messaged player for a player. + * + * @param player the player + */ + fun setLastMessaged(player: UUID, lastMessaged: UUID) { + Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.hset("$key:$player", hashMapOf("lastMessaged" to "$lastMessaged")) + } + } + + /** + * Gets if a player's messages are disabled. + * + * @param player the player + */ + fun isMessagesDisabled(player: UUID): Boolean { + return Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.hget("$key:$player", "messagesDisabled") + }?.toBoolean() ?: false + } + + /** + * Toggles if a player's messages are disabled. + * + * @param player the player + * + * @return if the [player]'s messages are disabled after toggle + */ + fun toggleMessages(player: UUID): Boolean { + val value = !isMessagesDisabled(player) + + Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.hmset("$key:$player", hashMapOf("messagesDisabled" to value.toString())) + } + + return value + } + + + /** + * Gets if a player's message sounds are disabled. + * + * @param player the player + */ + fun isSoundsDisabled(player: UUID): Boolean { + return Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.hget("$key:$player", "soundsDisabled") + }?.toBoolean() ?: false + } + /** + * Toggles if a player's message sounds are disabled. + * + * @param player the player + * + * @return if the [player]'s message sounds are disabled after toggle + */ + fun toggleSounds(player: UUID): Boolean { + val value = !isSoundsDisabled(player) + + Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.hmset("$key:$player", hashMapOf("soundsDisabled" to value.toString())) + } + + return value + } + + /** + * Gets whether or not the [player] is ignored by the [target]. + * + * @param player the player + * @param target the target + * + * @return if the [player] is ignored by the [target] + */ + fun isIgnored(player: UUID, target: UUID): Boolean { + return Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.hget("$key:ignoreList:$target", "$player") + }?.toBoolean() ?: false + } + + /** + * Adds a target to a player's ignore list. + */ + fun addToIgnoreList(player: UUID, target: UUID) { + Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.hmset("$key:ignoreList:$player", hashMapOf(target.toString() to "true")) + } + } + + /** + * Removes a target from a player's ignore list. + * + * @param player the player's UUID + * @param target the target's UUID + */ + fun removeFromIgnoreList(player: UUID, target: UUID) { + Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.hdel("$key:ignoreList:$player", target.toString()) + } + } + + /** + * Clears a player's ignore list. + * + * @param player the player's UUID + */ + fun clearIgnoreList(player: UUID) { + Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.del("$key:ignoreList:$player") + } + } + + /** + * Retrieves a player's ignore list. + * + * @param player the player's UUID + * + * @return the player's ignore list as {@link List} + */ + fun getIgnoreList(player: UUID): List { + return Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.hgetAll("$key:ignoreList:$player").map { entry -> UUID.fromString(entry.key) } + } + } + + /** + * Determines whether a sender player can message a target. + * + * @param sender the player trying to send the message + * @param target the player the [sender] is trying to send a message to + * + * @return true if the sender can message the target. + */ + fun canMessage(sender: Player, target: Player): Boolean { + if (sender.hasPermission("stark.staff")) { + return true + } + + if (isIgnored(sender.uniqueId, target.uniqueId)) { + sender.sendMessage(ChatColor.RED.toString() + "That player is ignoring you.") + return false + } + + if (isIgnored(target.uniqueId, sender.uniqueId)) { + sender.sendMessage(ChatColor.RED.toString() + "You are ignoring that player.") + return false + } + + if (isMessagesDisabled(sender.uniqueId)) { + sender.sendMessage(ChatColor.RED.toString() + "You have messages turned off.") + } + + if (isMessagesDisabled(target.uniqueId)) { + sender.sendMessage(target.displayName + ChatColor.RED + " has messages turned off.") + return false + } + + return true + } + + /** + * Sends a [message] from the [sender] to the [target]. + * + * @param sender the player sending the [message] + * @param target the player the [sender] is sending the [message] to + * @param message the message the [sender] is trying to send to the [target] + */ + fun sendMessage(sender: Player, target: Player, message: String) { + val event = PlayerMessageEvent(sender, target, message) + + Stark.instance.server.pluginManager.callEvent(event) + + if (event.isCancelled) { + return + } + + setLastMessaged(sender.uniqueId, target.uniqueId) + setLastMessaged(target.uniqueId, sender.uniqueId) + + val senderProfile = Stark.instance.core.getProfileHandler().getByUUID(sender.uniqueId)!! + val targetProfile = Stark.instance.core.getProfileHandler().getByUUID(target.uniqueId)!! + + target.sendMessage(ChatColor.RED.toString() + "(From " + ChatColor.WHITE + senderProfile.getPlayerListName() + ChatColor.RED + ") " + message) + sender.sendMessage(ChatColor.RED.toString() + "(To " + ChatColor.WHITE + targetProfile.getPlayerListName() + ChatColor.RED + ") " + message) + + if (!isSoundsDisabled(target.uniqueId)) { + SoundCompat.MESSAGE_RECEIVED.playSound(target) + } + + for (player in Bukkit.getOnlinePlayers()) { + if (player !== sender) { + if (player === target) { + continue + } + + if (globalSpy.contains(player.uniqueId)) { + player.sendMessage(ChatColor.GRAY.toString() + "(" + ChatColor.WHITE + sender.displayName + ChatColor.GRAY + " to " + ChatColor.WHITE + target.displayName + ChatColor.GRAY + ") " + message) + } + } + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/IgnoreCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/IgnoreCommand.kt new file mode 100644 index 0000000..165e1a0 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/IgnoreCommand.kt @@ -0,0 +1,28 @@ +package net.evilblock.stark.messaging.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.core.util.mojanguser.MojangUser +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object IgnoreCommand { + @Command(["ignore"], description = "Start ignoring a player. You won't receive private messages from them or see their public chat messages", async = true) + @JvmStatic + fun execute(player: Player, @Param("player") target: MojangUser) { + if (player.uniqueId == target.uuid) { + player.sendMessage("${ChatColor.RED}You can't ignore yourself.") + return + } + + if (Stark.instance.messagingManager.isIgnored(player.uniqueId, target.uuid)) { + player.sendMessage("${ChatColor.RED}You are already ignoring ${ChatColor.WHITE}${target.username}${ChatColor.RED}.") + return + } + + Stark.instance.messagingManager.addToIgnoreList(player.uniqueId, target.uuid) + + player.sendMessage("${ChatColor.YELLOW}Now ignoring ${ChatColor.WHITE}${target.username}${ChatColor.YELLOW}.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/IgnoreListClearCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/IgnoreListClearCommand.kt new file mode 100644 index 0000000..485dfcd --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/IgnoreListClearCommand.kt @@ -0,0 +1,15 @@ +package net.evilblock.stark.messaging.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object IgnoreListClearCommand { + @Command(["ignore clear"], description = "Clear your ignore list", async = true) + @JvmStatic + fun execute(player: Player) { + Stark.instance.messagingManager.clearIgnoreList(player.uniqueId) + player.sendMessage("${ChatColor.YELLOW}You've cleared your ignore list.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/IgnoreListCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/IgnoreListCommand.kt new file mode 100644 index 0000000..10bb44c --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/IgnoreListCommand.kt @@ -0,0 +1,27 @@ +package net.evilblock.stark.messaging.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object IgnoreListCommand { + @Command(["ignore list"], description = "See a list of people you're currently ignoring", async = true) + @JvmStatic + fun execute(player: Player) { + val ignoreList = Stark.instance.messagingManager.getIgnoreList(player.uniqueId) + + if (ignoreList.isEmpty()) { + player.sendMessage("${ChatColor.RED}You aren't ignoring anyone.") + return + } + + val names = arrayListOf() + for (uuid in ignoreList) { + names.add(Stark.instance.core.uuidCache.name(uuid)) + } + + player.sendMessage("${ChatColor.YELLOW}You are currently ignoring ${ChatColor.RED}${names.size} ${ChatColor.YELLOW}player${if (names.size == 1) "" else "s"}:") + player.sendMessage(ChatColor.translateAlternateColorCodes('&', names.joinToString("&e, ", "&c"))) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/IgnoreRemoveCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/IgnoreRemoveCommand.kt new file mode 100644 index 0000000..5211c3a --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/IgnoreRemoveCommand.kt @@ -0,0 +1,23 @@ +package net.evilblock.stark.messaging.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.core.util.mojanguser.MojangUser +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object IgnoreRemoveCommand { + @Command(["ignore remove"], description = "Stop ignoring a player", async = true) + @JvmStatic + fun execute(player: Player, @Param("player") target: MojangUser) { + if (!Stark.instance.messagingManager.isIgnored(player.uniqueId, target.uuid)) { + player.sendMessage("${ChatColor.RED}You aren't ignoring ${ChatColor.WHITE}${target.username}${ChatColor.RED}.") + return + } + + Stark.instance.messagingManager.removeFromIgnoreList(player.uniqueId, target.uuid) + + player.sendMessage("${ChatColor.YELLOW}You are no longer ignoring ${ChatColor.WHITE}${target.username}${ChatColor.YELLOW}.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/MessageCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/MessageCommand.kt new file mode 100644 index 0000000..f002dfc --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/MessageCommand.kt @@ -0,0 +1,24 @@ +package net.evilblock.stark.messaging.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object MessageCommand { + @Command(["message", "msg", "m", "whisper", "w", "tell", "t"], description = "Send a player a private message", async = true) + @JvmStatic + fun message(sender: Player, @Param(name = "player") target: Player, @Param(name = "message", wildcard = true) message: String) { + if (sender.uniqueId == target.uniqueId) { + sender.sendMessage("${ChatColor.RED}You can't message yourself.") + return + } + + if (!Stark.instance.messagingManager.canMessage(sender, target)) { + return + } + + Stark.instance.messagingManager.sendMessage(sender, target, message) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/ReplyCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/ReplyCommand.kt new file mode 100644 index 0000000..bdb1f6e --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/ReplyCommand.kt @@ -0,0 +1,44 @@ +package net.evilblock.stark.messaging.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object ReplyCommand { + @Command(["reply", "r"], description = "Reply to the player you're in a conversation with", async = true) + @JvmStatic + fun reply(sender: Player, @Param(name = "message", defaultValue = " ", wildcard = true) message: String) { + val lastMessaged = Stark.instance.messagingManager.getLastMessaged(sender.uniqueId) + + if (message == " ") { + if (lastMessaged == null) { + sender.sendMessage("${ChatColor.RED}You aren't in a conversation.") + } else { + sender.sendMessage("${ChatColor.GOLD}You are in a conversation with ${ChatColor.WHITE}${Stark.instance.core.uuidCache.name(lastMessaged)}${ChatColor.GOLD}.") + } + + return + } + + if (lastMessaged == null) { + sender.sendMessage("${ChatColor.RED}You have no one to reply to.") + return + } + + val target = Bukkit.getPlayer(lastMessaged) + + if (target == null) { + sender.sendMessage("${ChatColor.RED}That player has logged out.") + return + } + + if (!Stark.instance.messagingManager.canMessage(sender, target)) { + return + } + + Stark.instance.messagingManager.sendMessage(sender, target, message) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/SpyCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/SpyCommand.kt new file mode 100644 index 0000000..4027c8f --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/SpyCommand.kt @@ -0,0 +1,22 @@ +package net.evilblock.stark.messaging.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object SpyCommand { + @Command(["spy"], permission = "stark.message.spy", description = "Toggle global private message spying") + @JvmStatic + fun spy(sender: Player) { + val contains = Stark.instance.messagingManager.globalSpy.contains(sender.uniqueId) + + if (contains) { + Stark.instance.messagingManager.globalSpy.remove(sender.uniqueId) + } else { + Stark.instance.messagingManager.globalSpy.add(sender.uniqueId) + } + + sender.sendMessage("${ChatColor.GOLD}Global chat spy has been set to ${ChatColor.WHITE}$contains${ChatColor.GOLD}.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/ToggleMessagesCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/ToggleMessagesCommand.kt new file mode 100644 index 0000000..64cf849 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/ToggleMessagesCommand.kt @@ -0,0 +1,20 @@ +package net.evilblock.stark.messaging.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object ToggleMessagesCommand { + @Command(["togglepm", "tpm"], description = "Toggle private messaging", async = true) + @JvmStatic + fun execute(player: Player) { + val toggle = Stark.instance.messagingManager.toggleMessages(player.uniqueId) + + if (toggle) { + player.sendMessage("${ChatColor.RED}Private messages have been disabled.") + } else { + player.sendMessage("${ChatColor.GREEN}Private messages have been enabled.") + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/ToggleSoundsCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/ToggleSoundsCommand.kt new file mode 100644 index 0000000..0caa64a --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/command/ToggleSoundsCommand.kt @@ -0,0 +1,20 @@ +package net.evilblock.stark.messaging.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object ToggleSoundsCommand { + @Command(["sounds", "togglesounds"], description = "Toggle messaging sounds", async = true) + @JvmStatic + fun execute(player: Player) { + val toggle = Stark.instance.messagingManager.toggleSounds(player.uniqueId) + + if (toggle) { + player.sendMessage(ChatColor.YELLOW.toString() + "Messaging sounds have been disabled.") + } else { + player.sendMessage(ChatColor.YELLOW.toString() + "Messaging sounds have been enabled.") + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/event/PlayerMessageEvent.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/event/PlayerMessageEvent.kt new file mode 100644 index 0000000..fb70071 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/messaging/event/PlayerMessageEvent.kt @@ -0,0 +1,28 @@ +package net.evilblock.stark.messaging.event + +import org.bukkit.entity.Player +import org.bukkit.event.Cancellable +import org.bukkit.event.Event +import org.bukkit.event.HandlerList + +class PlayerMessageEvent(val sender: Player, val target: Player, val message: String) : Event(), Cancellable { + + private var cancelled: Boolean = false + + companion object { + @JvmStatic val handlerList = HandlerList() + } + + override fun getHandlers(): HandlerList { + return handlerList + } + + override fun isCancelled(): Boolean { + return cancelled + } + + override fun setCancelled(cancelled: Boolean) { + this.cancelled = cancelled + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/ModRequestHandler.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/ModRequestHandler.kt new file mode 100644 index 0000000..e9ef473 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/ModRequestHandler.kt @@ -0,0 +1,46 @@ +package net.evilblock.stark.modsuite + +import net.evilblock.stark.Stark +import java.util.concurrent.TimeUnit +import org.bukkit.entity.Player +import redis.clients.jedis.Jedis +import java.util.* + +object ModRequestHandler { + + fun hasRequestCooldown(player: Player): Boolean { + val key = "stark:request:" + player.uniqueId + ":cooldown" + + return Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.exists(key) + } + } + + fun addRequestCooldown(player: Player, time: Long, unit: TimeUnit) { + val key = "stark:request:" + player.uniqueId + ":cooldown" + + Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.setex(key, unit.toSeconds(time).toInt(), "") + } + } + + fun getReportCount(target: UUID): Int { + val key = "stark:request:$target:reports" + val time = System.currentTimeMillis() + + return Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.zcount(key, (time - TimeUnit.MINUTES.toMillis(15L)).toDouble(), time.toDouble())!!.toInt() + } + } + + fun incrementReportCount(target: Player) { + val key = "stark:request:" + target.uniqueId + ":reports" + val time = System.currentTimeMillis() + + Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.zadd(key, time.toDouble(), time.toString()) + redis.expire(key, TimeUnit.MINUTES.toMillis(15L).toInt()) + } + } + +} diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/ModSuiteMessageListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/ModSuiteMessageListeners.kt new file mode 100644 index 0000000..e91e925 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/ModSuiteMessageListeners.kt @@ -0,0 +1,66 @@ +package net.evilblock.stark.modsuite + +import nig.cock.nigger.message.handler.IncomingMessageHandler +import nig.cock.nigger.message.listener.MessageListener +import com.google.gson.JsonObject +import net.evilblock.stark.modsuite.options.ModOptionsHandler +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import java.util.* + +class ModSuiteMessageListeners : MessageListener { + + @IncomingMessageHandler("STAFF_CHAT") + fun onIncomingStaffChatMessage(data: JsonObject) { + val serverName = data.get("serverName").asString + val senderName = data.get("senderName").asString + val message = data.get("message").asString + + Bukkit.getOnlinePlayers().forEach { + if (it.hasPermission("stark.staff")) { + val options = ModOptionsHandler.get(it) + if (options.receivingStaffChat) { + it.sendMessage("${ChatColor.RED}[SC] [$serverName]${ChatColor.DARK_PURPLE}$senderName${ChatColor.LIGHT_PURPLE}: $message") + } + } + } + } + + @IncomingMessageHandler("REQUEST") + fun onIncomingRequestMessage(data: JsonObject) { + val serverName = data.get("serverName").asString + val senderName = data.get("senderName").asString + val reason = data.get("reason").asString + + Bukkit.getOnlinePlayers().forEach { + if (it.hasPermission("stark.staff")) { + val options = ModOptionsHandler.get(it) + if (options.receivingRequests) { + it.sendMessage("${ChatColor.BLUE}${ChatColor.BOLD}[Request] ${ChatColor.GRAY}[$serverName] ${ChatColor.AQUA}$senderName ${ChatColor.GRAY}requested assistance") + it.sendMessage("${ChatColor.BLUE}${ChatColor.BOLD} Reason: ${ChatColor.GRAY}$reason") + } + } + } + } + + @IncomingMessageHandler("REPORT") + fun onIncomingReportMessage(data: JsonObject) { + val serverName = data.get("serverName").asString + val senderName = data.get("senderName").asString + val reportedUuid = data.get("reportedUuid").asString + val reportedName = data.get("reportedName").asString + val reason = data.get("reason").asString + val reportCount = ModRequestHandler.getReportCount(UUID.fromString(reportedUuid)) + + Bukkit.getOnlinePlayers().forEach { + if (it.hasPermission("stark.staff")) { + val options = ModOptionsHandler.get(it) + if (options.receivingRequests) { + it.sendMessage("${ChatColor.BLUE}${ChatColor.BOLD}[Report] ${ChatColor.GRAY}[$serverName] ${ChatColor.AQUA}$reportedName ${ChatColor.GRAY}($reportCount) reported by ${ChatColor.AQUA}$senderName") + it.sendMessage("${ChatColor.BLUE}${ChatColor.BOLD} Reason: ${ChatColor.GRAY}$reason") + } + } + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/AltsCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/AltsCommand.kt new file mode 100644 index 0000000..c291c2f --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/AltsCommand.kt @@ -0,0 +1,29 @@ +package net.evilblock.stark.modsuite.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.profile.BukkitProfile +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender + +object AltsCommand { + + @Command(["alts"], permission = "stark.alts", description = "Find a player's alts", async = true) + @JvmStatic + fun execute(sender: CommandSender, @Param("player") target: BukkitProfile) { + val alts = Stark.instance.core.getProfileHandler().findAlts(target) + + sender.sendMessage("${ChatColor.RED}Found ${ChatColor.YELLOW}${alts.size} ${ChatColor.RED}alt${if (alts.size == 1) "" else "s"} for ${ChatColor.YELLOW}${target.getPlayerListName()}") + + if (alts.isNotEmpty()) { + val names = alts.map { uuid -> + val availability = Stark.instance.core.availabilityHandler.fetch(uuid) + (if (availability.isOnline()) ChatColor.GREEN else ChatColor.GRAY).toString() + Stark.instance.core.uuidCache.name(uuid) + }.toList() + + sender.sendMessage(names.joinToString("${ChatColor.GRAY}, ")) + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/ReportCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/ReportCommand.kt new file mode 100644 index 0000000..0be18af --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/ReportCommand.kt @@ -0,0 +1,60 @@ +package net.evilblock.stark.modsuite.command + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.modsuite.ModRequestHandler +import net.evilblock.stark.modsuite.event.PlayerRequestReportEvent +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.entity.Player +import java.util.concurrent.TimeUnit + +object ReportCommand { + @Command(["report"], description = "Report a player for violating the rules", async = true) + @JvmStatic + fun execute(player: Player, @Param("player") target: Player, @Param("reason", wildcard = true) reason: String) { + if (player == target) { + player.sendMessage("${ChatColor.RED}You can't report yourself...") + return + } + + if (target.hasPermission("stark.staff")) { + player.sendMessage("${ChatColor.RED}You can't report that player.") + return + } + + if (ModRequestHandler.hasRequestCooldown(player)) { + player.sendMessage("${ChatColor.RED}You can only make one staff request every 2 minutes.") + return + } + + val event = PlayerRequestReportEvent(player) + Bukkit.getServer().pluginManager.callEvent(event) + + if (event.isCancelled) { + if (event.cancelMessage != null) { + player.sendMessage(event.cancelMessage) + } else { + player.sendMessage("${ChatColor.RED}Unknown cancellation.") + } + } else { + ModRequestHandler.addRequestCooldown(player, 2L, TimeUnit.SECONDS) + ModRequestHandler.incrementReportCount(target) + + val data = mapOf( + "serverName" to Bukkit.getServerId(), + "senderName" to player.name, + "reportedUuid" to target.uniqueId.toString(), + "reportedName" to target.name, + "reason" to reason + ) + + Stark.instance.core.globalMessageChannel.sendMessage(Message("REPORT", data)) + + player.sendMessage("${ChatColor.GREEN}We have received your report.") + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/RequestCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/RequestCommand.kt new file mode 100644 index 0000000..61689d9 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/RequestCommand.kt @@ -0,0 +1,46 @@ +package net.evilblock.stark.modsuite.command + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.modsuite.ModRequestHandler +import net.evilblock.stark.modsuite.event.PlayerRequestReportEvent +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.entity.Player +import java.util.concurrent.TimeUnit + +object RequestCommand { + @Command(["request", "helpop"], description = "Request assistance", async = true) + @JvmStatic + fun execute(player: Player, @Param("message", wildcard = true) reason: String) { + if (ModRequestHandler.hasRequestCooldown(player)) { + player.sendMessage("${ChatColor.RED}You can only make one staff request every 2 minutes.") + return + } + + val event = PlayerRequestReportEvent(player) + Bukkit.getServer().pluginManager.callEvent(event) + + if (event.isCancelled) { + if (event.cancelMessage != null) { + player.sendMessage(event.cancelMessage) + } else { + player.sendMessage("${ChatColor.RED}Unknown cancellation.") + } + } else { + ModRequestHandler.addRequestCooldown(player, 2L, TimeUnit.MINUTES) + + val data = mapOf( + "serverName" to Bukkit.getServerId(), + "senderName" to player.name, + "reason" to reason + ) + + Stark.instance.core.globalMessageChannel.sendMessage(Message("REQUEST", data)) + + player.sendMessage("${ChatColor.GREEN}We have received your request.") + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/StaffChatCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/StaffChatCommand.kt new file mode 100644 index 0000000..debd5a5 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/StaffChatCommand.kt @@ -0,0 +1,31 @@ +package net.evilblock.stark.modsuite.command + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.modsuite.options.ModOptionsHandler +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object StaffChatCommand { + @Command(["staffchat", "sc"], permission = "stark.staff", description = "Send a message through staff chat.", async = true) + @JvmStatic + fun execute(player: Player, @Param(name = "message", wildcard = true) message: String) { + val options = ModOptionsHandler.get(player) + + if (!options.receivingStaffChat) { + player.sendMessage("${ChatColor.RED}You have staff chat disabled.") + return + } + + val data = mapOf( + "serverName" to Bukkit.getServerId(), + "senderName" to player.name, + "message" to message + ) + + Stark.instance.core.globalMessageChannel.sendMessage(Message("STAFF_CHAT", data)) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/ToggleRequestsCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/ToggleRequestsCommand.kt new file mode 100644 index 0000000..0d2100f --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/ToggleRequestsCommand.kt @@ -0,0 +1,23 @@ +package net.evilblock.stark.modsuite.command + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.modsuite.options.ModOptionsHandler +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object ToggleRequestsCommand { + @Command(["tsr", "togglereports", "togglerequests"], permission = "stark.staff", description = "Toggle receiving staff requests", async = true) + @JvmStatic + fun execute(player: Player) { + val options = ModOptionsHandler.get(player) + options.receivingRequests = !options.receivingRequests + + ModOptionsHandler.update(player.uniqueId, options) + + if (options.receivingRequests) { + player.sendMessage("${ChatColor.GREEN}You're now receiving requests and reports.") + } else { + player.sendMessage("${ChatColor.RED}You're no longer receiving requests and reports.") + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/ToggleStaffChatCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/ToggleStaffChatCommand.kt new file mode 100644 index 0000000..0528a50 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/command/ToggleStaffChatCommand.kt @@ -0,0 +1,23 @@ +package net.evilblock.stark.modsuite.command + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.modsuite.options.ModOptionsHandler +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object ToggleStaffChatCommand { + @Command(["tsc", "togglestaffchat"], permission = "stark.staff", description = "Toggle receiving staff chat", async = true) + @JvmStatic + fun toggleStaffChat(player: Player) { + val options = ModOptionsHandler.get(player) + options.receivingStaffChat = !options.receivingStaffChat + + ModOptionsHandler.update(player.uniqueId, options) + + if (options.receivingStaffChat) { + player.sendMessage("${ChatColor.GREEN}You're now receiving staff chat.") + } else { + player.sendMessage("${ChatColor.RED}You're no longer receiving staff chat.") + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/event/PlayerRequestReportEvent.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/event/PlayerRequestReportEvent.kt new file mode 100644 index 0000000..f37df0c --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/event/PlayerRequestReportEvent.kt @@ -0,0 +1,34 @@ +package net.evilblock.stark.modsuite.event + +import org.bukkit.entity.Player +import org.bukkit.event.Cancellable +import org.bukkit.event.HandlerList +import org.bukkit.event.player.PlayerEvent + +class PlayerRequestReportEvent(player: Player) : PlayerEvent(player), Cancellable { + + var cancel: Boolean = false + var cancelMessage: String? = null + + override fun setCancelled(cancel: Boolean) { + this.cancel = cancel + } + + override fun isCancelled(): Boolean { + return cancel + } + + override fun getHandlers(): HandlerList? { + return handlerList + } + + companion object { + private val handlerList = HandlerList() + + @JvmStatic + fun getHandlerList(): HandlerList { + return handlerList + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/options/ModOptions.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/options/ModOptions.kt new file mode 100644 index 0000000..56871a5 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/options/ModOptions.kt @@ -0,0 +1,3 @@ +package net.evilblock.stark.modsuite.options + +data class ModOptions(var receivingStaffChat: Boolean, var receivingRequests: Boolean, var silentMode: Boolean) \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/options/ModOptionsHandler.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/options/ModOptionsHandler.kt new file mode 100644 index 0000000..584f654 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/options/ModOptionsHandler.kt @@ -0,0 +1,42 @@ +package net.evilblock.stark.modsuite.options + +import net.evilblock.stark.Stark +import org.bukkit.entity.Player +import redis.clients.jedis.Jedis +import java.util.* +import kotlin.collections.HashMap + +object ModOptionsHandler { + + val cache = HashMap() + + fun get(player: Player): ModOptions { + return cache[player.uniqueId]?: ModOptions(receivingStaffChat = true, receivingRequests = true, silentMode = false) + } + + fun fetch(uuid: UUID): ModOptions { + return Stark.instance.core.redis.runBackboneRedisCommand { redis -> + if (redis.exists("stark:modOptions:player.$uuid")) { + val data = redis.hgetAll("stark:modOptions:player.$uuid") + ModOptions(data["receivingStaffChat"]!!.toBoolean(), data["receivingRequests"]!!.toBoolean(), data["silentMode"]!!.toBoolean()) + } + + ModOptions(receivingStaffChat = true, receivingRequests = true, silentMode = false) + } + } + + fun update(uuid: UUID, options: ModOptions) { + cache[uuid] = options + + val data = mapOf( + "receivingStaffChat" to options.receivingStaffChat.toString(), + "receivingRequests" to options.receivingRequests.toString(), + "silentMode" to options.silentMode.toString() + ) + + Stark.instance.core.redis.runBackboneRedisCommand { redis -> + redis.hmset("stark:modOptions:player.$uuid", data) + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/options/ModOptionsListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/options/ModOptionsListeners.kt new file mode 100644 index 0000000..6cadd13 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/modsuite/options/ModOptionsListeners.kt @@ -0,0 +1,23 @@ +package net.evilblock.stark.modsuite.options + +import net.evilblock.stark.Stark +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerJoinEvent +import org.bukkit.event.player.PlayerQuitEvent + +class ModOptionsListeners : Listener { + + @EventHandler + fun onPlayerJoinEvent(event: PlayerJoinEvent) { + Stark.instance.server.scheduler.runTaskAsynchronously(Stark.instance) { + ModOptionsHandler.cache[event.player.uniqueId] = ModOptionsHandler.fetch(event.player.uniqueId) + } + } + + @EventHandler + fun onPlayerQuitEvent(event: PlayerQuitEvent) { + ModOptionsHandler.cache.remove(event.player.uniqueId) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/BukkitProfile.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/BukkitProfile.kt new file mode 100644 index 0000000..14a1f4c --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/BukkitProfile.kt @@ -0,0 +1,52 @@ +package net.evilblock.stark.profile + +import net.evilblock.stark.Stark +import net.evilblock.stark.core.profile.Profile +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.entity.Player +import org.bukkit.metadata.FixedMetadataValue +import org.bukkit.permissions.PermissionAttachment +import java.util.* + +class BukkitProfile(uuid: UUID): Profile(uuid) { + + var attachment: PermissionAttachment? = null + + fun getPlayer(): Player? { + return Bukkit.getPlayer(this.uuid) + } + + override fun apply() { + val player = this.getPlayer() ?: return + + if (attachment == null) { + attachment = player.addAttachment(Stark.instance) + } else { + val permissions = listOf(*attachment!!.permissions.keys.toTypedArray()) + + for (permission in permissions) { + attachment!!.unsetPermission(permission) + } + } + + for (permission in getCompoundedPermissions()) { + if (permission.startsWith("-")) { + attachment!!.setPermission(permission.substring(1), false) + } else { + attachment!!.setPermission(permission, true) + } + } + + var playerListName = getPlayerListName() + + if (playerListName.length > 16) { + playerListName = playerListName.substring(0, 15) + } + + player.displayName = ChatColor.translateAlternateColorCodes('&', getRank().gameColor) + Stark.instance.core.uuidCache.name(uuid) +// player.playerListName = playerListName + player.setMetadata("RankPrefix", FixedMetadataValue(Stark.instance, ChatColor.translateAlternateColorCodes('&', getRank().prefix))) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/ProfileCommands.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/ProfileCommands.kt new file mode 100644 index 0000000..24e2b38 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/ProfileCommands.kt @@ -0,0 +1,27 @@ +package net.evilblock.stark.profile + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object ProfileCommands { + + @Command(names = ["geoinfo"], permission = "op", async = true) + @JvmStatic + fun execute(player: Player, @Param(name = "player") target: BukkitProfile) { + if (target.geoInfo == null) { + player.sendMessage("${ChatColor.RED}No GeoInfo stored for ${target.getPlayerListName()}${ChatColor.RED}.") + } else { + player.sendMessage("") + player.sendMessage("${ChatColor.GOLD}Geo Information of ${Stark.instance.core.uuidCache.name(target.uuid)}") + player.sendMessage("${ChatColor.GOLD}City: ${ChatColor.WHITE}${target.geoInfo!!.city}") + player.sendMessage("${ChatColor.GOLD}Region: ${ChatColor.WHITE}${target.geoInfo!!.region}") + player.sendMessage("${ChatColor.GOLD}Country: ${ChatColor.WHITE}${target.geoInfo!!.country}") + player.sendMessage("${ChatColor.GOLD}Postal: ${ChatColor.WHITE}${target.geoInfo!!.postal}") + player.sendMessage("") + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/ProfileListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/ProfileListeners.kt new file mode 100644 index 0000000..149073e --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/ProfileListeners.kt @@ -0,0 +1,126 @@ +package net.evilblock.stark.profile + +import net.evilblock.stark.Stark +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentType +import net.minecraft.server.v1_7_R4.Block.p +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.event.EventHandler +import org.bukkit.event.EventPriority +import org.bukkit.event.Listener +import org.bukkit.event.player.AsyncPlayerChatEvent +import org.bukkit.event.player.AsyncPlayerPreLoginEvent +import org.bukkit.event.player.PlayerJoinEvent +import org.bukkit.event.player.PlayerQuitEvent +import java.lang.Exception + +class ProfileListeners : Listener { + + @EventHandler + fun onAsyncPlayerPreLoginEvent(event: AsyncPlayerPreLoginEvent) { + if (System.currentTimeMillis() - 1000 < Stark.instance.enabledAt) { + event.loginResult = AsyncPlayerPreLoginEvent.Result.KICK_OTHER + event.kickMessage = "${ChatColor.RED}This server is still loading..." + 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) + val player = Bukkit.getPlayer(event.uniqueId) + if (player != null && player.isOnline) { + event.loginResult = AsyncPlayerPreLoginEvent.Result.KICK_OTHER + event.kickMessage = "${ChatColor.RED}You tried to login too quickly after disconnecting.\nTry again in a few seconds." + + Stark.instance.server.scheduler.runTask(Stark.instance) { + player.kickPlayer("${ChatColor.RED}Duplicate login kick") + } + + return + } + + // Search for an active ban + // - by uuid + // - by current ip address + try { + val punishment = Stark.instance.core.getProfileHandler().findActivePunishment(event.uniqueId, event.address.hostAddress) + + if (punishment != null) { + var kickMessage = if (punishment.second.type == ProfilePunishmentType.BAN) { + "${ChatColor.RED}Your account has been banned from the Kihar Network.\nCreate a support ticket on our site to appeal." + } else { + "${ChatColor.RED}Your account has been blacklisted from the Kihar Network.\nThis type of appeal can't be appealed." + } + + if (event.uniqueId != punishment.first) { + kickMessage += "\nThis punishment is in relation to ${ChatColor.YELLOW}${Stark.instance.core.uuidCache.name(punishment.first)}${ChatColor.RED}." + } + + event.loginResult = AsyncPlayerPreLoginEvent.Result.KICK_BANNED + event.kickMessage = kickMessage + } + } catch (e: Exception) { + Stark.instance.logger.info("Failed to search for active punishment for " + event.name + ":") + e.printStackTrace() + return + } + + try { + val profile = Stark.instance.core.getProfileHandler().loadProfile(event.uniqueId, event.address.hostAddress) + Stark.instance.core.getProfileHandler().profiles[profile.uuid] = profile + } catch (e: Exception) { + Stark.instance.logger.info("Failed to load " + event.name + "'s target:") + e.printStackTrace() + event.loginResult = AsyncPlayerPreLoginEvent.Result.KICK_OTHER + event.kickMessage = ChatColor.RED.toString() + "Failed to load your target.\nTry reconnecting later!" + } + } + + @EventHandler(priority = EventPriority.LOWEST) + fun onAsyncPlayerChatEventLow(event: AsyncPlayerChatEvent) { + val player = event.player + val profile = Stark.instance.core.getProfileHandler().getByUUID(player.uniqueId)!! + + if (!event.isCancelled && profile.getActivePunishment(ProfilePunishmentType.MUTE) != null) { + player.sendMessage("${ChatColor.RED}You're currently muted.") + event.isCancelled = true + return + } + + val rank = profile.getRank() + val prefix = rank.prefix + + Stark.instance.logger.info(rank.displayName + " - " + prefix) // compile + + event.isCancelled = true + for (p in event.recipients) { + p.sendMessage(ChatColor.translateAlternateColorCodes('&', prefix) + player.name + ChatColor.RESET + ": " + event.message) + }//compile + // event.format = ChatColor.translateAlternateColorCodes('&', "$prefix%s${ChatColor.RESET}: %s") + } + + @EventHandler(priority = EventPriority.LOWEST) + fun onPlayerJoinEvent(event: PlayerJoinEvent) { + event.joinMessage = null + + val player = event.player + val profile = Stark.instance.core.getProfileHandler().getByUUID(player.uniqueId) + + profile!!.apply() + } + + @EventHandler + fun onPlayerQuitEvent(event: PlayerQuitEvent) { + event.quitMessage = null + + val profile = Stark.instance.core.getProfileHandler().getByUUID(event.player.uniqueId) + + if (profile != null) { + if (profile.attachment != null) { + event.player.removeAttachment(profile.attachment) + } + } + + Stark.instance.core.getProfileHandler().profiles.remove(event.player.uniqueId) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/ProfileMessageListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/ProfileMessageListeners.kt new file mode 100644 index 0000000..1c3add4 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/ProfileMessageListeners.kt @@ -0,0 +1,143 @@ +package net.evilblock.stark.profile + +import nig.cock.nigger.message.handler.IncomingMessageHandler +import nig.cock.nigger.message.listener.MessageListener +import com.google.gson.JsonObject +import net.evilblock.stark.Stark +import net.evilblock.stark.profile.grant.event.GrantRemovedEvent +import net.evilblock.stark.core.profile.punishment.ProfilePunishment +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentType +import net.evilblock.stark.core.util.TimeUtils +import mkremins.fanciful.FancyMessage +import net.evilblock.stark.profile.grant.event.GrantCreatedEvent +import org.apache.commons.lang.StringUtils +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import java.util.* +import java.util.concurrent.TimeUnit + +class ProfileMessageListeners : MessageListener { + + @IncomingMessageHandler("GRANT_UPDATE") + fun onGrantUpdateMessage(json: JsonObject) { + val uuid = UUID.fromString(json.get("uuid").asString) + val grantId = UUID.fromString(json.get("grant").asString) + val player = Bukkit.getPlayer(uuid) + + if (player != null) { + val profile = Stark.instance.core.getProfileHandler().pullProfileUpdates(uuid) + + for (grant in profile.rankGrants) { + if (grant.uuid == grantId) { + if (grant.removedAt != null) { + Stark.instance.server.pluginManager.callEvent(GrantRemovedEvent(player, grant)) + } else { + Stark.instance.server.pluginManager.callEvent(GrantCreatedEvent(player, grant)) + } + } + } + } + } + + @IncomingMessageHandler("PUNISHMENT_UPDATE") + fun onPunishmentUpdateMessage(json: JsonObject) { + val uuid = UUID.fromString(json.get("uuid").asString) + val punishmentId = UUID.fromString(json.get("punishment").asString) + val profile = Stark.instance.core.getProfileHandler().pullProfileUpdates(uuid) + + for (punishment in profile.punishments) { + if (punishment.uuid == punishmentId) { + executePunishment(profile, punishment, json.get("silent").asBoolean) + } + } + } + + private fun executePunishment(recipient: BukkitProfile, punishment: ProfilePunishment, silent: Boolean) { + val recipientName = recipient.getPlayerListName() + + val issuerName: String = if (punishment.removedAt != null) { + if (punishment.removedBy == null) { + ChatColor.DARK_RED.toString() + "Console" + } else { + Stark.instance.core.getProfileHandler().loadProfile(punishment.removedBy!!).getPlayerListName() + } + } else { + if (punishment.issuedBy == null) { + ChatColor.DARK_RED.toString() + "Console" + } else { + Stark.instance.core.getProfileHandler().loadProfile(punishment.issuedBy!!).getPlayerListName() + } + } + + val silently = if (silent) ChatColor.YELLOW.toString() + "silently " else "" + val player = recipient.getPlayer() + + if (punishment.removalReason == null) { + val context = if (punishment.type == ProfilePunishmentType.BLACKLIST) "" else if (punishment.expiresAt == null) "permanently " else "temporarily " + + val staffMessage = FancyMessage("$recipientName ${ChatColor.GREEN}was $silently${ChatColor.GREEN}$context${punishment.type.action} by $issuerName${ChatColor.GREEN}.") + .formattedTooltip(Arrays.asList( + FancyMessage("Issued by: ") + .color(ChatColor.YELLOW) + .then(issuerName), + FancyMessage("Reason: ") + .color(ChatColor.YELLOW) + .then(punishment.reason) + .color(ChatColor.RED), + FancyMessage("Duration: ") + .color(ChatColor.YELLOW) + .then(if (punishment.expiresAt == null) "Permanent" else TimeUtils.formatIntoDetailedString(TimeUnit.MILLISECONDS.toSeconds(punishment.expiresAt!! - System.currentTimeMillis()).toInt())) + .color(ChatColor.RED) + )) + + for (onlinePlayer in Bukkit.getOnlinePlayers()) { + if (onlinePlayer.hasPermission("stark.punishment." + punishment.type.name + ".view")) { + staffMessage.send(onlinePlayer) + } else if (!silent) { + onlinePlayer.sendMessage("$recipientName ${ChatColor.GREEN}was ${punishment.type.action} by $issuerName${ChatColor.GREEN}.") + } + } + + // kick players + if (punishment.type !== ProfilePunishmentType.MUTE) { + // kick player if they're online + if (player != null) { + Stark.instance.server.scheduler.runTask(Stark.instance) { + player.kickPlayer(ChatColor.translateAlternateColorCodes('&', StringUtils.join(punishment.type.kickMessages, "\n"))) + } + } + + // kick players sharing ip addresses + for (it in Stark.instance.server.onlinePlayers) { + if (it.uniqueId != recipient.uuid) { + if (recipient.ipAddresses.contains(it.address.address.hostAddress)) { + Stark.instance.server.scheduler.runTask(Stark.instance) { + it.kickPlayer(ChatColor.translateAlternateColorCodes('&', StringUtils.join(punishment.type.kickMessages, "\n"))) + } + } + } + } + } + } else { + val staffMessage = FancyMessage("$recipientName ${ChatColor.GREEN}was $silently${ChatColor.GREEN}un${punishment.type.action} by $issuerName${ChatColor.GREEN}.") + .formattedTooltip(Arrays.asList( + FancyMessage("Removed by: ") + .color(ChatColor.YELLOW) + .then(issuerName), + FancyMessage("Reason: ") + .color(ChatColor.YELLOW) + .then(punishment.removalReason) + .color(ChatColor.RED) + )) + + for (onlinePlayer in Bukkit.getOnlinePlayers()) { + if (onlinePlayer.hasPermission("stark.punishment." + punishment.type.name + ".view")) { + staffMessage.send(onlinePlayer) + } else if (!silent) { + onlinePlayer.sendMessage("$recipientName ${ChatColor.GREEN}was un${punishment.type.action} by $issuerName${ChatColor.GREEN}.") + } + } + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/ProfileParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/ProfileParameterType.kt new file mode 100644 index 0000000..7469103 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/ProfileParameterType.kt @@ -0,0 +1,37 @@ +package net.evilblock.stark.profile + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import java.util.ArrayList + +class ProfileParameterType : ParameterType { + + override fun transform(sender: CommandSender, source: String): BukkitProfile? { + val profile = Stark.instance.core.getProfileHandler().fetchProfileByUsername(source) + + if (profile == null) { + sender.sendMessage(ChatColor.RED.toString() + "A player by the name " + source + " couldn't be found.") + return null + } + + return profile + } + + override fun tabComplete(sender: Player, flags: Set, source: String): List { + val completions = ArrayList() + + Bukkit.getOnlinePlayers().forEach { player -> + // TODO: add support for visibility engine + if (sender.canSee(player)) { + completions.add(player.name) + } + } + + return completions + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/ProfileGrantListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/ProfileGrantListeners.kt new file mode 100644 index 0000000..921d632 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/ProfileGrantListeners.kt @@ -0,0 +1,24 @@ +package net.evilblock.stark.profile.grant + +import net.evilblock.stark.Stark +import net.evilblock.stark.profile.grant.event.GrantCreatedEvent +import net.evilblock.stark.profile.grant.event.GrantRemovedEvent +import net.evilblock.stark.core.util.TimeUtils +import org.bukkit.ChatColor +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener + +class ProfileGrantListeners : Listener { + + @EventHandler + fun onGrantCreatedEvent(event: GrantCreatedEvent) { + event.player.sendMessage("${ChatColor.GREEN}You've been granted the ${ChatColor.translateAlternateColorCodes('&', event.grant.rank.gameColor + event.grant.rank.displayName)} ${ChatColor.GREEN}rank for a period of ${ChatColor.YELLOW}${if (event.grant.expiresAt == null) "forever" else TimeUtils.formatIntoDetailedString(((event.grant.expiresAt!! - System.currentTimeMillis()) / 1000).toInt())}${ChatColor.GREEN}.") + Stark.instance.core.getProfileHandler().getByUUID(event.player.uniqueId)?.apply() + } + + @EventHandler + fun onGrantRemovedEvent(event: GrantRemovedEvent) { + Stark.instance.core.getProfileHandler().getByUUID(event.player.uniqueId)?.apply() + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/command/GrantCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/command/GrantCommand.kt new file mode 100644 index 0000000..609f771 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/command/GrantCommand.kt @@ -0,0 +1,81 @@ +package net.evilblock.stark.profile.grant.command + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.profile.grant.ProfileGrant +import net.evilblock.stark.profile.grant.menu.GrantMenu +import net.evilblock.stark.core.rank.Rank +import net.evilblock.stark.core.util.TimeUtils +import net.evilblock.stark.util.DateUtil +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import java.lang.Exception +import java.util.* + +object GrantCommand { + @Command(["grant"], "stark.grant", description = "Grant a player a rank", async = true) + @JvmStatic fun execute(player: Player, @Param("target") target: BukkitProfile) { + GrantMenu(target).openMenu(player) + } + + @Command(["ogrant"], "op", description = "Grant a player a rank", async = true) + @JvmStatic fun execute(sender: CommandSender, @Param("target") target: BukkitProfile, @Param("rank") rank: Rank, @Param("duration") duration: String, @Param("reason", wildcard = true) reason: String) { + if (sender is Player) { + sender.sendMessage("${ChatColor.RED}This command can only be executed by console.") + return + } + + var perm = false + var expiresAt = 0L + var issuer: UUID? = null + + if (!(sender.hasPermission("stark.grant." + rank.id))) { + sender.sendMessage("${ChatColor.RED}You don't have permission to do this.") + return + } + + if (sender is Player) { + issuer = sender.uniqueId + } + + if (duration.toLowerCase() == "perm") { + perm = true + } + + if (!(perm)) { + try { + expiresAt = System.currentTimeMillis() - DateUtil.parseDateDiff(duration, false) + } catch (exception: Exception) { + sender.sendMessage("${ChatColor.RED}Invalid duration.") + return + } + } + + val grant = ProfileGrant() + grant.rank = rank + grant.reason = reason + grant.issuedBy = issuer + grant.issuedAt = System.currentTimeMillis() + + if (!perm) { + grant.expiresAt = expiresAt + System.currentTimeMillis() + } + + target.rankGrants.add(grant) + + Stark.instance.core.getProfileHandler().saveProfile(target) + Stark.instance.core.globalMessageChannel.sendMessage(Message("GRANT_UPDATE", mapOf("uuid" to target.uuid.toString(), "grant" to grant.uuid.toString()))) + + val period = if (grant.expiresAt == null) { + "forever" + } else { + TimeUtils.formatIntoDetailedString(((grant.expiresAt!! - System.currentTimeMillis()) / 1000).toInt()) + } + + sender.sendMessage("${ChatColor.GREEN}You've granted " + target.getPlayerListName() + " ${ChatColor.GREEN}the ${grant.rank.getColoredName()} ${ChatColor.GREEN}rank for ${ChatColor.YELLOW}$period${ChatColor.GREEN}.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/command/GrantsCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/command/GrantsCommand.kt new file mode 100644 index 0000000..ff4cfca --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/command/GrantsCommand.kt @@ -0,0 +1,15 @@ +package net.evilblock.stark.profile.grant.command + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.profile.grant.menu.GrantsMenu +import org.bukkit.entity.Player + +object GrantsCommand { + @Command(["grants"], description = "View a player's grants", permission = "stark.grants", async = true) + @JvmStatic + fun execute(player: Player, @Param("target") target: BukkitProfile) { + GrantsMenu(target).openMenu(player) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/command/RankCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/command/RankCommand.kt new file mode 100644 index 0000000..2d7eebe --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/command/RankCommand.kt @@ -0,0 +1,20 @@ +package net.evilblock.stark.profile.grant.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.core.util.TimeUtils +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object RankCommand { + @Command(["rank"], description = "Check your current rank and its expiration") + @JvmStatic + fun execute(player: Player) { + val profile = Stark.instance.core.getProfileHandler().getByUUID(player.uniqueId) + if (profile != null) { + val rank = profile.getRank() + val grant = profile.rankGrants.filter { it.rank == rank }.toList() + player.sendMessage("${ChatColor.GOLD}You're the ${ChatColor.translateAlternateColorCodes('&', rank.gameColor)}${rank.displayName} ${ChatColor.GOLD}rank.${if (grant.isEmpty() || grant[0].expiresAt == null) "" else "${ChatColor.GRAY}(Expires in ${TimeUtils.formatIntoDetailedString(((grant[0].expiresAt!! - System.currentTimeMillis()) / 1000).toInt())})"}") + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/conversation/GrantCreationPeriodPrompt.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/conversation/GrantCreationPeriodPrompt.kt new file mode 100644 index 0000000..de23d53 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/conversation/GrantCreationPeriodPrompt.kt @@ -0,0 +1,66 @@ +package net.evilblock.stark.profile.grant.conversation + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.profile.grant.ProfileGrant +import net.evilblock.stark.core.rank.Rank +import net.evilblock.stark.core.util.TimeUtils +import net.evilblock.stark.util.DateUtil +import org.bukkit.ChatColor +import org.bukkit.conversations.ConversationContext +import org.bukkit.conversations.Prompt +import org.bukkit.conversations.StringPrompt +import org.bukkit.entity.Player +import java.lang.Exception + +class GrantCreationPeriodPrompt(val target: BukkitProfile) : StringPrompt() { + + override fun getPromptText(conversationContext: ConversationContext): String { + return ChatColor.GREEN.toString() + "Please specify a valid time." + } + + override fun acceptInput(conversationContext: ConversationContext, input: String): Prompt? { + var perm = false + var expiresAt = 0L + + if (input.toLowerCase() == "perm") { + perm = true + } + + if (!(perm)) { + try { + expiresAt = System.currentTimeMillis() - DateUtil.parseDateDiff(input, false) + } catch (exception: Exception) { + conversationContext.forWhom.sendRawMessage("${ChatColor.RED}Invalid duration.") + return Prompt.END_OF_CONVERSATION + } + } + + val grant = ProfileGrant() + grant.rank = conversationContext.getSessionData("rank") as Rank + grant.reason = conversationContext.getSessionData("reason") as String + grant.issuedBy = (conversationContext.forWhom as Player).uniqueId + grant.issuedAt = System.currentTimeMillis() + + if (!perm) { + grant.expiresAt = expiresAt + System.currentTimeMillis() + } + + target.rankGrants.add(grant) + + Stark.instance.core.getProfileHandler().saveProfile(target) + Stark.instance.core.globalMessageChannel.sendMessage(Message("GRANT_UPDATE", mapOf("uuid" to target.uuid.toString(), "grant" to grant.uuid.toString()))) + + val period = if (grant.expiresAt == null) { + "forever" + } else { + TimeUtils.formatIntoDetailedString(((grant.expiresAt!! - System.currentTimeMillis()) / 1000).toInt()) + } + + conversationContext.forWhom.sendRawMessage("${ChatColor.GREEN}You've granted " + target.getPlayerListName() + " ${ChatColor.GREEN}the ${grant.rank.getColoredName()} ${ChatColor.GREEN}rank for ${ChatColor.YELLOW}$period${ChatColor.GREEN}.") + + return Prompt.END_OF_CONVERSATION + } + +} diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/conversation/GrantCreationReasonPrompt.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/conversation/GrantCreationReasonPrompt.kt new file mode 100644 index 0000000..bf843ab --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/conversation/GrantCreationReasonPrompt.kt @@ -0,0 +1,22 @@ +package net.evilblock.stark.profile.grant.conversation + +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.rank.Rank +import org.bukkit.ChatColor +import org.bukkit.conversations.ConversationContext +import org.bukkit.conversations.Prompt +import org.bukkit.conversations.StringPrompt + +class GrantCreationReasonPrompt(val target: BukkitProfile, val rank: Rank) : StringPrompt() { + + override fun getPromptText(conversationContext: ConversationContext): String { + return ChatColor.GREEN.toString() + "Please specify a valid reason." + } + + override fun acceptInput(conversationContext: ConversationContext, s: String): Prompt? { + conversationContext.setSessionData("rank", rank) + conversationContext.setSessionData("reason", s) + return GrantCreationPeriodPrompt(target) + } + +} diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/conversation/GrantRemovalPrompt.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/conversation/GrantRemovalPrompt.kt new file mode 100644 index 0000000..33061a4 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/conversation/GrantRemovalPrompt.kt @@ -0,0 +1,34 @@ +package net.evilblock.stark.profile.grant.conversation + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.profile.grant.ProfileGrant +import org.bukkit.ChatColor +import org.bukkit.conversations.ConversationContext +import org.bukkit.conversations.Prompt +import org.bukkit.conversations.StringPrompt +import org.bukkit.entity.Player + +class GrantRemovalPrompt(val profile: BukkitProfile, val grant: ProfileGrant) : StringPrompt() { + + override fun getPromptText(conversationContext: ConversationContext): String { + return ChatColor.GREEN.toString() + "Please specify a valid reason." + } + + override fun acceptInput(conversationContext: ConversationContext, s: String): Prompt? { + grant.removedBy = (conversationContext.forWhom as Player).uniqueId + grant.removedAt = System.currentTimeMillis() + grant.removalReason = s + + Stark.instance.server.scheduler.runTaskAsynchronously(Stark.instance) { + Stark.instance.core.getProfileHandler().saveProfile(profile) + Stark.instance.core.globalMessageChannel.sendMessage(Message("GRANT_UPDATE", mapOf("uuid" to profile.uuid.toString(), "grant" to grant.uuid.toString()))) + } + + conversationContext.forWhom.sendRawMessage("${ChatColor.GOLD}Grant removed.") + + return Prompt.END_OF_CONVERSATION + } + +} diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/event/GrantCreatedEvent.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/event/GrantCreatedEvent.kt new file mode 100644 index 0000000..165befc --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/event/GrantCreatedEvent.kt @@ -0,0 +1,18 @@ +package net.evilblock.stark.profile.grant.event + +import net.evilblock.stark.core.profile.grant.ProfileGrant +import org.bukkit.entity.Player +import org.bukkit.event.Event +import org.bukkit.event.HandlerList + +class GrantCreatedEvent(val player: Player, val grant: ProfileGrant) : Event() { + + companion object { + @JvmStatic val handlerList = HandlerList() + } + + override fun getHandlers(): HandlerList { + return handlerList + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/event/GrantRemovedEvent.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/event/GrantRemovedEvent.kt new file mode 100644 index 0000000..1f40e5e --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/event/GrantRemovedEvent.kt @@ -0,0 +1,18 @@ +package net.evilblock.stark.profile.grant.event + +import net.evilblock.stark.core.profile.grant.ProfileGrant +import org.bukkit.entity.Player +import org.bukkit.event.Event +import org.bukkit.event.HandlerList + +class GrantRemovedEvent(val player: Player, val grant: ProfileGrant) : Event() { + + companion object { + @JvmStatic val handlerList = HandlerList() + } + + override fun getHandlers(): HandlerList { + return handlerList + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/menu/GrantMenu.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/menu/GrantMenu.kt new file mode 100644 index 0000000..a0bafd7 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/menu/GrantMenu.kt @@ -0,0 +1,25 @@ +package net.evilblock.stark.profile.grant.menu + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.menu.Button +import net.evilblock.stark.engine.menu.Menu +import net.evilblock.stark.profile.BukkitProfile +import org.bukkit.entity.Player + +class GrantMenu(val target: BukkitProfile) : Menu("Grant ${target.getPlayerListName()}") { + + override fun getButtons(player: Player): Map { + val buttons = hashMapOf() + + for (rank in Stark.instance.core.rankHandler.getRanks().sortedBy { it.displayOrder }) { + if (rank.default) continue + + if (player.hasPermission("stark.grant") && player.hasPermission("stark.grant." + rank.id)) { + buttons[buttons.size] = GrantRankButton(rank, target) + } + } + + return buttons + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/menu/GrantRankButton.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/menu/GrantRankButton.kt new file mode 100644 index 0000000..e68d888 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/menu/GrantRankButton.kt @@ -0,0 +1,55 @@ +package net.evilblock.stark.profile.grant.menu + +import net.evilblock.stark.Stark +import net.evilblock.stark.core.rank.Rank +import net.evilblock.stark.engine.menu.Button +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.profile.grant.conversation.GrantCreationReasonPrompt +import net.evilblock.stark.util.ColorMap +import org.bukkit.ChatColor +import org.bukkit.Material +import org.bukkit.conversations.ConversationFactory +import org.bukkit.entity.Player +import org.bukkit.event.inventory.ClickType +import java.lang.Exception + +class GrantRankButton(private val rank: Rank, private val target: BukkitProfile) : Button() { + + override fun getName(player: Player): String? { + var name = rank.gameColor + rank.displayName + + if (rank.prefix.isNotBlank()) { + name = name + " " + rank.prefix + } + + return name + } + + override fun getDescription(player: Player): List? { + return listOf("&7Click to grant &r${target.getPlayerListName()}&7 the ${rank.gameColor + rank.displayName}&7 rank.") + } + + override fun getMaterial(player: Player): Material? { + return Material.INK_SACK + } + + override fun getDamageValue(player: Player): Byte { + return try { + (ColorMap.dyeMap[ChatColor.getByChar(rank.gameColor.replace("&", ""))]?: 15).toByte() + } catch (e: Exception) { + return 15.toByte() + } + } + + override fun clicked(player: Player, slot: Int, clickType: ClickType) { + player.closeInventory() + + val factory = ConversationFactory(Stark.instance) + .withFirstPrompt(GrantCreationReasonPrompt(target, rank)) + .withLocalEcho(false) + .thatExcludesNonPlayersWithMessage("Go away evil console!") + + player.beginConversation(factory.buildConversation(player)) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/menu/GrantsMenu.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/menu/GrantsMenu.kt new file mode 100644 index 0000000..4e22193 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/grant/menu/GrantsMenu.kt @@ -0,0 +1,116 @@ +package net.evilblock.stark.profile.grant.menu + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.menu.Button +import net.evilblock.stark.engine.menu.pagination.PaginatedMenu +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.profile.grant.conversation.GrantRemovalPrompt +import net.evilblock.stark.util.ItemBuilder +import net.evilblock.stark.core.util.TimeUtils +import org.apache.commons.lang.StringUtils +import org.bukkit.ChatColor +import org.bukkit.Material +import org.bukkit.conversations.ConversationFactory +import org.bukkit.entity.Player +import org.bukkit.event.inventory.ClickType +import org.bukkit.inventory.ItemStack +import java.util.* +import kotlin.collections.HashMap + +class GrantsMenu(private val target: BukkitProfile) : PaginatedMenu() { + + override fun getPrePaginatedTitle(player: Player): String { + return "${ChatColor.RED}Grants - ${Stark.instance.core.uuidCache.name(target.uuid)}" + } + + override fun getAllPagesButtons(player: Player): Map { + val senderProfile = Stark.instance.core.getProfileHandler().getByUUID(player.uniqueId)!! + val buttons = HashMap() + val grants = target.rankGrants.sortedBy { -it.issuedAt } + for (grant in grants) { + val builder = ItemBuilder(Material.INK_SACK) + + if (grant.isActive()) { + builder.data(10) + } else { + builder.data(1) + } + + var issuerProfile: BukkitProfile? = null + var removerProfile: BukkitProfile? = null + + if (grant.issuedBy != null) { + issuerProfile = Stark.instance.core.getProfileHandler().loadProfile(grant.issuedBy!!) + } + + if (grant.removedBy != null) { + removerProfile = Stark.instance.core.getProfileHandler().loadProfile(grant.removedBy!!) + } + + builder.name(grant.rank.gameColor + StringUtils.capitalize(grant.rank.displayName.toLowerCase())) + builder.addToLore(BAR) + builder.addToLore("&eIssued By:&c " + if (grant.issuedBy == null) "Console" else issuerProfile?.getPlayerListName()) + builder.addToLore("&eIssued On:&c ${TimeUtils.formatIntoCalendarString(Date(grant.issuedAt))}") + builder.addToLore("&eReason:&c&o ${grant.reason}") + builder.addToLore("&eScopes:&c Global") + + if (grant.isActive()) { + if (grant.expiresAt == null) { + builder.addToLore("&eDuration:&c Permanent") + } else { + builder.addToLore("&eTime remaining:&c " + TimeUtils.formatIntoDetailedString(((grant.expiresAt!! - System.currentTimeMillis()) / 1_000L).toInt())) + } + + if ((senderProfile.getRank().id == "owner" || senderProfile.getRank().displayOrder < grant.rank.displayOrder) && player.hasPermission("stark.grant.delete")) { + builder.addToLore(BAR) + builder.addToLore("&eClick to remove this grant.") + } + } else if (grant.removedAt != null) { + builder.addToLore(BAR) + builder.addToLore("&eRemoved By:&c " + (if (grant.removedBy == null) "Console" else removerProfile?.getPlayerListName())) + builder.addToLore("&eRemoved On:&c ${TimeUtils.formatIntoCalendarString(Date(grant.removedAt!!))}") + builder.addToLore("&eReason:&c&o ${grant.removalReason}") + } + + builder.addToLore(BAR) + + buttons[buttons.size] = object: Button() { + override fun getName(player: Player): String? { + return null + } + + override fun getDescription(player: Player): List? { + return null + } + + override fun getMaterial(player: Player): Material? { + return null + } + + override fun getButtonItem(player: Player): ItemStack { + return builder.build() + } + + override fun clicked(player: Player, slot: Int, clickType: ClickType) { + if (grant.removedAt == null && (senderProfile.getRank().id == "owner" || senderProfile.getRank().displayOrder < grant.rank.displayOrder) && player.hasPermission("stark.grant.delete")) { + player.closeInventory() + + val factory = ConversationFactory(Stark.instance) + .withFirstPrompt(GrantRemovalPrompt(target, grant)) + .withLocalEcho(false) + .thatExcludesNonPlayersWithMessage("Go away evil console!") + + player.beginConversation(factory.buildConversation(player)) + } + } + } + } + + return buttons + } + + companion object { + private val BAR = "&7&m${StringUtils.repeat("-", 32)}" + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/create/BanCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/create/BanCommand.kt new file mode 100644 index 0000000..32e3c2b --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/create/BanCommand.kt @@ -0,0 +1,57 @@ +package net.evilblock.stark.profile.punishment.command.create + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.flag.Flag +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.profile.punishment.ProfilePunishment +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentType +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import java.util.* + +object BanCommand { + @Command(["ban"], "stark.punishment.BAN.create.permanent", description = "Ban a player", async = true) + @JvmStatic + fun execute(sender: CommandSender, @Flag(value = ["s", "silentMode"], description = "Silently ban the player") silent: Boolean, @Param("target") target: BukkitProfile, @Param("reason", wildcard = true) reason: String) { + var issuer: UUID? = null + if (sender is Player) { + val senderProfile = Stark.instance.core.getProfileHandler().getByUUID(sender.uniqueId) + if (senderProfile == null) { + sender.sendMessage("${ChatColor.RED}Couldn't retrieve your target.") + return + } + + if (target.getRank().displayOrder <= senderProfile.getRank().displayOrder) { + sender.sendMessage("${ChatColor.RED}You can't ban this player.") + return + } + + if (target.hasPermission("stark.punishment.protected")) { + sender.sendMessage("${ChatColor.RED}User is protected from punishments.") + return + } + + issuer = sender.uniqueId + } + + if (target.getActivePunishment(ProfilePunishmentType.BAN) != null) { + sender.sendMessage("${target.getPlayerListName()} ${ChatColor.RED}is already banned.") + return + } + + val punishment = ProfilePunishment() + punishment.type = ProfilePunishmentType.BAN + punishment.reason = reason + punishment.issuedBy = issuer + punishment.issuedAt = System.currentTimeMillis() + + target.punishments.add(punishment) + + Stark.instance.core.getProfileHandler().saveProfile(target) + Stark.instance.core.globalMessageChannel.sendMessage(Message("PUNISHMENT_UPDATE", mapOf("uuid" to target.uuid.toString(), "punishment" to punishment.uuid.toString(), "silent" to silent))) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/create/BlacklistCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/create/BlacklistCommand.kt new file mode 100644 index 0000000..9e2e83b --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/create/BlacklistCommand.kt @@ -0,0 +1,54 @@ +package net.evilblock.stark.profile.punishment.command.create + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.flag.Flag +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.profile.punishment.ProfilePunishment +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentType +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import java.util.* + +object BlacklistCommand { + + @Command(["blacklist"], permission = "stark.punishment.BLACKLIST.create", description = "Blacklist a player", async = true) + @JvmStatic + fun execute(sender: CommandSender, @Flag(value = ["s", "silentMode"], description = "Silently ban the player") silent: Boolean, @Param("target") target: BukkitProfile, @Param("reason", wildcard = true) reason: String) { + var issuer: UUID? = null + if (sender is Player) { + val senderProfile = Stark.instance.core.getProfileHandler().getByUUID(sender.uniqueId) + if (senderProfile == null) { + sender.sendMessage("${ChatColor.RED}Error verifying your target. Try again later.") + return + } + + if (target.getRank().displayOrder <= senderProfile.getRank().displayOrder) { + sender.sendMessage("${ChatColor.RED}You can't blacklist this player.") + return + } + + issuer = sender.uniqueId + } + + if (target.getActivePunishment(ProfilePunishmentType.BLACKLIST) != null) { + sender.sendMessage("${target.getPlayerListName()} ${ChatColor.RED}is already blacklisted.") + return + } + + val punishment = ProfilePunishment() + punishment.type = ProfilePunishmentType.BLACKLIST + punishment.reason = reason + punishment.issuedBy = issuer + punishment.issuedAt = System.currentTimeMillis() + + target.punishments.add(punishment) + + Stark.instance.core.getProfileHandler().saveProfile(target) + Stark.instance.core.globalMessageChannel.sendMessage(Message("PUNISHMENT_UPDATE", mapOf("uuid" to target.uuid.toString(), "punishment" to punishment.uuid.toString(), "silent" to silent))) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/create/MuteCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/create/MuteCommand.kt new file mode 100644 index 0000000..e994f63 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/create/MuteCommand.kt @@ -0,0 +1,58 @@ +package net.evilblock.stark.profile.punishment.command.create + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.flag.Flag +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.profile.punishment.ProfilePunishment +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentType +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import java.util.* + +object MuteCommand { + @Command(["mute"], permission = "stark.punishment.MUTE.create.permanent", description = "Mute a player", async = true) + @JvmStatic + fun execute(sender: CommandSender, @Flag(value = ["s", "silentMode"], description = "Silently mute the player") silent: Boolean, @Param("target") target: BukkitProfile, @Param("reason", wildcard = true) reason: String) { + var issuer: UUID? = null + if (sender is Player) { + val senderProfile = Stark.instance.core.getProfileHandler().getByUUID(sender.uniqueId) + + if (senderProfile == null) { + sender.sendMessage("${ChatColor.RED}Error verifying your target. Try again later.") + return + } + + if (target.getRank().displayOrder <= senderProfile.getRank().displayOrder) { + sender.sendMessage("${ChatColor.RED}You can't mute this player.") + return + } + + if (target.hasPermission("stark.punishment.protected")) { + sender.sendMessage("${ChatColor.RED}User is protected from punishments.") + return + } + + issuer = sender.uniqueId + } + + if (target.getActivePunishment(ProfilePunishmentType.MUTE) != null) { + sender.sendMessage("${target.getPlayerListName()} ${ChatColor.RED}is already muted.") + return + } + + val punishment = ProfilePunishment() + punishment.type = ProfilePunishmentType.MUTE + punishment.reason = reason + punishment.issuedBy = issuer + punishment.issuedAt = System.currentTimeMillis() + + target.punishments.add(punishment) + + Stark.instance.core.getProfileHandler().saveProfile(target) + Stark.instance.core.globalMessageChannel.sendMessage(Message("PUNISHMENT_UPDATE", mapOf("uuid" to target.uuid.toString(), "punishment" to punishment.uuid.toString(), "silent" to silent))) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/create/TempBanCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/create/TempBanCommand.kt new file mode 100644 index 0000000..f80ad6a --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/create/TempBanCommand.kt @@ -0,0 +1,77 @@ +package net.evilblock.stark.profile.punishment.command.create + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.flag.Flag +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.profile.punishment.ProfilePunishment +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentType +import net.evilblock.stark.core.util.TimeUtils +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import java.util.* + +object TempBanCommand { + @Command(["tempban"], "stark.punishment.BAN.create", description = "Temporarily ban a player", async = true) + @JvmStatic + fun execute(sender: CommandSender, @Flag(value = ["s", "silentMode"], description = "Silently ban the player") silent: Boolean, @Param("target") target: BukkitProfile, @Param("time") time: String, @Param("reason", wildcard = true) reason: String) { + var issuer: UUID? = null + if (sender is Player) { + val senderProfile = Stark.instance.core.getProfileHandler().getByUUID(sender.uniqueId) + + if (senderProfile == null) { + sender.sendMessage("${ChatColor.RED}Couldn't retrieve your target.") + return + } + + if (target.getRank().displayOrder <= senderProfile.getRank().displayOrder) { + sender.sendMessage("${ChatColor.RED}You can't ban this player.") + return + } + + if (target.hasPermission("stark.punishment.protected")) { + sender.sendMessage("${ChatColor.RED}User is protected from punishments.") + return + } + + issuer = sender.uniqueId + } + + if (target.getActivePunishment(ProfilePunishmentType.BAN) != null) { + sender.sendMessage("${target.getPlayerListName()} ${ChatColor.RED}is already banned.") + return + } + + val expiresAt: Long? + + try { + val seconds = if (time.equals("perm", ignoreCase = true) || time.equals("permanent", ignoreCase = true)) -1 else TimeUtils.parseTime(time) + + if (sender is Player && !sender.hasPermission("stark.punishment.BAN.create.permanent")) { + if (seconds == -1 || seconds > 7_862_400) { + sender.sendMessage("${ChatColor.RED}You don't have permission to create a ban this long. Maximum time allowed: 90 days.") + } + } + + expiresAt = if (seconds == -1) null else System.currentTimeMillis() + (seconds * 1_000L) + } catch (e: Exception) { + sender.sendMessage("${ChatColor.RED}Time provided is invalid.") + return + } + + val punishment = ProfilePunishment() + punishment.type = ProfilePunishmentType.BAN + punishment.reason = reason + punishment.issuedBy = issuer + punishment.issuedAt = System.currentTimeMillis() + punishment.expiresAt = expiresAt + + target.punishments.add(punishment) + + Stark.instance.core.getProfileHandler().saveProfile(target) + Stark.instance.core.globalMessageChannel.sendMessage(Message("PUNISHMENT_UPDATE", mapOf("uuid" to target.uuid.toString(), "punishment" to punishment.uuid.toString(), "silent" to silent))) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/create/TempMuteCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/create/TempMuteCommand.kt new file mode 100644 index 0000000..ad20437 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/create/TempMuteCommand.kt @@ -0,0 +1,77 @@ +package net.evilblock.stark.profile.punishment.command.create + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.flag.Flag +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.profile.punishment.ProfilePunishment +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentType +import net.evilblock.stark.core.util.TimeUtils +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import java.util.* + +object TempMuteCommand { + @Command(["tempmute"], permission = "stark.punishment.MUTE.create", description = "Mute a player", async = true) + @JvmStatic + fun execute(sender: CommandSender, @Flag(value = ["s", "silentMode"], description = "Silently mute the player") silent: Boolean, @Param("target") target: BukkitProfile, @Param("time") time: String, @Param("reason", wildcard = true) reason: String) { + var issuer: UUID? = null + if (sender is Player) { + val senderProfile = Stark.instance.core.getProfileHandler().getByUUID(sender.uniqueId) + + if (senderProfile == null) { + sender.sendMessage("${ChatColor.RED}Error verifying your target. Try again later.") + return + } + + if (target.getRank().displayOrder <= senderProfile.getRank().displayOrder) { + sender.sendMessage("${ChatColor.RED}You can't mute this player.") + return + } + + if (target.hasPermission("stark.punishment.protected")) { + sender.sendMessage("${ChatColor.RED}User is protected from punishments.") + return + } + + issuer = sender.uniqueId + } + + if (target.getActivePunishment(ProfilePunishmentType.MUTE) != null) { + sender.sendMessage("${ChatColor.RED}${target.getPlayerListName()} is already muted.") + return + } + + val expiresAt: Long? + + try { + val seconds = if (time.equals("perm", ignoreCase = true) || time.equals("permanent", ignoreCase = true)) -1 else TimeUtils.parseTime(time) + + if (sender is Player && !sender.hasPermission("stark.punishment.MUTE.create.permanent")) { + if (seconds == -1 || seconds > 7_862_400) { + sender.sendMessage("${ChatColor.RED}You don't have permission to create a mute this long. Maximum time allowed: 90 days.") + } + } + + expiresAt = if (seconds == -1) null else System.currentTimeMillis() + (seconds * 1_000L) + } catch (e: Exception) { + sender.sendMessage("${ChatColor.RED}Time provided is invalid.") + return + } + + val punishment = ProfilePunishment() + punishment.type = ProfilePunishmentType.MUTE + punishment.reason = reason + punishment.issuedBy = issuer + punishment.issuedAt = System.currentTimeMillis() + punishment.expiresAt = expiresAt + + target.punishments.add(punishment) + + Stark.instance.core.getProfileHandler().saveProfile(target) + Stark.instance.core.globalMessageChannel.sendMessage(Message("PUNISHMENT_UPDATE", mapOf("uuid" to target.uuid.toString(), "punishment" to punishment.uuid.toString(), "silent" to silent))) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/remove/UnbanCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/remove/UnbanCommand.kt new file mode 100644 index 0000000..b54f9b7 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/remove/UnbanCommand.kt @@ -0,0 +1,37 @@ +package net.evilblock.stark.profile.punishment.command.remove + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.flag.Flag +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentType +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import java.util.* + +object UnbanCommand { + @Command(["unban"], permission = "stark.punishment.BAN.delete", description = "Unban a player", async = true) + @JvmStatic + fun execute(sender: CommandSender, @Flag(value = ["s", "silentMode"], description = "Silently unban the player") silent: Boolean, @Param("target") target: BukkitProfile, @Param("reason", wildcard = true) reason: String) { + var issuer: UUID? = null + if (sender is Player) { + issuer = sender.uniqueId + } + + val activePunishment = target.getActivePunishment(ProfilePunishmentType.BAN) + if (activePunishment == null) { + sender.sendMessage("${target.getPlayerListName()} ${ChatColor.RED}is not banned.") + return + } + + activePunishment.removedBy = issuer + activePunishment.removedAt = System.currentTimeMillis() + activePunishment.removalReason = reason + + Stark.instance.core.getProfileHandler().saveProfile(target) + Stark.instance.core.globalMessageChannel.sendMessage(Message("PUNISHMENT_UPDATE", mapOf("uuid" to target.uuid.toString(), "punishment" to activePunishment.uuid.toString(), "silent" to silent))) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/remove/UnblacklistCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/remove/UnblacklistCommand.kt new file mode 100644 index 0000000..e4d2b1d --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/remove/UnblacklistCommand.kt @@ -0,0 +1,37 @@ +package net.evilblock.stark.profile.punishment.command.remove + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.flag.Flag +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentType +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import java.util.* + +object UnblacklistCommand { + @Command(["unblacklist"], permission = "stark.punishment.BLACKLIST.delete", description = "Unblacklist a player", async = true) + @JvmStatic + fun execute(sender: CommandSender, @Flag(value = ["s", "silentMode"], description = "Silently unblacklist the player") silent: Boolean, @Param("target") target: BukkitProfile, @Param("reason", wildcard = true) reason: String) { + var issuer: UUID? = null + if (sender is Player) { + issuer = sender.uniqueId + } + + val activePunishment = target.getActivePunishment(ProfilePunishmentType.BLACKLIST) + if (activePunishment == null) { + sender.sendMessage("${target.getPlayerListName()} ${ChatColor.RED}is not blacklisted.") + return + } + + activePunishment.removedBy = issuer + activePunishment.removedAt = System.currentTimeMillis() + activePunishment.removalReason = reason + + Stark.instance.core.getProfileHandler().saveProfile(target) + Stark.instance.core.globalMessageChannel.sendMessage(Message("PUNISHMENT_UPDATE", mapOf("uuid" to target.uuid.toString(), "punishment" to activePunishment.uuid.toString(), "silent" to silent))) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/remove/UnmuteCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/remove/UnmuteCommand.kt new file mode 100644 index 0000000..2db0fe3 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/remove/UnmuteCommand.kt @@ -0,0 +1,37 @@ +package net.evilblock.stark.profile.punishment.command.remove + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.flag.Flag +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentType +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import java.util.* + +object UnmuteCommand { + @Command(["unmute"], permission = "stark.punishment.MUTE.delete", description = "Unmute a player", async = true) + @JvmStatic + fun execute(sender: CommandSender, @Flag(value = ["s", "silentMode"], description = "Silently unmute the player") silent: Boolean, @Param("target") target: BukkitProfile, @Param("reason", wildcard = true) reason: String) { + var issuer: UUID? = null + if (sender is Player) { + issuer = sender.uniqueId + } + + val activePunishment = target.getActivePunishment(ProfilePunishmentType.MUTE) + if (activePunishment == null) { + sender.sendMessage("${target.getPlayerListName()} ${ChatColor.RED}is not muted.") + return + } + + activePunishment.removedBy = issuer + activePunishment.removedAt = System.currentTimeMillis() + activePunishment.removalReason = reason + + Stark.instance.core.getProfileHandler().saveProfile(target) + Stark.instance.core.globalMessageChannel.sendMessage(Message("PUNISHMENT_UPDATE", mapOf("uuid" to target.uuid.toString(), "punishment" to activePunishment.uuid.toString(), "silent" to silent))) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/view/CheckCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/view/CheckCommand.kt new file mode 100644 index 0000000..e5e58fe --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/command/view/CheckCommand.kt @@ -0,0 +1,15 @@ +package net.evilblock.stark.profile.punishment.command.view + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.profile.punishment.menu.SelectPunishmentTypeMenu +import org.bukkit.entity.Player + +object CheckCommand { + @Command(["check", "c"], description = "View a player's punishments", permission = "stark.check", async = true) + @JvmStatic + fun execute(player: Player, @Param("target") target: BukkitProfile) { + SelectPunishmentTypeMenu(target).openMenu(player) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/conversation/PunishmentRemovalPrompt.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/conversation/PunishmentRemovalPrompt.kt new file mode 100644 index 0000000..41809e5 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/conversation/PunishmentRemovalPrompt.kt @@ -0,0 +1,34 @@ +package net.evilblock.stark.profile.punishment.conversation + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.profile.punishment.ProfilePunishment +import org.bukkit.ChatColor +import org.bukkit.conversations.ConversationContext +import org.bukkit.conversations.Prompt +import org.bukkit.conversations.StringPrompt +import org.bukkit.entity.Player + +class PunishmentRemovalPrompt(val profile: BukkitProfile, val punishment: ProfilePunishment) : StringPrompt() { + + override fun getPromptText(conversationContext: ConversationContext): String { + return ChatColor.GREEN.toString() + "Please specify a valid reason." + } + + override fun acceptInput(conversationContext: ConversationContext, s: String): Prompt? { + punishment.removedBy = (conversationContext.forWhom as Player).uniqueId + punishment.removedAt = System.currentTimeMillis() + punishment.removalReason = s + + Stark.instance.server.scheduler.runTaskAsynchronously(Stark.instance) { + Stark.instance.core.getProfileHandler().saveProfile(profile) + Stark.instance.core.globalMessageChannel.sendMessage(Message("PUNISHMENT_UPDATE", mapOf("uuid" to profile.uuid.toString(), "punishment" to punishment.uuid.toString(), "silent" to true))) + } + + conversationContext.forWhom.sendRawMessage("${ChatColor.GOLD}Punishment removed.") + + return Prompt.END_OF_CONVERSATION + } + +} diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/menu/PunishmentsMenu.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/menu/PunishmentsMenu.kt new file mode 100644 index 0000000..afce7bd --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/menu/PunishmentsMenu.kt @@ -0,0 +1,129 @@ +package net.evilblock.stark.profile.punishment.menu + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.menu.Button +import net.evilblock.stark.engine.menu.buttons.BackButton +import net.evilblock.stark.engine.menu.pagination.PaginatedMenu +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentType +import net.evilblock.stark.profile.punishment.conversation.PunishmentRemovalPrompt +import net.evilblock.stark.util.Callback +import net.evilblock.stark.util.ItemBuilder +import net.evilblock.stark.core.util.TimeUtils +import org.apache.commons.lang.StringUtils +import org.bukkit.ChatColor +import org.bukkit.Material +import org.bukkit.conversations.ConversationFactory +import org.bukkit.entity.Player +import org.bukkit.event.inventory.ClickType +import org.bukkit.inventory.ItemStack +import java.util.* +import kotlin.collections.HashMap + +class PunishmentsMenu(private val target: BukkitProfile, private val punishmentType: ProfilePunishmentType) : PaginatedMenu() { + + override fun getPrePaginatedTitle(player: Player): String { + return "${ChatColor.RED}${punishmentType.name.toLowerCase().capitalize()}s" + } + + override fun getGlobalButtons(player: Player): Map? { + val buttons = HashMap() + + buttons[4] = BackButton(object : Callback { + override fun callback(value: Player) { + SelectPunishmentTypeMenu(target).openMenu(player) + } + }) + + return buttons + } + + override fun getAllPagesButtons(player: Player): Map { + val buttons = HashMap() + val punishments = target.punishments.sortedBy { -it.issuedAt } + for (punishment in punishments) { + if (punishment.type != punishmentType) { + continue + } + + val builder = ItemBuilder(Material.INK_SACK) + + if (punishment.isActive()) { + builder.data(10) + } else { + builder.data(1) + } + + var addedByProfile: BukkitProfile? = null + var removedByProfile: BukkitProfile? = null + + if (punishment.issuedBy != null) { + addedByProfile = Stark.instance.core.getProfileHandler().loadProfile(punishment.issuedBy!!) + } + + if (punishment.removedBy != null) { + removedByProfile = Stark.instance.core.getProfileHandler().loadProfile(punishment.removedBy!!) + } + + builder.name("${ChatColor.translateAlternateColorCodes('&', punishment.type.color)}&l${punishment.type.name}") + builder.addToLore(BAR) + builder.addToLore("&eIssued By:&c " + if (punishment.issuedBy == null) "Console" else addedByProfile?.getPlayerListName()) + builder.addToLore("&eIssued On:&c ${TimeUtils.formatIntoCalendarString(Date(punishment.issuedAt))}") + builder.addToLore("&eReason:&c&o ${punishment.reason}") + + if (punishment.isActive()) { + if (punishment.expiresAt == null) { + builder.addToLore("&eDuration:&c Permanent") + } else { + builder.addToLore("&eTime remaining:&c " + TimeUtils.formatIntoDetailedString(((punishment.expiresAt!! - System.currentTimeMillis()) / 1_000L).toInt())) + } + + if (player.hasPermission("stark.punishment.${punishment.type.name.toLowerCase()}.delete")) { + builder.addToLore(BAR) + builder.addToLore("&eClick to remove this punishment.") + } + } else if (punishment.removedAt != null) { + builder.addToLore(BAR) + builder.addToLore("&eRemoved By:&c " + (if (punishment.removedBy == null) "Console" else removedByProfile?.getPlayerListName())) + builder.addToLore("&eRemoved On:&c ${TimeUtils.formatIntoCalendarString(Date(punishment.removedAt!!))}") + builder.addToLore("&eReason:&c&o ${punishment.removalReason}") + } + + builder.addToLore(BAR) + + buttons[buttons.size] = object: Button() { + override fun getName(player: Player): String? { + return null + } + + override fun getDescription(player: Player): List? { + return null + } + + override fun getMaterial(player: Player): Material? { + return null + } + + override fun getButtonItem(player: Player): ItemStack { + return builder.build() + } + + override fun clicked(player: Player, slot: Int, clickType: ClickType) { + if (punishment.removedAt == null) { + player.closeInventory() + + val factory = ConversationFactory(Stark.instance) + .withFirstPrompt(PunishmentRemovalPrompt(target, punishment)) + .withLocalEcho(false) + .thatExcludesNonPlayersWithMessage("Go away evil console!") + + player.beginConversation(factory.buildConversation(player)) + } + } + } + } + + return buttons + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/menu/SelectPunishmentTypeButton.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/menu/SelectPunishmentTypeButton.kt new file mode 100644 index 0000000..37edc56 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/menu/SelectPunishmentTypeButton.kt @@ -0,0 +1,48 @@ +package net.evilblock.stark.profile.punishment.menu + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.menu.Button +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentType +import net.evilblock.stark.util.ItemBuilder +import org.bukkit.ChatColor +import org.bukkit.Material +import org.bukkit.entity.Player +import org.bukkit.event.inventory.ClickType +import org.bukkit.inventory.ItemStack + +class SelectPunishmentTypeButton(private val target: BukkitProfile, private val punishmentType: ProfilePunishmentType) : Button() { + + override fun getName(player: Player): String? { + return null + } + + override fun getDescription(player: Player): List? { + return null + } + + override fun getMaterial(player: Player): Material? { + return null + } + + override fun getButtonItem(player: Player): ItemStack { + return ItemBuilder.of(Material.WOOL) + .name("${ChatColor.translateAlternateColorCodes('&', punishmentType.color)}${punishmentType.name.toLowerCase().capitalize()}s") + .data(when (punishmentType) { + ProfilePunishmentType.BLACKLIST -> 14 + ProfilePunishmentType.BAN -> 1 + ProfilePunishmentType.MUTE -> 4 + ProfilePunishmentType.WARN -> 5 + }) + .build() + } + + override fun clicked(player: Player, slot: Int, clickType: ClickType) { + if (punishmentType != ProfilePunishmentType.WARN) { + Stark.instance.server.scheduler.runTaskAsynchronously(Stark.instance) { + PunishmentsMenu(target, punishmentType).openMenu(player) + } + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/menu/SelectPunishmentTypeMenu.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/menu/SelectPunishmentTypeMenu.kt new file mode 100644 index 0000000..1d3175e --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/punishment/menu/SelectPunishmentTypeMenu.kt @@ -0,0 +1,32 @@ +package net.evilblock.stark.profile.punishment.menu + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.menu.Button +import net.evilblock.stark.engine.menu.Menu +import net.evilblock.stark.profile.BukkitProfile +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentType +import org.bukkit.ChatColor +import org.bukkit.Material +import org.bukkit.entity.Player + +class SelectPunishmentTypeMenu(private val target: BukkitProfile) : Menu("${ChatColor.BLUE}Punishments - ${Stark.instance.core.uuidCache.name(target.uuid)}") { + + override fun getButtons(player: Player): Map { + val buttons = hashMapOf() + + if (player.hasPermission("stark.punishment.BLACKLIST.view")) { + buttons[10] = SelectPunishmentTypeButton(target, ProfilePunishmentType.BLACKLIST) + buttons[12] = SelectPunishmentTypeButton(target, ProfilePunishmentType.BAN) + buttons[14] = SelectPunishmentTypeButton(target, ProfilePunishmentType.MUTE) + buttons[16] = SelectPunishmentTypeButton(target, ProfilePunishmentType.WARN) + } else { + buttons[11] = SelectPunishmentTypeButton(target, ProfilePunishmentType.BAN) + buttons[13] = SelectPunishmentTypeButton(target, ProfilePunishmentType.MUTE) + buttons[15] = SelectPunishmentTypeButton(target, ProfilePunishmentType.WARN) + } + + buttons[26] = Button.placeholder(Material.AIR) + return buttons + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/runnable/ProfileHeartbeatRunnable.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/runnable/ProfileHeartbeatRunnable.kt new file mode 100644 index 0000000..b253731 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/profile/runnable/ProfileHeartbeatRunnable.kt @@ -0,0 +1,16 @@ +package net.evilblock.stark.profile.runnable + +import net.evilblock.stark.Stark +import net.evilblock.stark.core.StarkCore + +class ProfileHeartbeatRunnable : Runnable { + + override fun run() { + // Heartbeat profiles every 30 seconds + for (player in Stark.instance.server.onlinePlayers) { + val profile = Stark.instance.core.getProfileHandler().pullProfileUpdates(player.uniqueId) + Stark.instance.core.getProfileHandler().profiles[player.uniqueId] = profile + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/rank/RankCommands.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/rank/RankCommands.kt new file mode 100644 index 0000000..5a9aaf6 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/rank/RankCommands.kt @@ -0,0 +1,17 @@ +package net.evilblock.stark.rank + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender + +object RankCommands { + + @Command(["ranks"], "op") + @JvmStatic fun execute(sender: CommandSender) { + for (rank in Stark.instance.core.rankHandler.getRanks().sortedBy { rank -> rank.displayOrder }) { + sender.sendMessage("${ChatColor.translateAlternateColorCodes('&', rank.gameColor)}${rank.displayName}") + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/rank/RankParameterType.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/rank/RankParameterType.kt new file mode 100644 index 0000000..d258e6a --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/rank/RankParameterType.kt @@ -0,0 +1,27 @@ +package net.evilblock.stark.rank + +import net.evilblock.stark.Stark +import net.evilblock.stark.core.rank.Rank +import net.evilblock.stark.engine.command.data.parameter.ParameterType +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import java.util.stream.Collectors + +class RankParameterType : ParameterType { + + override fun transform(sender: CommandSender, source: String): Rank? { + val rank = Stark.instance.core.rankHandler.getById(source.toLowerCase()) + + if (rank == null) { + sender.sendMessage(ChatColor.RED.toString() + "Rank named " + source + " doesn't exist") + } + + return rank + } + + override fun tabComplete(player: Player, flags: Set, source: String): List { + return Stark.instance.core.rankHandler.getRanks().stream().map { it.displayName }.collect(Collectors.toList()) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/reboot/RebootCommands.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/reboot/RebootCommands.kt new file mode 100644 index 0000000..fc31988 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/reboot/RebootCommands.kt @@ -0,0 +1,39 @@ +package net.evilblock.stark.reboot + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.core.util.TimeUtils +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import java.util.concurrent.TimeUnit + +object RebootCommands { + @Command(["reboot"], permission = "stark.reboot") + @JvmStatic + fun execute(sender: CommandSender, @Param(name = "time") unparsedTime: String) { + try { + val time = TimeUtils.parseTime(unparsedTime.toLowerCase()) + Stark.instance.rebootHandler.rebootServer(time, TimeUnit.SECONDS) + sender.sendMessage("${ChatColor.GREEN}Started auto reboot.") + } catch (ex: Exception) { + sender.sendMessage(ChatColor.RED.toString() + ex.message) + } + + } + + @Command(["reboot cancel"], permission = "stark.reboot") + @JvmStatic + fun execute(sender: CommandSender) { + if (!Stark.instance.rebootHandler.isRebooting()) { + sender.sendMessage("${ChatColor.RED}No reboot has been scheduled.") + } else { + Stark.instance.rebootHandler.rebootTask?.cancel() + + Stark.instance.server.broadcastMessage("") + Stark.instance.server.broadcastMessage("${ChatColor.RED}${ChatColor.BOLD}⚠ ${ChatColor.YELLOW}Server reboot cancelled!") + Stark.instance.server.broadcastMessage("") + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/reboot/RebootHandler.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/reboot/RebootHandler.kt new file mode 100644 index 0000000..27801dc --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/reboot/RebootHandler.kt @@ -0,0 +1,48 @@ +package net.evilblock.stark.reboot + +import net.evilblock.stark.Stark +import java.util.ArrayList +import java.util.concurrent.TimeUnit + +class RebootHandler { + + var rebootTimes: List = ArrayList() + var rebootTask: RebootTask? = null + + fun isRebooting(): Boolean { + return rebootTask != null + } + + fun rebootSecondsRemaining(): Int { + return if (rebootTask == null) { + -1 + } else { + rebootTask!!.secondsRemaining + } + } + + fun load() { + rebootTimes = Stark.instance.config.getIntegerList("RebootTimes") + } + + fun rebootServer(seconds: Int) { + rebootServer(seconds, TimeUnit.SECONDS) + } + + fun rebootServer(timeUnitAmount: Int, timeUnit: TimeUnit) { + if (rebootTask != null) { + throw IllegalStateException("Reboot already in progress") + } + + rebootTask = RebootTask(timeUnitAmount, timeUnit) + rebootTask!!.runTaskTimer(Stark.instance, 20L, 20L) + } + + fun cancelReboot() { + if (rebootTask != null) { + rebootTask!!.cancel() + rebootTask = null + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/reboot/RebootListener.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/reboot/RebootListener.kt new file mode 100644 index 0000000..96adfde --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/reboot/RebootListener.kt @@ -0,0 +1,18 @@ +package net.evilblock.stark.reboot + +import net.evilblock.stark.Stark +import net.evilblock.stark.util.event.HourEvent +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import java.util.concurrent.TimeUnit + +class RebootListener : Listener { + + @EventHandler + fun onHour(event: HourEvent) { + if (Stark.instance.rebootHandler.rebootTimes.contains(event.hour)) { + Stark.instance.rebootHandler.rebootServer(5, TimeUnit.MINUTES) + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/reboot/RebootTask.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/reboot/RebootTask.kt new file mode 100644 index 0000000..32c8047 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/reboot/RebootTask.kt @@ -0,0 +1,39 @@ +package net.evilblock.stark.reboot + +import net.evilblock.stark.Stark +import net.evilblock.stark.config.LangConfigEntry +import net.evilblock.stark.core.util.TimeUtils +import org.bukkit.ChatColor +import org.bukkit.scheduler.BukkitRunnable +import java.util.concurrent.TimeUnit + +class RebootTask(timeUnitAmount: Int, timeUnit: TimeUnit) : BukkitRunnable() { + + var secondsRemaining: Int = timeUnit.toSeconds(timeUnitAmount.toLong()).toInt() + + override fun run() { + if (this.secondsRemaining == 0) { + Stark.instance.server.shutdown() + } + + when (this.secondsRemaining) { + 5, 10, 15, 30, 60, 120, 180, 240, 300 -> { + if (LangConfigEntry.REBOOT_LINES.get() == null) { + println("dumbass nigger") + } + LangConfigEntry.REBOOT_LINES.get()?.forEach { message -> + Stark.instance.server.broadcastMessage(message) + } + } + } + + --this.secondsRemaining + } + + @Synchronized + @Throws(IllegalStateException::class) + override fun cancel() { + super.cancel() + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/ServerHandler.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/ServerHandler.kt new file mode 100644 index 0000000..75954ac --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/ServerHandler.kt @@ -0,0 +1,91 @@ +package net.evilblock.stark.server + +import net.evilblock.stark.Stark +import net.evilblock.stark.server.chat.ChatFilterEntry +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.entity.Player +import java.util.UUID +import java.util.concurrent.TimeUnit +import org.bukkit.metadata.FixedMetadataValue +import org.bukkit.metadata.MetadataValue +import java.util.HashSet + +class ServerHandler { + + var frozen: Boolean = false + val disallowedCommands = HashSet() + + val bannedRegexes = setOf( + ChatFilterEntry("Restricted Phrase \"ip farm\"", "[i1l1|]+p+ ?f[a4]+rm+"), + ChatFilterEntry("Racism \"Nigger\"", "n+[i1l|]+gg+[e3]+r+"), + ChatFilterEntry("Racism \"Beaner\"", "b+e+a+n+e+r+"), + ChatFilterEntry("Suicide Encouragement", "k+i+l+l+ *y*o*u+r+ *s+e+l+f+"), + ChatFilterEntry("Suicide Encouragement", "\\bk+y+s+\\b"), + ChatFilterEntry("Offensive \"Faggot\"", "f+a+g+[o0]+t+"), + ChatFilterEntry("IP Address", "(([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])"), + ChatFilterEntry("Phishing Link \"optifine\"", "optifine\\.(?=\\w+)(?!net)"), + ChatFilterEntry("Phishing Link \"gyazo\"", "gyazo\\.(?=\\w+)(?!com)"), + ChatFilterEntry("Phishing Link \"prntscr\"", "prntscr\\.(?=\\w+)(?!com)") + ) + + fun load() { + val list = Stark.instance.config.getStringList("disallowedCommands") + + if (list != null) { + disallowedCommands.addAll(list) + } + + disallowedCommands.add("/calc") + disallowedCommands.add("/calculate") + disallowedCommands.add("/eval") + disallowedCommands.add("/evaluate") + disallowedCommands.add("/solve") + disallowedCommands.add("/worldedit:calc") + disallowedCommands.add("/worldedit:eval") + disallowedCommands.add("me") + disallowedCommands.add("pl") + disallowedCommands.add("bukkit:me") + disallowedCommands.add("icanhasbukkit") + disallowedCommands.add("bukkit:icanhasbukkit") + disallowedCommands.add("minecraft:me") + disallowedCommands.add("bukkit:plugins") + disallowedCommands.add("bukkit:pl") + } + + fun freeze(player: Player) { + player.setMetadata("frozen", FixedMetadataValue(Stark.instance, true as Any) as MetadataValue) + player.sendMessage("${ChatColor.RED}You have been frozen by a staff member.") + + val uuid = player.uniqueId + + Bukkit.getScheduler().runTaskLater(Stark.instance, { this.unfreeze(uuid) }, 20L * TimeUnit.HOURS.toSeconds(2L)) + + val location = player.location + var tries = 0 + + while (1.0 <= location.y && !location.block.type.isSolid && tries++ < 100) { + location.subtract(0.0, 1.0, 0.0) + if (location.y <= 0.0) { + break + } + } + + if (100 <= tries) { + Bukkit.getLogger().info("Hit the 100 try limit on the freeze command.") + } + + location.y = location.blockY.toDouble() + + player.teleport(location.add(0.0, 1.0, 0.0)) + } + + fun unfreeze(uuid: UUID) { + val player = Bukkit.getPlayer(uuid) + if (player != null) { + player.removeMetadata("frozen", Stark.instance) + player.sendMessage("${ChatColor.GREEN}You have been unfrozen by a staff member.") + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/ServerSyncRunnable.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/ServerSyncRunnable.kt new file mode 100644 index 0000000..e6c06a2 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/ServerSyncRunnable.kt @@ -0,0 +1,28 @@ +package net.evilblock.stark.server + +import net.evilblock.stark.Stark +import net.evilblock.stark.util.Reflections +import org.bukkit.Bukkit + +class ServerSyncRunnable : Runnable { + + override fun run() { + val optionalServer = Stark.instance.core.servers.getServerByName(Bukkit.getServerName()) + + val server = if (optionalServer.isPresent) { + optionalServer.get() + } else { + Stark.instance.core.servers.loadOrCreateServer(Bukkit.getServerName(), Bukkit.getPort()) + } + + server.lastHeartbeat = System.currentTimeMillis() + server.currentUptime = System.currentTimeMillis() - Stark.instance.enabledAt + server.currentTps = Reflections.getTPS() + server.playerCount = Bukkit.getOnlinePlayers().size + server.maxSlots = Bukkit.getMaxPlayers() + server.whitelisted = Bukkit.hasWhitelist() + + Stark.instance.core.servers.saveServer(server) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/chat/ChatFilterEntry.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/chat/ChatFilterEntry.kt new file mode 100644 index 0000000..bf3552d --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/chat/ChatFilterEntry.kt @@ -0,0 +1,9 @@ +package net.evilblock.stark.server.chat + +import java.util.regex.Pattern + +class ChatFilterEntry(val id: String, regex: String) { + + val pattern = Pattern.compile(regex) + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/chat/ServerChatSettings.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/chat/ServerChatSettings.kt new file mode 100644 index 0000000..018a4ab --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/chat/ServerChatSettings.kt @@ -0,0 +1,8 @@ +package net.evilblock.stark.server.chat + +object ServerChatSettings { + + var muted = false + var slowed: SlowedChatSession? = null + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/chat/SlowedChatSession.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/chat/SlowedChatSession.kt new file mode 100644 index 0000000..f5b41cb --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/chat/SlowedChatSession.kt @@ -0,0 +1,8 @@ +package net.evilblock.stark.server.chat + +import java.util.* +import kotlin.collections.HashMap + +class SlowedChatSession(val issuer: String, val duration: Long) { + val players = HashMap() +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/command/ClearChatCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/command/ClearChatCommand.kt new file mode 100644 index 0000000..dc4a379 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/command/ClearChatCommand.kt @@ -0,0 +1,32 @@ +package net.evilblock.stark.server.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.scheduler.BukkitRunnable + +object ClearChatCommand { + @Command(["clearchat", "cc"], permission = "stark.clearchat", async = true) + @JvmStatic + fun execute(sender: CommandSender) { + object: BukkitRunnable() { + override fun run() { + Bukkit.getOnlinePlayers().forEach { + if (!it.hasPermission("stark.staff")) { + for (i in 1..100) it.sendMessage("") + } + + if (it.hasPermission("stark.staff")) { + it.sendMessage("${ChatColor.LIGHT_PURPLE}The chat has been cleared by " + sender.name + ".") + } else { + it.sendMessage("${ChatColor.LIGHT_PURPLE}The chat has been cleared by a staff member.") + } + } + } + }.runTaskAsynchronously(Stark.instance) + + Bukkit.getConsoleSender().sendMessage("${ChatColor.LIGHT_PURPLE}The chat has been cleared by " + sender.name + ".") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/command/FreezeServerCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/command/FreezeServerCommand.kt new file mode 100644 index 0000000..459e18e --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/command/FreezeServerCommand.kt @@ -0,0 +1,24 @@ +package net.evilblock.stark.server.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.engine.command.Command +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender + +object FreezeServerCommand { + @Command(["freezeserver"], permission = "stark.admin.freezeserver", description = "Freeze the server. Normal players won't be able to move or interact") + fun execute(sender: CommandSender) { + Stark.instance.serverHandler.frozen = !Stark.instance.serverHandler.frozen + + Stark.instance.server.onlinePlayers.forEach { + if (it.hasPermission("stark.staff")) { + it.sendMessage("${ChatColor.RED}${ChatColor.BOLD}The server has been " + (if (Stark.instance.serverHandler.frozen) "" else "un") + "frozen by ${sender.name}.") + } else { + it.sendMessage("${ChatColor.RED}${ChatColor.BOLD}The server has been " + (if (Stark.instance.serverHandler.frozen) "" else "un") + "frozen.") + } + } + + Bukkit.getConsoleSender().sendMessage("${ChatColor.RED}${ChatColor.BOLD}The server has been " + (if (Stark.instance.serverHandler.frozen) "" else "un") + "frozen by ${sender.name}.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/command/MuteChatCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/command/MuteChatCommand.kt new file mode 100644 index 0000000..6a570d7 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/command/MuteChatCommand.kt @@ -0,0 +1,17 @@ +package net.evilblock.stark.server.command + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.server.chat.ServerChatSettings +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender + +object MuteChatCommand { + @Command(["mutechat"], permission = "stark.mutechat") + @JvmStatic + fun execute(sender: CommandSender) { + ServerChatSettings.muted = !ServerChatSettings.muted + val muted = ServerChatSettings.muted + Bukkit.broadcastMessage("${ChatColor.LIGHT_PURPLE}Public chat has been ${if (muted) "" else "un"}muted by ${sender.name}.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/command/SlowChatCommand.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/command/SlowChatCommand.kt new file mode 100644 index 0000000..6719718 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/command/SlowChatCommand.kt @@ -0,0 +1,36 @@ +package net.evilblock.stark.server.command + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.server.chat.ServerChatSettings +import net.evilblock.stark.server.chat.SlowedChatSession +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender + +object SlowChatCommand { + @Command(["slowchat"], permission = "stark.slowchat") + @JvmStatic + fun execute(sender: CommandSender, @Param("duration", defaultValue = "0") duration: Int) { + if (duration > 30) { + sender.sendMessage("${ChatColor.RED}You can't slow the chat for this long.") + return + } + + if (duration == 0) { + val session = ServerChatSettings.slowed + if (session == null) { + sender.sendMessage("${ChatColor.RED}The chat isn't being slowed.") + return + } else { + ServerChatSettings.slowed = null + Bukkit.broadcastMessage("${ChatColor.LIGHT_PURPLE}Public chat has been unslowed by ${sender.name}.") + } + return + } + + val session = SlowedChatSession(sender.name, duration * 1000L) + ServerChatSettings.slowed = session + Bukkit.broadcastMessage("${ChatColor.LIGHT_PURPLE}Public chat has been slowed by ${sender.name}.") + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/command/WhitelistCommands.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/command/WhitelistCommands.kt new file mode 100644 index 0000000..9ab0c9b --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/command/WhitelistCommands.kt @@ -0,0 +1,107 @@ +package net.evilblock.stark.server.command + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.core.whitelist.WhitelistType +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.engine.command.data.parameter.Param +import net.evilblock.stark.profile.BukkitProfile +import net.md_5.bungee.api.ChatColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +object WhitelistCommands { + + @JvmStatic + @Command(names = ["whitelist mode"], permission = "stark.whitelist.mode", async = true) + fun mode(sender: CommandSender, @Param(name = "mode") mode: Int) { + val modeType = when (mode) { + 0 -> WhitelistType.NONE + 1 -> WhitelistType.PURCHASED + 2 -> WhitelistType.MAINTENANCE + else -> { + sender.sendMessage("${ChatColor.RED}Invalid mode.") + return + } + } + + Stark.instance.core.whitelist.setMode(modeType, true) + Stark.instance.core.globalMessageChannel.sendMessage(Message("WHITELIST_UPDATE", hashMapOf("mode" to modeType.name))) + + sender.sendMessage("${ChatColor.GOLD}Set whitelist mode to ${ChatColor.WHITE}${modeType.displayName}") + } + + @JvmStatic + @Command(names = ["whitelist"], permission = "stark.whitelist", async = true) + fun whitelist(sender: CommandSender, @Param(name = "player") profile: BukkitProfile, @Param(name = "access") access: Int) { + val modeType = when (access) { + 0 -> WhitelistType.NONE + 1 -> WhitelistType.PURCHASED + 2 -> WhitelistType.MAINTENANCE + else -> { + sender.sendMessage("${ChatColor.RED}Invalid mode.") + return + } + } + + Stark.instance.core.whitelist.setWhitelist(profile.uuid, modeType) + + sender.sendMessage("${ChatColor.GOLD}Set ${ChatColor.RESET}${profile.getPlayerListName()}${ChatColor.GOLD}'s whitelist access to ${ChatColor.WHITE}${modeType.displayName}") + } + + @JvmStatic + @Command(names = ["whitelist read"], permission = "stark.whitelist", async = true) + fun read(sender: CommandSender, @Param(name = "player") profile: BukkitProfile) { + sender.sendMessage("${profile.getPlayerListName()}${ChatColor.GOLD}'s has whitelist access ${ChatColor.WHITE}${Stark.instance.core.whitelist.getWhitelist(profile.uuid).displayName}") + } + + @JvmStatic + @Command(names = ["wltokens use"], async = true) + fun use(player: Player, @Param(name = "player") target: BukkitProfile) { + if (Stark.instance.core.whitelist.getWhitelist(target.uuid).isAboveOrEqual(WhitelistType.PURCHASED)) { + player.sendMessage("${ChatColor.RED}That player already has whitelist access.") + return + } + + val remaining = Stark.instance.core.whitelist.getWhitelistTokens(player.uniqueId) + + if (remaining <= 0) { + player.sendMessage("${ChatColor.RED}You have no whitelist tokens to use.") + return + } + + Stark.instance.core.whitelist.setWhitelistTokens(player.uniqueId, remaining - 1) + Stark.instance.core.whitelist.setWhitelist(target.uuid, WhitelistType.PURCHASED) + + player.sendMessage("${ChatColor.GOLD}You've used a whitelist token on ${ChatColor.RESET}${target.getPlayerListName()}") + } + + @JvmStatic + @Command(names = ["wltokens give"], permission = "op", async = true) + fun give(sender: CommandSender, @Param(name = "player") profile: BukkitProfile, @Param(name = "amount") amount: Int) { + val tokens = Stark.instance.core.whitelist.getWhitelistTokens(profile.uuid) + amount + Stark.instance.core.whitelist.setWhitelistTokens(profile.uuid, tokens) + sender.sendMessage("${ChatColor.GOLD}You've given ${ChatColor.RESET}${profile.getPlayerListName()} ${ChatColor.GOLD}$tokens tokens.") + } + + @JvmStatic + @Command(names = ["verifiedstatus add"], permission = "op", async = true) + fun verifiedStatusAdd(sender: CommandSender, @Param(name = "player") profile: BukkitProfile) { + if (Stark.instance.core.whitelist.setVerified(profile.uuid, true)) { + sender.sendMessage("${ChatColor.GOLD}You've added verified status to ${ChatColor.RESET}${profile.getPlayerListName()}${ChatColor.GOLD}.") + } else { + sender.sendMessage("${profile.getPlayerListName()} ${ChatColor.RED}is already has verified status.") + } + } + + @JvmStatic + @Command(names = ["verifiedstatus remove"], permission = "op", async = true) + fun verifiedStatusRemove(sender: CommandSender, @Param(name = "player") profile: BukkitProfile) { + if (Stark.instance.core.whitelist.setVerified(profile.uuid, false)) { + sender.sendMessage("${ChatColor.GOLD}You've removed verified status from ${ChatColor.RESET}${profile.getPlayerListName()}${ChatColor.GOLD}.") + } else { + sender.sendMessage("${profile.getPlayerListName()} ${ChatColor.RED}is not verified status.") + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/ChatFilterListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/ChatFilterListeners.kt new file mode 100644 index 0000000..58ed7d7 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/ChatFilterListeners.kt @@ -0,0 +1,99 @@ +package net.evilblock.stark.server.listener + +import net.evilblock.stark.Stark +import net.evilblock.stark.server.chat.ServerChatSettings +import com.google.common.collect.Maps +import org.bukkit.event.EventHandler +import mkremins.fanciful.FancyMessage +import org.bukkit.event.player.PlayerQuitEvent +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.event.EventPriority +import org.bukkit.event.Listener +import org.bukkit.event.player.AsyncPlayerChatEvent +import java.util.UUID + +class ChatFilterListeners : Listener { + + companion object { + val DELAY_MESSAGE: FancyMessage = FancyMessage("") + .then("Purchase a rank at ").color(ChatColor.RED) + .then("buy.kihar.net").link("http://buy.kihar.net") + .tooltip("${ChatColor.GREEN}Click to visit our store").color(ChatColor.YELLOW).style(ChatColor.UNDERLINE) + .then(" to bypass this restriction.").color(ChatColor.RED) + } + + private val badMessages: MutableMap = Maps.newConcurrentMap() + + @EventHandler(priority = EventPriority.LOWEST) + fun onPlayerChatLowest(event: AsyncPlayerChatEvent) { + val chatFilter = Stark.instance.serverHandler.bannedRegexes + + for (filter in chatFilter) { + if (filter.pattern.matcher(event.message).find()) { + this.badMessages[event.player.uniqueId] = event.message + break + } + } + } + + @EventHandler(priority = EventPriority.HIGH) + fun onPlayerChatHigh(event: AsyncPlayerChatEvent) { + val player = event.player + + if (player.hasMetadata("NoSpamCheck")) { + return + } + + if (ServerChatSettings.muted && !player.hasPermission("stark.mutechat.bypass")) { + player.sendMessage("${ChatColor.RED}The chat is currently muted.") + event.isCancelled = true + return + } + + val session = ServerChatSettings.slowed + if (session != null && !player.hasPermission("stark.slowchat.bypass")) { + val time = session.players[player.uniqueId] + if (time == null || time < System.currentTimeMillis()) { + session.players[player.uniqueId] = System.currentTimeMillis() + session.duration + } else { + player.sendMessage("${ChatColor.RED}The chat is currently slowed. You can send another message in ${(time - System.currentTimeMillis()) / 1000} seconds.") + event.isCancelled = true + return + } + } + } + + @EventHandler(priority = EventPriority.MONITOR) + fun onPlayerChatMonitor(event: AsyncPlayerChatEvent) { + val player = event.player + + if (this.badMessages.containsKey(player.uniqueId)) { + if (event.player.hasMetadata("NoSpamCheck")) { + return + } + + event.recipients.clear() + event.recipients.add(event.player) + + val toSend = FancyMessage("[Filtered] ") + .color(ChatColor.RED) + .tooltip("${ChatColor.YELLOW}This message was hidden from public chat.") + .then(event.format.format(player.displayName, event.message)) + + for (other in Bukkit.getOnlinePlayers()) { + if (other.hasPermission("stark.staff")) { + toSend.send(other) + } + } + + this.badMessages.remove(player.uniqueId) + } + } + + @EventHandler(priority = EventPriority.MONITOR) + fun onQuit(event: PlayerQuitEvent) { + this.badMessages.remove(event.player.uniqueId) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/ColoredSignListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/ColoredSignListeners.kt new file mode 100644 index 0000000..5e89831 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/ColoredSignListeners.kt @@ -0,0 +1,21 @@ +package net.evilblock.stark.server.listener + +import org.bukkit.ChatColor +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.block.SignChangeEvent + +class ColoredSignListeners : Listener { + + @EventHandler + fun onSignChange(event: SignChangeEvent) { + val player = event.player + if (!player.hasPermission("essentials.coloredsigns")) { + return + } + for (i in 0 until event.lines.size) { + event.setLine(i, ChatColor.translateAlternateColorCodes('&', event.lines[i]).trim()) + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/DisallowedCommandsListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/DisallowedCommandsListeners.kt new file mode 100644 index 0000000..b8e0878 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/DisallowedCommandsListeners.kt @@ -0,0 +1,27 @@ +package net.evilblock.stark.server.listener + +import net.evilblock.stark.Stark +import org.bukkit.ChatColor +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerCommandPreprocessEvent + +class DisallowedCommandsListeners : Listener { + @EventHandler + fun onCommand(event: PlayerCommandPreprocessEvent) { + val command = if (event.message.contains(" ")) event.message.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0] else event.message + Stark.instance.serverHandler.disallowedCommands.stream().filter { blocked -> blocked.equals(this.strip(command), ignoreCase = true) }.forEach { + event.isCancelled = true + event.player.sendMessage("${ChatColor.RED}This action can only be performed by the console.") + } + } + + private fun strip(command: String): String { + var command = command + command = command.toLowerCase().replaceFirst("/".toRegex(), "") + command = command.replace("minecraft:", "") + command = command.replace("bukkit:", "") + command = command.replace("worldedit:", "") + return command + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/FreezeListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/FreezeListeners.kt new file mode 100644 index 0000000..a266e55 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/FreezeListeners.kt @@ -0,0 +1,97 @@ +package net.evilblock.stark.server.listener + +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.EventPriority +import org.bukkit.event.Listener +import org.bukkit.event.block.BlockBreakEvent +import org.bukkit.event.block.BlockPlaceEvent +import org.bukkit.event.entity.EntityDamageByEntityEvent +import org.bukkit.event.inventory.InventoryClickEvent +import org.bukkit.event.inventory.InventoryOpenEvent +import org.bukkit.event.player.* + +class FreezeListeners : Listener { + + @EventHandler(priority = EventPriority.HIGHEST) + fun onPlayerCommandPreprocess(event: PlayerCommandPreprocessEvent) { + val command = event.message.toLowerCase() + val whitelistedCommand = command.startsWith("/freezeserver") || command.startsWith("/auth") || command.startsWith("/register") || command.startsWith("/2fasetup") || command.startsWith("/setup2fa") + if (!whitelistedCommand && event.player.hasMetadata("Locked")) { + event.player.sendMessage(event.player.getMetadata("Locked")[0].asString()) + event.isCancelled = true + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + fun onAsyncPlayerChat(event: AsyncPlayerChatEvent) { + if (event.player.hasMetadata("Locked")) { + event.player.sendMessage(event.player.getMetadata("Locked")[0].asString()) + event.isCancelled = true + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + fun onPlayerDropItem(event: PlayerDropItemEvent) { + if (event.player.hasMetadata("Locked")) { + event.isCancelled = true + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + fun onInventoryOpen(event: InventoryOpenEvent) { + if (event.player.hasMetadata("Locked")) { + event.isCancelled = true + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + fun onBlockBreak(event: BlockBreakEvent) { + if (event.player.hasMetadata("Locked")) { + event.isCancelled = true + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + fun onBlockPlace(event: BlockPlaceEvent) { + if (event.player.hasMetadata("Locked")) { + event.isCancelled = true + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + fun onEntityDamage(event: EntityDamageByEntityEvent) { + if (event.damager is Player && event.damager.hasMetadata("Locked")) { + event.isCancelled = true + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + fun onPlayerInteract(event: PlayerInteractEvent) { + if (event.player.hasMetadata("Locked")) { + event.isCancelled = true + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + fun onPlayerBucketEmpty(event: PlayerBucketEmptyEvent) { + if (event.player.hasMetadata("Locked")) { + event.isCancelled = true + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + fun onPlayerBucketFill(event: PlayerBucketFillEvent) { + if (event.player.hasMetadata("Locked")) { + event.isCancelled = true + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + fun onInventoryClick(event: InventoryClickEvent) { + if (event.whoClicked.hasMetadata("Locked")) { + event.isCancelled = true + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/FrozenPlayerListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/FrozenPlayerListeners.kt new file mode 100644 index 0000000..93fb487 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/FrozenPlayerListeners.kt @@ -0,0 +1,152 @@ +package net.evilblock.stark.server.listener + +import net.evilblock.stark.Stark +import net.evilblock.stark.messaging.event.PlayerMessageEvent +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.EventPriority +import org.bukkit.event.Listener +import org.bukkit.event.block.BlockBreakEvent +import org.bukkit.event.block.BlockPlaceEvent +import org.bukkit.event.entity.EntityDamageByEntityEvent +import org.bukkit.event.entity.EntityDamageEvent +import org.bukkit.event.inventory.InventoryClickEvent +import org.bukkit.event.player.* + +class FrozenPlayerListeners : Listener { + + companion object { + private val FROZEN_MESSAGE = "${ChatColor.RED}You cannot do this while frozen." + } + + @EventHandler(ignoreCancelled = true) + fun onPlayerMessage(event: PlayerMessageEvent) { + if (event.sender.hasMetadata("frozen")) { + if (!event.target.hasPermission("stark.staff")) { + event.isCancelled = true + event.sender.sendMessage(FROZEN_MESSAGE) + } + } + } + + @EventHandler(ignoreCancelled = true) + fun onAsyncPlayerChat(event: AsyncPlayerChatEvent) { + val player = event.player + if (player.hasMetadata("frozen")) { + player.sendMessage(FROZEN_MESSAGE) + event.isCancelled = true + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + fun onPlayerKick(event: PlayerKickEvent) { + val player = event.player + if (player.hasMetadata("frozen")) { + player.removeMetadata("frozen", Stark.instance) + } + } + + @EventHandler + fun onPlayerQuit(event: PlayerQuitEvent) { + val player = event.player + if (player.hasMetadata("frozen")) { + player.removeMetadata("frozen", Stark.instance) + for (otherPlayer in Bukkit.getOnlinePlayers()) { + if (otherPlayer.hasPermission("stark.staff")) { + otherPlayer.sendMessage("") + otherPlayer.sendMessage("${ChatColor.DARK_RED}${ChatColor.BOLD}${player.name} logged out while frozen!") + otherPlayer.sendMessage("") + } + } + } + } + + @EventHandler + fun onPlayerMove(event: PlayerMoveEvent) { + val player = event.player + if (player.hasMetadata("frozen")) { + val from = event.from + val to = event.to + if (from.x != to.x || event.from.z != event.to.z) { + val newLocation = from.block.location.add(0.5, 0.0, 0.5) + newLocation.pitch = to.pitch + newLocation.yaw = to.yaw + event.to = newLocation + } + } + } + + @EventHandler + fun onEntityDamage(event: EntityDamageEvent) { + if (event.entity !is Player) { + return + } + val player = event.entity as Player + if (player.hasMetadata("frozen")) { + event.isCancelled = true + } + } + + @EventHandler + fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) { + if (event.damager !is Player) { + return + } + if (event.damager.hasMetadata("frozen")) { + event.isCancelled = true + (event.damager as Player).sendMessage(FROZEN_MESSAGE) + } + if (event.entity is Player && event.entity.hasMetadata("frozen")) { + (event.damager as Player).sendMessage((event.entity as Player).displayName + ChatColor.RED + " is currently frozen and cannot be damaged.") + } + } + + @EventHandler + fun onInventoryClick(event: InventoryClickEvent) { + val player = event.whoClicked as Player + if (player.hasMetadata("frozen")) { + event.isCancelled = true + player.sendMessage(FROZEN_MESSAGE) + } + } + + @EventHandler + fun onPlayerDropItem(event: PlayerDropItemEvent) { + val player = event.player + if (player.hasMetadata("frozen")) { + event.isCancelled = true + player.updateInventory() + player.sendMessage(FROZEN_MESSAGE) + } + } + + @EventHandler + fun onPlayerInteract(event: PlayerInteractEvent) { + val player = event.player + if (player.hasMetadata("frozen")) { + event.isCancelled = true + player.updateInventory() + } + } + + @EventHandler + fun onBlockBreak(event: BlockBreakEvent) { + val player = event.player + if (player.hasMetadata("frozen")) { + event.isCancelled = true + player.updateInventory() + } + } + + @EventHandler + fun onBlockPlace(event: BlockPlaceEvent) { + val player = event.player + if (player.hasMetadata("frozen")) { + event.isCancelled = true + player.updateInventory() + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/FrozenServerListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/FrozenServerListeners.kt new file mode 100644 index 0000000..723ba67 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/FrozenServerListeners.kt @@ -0,0 +1,117 @@ +package net.evilblock.stark.server.listener + +import net.evilblock.stark.Stark +import net.md_5.bungee.api.ChatColor +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.entity.EntityDamageByEntityEvent +import org.bukkit.event.entity.EntityDamageEvent +import org.bukkit.event.player.* + +class FrozenServerListeners : Listener { + + @EventHandler + fun onPlayerMove(event: PlayerMoveEvent) { + if (!Stark.instance.serverHandler.frozen) { + return + } + + val player = event.player + if (player.hasPermission("stark.staff")) { + return + } + + if (event.from.blockX == event.to.blockX && event.from.blockZ == event.to.blockZ) { + return + } + + val newTo = event.from.block.location.clone().add(0.5, 0.0, 0.5) + newTo.pitch = event.to.pitch + newTo.yaw = event.to.yaw + event.to = newTo + } + + @EventHandler + fun onEntityDamage(event: EntityDamageEvent) { + if (Stark.instance.serverHandler.frozen) { + event.isCancelled = true + } + } + + @EventHandler + fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) { + if (!Stark.instance.serverHandler.frozen) { + return + } + + if (event.damager is Player) { + val player = event.damager as Player + if (player.hasPermission("stark.staff")) { + return + } + + event.isCancelled = true + player.sendMessage(DENY_MESSAGE) + } + } + + @EventHandler + fun onPlayerInteract(event: PlayerInteractEvent) { + if (!Stark.instance.serverHandler.frozen) { + return + } + + val player = event.player + if (player.hasPermission("stark.staff")) { + return + } + + event.isCancelled = true + player.sendMessage(DENY_MESSAGE) + } + + @EventHandler + fun onPlayerJoin(event: PlayerJoinEvent) { + if (Stark.instance.serverHandler.frozen) { + event.player.sendMessage(DENY_MESSAGE) + } + } + + @EventHandler + fun onPlayerTeleport(event: PlayerTeleportEvent) { + if (!Stark.instance.serverHandler.frozen) { + return + } + + val player = event.player + if (player.hasPermission("stark.staff") || event.cause != PlayerTeleportEvent.TeleportCause.ENDER_PEARL) { + return + } + + event.isCancelled = true + event.to = event.from + player.sendMessage(DENY_MESSAGE) + } + + @EventHandler + fun onItemDrop(event: PlayerDropItemEvent) { + if (!Stark.instance.serverHandler.frozen) { + return + } + + val player = event.player + if (player.hasPermission("stark.staff")) { + return + } + + event.isCancelled = true + player.updateInventory() + player.sendMessage(DENY_MESSAGE) + } + + companion object { + private val DENY_MESSAGE: String = ChatColor.RED.toString() + "The server is currently frozen." + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/HeadNameListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/HeadNameListeners.kt new file mode 100644 index 0000000..6808e1b --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/HeadNameListeners.kt @@ -0,0 +1,33 @@ +package net.evilblock.stark.server.listener + +import org.bukkit.ChatColor +import org.bukkit.SkullType +import org.bukkit.block.Skull +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.block.Action +import org.bukkit.event.player.PlayerInteractEvent + +class HeadNameListeners : Listener { + + @EventHandler + fun onPlayerInteract(event: PlayerInteractEvent) { + if (event.action != Action.RIGHT_CLICK_BLOCK) { + return + } + + val player = event.player + val block = event.clickedBlock + + if (block.state is Skull) { + val skull = block.state as Skull + if (skull.skullType !== SkullType.PLAYER) { + return + } + + val owner = if (skull.owner == null) "Steve" else skull.owner + player.sendMessage("${ChatColor.YELLOW}This is the head of: " + owner) + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/TeleportationListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/TeleportationListeners.kt new file mode 100644 index 0000000..d4ccebe --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/server/listener/TeleportationListeners.kt @@ -0,0 +1,39 @@ +package net.evilblock.stark.server.listener + +import org.bukkit.Location +import java.util.UUID +import java.util.HashMap +import org.bukkit.event.EventHandler +import org.bukkit.event.entity.PlayerDeathEvent +import org.bukkit.event.EventPriority +import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerTeleportEvent + +class TeleportationListeners : Listener { + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + fun onPlayerTeleport(event: PlayerTeleportEvent) { + val player = event.player + val cause = event.cause + + if (cause.name.contains("PEARL") || cause.name.contains("PORTAL")) { + return + } + + if (player.hasPermission("essentials.teleport")) { + lastLocation[player.uniqueId] = event.from + } + } + + @EventHandler(priority = EventPriority.MONITOR) + fun onPlayerDeath(event: PlayerDeathEvent) { + val player = event.entity + if (player.hasPermission("essentials.teleport")) { + lastLocation[player.uniqueId] = player.location + } + } + + companion object { + @JvmStatic val lastLocation = HashMap() + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/BungeeUtil.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/BungeeUtil.kt new file mode 100644 index 0000000..682d2b6 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/BungeeUtil.kt @@ -0,0 +1,22 @@ +package net.evilblock.stark.util + +import net.evilblock.stark.Stark +import com.google.common.io.ByteStreams +import org.bukkit.entity.Player + +object BungeeUtil { + + fun sendToServer(player: Player, server: String) { + try { + val out = ByteStreams.newDataOutput() + out.writeUTF("Connect") + out.writeUTF(server) + + player.sendPluginMessage(Stark.instance, "BungeeCord", out.toByteArray()) + } catch (e: Exception) { + e.printStackTrace() + } + + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/Callback.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/Callback.kt new file mode 100644 index 0000000..e4213c7 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/Callback.kt @@ -0,0 +1,7 @@ +package net.evilblock.stark.util + +interface Callback { + + fun callback(value: T) + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/ClassUtils.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/ClassUtils.kt new file mode 100644 index 0000000..a6da1d1 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/ClassUtils.kt @@ -0,0 +1,30 @@ +package net.evilblock.stark.util + +import com.google.common.collect.ImmutableSet +import org.bukkit.plugin.Plugin +import org.reflections.util.ClasspathHelper +import org.reflections.vfs.Vfs +import java.util.HashSet + +object ClassUtils { + @JvmStatic + fun getClassesInPackage(plugin: Plugin, packageName: String): Collection> { + val classes = HashSet>() + for (url in ClasspathHelper.forClassLoader(ClasspathHelper.contextClassLoader(), ClasspathHelper.staticClassLoader(), plugin.javaClass.classLoader)) { + val dir = Vfs.fromURL(url) + try { + for (file in dir.files) { + val name = file.relativePath.replace("/", ".").replace(".class", "") + if (name.startsWith(packageName)) { + classes.add(Class.forName(name)) + } + } + } catch (ex: Exception) { + ex.printStackTrace() + } finally { + dir.close() + } + } + return ImmutableSet.copyOf(classes) + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/ColorMap.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/ColorMap.kt new file mode 100644 index 0000000..a4132a7 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/ColorMap.kt @@ -0,0 +1,43 @@ +package net.evilblock.stark.util + +import org.bukkit.ChatColor + +object ColorMap { + + val dyeMap = mapOf( + ChatColor.WHITE to 15, + ChatColor.GOLD to 14, + ChatColor.AQUA to 12, + ChatColor.YELLOW to 11, + ChatColor.GREEN to 10, + ChatColor.LIGHT_PURPLE to 9, + ChatColor.GRAY to 8, + ChatColor.DARK_GRAY to 7, + ChatColor.DARK_AQUA to 6, + ChatColor.DARK_PURPLE to 5, + ChatColor.BLUE to 4, + ChatColor.DARK_GREEN to 2, + ChatColor.RED to 1, + ChatColor.DARK_RED to 1, + ChatColor.BLACK to 0 + ) + + val woolMap = mapOf( + ChatColor.DARK_RED to 14, + ChatColor.RED to 14, + ChatColor.GOLD to 1, + ChatColor.YELLOW to 4, + ChatColor.GREEN to 5, + ChatColor.DARK_GREEN to 13, + ChatColor.DARK_AQUA to 9, + ChatColor.AQUA to 3, + ChatColor.BLUE to 11, + ChatColor.DARK_PURPLE to 10, + ChatColor.LIGHT_PURPLE to 2, + ChatColor.WHITE to 0, + ChatColor.GRAY to 8, + ChatColor.DARK_GRAY to 7, + ChatColor.BLACK to 15 + ) + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/Cuboid.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/Cuboid.kt new file mode 100644 index 0000000..89bb65b --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/Cuboid.kt @@ -0,0 +1,514 @@ +package net.evilblock.stark.util + +import net.evilblock.stark.Stark +import org.bukkit.Chunk +import org.bukkit.Location +import org.bukkit.World +import org.bukkit.block.Block +import java.util.ArrayList +import java.util.HashMap +import org.bukkit.configuration.serialization.ConfigurationSerializable +import org.bukkit.entity.Entity + +class Cuboid : Iterable, Cloneable, ConfigurationSerializable { + + protected val worldName: String + val lowerX: Int + val lowerY: Int + val lowerZ: Int + val upperX: Int + val upperY: Int + val upperZ: Int + + val lowerNE: Location + get() = Location(this.world, this.lowerX.toDouble(), this.lowerY.toDouble(), this.lowerZ.toDouble()) + + val upperSW: Location + get() = Location(this.world, this.upperX.toDouble(), this.upperY.toDouble(), this.upperZ.toDouble()) + + val blocks: List + get() { + val blockI = this.iterator() + val copy = ArrayList() + while (blockI.hasNext()) { + copy.add(blockI.next()) + } + return copy + } + + val center: Location + get() { + val x1 = this.upperX + 1 + val y1 = this.upperY + 1 + val z1 = this.upperZ + 1 + return Location(this.world, this.lowerX + (x1 - this.lowerX) / 2.0, this.lowerY + (y1 - this.lowerY) / 2.0, this.lowerZ + (z1 - this.lowerZ) / 2.0) + } + + val world: World + get() = + Stark.instance.server.getWorld(worldName) + ?: throw IllegalStateException("World '$worldName' is not loaded") + + val sizeX: Int + get() = this.upperX - this.lowerX + 1 + + val sizeY: Int + get() = this.upperY - this.lowerY + 1 + + val sizeZ: Int + get() = this.upperZ - this.lowerZ + 1 + + val volume: Int + get() = this.sizeX * this.sizeY * this.sizeZ + + val averageLightLevel: Byte + get() { + var total = 0L + var n = 0 + for (b in this) { + if (b.isEmpty()) { + total += b.getLightLevel() + ++n + } + } + return (if (n > 0) (total / n).toByte() else 0).toByte() + } + + val chunks: List + get() { + val res = ArrayList() + val w = this.world + val x1 = this.lowerX and -0x10 + val x2 = this.upperX and -0x10 + val z1 = this.lowerZ and -0x10 + val z2 = this.upperZ and -0x10 + var x3 = x1 + while (x3 <= x2) { + var z3 = z1 + while (z3 <= z2) { + res.add(w.getChunkAt(x3 shr 4, z3 shr 4)) + z3 += 16 + } + x3 += 16 + } + return res + } + + val walls: List + get() { + val blocks = ArrayList() + val min = Location(this.world, this.lowerX.toDouble(), this.lowerY.toDouble(), this.lowerZ.toDouble()) + val max = Location(this.world, this.upperX.toDouble(), this.upperY.toDouble(), this.upperZ.toDouble()) + val minX = min.getBlockX() + val minY = min.getBlockY() + val minZ = min.getBlockZ() + val maxX = max.getBlockX() + val maxY = max.getBlockY() + val maxZ = max.getBlockZ() + for (x in minX..maxX) { + for (y in minY..maxY) { + val minLoc = Location(this.world, x.toDouble(), y.toDouble(), minZ.toDouble()) + val maxLoc = Location(this.world, x.toDouble(), y.toDouble(), maxZ.toDouble()) + blocks.add(minLoc.getBlock()) + blocks.add(maxLoc.getBlock()) + } + } + for (y2 in minY..maxY) { + for (z in minZ..maxZ) { + val minLoc = Location(this.world, minX.toDouble(), y2.toDouble(), z.toDouble()) + val maxLoc = Location(this.world, maxX.toDouble(), y2.toDouble(), z.toDouble()) + blocks.add(minLoc.getBlock()) + blocks.add(maxLoc.getBlock()) + } + } + return blocks + } + + val faces: List + get() { + val blocks = ArrayList() + val min = Location(this.world, this.lowerX.toDouble(), this.lowerY.toDouble(), this.lowerZ.toDouble()) + val max = Location(this.world, this.upperX.toDouble(), this.upperY.toDouble(), this.upperZ.toDouble()) + val minX = min.getBlockX() + val minY = min.getBlockY() + val minZ = min.getBlockZ() + val maxX = max.getBlockX() + val maxY = max.getBlockY() + val maxZ = max.getBlockZ() + for (x in minX..maxX) { + for (y in minY..maxY) { + blocks.add(Location(this.world, x.toDouble(), y.toDouble(), minZ.toDouble()).getBlock()) + blocks.add(Location(this.world, x.toDouble(), y.toDouble(), maxZ.toDouble()).getBlock()) + } + } + for (y2 in minY..maxY) { + for (z in minZ..maxZ) { + blocks.add(Location(this.world, minX.toDouble(), y2.toDouble(), z.toDouble()).getBlock()) + blocks.add(Location(this.world, maxX.toDouble(), y2.toDouble(), z.toDouble()).getBlock()) + } + } + for (z2 in minZ..maxZ) { + for (x2 in minX..maxX) { + blocks.add(Location(this.world, x2.toDouble(), minY.toDouble(), z2.toDouble()).getBlock()) + blocks.add(Location(this.world, x2.toDouble(), maxY.toDouble(), z2.toDouble()).getBlock()) + } + } + return blocks + } + + @JvmOverloads + constructor(l1: Location, l2: Location = l1) { + if (!l1.getWorld().equals(l2.getWorld())) { + throw IllegalArgumentException("Locations must be on the same world") + } + this.worldName = l1.getWorld().getName() + this.lowerX = Math.min(l1.getBlockX(), l2.getBlockX()) + this.lowerY = Math.min(l1.getBlockY(), l2.getBlockY()) + this.lowerZ = Math.min(l1.getBlockZ(), l2.getBlockZ()) + this.upperX = Math.max(l1.getBlockX(), l2.getBlockX()) + this.upperY = Math.max(l1.getBlockY(), l2.getBlockY()) + this.upperZ = Math.max(l1.getBlockZ(), l2.getBlockZ()) + } + + constructor(other: Cuboid) : this(other.world.getName(), other.lowerX, other.lowerY, other.lowerZ, other.upperX, other.upperY, other.upperZ) {} + + constructor(world: World, x1: Int, y1: Int, z1: Int, x2: Int, y2: Int, z2: Int) { + this.worldName = world.getName() + this.lowerX = Math.min(x1, x2) + this.upperX = Math.max(x1, x2) + this.lowerY = Math.min(y1, y2) + this.upperY = Math.max(y1, y2) + this.lowerZ = Math.min(z1, z2) + this.upperZ = Math.max(z1, z2) + } + + private constructor(worldName: String, x1: Int, y1: Int, z1: Int, x2: Int, y2: Int, z2: Int) { + this.worldName = worldName + this.lowerX = Math.min(x1, x2) + this.upperX = Math.max(x1, x2) + this.lowerY = Math.min(y1, y2) + this.upperY = Math.max(y1, y2) + this.lowerZ = Math.min(z1, z2) + this.upperZ = Math.max(z1, z2) + } + + constructor(map: Map) { + this.worldName = map["worldName"] as String + this.lowerX = map["x1"] as Int + this.upperX = map["x2"] as Int + this.lowerY = map["y1"] as Int + this.upperY = map["y2"] as Int + this.lowerZ = map["z1"] as Int + this.upperZ = map["z2"] as Int + } + + override fun serialize(): Map { + val map = HashMap() + map["worldName"] = this.worldName + map["x1"] = this.lowerX + map["y1"] = this.lowerY + map["z1"] = this.lowerZ + map["x2"] = this.upperX + map["y2"] = this.upperY + map["z2"] = this.upperZ + return map + } + + fun corners(): Array { + val w = this.world + return arrayOf( + w.getBlockAt(this.lowerX, this.lowerY, this.lowerZ), + w.getBlockAt(this.lowerX, this.lowerY, this.upperZ), + w.getBlockAt(this.lowerX, this.upperY, this.lowerZ), + w.getBlockAt(this.lowerX, this.upperY, this.upperZ), + w.getBlockAt(this.upperX, this.lowerY, this.lowerZ), + w.getBlockAt(this.upperX, this.lowerY, this.upperZ), + w.getBlockAt(this.upperX, this.upperY, this.lowerZ), + w.getBlockAt(this.upperX, this.upperY, this.upperZ) + ) + } + + fun minCorners(): Array { + val w = this.world + return Array(4) { w.getBlockAt(this.lowerX, this.lowerY, this.lowerZ) } + } + + fun expand(dir: CuboidDirection, amount: Int): Cuboid { + when (dir) { + CuboidDirection.NORTH -> { + return Cuboid(this.worldName, this.lowerX - amount, this.lowerY, this.lowerZ, this.upperX, this.upperY, this.upperZ) + } + CuboidDirection.SOUTH -> { + return Cuboid(this.worldName, this.lowerX, this.lowerY, this.lowerZ, this.upperX + amount, this.upperY, this.upperZ) + } + CuboidDirection.EAST -> { + return Cuboid(this.worldName, this.lowerX, this.lowerY, this.lowerZ - amount, this.upperX, this.upperY, this.upperZ) + } + CuboidDirection.WEST -> { + return Cuboid(this.worldName, this.lowerX, this.lowerY, this.lowerZ, this.upperX, this.upperY, this.upperZ + amount) + } + CuboidDirection.DOWN -> { + return Cuboid(this.worldName, this.lowerX, this.lowerY - amount, this.lowerZ, this.upperX, this.upperY, this.upperZ) + } + CuboidDirection.UP -> { + return Cuboid(this.worldName, this.lowerX, this.lowerY, this.lowerZ, this.upperX, this.upperY + amount, this.upperZ) + } + else -> { + throw IllegalArgumentException("Invalid direction $dir") + } + } + } + + fun shift(dir: CuboidDirection, amount: Int): Cuboid { + return this.expand(dir, amount).expand(dir.opposite(), -amount) + } + + fun outset(dir: CuboidDirection, amount: Int): Cuboid? { + var c: Cuboid? = null + when (dir) { + CuboidDirection.HORIZONTAL -> { + c = this.expand(CuboidDirection.NORTH, amount).expand(CuboidDirection.SOUTH, amount).expand(CuboidDirection.EAST, amount).expand(CuboidDirection.WEST, amount) + } + CuboidDirection.VERTICAL -> { + c = this.expand(CuboidDirection.DOWN, amount).expand(CuboidDirection.UP, amount) + } + CuboidDirection.BOTH -> { + c = this.outset(CuboidDirection.HORIZONTAL, amount)!!.outset(CuboidDirection.VERTICAL, amount) + } + else -> { + throw IllegalArgumentException("Invalid direction $dir") + } + } + return c + } + + fun inset(dir: CuboidDirection, amount: Int): Cuboid? { + return this.outset(dir, -amount) + } + + fun contains(x: Int, y: Int, z: Int): Boolean { + return x >= this.lowerX && x <= this.upperX && y >= this.lowerY && y <= this.upperY && z >= this.lowerZ && z <= this.upperZ + } + + operator fun contains(b: Block): Boolean { + return this.contains(b.getLocation()) + } + + operator fun contains(l: Location): Boolean { + return this.worldName == l.getWorld().getName() && this.contains(l.getBlockX(), l.getBlockY(), l.getBlockZ()) + } + + operator fun contains(e: Entity): Boolean { + return this.contains(e.getLocation()) + } + + fun grow(i: Int): Cuboid { + return this.expand(CuboidDirection.NORTH, i).expand(CuboidDirection.SOUTH, i).expand(CuboidDirection.EAST, i).expand(CuboidDirection.WEST, i) + } + + fun contract(): Cuboid { + return this.contract(CuboidDirection.DOWN).contract(CuboidDirection.SOUTH).contract(CuboidDirection.EAST).contract(CuboidDirection.UP).contract(CuboidDirection.NORTH).contract(CuboidDirection.WEST) + } + + fun contract(dir: CuboidDirection): Cuboid { + var face = this.getFace(dir.opposite()) + when (dir) { + CuboidDirection.DOWN -> { + while (face.containsOnly(0) && face.lowerY > this.lowerY) { + face = face.shift(CuboidDirection.DOWN, 1) + } + return Cuboid(this.worldName, this.lowerX, this.lowerY, this.lowerZ, this.upperX, face.upperY, this.upperZ) + } + CuboidDirection.UP -> { + while (face.containsOnly(0) && face.upperY < this.upperY) { + face = face.shift(CuboidDirection.UP, 1) + } + return Cuboid(this.worldName, this.lowerX, face.lowerY, this.lowerZ, this.upperX, this.upperY, this.upperZ) + } + CuboidDirection.NORTH -> { + while (face.containsOnly(0) && face.lowerX > this.lowerX) { + face = face.shift(CuboidDirection.NORTH, 1) + } + return Cuboid(this.worldName, this.lowerX, this.lowerY, this.lowerZ, face.upperX, this.upperY, this.upperZ) + } + CuboidDirection.SOUTH -> { + while (face.containsOnly(0) && face.upperX < this.upperX) { + face = face.shift(CuboidDirection.SOUTH, 1) + } + return Cuboid(this.worldName, face.lowerX, this.lowerY, this.lowerZ, this.upperX, this.upperY, this.upperZ) + } + CuboidDirection.EAST -> { + while (face.containsOnly(0) && face.lowerZ > this.lowerZ) { + face = face.shift(CuboidDirection.EAST, 1) + } + return Cuboid(this.worldName, this.lowerX, this.lowerY, this.lowerZ, this.upperX, this.upperY, face.upperZ) + } + CuboidDirection.WEST -> { + while (face.containsOnly(0) && face.upperZ < this.upperZ) { + face = face.shift(CuboidDirection.WEST, 1) + } + return Cuboid(this.worldName, this.lowerX, this.lowerY, face.lowerZ, this.upperX, this.upperY, this.upperZ) + } + else -> { + throw IllegalArgumentException("Invalid direction $dir") + } + } + } + + fun getFace(dir: CuboidDirection): Cuboid { + when (dir) { + CuboidDirection.DOWN -> { + return Cuboid(this.worldName, this.lowerX, this.lowerY, this.lowerZ, this.upperX, this.lowerY, this.upperZ) + } + CuboidDirection.UP -> { + return Cuboid(this.worldName, this.lowerX, this.upperY, this.lowerZ, this.upperX, this.upperY, this.upperZ) + } + CuboidDirection.NORTH -> { + return Cuboid(this.worldName, this.lowerX, this.lowerY, this.lowerZ, this.lowerX, this.upperY, this.upperZ) + } + CuboidDirection.SOUTH -> { + return Cuboid(this.worldName, this.upperX, this.lowerY, this.lowerZ, this.upperX, this.upperY, this.upperZ) + } + CuboidDirection.EAST -> { + return Cuboid(this.worldName, this.lowerX, this.lowerY, this.lowerZ, this.upperX, this.upperY, this.lowerZ) + } + CuboidDirection.WEST -> { + return Cuboid(this.worldName, this.lowerX, this.lowerY, this.upperZ, this.upperX, this.upperY, this.upperZ) + } + else -> { + throw IllegalArgumentException("Invalid direction $dir") + } + } + } + + fun containsOnly(blockId: Int): Boolean { + for (b in this) { + if (b.getTypeId() !== blockId) { + return false + } + } + return true + } + + fun getBoundingCuboid(other: Cuboid?): Cuboid { + if (other == null) { + return this + } + val xMin = Math.min(this.lowerX, other.lowerX) + val yMin = Math.min(this.lowerY, other.lowerY) + val zMin = Math.min(this.lowerZ, other.lowerZ) + val xMax = Math.max(this.upperX, other.upperX) + val yMax = Math.max(this.upperY, other.upperY) + val zMax = Math.max(this.upperZ, other.upperZ) + return Cuboid(this.worldName, xMin, yMin, zMin, xMax, yMax, zMax) + } + + fun getRelativeBlock(x: Int, y: Int, z: Int): Block { + return this.world.getBlockAt(this.lowerX + x, this.lowerY + y, this.lowerZ + z) + } + + fun getRelativeBlock(w: World, x: Int, y: Int, z: Int): Block { + return w.getBlockAt(this.lowerX + x, this.lowerY + y, this.lowerZ + z) + } + + override fun iterator(): Iterator { + return CuboidIterator(this.world, this.lowerX, this.lowerY, this.lowerZ, this.upperX, this.upperY, this.upperZ) + } + + public override fun clone(): Cuboid { + return Cuboid(this) + } + + override fun toString(): String { + return "Cuboid: " + this.worldName + "," + this.lowerX + "," + this.lowerY + "," + this.lowerZ + "=>" + this.upperX + "," + this.upperY + "," + this.upperZ + } + + enum class CuboidDirection { + NORTH, + EAST, + SOUTH, + WEST, + UP, + DOWN, + HORIZONTAL, + VERTICAL, + BOTH, + UNKNOWN; + + fun opposite(): CuboidDirection { + when (this) { + NORTH -> { + return SOUTH + } + EAST -> { + return WEST + } + SOUTH -> { + return NORTH + } + WEST -> { + return EAST + } + HORIZONTAL -> { + return VERTICAL + } + VERTICAL -> { + return HORIZONTAL + } + UP -> { + return DOWN + } + DOWN -> { + return UP + } + BOTH -> { + return BOTH + } + else -> { + return UNKNOWN + } + } + } + } + + inner class CuboidIterator(private val w: World, x1: Int, y1: Int, z1: Int, x2: Int, y2: Int, z2: Int) : Iterator { + private val baseX: Int + private val baseY: Int + private val baseZ: Int + private var x: Int = 0 + private var y: Int = 0 + private var z: Int = 0 + private val sizeX: Int + private val sizeY: Int + private val sizeZ: Int + + init { + this.baseX = Math.min(x1, x2) + this.baseY = Math.min(y1, y2) + this.baseZ = Math.min(z1, z2) + this.sizeX = Math.abs(x2 - x1) + 1 + this.sizeY = Math.abs(y2 - y1) + 1 + this.sizeZ = Math.abs(z2 - z1) + 1 + val x3 = false + this.z = if (x3) 1 else 0 + this.y = if (x3) 1 else 0 + this.x = if (x3) 1 else 0 + } + + override fun hasNext(): Boolean { + return this.x < this.sizeX && this.y < this.sizeY && this.z < this.sizeZ + } + + override fun next(): Block { + val b = this.w.getBlockAt(this.baseX + this.x, this.baseY + this.y, this.baseZ + this.z) + if (++this.x >= this.sizeX) { + this.x = 0 + if (++this.y >= this.sizeY) { + this.y = 0 + ++this.z + } + } + return b + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/DateUtil.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/DateUtil.kt new file mode 100644 index 0000000..d349c35 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/DateUtil.kt @@ -0,0 +1,239 @@ +package net.evilblock.stark.util + +import java.math.BigDecimal +import java.text.DecimalFormat +import java.util.* +import java.util.regex.Pattern + +object DateUtil { + private val timePattern = Pattern.compile("(?:([0-9]+)\\s*y[a-z]*[,\\s]*)?(?:([0-9]+)\\s*mo[a-z]*[,\\s]*)?(?:([0-9]+)\\s*w[a-z]*[,\\s]*)?(?:([0-9]+)\\s*d[a-z]*[,\\s]*)?(?:([0-9]+)\\s*h[a-z]*[,\\s]*)?(?:([0-9]+)\\s*m[a-z]*[,\\s]*)?(?:([0-9]+)\\s*(?:s[a-z]*)?)?", 2) + + fun removeTimePattern(input: String): String { + return timePattern.matcher(input).replaceFirst("").trim { it <= ' ' } + } + + @Throws(Exception::class) + fun parseDateDiff(time: String, future: Boolean): Long { + val m = timePattern.matcher(time) + var years = 0 + var months = 0 + var weeks = 0 + var days = 0 + var hours = 0 + var minutes = 0 + var seconds = 0 + var found = false + + while (m.find()) { + if (m.group() != null && !m.group().isEmpty()) { + for (c in 0 until m.groupCount()) { + if (m.group(c) != null && !m.group(c).isEmpty()) { + found = true + break + } + } + + if (found) { + if (m.group(1) != null && !m.group(1).isEmpty()) { + years = Integer.parseInt(m.group(1)) + } + + if (m.group(2) != null && !m.group(2).isEmpty()) { + months = Integer.parseInt(m.group(2)) + } + + if (m.group(3) != null && !m.group(3).isEmpty()) { + weeks = Integer.parseInt(m.group(3)) + } + + if (m.group(4) != null && !m.group(4).isEmpty()) { + days = Integer.parseInt(m.group(4)) + } + + if (m.group(5) != null && !m.group(5).isEmpty()) { + hours = Integer.parseInt(m.group(5)) + } + + if (m.group(6) != null && !m.group(6).isEmpty()) { + minutes = Integer.parseInt(m.group(6)) + } + + if (m.group(7) != null && !m.group(7).isEmpty()) { + seconds = Integer.parseInt(m.group(7)) + } + break + } + } + } + + if (!found) { + throw Exception("Illegal Date") + } else { + val var13 = GregorianCalendar() + if (years > 0) { + var13.add(1, years * if (future) 1 else -1) + } + + if (months > 0) { + var13.add(2, months * if (future) 1 else -1) + } + + if (weeks > 0) { + var13.add(3, weeks * if (future) 1 else -1) + } + + if (days > 0) { + var13.add(5, days * if (future) 1 else -1) + } + + if (hours > 0) { + var13.add(11, hours * if (future) 1 else -1) + } + + if (minutes > 0) { + var13.add(12, minutes * if (future) 1 else -1) + } + + if (seconds > 0) { + var13.add(13, seconds * if (future) 1 else -1) + } + + val max = GregorianCalendar() + max.add(1, 10) + return if (var13.after(max)) max.timeInMillis else var13.timeInMillis + } + } + + internal fun dateDiff(type: Int, fromDate: Calendar, toDate: Calendar, future: Boolean): Int { + var diff = 0 + + var savedDate: Long + savedDate = fromDate.timeInMillis + while (future && !fromDate.after(toDate) || !future && !fromDate.before(toDate)) { + savedDate = fromDate.timeInMillis + fromDate.add(type, if (future) 1 else -1) + ++diff + } + + --diff + fromDate.timeInMillis = savedDate + return diff + } + + fun formatDateDiff(date: Long): String { + val c = GregorianCalendar() + c.timeInMillis = date + val now = GregorianCalendar() + return formatDateDiff(now, c) + } + + fun formatDateDiff(fromDate: Calendar, toDate: Calendar): String { + var future = false + if (toDate == fromDate) { + return "now" + } else { + if (toDate.after(fromDate)) { + future = true + } + + val sb = StringBuilder() + val types = intArrayOf(1, 2, 5, 11, 12, 13) + val names = arrayOf("year", "years", "month", "months", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds") + var accuracy = 0 + + var i = 0 + while (i < types.size && accuracy <= 2) { + val diff = dateDiff(types[i], fromDate, toDate, future) + if (diff > 0) { + ++accuracy + sb.append(" ").append(diff).append(" ").append(names[i * 2 + (if (diff > 1) 1 else 0)]) + } + ++i + } + + return if (sb.length == 0) "now" else sb.toString().trim { it <= ' ' } + } + } + + fun formatSimplifiedDateDiff(date: Long): String { + val c = GregorianCalendar() + c.timeInMillis = date + val now = GregorianCalendar() + return formatSimplifiedDateDiff(now, c) + } + + fun formatSimplifiedDateDiff(fromDate: Calendar, toDate: Calendar): String { + var future = false + if (toDate == fromDate) { + return "now" + } else { + if (toDate.after(fromDate)) { + future = true + } + + val sb = StringBuilder() + val types = intArrayOf(1, 2, 5, 11, 12, 13) + val names = arrayOf("y", "y", "m", "m", "d", "d", "h", "h", "m", "m", "s", "s") + var accuracy = 0 + + var i = 0 + while (i < types.size && accuracy <= 2) { + val diff = dateDiff(types[i], fromDate, toDate, future) + if (diff > 0) { + ++accuracy + sb.append(" ").append(diff).append("").append(names[i * 2 + (if (diff > 1) 1 else 0)]) + } + ++i + } + + return if (sb.length == 0) "now" else sb.toString().trim { it <= ' ' } + } + } + + fun readableTime(time: Long): String { + val SECOND: Short = 1000 + val MINUTE = 60 * SECOND + val HOUR = 60 * MINUTE + val DAY = 24 * HOUR + var ms = time + val text = StringBuilder("") + if (time > DAY.toLong()) { + text.append(time / DAY.toLong()).append(" days ") + ms = time % DAY.toLong() + } + + if (ms > HOUR.toLong()) { + text.append(ms / HOUR.toLong()).append(" hours ") + ms %= HOUR.toLong() + } + + if (ms > MINUTE.toLong()) { + text.append(ms / MINUTE.toLong()).append(" minutes ") + ms %= MINUTE.toLong() + } + + if (ms > SECOND.toLong()) { + text.append(ms / SECOND.toLong()).append(" seconds ") + val var10000 = ms % SECOND.toLong() + } + + return text.toString() + } + + fun readableTime(time: BigDecimal): String? { + var time = time + val text = "" + + if (time.toDouble() <= 60) { + time = time.add(BigDecimal.valueOf(0.1)) + return text + " " + time + "s" + } else if (time.toDouble() <= 3600) { + val minutes = time.toInt() / 60 + val seconds = time.toInt() % 60 + val formatter = DecimalFormat("00") + return text + " " + formatter.format(minutes.toLong()) + ":" + formatter.format(seconds.toLong()) + "m" + } + + return null + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/EntityUtils.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/EntityUtils.kt new file mode 100644 index 0000000..44158a9 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/EntityUtils.kt @@ -0,0 +1,98 @@ +package net.evilblock.stark.util + +import org.bukkit.entity.EntityType +import java.util.EnumMap + + +object EntityUtils { + private val displayNames: EnumMap = EnumMap(EntityType::class.java) + private var currentFakeEntityId: Int = -1 + + @JvmStatic + fun fakeEntityId(): Int { + return currentFakeEntityId-- + } + + @JvmStatic + fun getName(type: EntityType): String? { + return displayNames[type] + } + + @JvmStatic + fun parse(input: String): EntityType? { + for ((key, value) in displayNames) { + if (value.replace(" ", "").equals(input, ignoreCase = true)) { + return key + } + } + for (type in EntityType.values()) { + if (input.equals(type.toString(), ignoreCase = true)) { + return type + } + } + return null + } + + init { + displayNames[EntityType.ARROW] = "Arrow" + displayNames[EntityType.BAT] = "Bat" + displayNames[EntityType.BLAZE] = "Blaze" + displayNames[EntityType.BOAT] = "Boat" + displayNames[EntityType.CAVE_SPIDER] = "Cave Spider" + displayNames[EntityType.CHICKEN] = "Chicken" + displayNames[EntityType.COMPLEX_PART] = "Complex Part" + displayNames[EntityType.COW] = "Cow" + displayNames[EntityType.CREEPER] = "Creeper" + displayNames[EntityType.DROPPED_ITEM] = "Item" + displayNames[EntityType.EGG] = "Egg" + displayNames[EntityType.ENDER_CRYSTAL] = "Ender Crystal" + displayNames[EntityType.ENDER_DRAGON] = "Ender Dragon" + displayNames[EntityType.ENDER_PEARL] = "Ender Pearl" + displayNames[EntityType.ENDER_SIGNAL] = "Ender Signal" + displayNames[EntityType.ENDERMAN] = "Enderman" + displayNames[EntityType.EXPERIENCE_ORB] = "Experience Orb" + displayNames[EntityType.FALLING_BLOCK] = "Falling Block" + displayNames[EntityType.FIREBALL] = "Fireball" + displayNames[EntityType.FIREWORK] = "Firework" + displayNames[EntityType.FISHING_HOOK] = "Fishing Rod Hook" + displayNames[EntityType.GHAST] = "Ghast" + displayNames[EntityType.GIANT] = "Giant" + displayNames[EntityType.HORSE] = "Horse" + displayNames[EntityType.IRON_GOLEM] = "Iron Golem" + displayNames[EntityType.ITEM_FRAME] = "Item Frame" + displayNames[EntityType.LEASH_HITCH] = "Lead Hitch" + displayNames[EntityType.LIGHTNING] = "Lightning" + displayNames[EntityType.MAGMA_CUBE] = "Magma Cube" + displayNames[EntityType.MINECART] = "Minecart" + displayNames[EntityType.MINECART_CHEST] = "Chest Minecart" + displayNames[EntityType.MINECART_FURNACE] = "Furnace Minecart" + displayNames[EntityType.MINECART_HOPPER] = "Hopper Minecart" + displayNames[EntityType.MINECART_MOB_SPAWNER] = "Spawner Minecart" + displayNames[EntityType.MINECART_TNT] = "TNT Minecart" + displayNames[EntityType.OCELOT] = "Ocelot" + displayNames[EntityType.PAINTING] = "Painting" + displayNames[EntityType.PIG] = "Pig" + displayNames[EntityType.PIG_ZOMBIE] = "Zombie Pigman" + displayNames[EntityType.PLAYER] = "Player" + displayNames[EntityType.PRIMED_TNT] = "TNT" + displayNames[EntityType.SHEEP] = "Sheep" + displayNames[EntityType.SILVERFISH] = "Silverfish" + displayNames[EntityType.SKELETON] = "Skeleton" + displayNames[EntityType.SLIME] = "Slime" + displayNames[EntityType.SMALL_FIREBALL] = "Fireball" + displayNames[EntityType.SNOWBALL] = "Snowball" + displayNames[EntityType.SNOWMAN] = "Snowman" + displayNames[EntityType.SPIDER] = "Spider" + displayNames[EntityType.SPLASH_POTION] = "Potion" + displayNames[EntityType.SQUID] = "Squid" + displayNames[EntityType.THROWN_EXP_BOTTLE] = "Experience Bottle" + displayNames[EntityType.UNKNOWN] = "Custom" + displayNames[EntityType.VILLAGER] = "Villager" + displayNames[EntityType.WEATHER] = "Weather" + displayNames[EntityType.WITCH] = "Witch" + displayNames[EntityType.WITHER] = "Wither" + displayNames[EntityType.WITHER_SKULL] = "Wither Skull" + displayNames[EntityType.WOLF] = "Wolf" + displayNames[EntityType.ZOMBIE] = "Zombie" + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/ItemBuilder.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/ItemBuilder.kt new file mode 100644 index 0000000..a3cbd48 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/ItemBuilder.kt @@ -0,0 +1,112 @@ +package net.evilblock.stark.util + +import org.bukkit.inventory.meta.LeatherArmorMeta +import java.util.stream.Collectors +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.Color +import org.bukkit.Material +import org.bukkit.enchantments.Enchantment +import org.bukkit.inventory.ItemStack +import java.util.* + +class ItemBuilder(private val item: ItemStack) { + + constructor(material: Material): this(ItemStack(material)) + constructor(material: Material, amount: Int): this(ItemStack(material, amount)) + + fun amount(amount: Int): ItemBuilder { + item.amount = amount + return this + } + + fun data(data: Short): ItemBuilder { + item.durability = data + return this + } + + fun enchant(enchantment: Enchantment, level: Int): ItemBuilder { + item.addUnsafeEnchantment(enchantment, level) + return this + } + + fun unenchant(enchantment: Enchantment): ItemBuilder { + item.removeEnchantment(enchantment) + return this + } + + fun name(displayName: String?): ItemBuilder { + val meta = item.itemMeta + meta.displayName = if (displayName == null) null else ChatColor.translateAlternateColorCodes('&', displayName) + item.itemMeta = meta + return this + } + + fun addToLore(vararg parts: String): ItemBuilder { + var meta = item.itemMeta + if (meta == null) { + meta = Bukkit.getItemFactory().getItemMeta(item.type) + } + + var lore: MutableList? = meta!!.lore + if (lore == null) { + lore = arrayListOf() + } + + lore.addAll(Arrays.stream(parts).map { part -> ChatColor.translateAlternateColorCodes('&', part) }.collect(Collectors.toList())) + + meta.lore = lore + item.itemMeta = meta + return this + } + + fun setLore(l: Collection): ItemBuilder { + val lore = ArrayList() + val meta = item.getItemMeta() + lore.addAll(l.stream().map { part -> ChatColor.translateAlternateColorCodes('&', part) }.collect(Collectors.toList())) + meta.lore = lore + item.itemMeta = meta + return this + } + + fun color(color: Color): ItemBuilder { + val meta = item.itemMeta as? LeatherArmorMeta ?: throw UnsupportedOperationException("Cannot set color of a non-leather armor item.") + meta.color = color + item.itemMeta = meta + return this + } + + fun setUnbreakable(unbreakable: Boolean): ItemBuilder { + val meta = item.itemMeta + meta.spigot().isUnbreakable = unbreakable + item.itemMeta = meta + return this + } + + fun build(): ItemStack { + return item.clone() + } + + companion object { + @JvmStatic + fun of (material: Material): ItemBuilder { + return ItemBuilder(material, 1) + } + + @JvmStatic + fun of (material: Material, amount: Int): ItemBuilder { + return ItemBuilder(material, amount) + } + + @JvmStatic + fun copyOf(builder: ItemBuilder): ItemBuilder { + return ItemBuilder(builder.build()) + } + + @JvmStatic + fun copyOf(item: ItemStack): ItemBuilder { + return ItemBuilder(item) + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/ItemUtils.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/ItemUtils.kt new file mode 100644 index 0000000..a69c5e9 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/ItemUtils.kt @@ -0,0 +1,201 @@ +package net.evilblock.stark.util + +import net.evilblock.stark.Stark +import org.apache.commons.io.IOUtils +import org.apache.commons.lang.WordUtils +import org.bukkit.ChatColor +import org.bukkit.Material +import java.util.ArrayList +import java.util.HashMap +import java.io.IOException +import org.bukkit.enchantments.Enchantment +import org.bukkit.inventory.ItemStack + +object ItemUtils { + + private val craftItemStack = Reflections.getCBClass("inventory.CraftItemStack")!! + private val asNmsCopyMethod = Reflections.getMethod(craftItemStack, "asNMSCopy", ItemStack::class.java) + private val nameMap: MutableMap = HashMap() + + init { + nameMap.clear() + + val lines = readLines() + for (line in lines!!) { + val parts = line.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + nameMap[parts[0]] = ItemData(Material.getMaterial(Integer.parseInt(parts[1])), java.lang.Short.parseShort(parts[2])) + } + } + + @JvmStatic + fun setDisplayName(itemStack: ItemStack, name: String) { + val itemMeta = itemStack.itemMeta + itemMeta.displayName = name + itemStack.itemMeta = itemMeta + } + + @JvmStatic + fun builder(type: Material): ItemBuilder { + return ItemBuilder(type) + } + + @JvmStatic + operator fun get(input: String, amount: Int): ItemStack? { + val item = get(input) + + if (item != null) { + item.amount = amount + } + + return item + } + + @JvmStatic + operator fun get(input: String): ItemStack? { + var input = input + input = input.toLowerCase().replace(" ", "") + + if (NumberUtils.isInteger(input)) { + return ItemStack(Material.getMaterial(Integer.parseInt(input))) + } + + if (input.contains(":")) { + if (!NumberUtils.isShort(input.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1])) { + return null + } + + if (NumberUtils.isInteger(input.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0])) { + return ItemStack(Material.getMaterial(Integer.parseInt(input.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0])), 1, java.lang.Short.parseShort(input.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1])) + } + + if (!nameMap.containsKey(input.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0].toLowerCase())) { + return null + } + + val data = nameMap[input.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0].toLowerCase()] + + return if (data != null) { + ItemStack(data.material, 1, java.lang.Short.parseShort(input.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1])) + } else null + } else { + return nameMap[input]?.toItemStack() + } + } + + @JvmStatic + fun getName(item: ItemStack): String { + var name = Reflections.callMethod(asNmsCopyMethod!!.invoke(null, item), "getName") as String + + if (name.contains(".")) { + name = WordUtils.capitalize(item.type.toString().toLowerCase().replace("_", " ")) + } + + return name + } + + private fun readLines(): List? { + return try { + IOUtils.readLines(Stark::class.java.classLoader.getResourceAsStream("items.csv")) + } catch (e: IOException) { + e.printStackTrace() + null + } + } + + class ItemData(val material: Material, val data: Short) { + fun getName(): String { + return getName(this.toItemStack()) + } + + fun matches(item: ItemStack?): Boolean { + return item != null && item.type === this.material && item.durability == data + } + + fun toItemStack(): ItemStack { + return ItemStack(this.material, 1, this.data) + } + } + + class ItemBuilder constructor(private var type: Material?) { + private var amount: Int = 0 + private var data: Short = 0 + private var name: String? = null + private var lore: MutableList? = null + private val enchantments: MutableMap + + init { + this.amount = 1 + this.data = 0 + this.lore = ArrayList() + this.enchantments = HashMap() + } + + fun type(type: Material): ItemBuilder { + this.type = type + return this + } + + fun amount(amount: Int): ItemBuilder { + this.amount = amount + return this + } + + fun data(data: Short): ItemBuilder { + this.data = data + return this + } + + fun name(name: String): ItemBuilder { + this.name = name + return this + } + + fun addLore(vararg lore: String): ItemBuilder { + this.lore!!.addAll(mutableListOf(*lore)) + return this + } + + fun addLore(index: Int, lore: String): ItemBuilder { + this.lore!![index] = lore + return this + } + + fun setLore(lore: MutableList): ItemBuilder { + this.lore = lore + return this + } + + fun enchant(enchantment: Enchantment, level: Int): ItemBuilder { + this.enchantments[enchantment] = level + return this + } + + fun unenchant(enchantment: Enchantment): ItemBuilder { + this.enchantments.remove(enchantment) + return this + } + + fun build(): ItemStack { + val item = ItemStack(this.type, this.amount, this.data) + val meta = item.itemMeta + meta.displayName = ChatColor.translateAlternateColorCodes('&', this.name) + + val finalLore = ArrayList() + + for (index in this.lore!!.indices) { + finalLore[index] = ChatColor.translateAlternateColorCodes('&', this.lore!![index]) + } + + meta.lore = finalLore + + for ((key, value) in this.enchantments) { + item.addUnsafeEnchantment(key, value) + } + + item.itemMeta = meta + + return item + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/NumberUtils.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/NumberUtils.kt new file mode 100644 index 0000000..36f9e32 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/NumberUtils.kt @@ -0,0 +1,57 @@ +package net.evilblock.stark.util + +object NumberUtils { + + fun isInteger(s: String): Boolean { + val radix = 10 + var result = 0 + var i = 0 + val len = s.length + var limit = -2147483647 + + if (len > 0) { + val firstChar = s[0] + if (firstChar < '0') { + if (firstChar == '-') { + limit = Integer.MIN_VALUE + } else if (firstChar != '+') { + return false + } + if (len == 1) { + return false + } + ++i + } + + val multmin = limit / radix + while (i < len) { + val digit = Character.digit(s[i++], radix) + if (digit < 0) { + return false + } + if (result < multmin) { + return false + } + result *= radix + if (result < limit + digit) { + return false + } + result -= digit + } + + return true + } + + return false + } + + fun isShort(input: String): Boolean { + if (!isInteger(input)) { + return false + } + + val value = Integer.parseInt(input) + return value > -32768 && value < 32767 + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/PlayerUtils.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/PlayerUtils.kt new file mode 100644 index 0000000..f19ffd6 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/PlayerUtils.kt @@ -0,0 +1,85 @@ +package net.evilblock.stark.util + +import net.evilblock.stark.engine.protocol.InventoryAdapter +import net.evilblock.stark.engine.protocol.PingAdapter +import org.bukkit.GameMode +import org.bukkit.entity.Entity +import org.bukkit.entity.Player +import org.bukkit.entity.Projectile +import java.lang.reflect.Field + +object PlayerUtils { + + private val CURRENT_TICK_FIELD: Field + + init { + val minecraftServerClass = Reflections.getNMSClass("MinecraftServer")!! + CURRENT_TICK_FIELD = Reflections.getField(minecraftServerClass, "currentTick")!! + } + + @JvmOverloads + @JvmStatic + fun resetInventory(player: Player, gameMode: GameMode? = null) { + player.health = player.maxHealth + player.fallDistance = 0.0f + player.foodLevel = 20 + player.saturation = 10.0f + player.level = 0 + player.exp = 0.0f + + if (!player.hasMetadata("modmode")) { + player.inventory.clear() + player.inventory.armorContents = null + } + + player.fireTicks = 0 + + for (potionEffect in player.activePotionEffects) { + player.removePotionEffect(potionEffect.type) + } + + if (gameMode != null && player.gameMode != gameMode) { + player.gameMode = gameMode + } + } + + @JvmStatic + fun getDamageSource(damager: Entity): Player? { + var playerDamager: Player? = null + if (damager is Player) { + playerDamager = damager + } else if (damager is Projectile) { + val projectile = damager + if (projectile.shooter is Player) { + playerDamager = projectile.shooter as Player + } + } + return playerDamager + } + + @JvmStatic + fun hasOpenInventory(player: Player): Boolean { + return hasOwnInventoryOpen(player) || hasOtherInventoryOpen(player) + } + + @JvmStatic + fun hasOwnInventoryOpen(player: Player): Boolean { + return InventoryAdapter.currentlyOpen.contains(player.uniqueId) + } + + @JvmStatic + fun hasOtherInventoryOpen(player: Player): Boolean { + return Reflections.getFieldValue(Reflections.getFieldValue(Reflections.getHandle(player)!!, "activeContainer")!!, "windowId") as Int != 0 + } + + @JvmStatic + fun getPing(player: Player): Int { + return Reflections.getFieldValue(Reflections.getHandle(player)!!, "ping") as Int + } + + @JvmStatic + fun isLagging(player: Player): Boolean { + return !PingAdapter.lastReply.containsKey(player.uniqueId) || (CURRENT_TICK_FIELD.get(null) as Int) - PingAdapter.lastReply[player.uniqueId]!! > 40 + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/Reflections.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/Reflections.kt new file mode 100644 index 0000000..7eedac2 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/Reflections.kt @@ -0,0 +1,214 @@ +package net.evilblock.stark.util + +import org.bukkit.Bukkit +import org.bukkit.World +import org.bukkit.entity.Entity +import java.lang.reflect.Constructor +import java.lang.reflect.Field +import java.lang.reflect.Method +import java.util.* + +object Reflections { + + val ENTITY_PLAYER_CLASS = getNMSClass("EntityPlayer")!! + val CRAFT_HUMAN_ENTITY_CLASS = getCBClass("entity.CraftHumanEntity")!! + val WORLD_CLASS = getNMSClass("World")!! + val WORLD_SERVER_CLASS = getNMSClass("WorldServer")!! + val MINECRAFT_SERVER_CLASS = getNMSClass("MinecraftServer")!! + val PLAYER_INTERACT_MANAGER_CLASS = getNMSClass("PlayerInteractManager")!! + private val GAME_PROFILE_CLASS_LEGACY: Class<*>? = getClass("net.minecraft.util.com.mojang.authlib.GameProfile") + private val GAME_PROFILE_CLASS: Class<*>? = getClass("com.mojang.authlib.GameProfile") + + fun cbVer(): String { + return "org.bukkit.craftbukkit." + ver() + "." + } + + fun nmsVer(): String { + return "net.minecraft.server." + ver() + "." + } + + fun ver(): String { + val pkg = Bukkit.getServer().javaClass.getPackage().name + return pkg.substring(pkg.lastIndexOf(".") + 1) + } + + fun wrapperToPrimitive(clazz: Class<*>): Class<*> { + if (clazz == Boolean::class.java) return Boolean::class.javaPrimitiveType as Class<*> + if (clazz == Int::class.java) return Int::class.javaPrimitiveType as Class<*> + if (clazz == Double::class.java) return Double::class.javaPrimitiveType as Class<*> + if (clazz == Float::class.java) return Float::class.javaPrimitiveType as Class<*> + if (clazz == Long::class.java) return Long::class.javaPrimitiveType as Class<*> + if (clazz == Short::class.java) return Short::class.javaPrimitiveType as Class<*> + if (clazz == Byte::class.java) return Byte::class.javaPrimitiveType as Class<*> + if (clazz == Void::class.java) return Void.TYPE + return if (clazz == Char::class.java) Char::class.javaPrimitiveType as Class<*> else clazz + } + + fun toParamTypes(vararg params: Any): Array?> { + val classes = arrayOfNulls>(params.size) + for (i in params.indices) + classes[i] = wrapperToPrimitive(params[i].javaClass) + return classes + } + + fun getMinecraftServer(): Any? { + return callMethod(Bukkit.getServer(), "getServer") + } + + fun getTPS(): Double { + return (getFieldValue(getMinecraftServer()!!, "recentTps") as DoubleArray)[0] + } + + fun getHandle(e: Entity): Any? { + return callMethod(e, "getHandle") + } + + fun getHandle(w: World): Any? { + return callMethod(w, "getHandle") + } + + fun getGameProfileClass(): Class<*> { + return if (GAME_PROFILE_CLASS_LEGACY != null) { + GAME_PROFILE_CLASS_LEGACY + } else { + GAME_PROFILE_CLASS!! + } + } + + fun createGameProfile(uuid: UUID, name: String): Any { + return callConstructor(getGameProfileClass(), uuid, name)!! + } + + fun getClass(name: String): Class<*>? { + try { + return Class.forName(name) + } catch (e: Exception) { + return null + } + } + + fun getNMSClass(name: String): Class<*>? { + return getClass(nmsVer() + name) + } + + fun getCBClass(name: String): Class<*>? { + return getClass(cbVer() + name) + } + + fun getMethod(clazz: Class<*>, method: String, vararg params: Class<*>): Method? { + try { + val m = clazz.getMethod(method, *params) + m.isAccessible = true + return m + } catch (e: Exception) { + e.printStackTrace() + return null + } + } + + fun getDeclaredMethod(clazz: Class<*>, method: String, vararg params: Class<*>): Method? { + return getDeclaredMethod(false, clazz, method, *params) + } + + fun getDeclaredMethod(suppressed: Boolean, clazz: Class<*>, method: String, vararg params: Class<*>): Method? { + try { + val m = clazz.getDeclaredMethod(method, *params) + m.isAccessible = true + return m + } catch (e: Exception) { + if (!suppressed) { + e.printStackTrace() + } + + return null + } + } + + fun callMethod(`object`: Any, method: String, vararg params: Any): Any? { + try { + val m = `object`.javaClass.getMethod(method, *toParamTypes(*params)) + m.isAccessible = true + return m.invoke(`object`, *params) + } catch (e: Exception) { + e.printStackTrace() + return null + } + + } + + fun callConstructor(clazz: Class<*>, vararg params: Any): Any? { + try { + val con = clazz.getConstructor(*toParamTypes(*params)) + con.isAccessible = true + return con.newInstance(*params) + } catch (e: Exception) { + e.printStackTrace() + return null + } + } + + fun getConstructor(clazz: Class<*>, vararg params: Class<*>): Constructor<*>? { + try { + val con = clazz.getConstructor(*params) + con.isAccessible = true + return con + } catch (e: Exception) { + e.printStackTrace() + return null + } + } + + fun getField(clazz: Class<*>, field: String): Field? { + try { + val f = clazz.getField(field) + f.isAccessible = true + return f + } catch (e: Exception) { + e.printStackTrace() + return null + } + } + + fun getFieldValue(`object`: Any, field: String): Any? { + try { + val f = `object`.javaClass.getField(field) + f.isAccessible = true + return f.get(`object`) + } catch (e: Exception) { + e.printStackTrace() + return null + } + } + + fun setFieldValue(`object`: Any, field: String, value: Any) { + try { + val f = `object`.javaClass.getField(field) + f.isAccessible = true + f.set(`object`, value) + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun setDeclaredField(`object`: Any, field: String, value: Any) { + try { + val f = `object`.javaClass.getDeclaredField(field) + f.isAccessible = true + f.set(`object`, value) + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun getDeclaredField(`object`: Any?, field: String): Any? { + try { + val f = `object`!!.javaClass.getDeclaredField(field) + f.isAccessible = true + return f.get(`object`) + } catch (e: Exception) { + e.printStackTrace() + return null + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/SoundCompat.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/SoundCompat.kt new file mode 100644 index 0000000..f5883db --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/SoundCompat.kt @@ -0,0 +1,68 @@ +package net.evilblock.stark.util + +import org.bukkit.Sound +import org.bukkit.entity.Player +import java.lang.Exception + +enum class SoundCompat( + legacyId: String, + legacyVolume: Float, + legacyPitch: Float, + latestId: String, + latestVolume: Float, + latestPitch: Float) { + + NEUTRAL_CLICK( + "CLICK", + 20.0F, + 1.0F, + "UI_BUTTON_CLICK", + 1.0F, + 1.0F + ), + SUCCESSFUL_CLICK( + "NOTE_PIANO", + 20.0F, + 15.0F, + "BLOCK_NOTE_HARP", + 20.0F, + 15.0F + ), + FAILED_CLICK( + "DIG_GRASS", + 20.0F, + 0.1F, + "BLOCK_GRASS_BREAK", + 20.0F, + 0.1F + ), + MESSAGE_RECEIVED( + "SUCCESSFUL_HIT", + 1.0F, + 0.1F, + "ENTITY_ARROW_HIT_PLAYER", + 1.0F, + 0.1F + ); + + private var sound: Sound + private var volume: Float + private var pitch: Float + + init { + try { + sound = Sound.valueOf(legacyId) + volume = legacyVolume + pitch = legacyPitch + } catch (e: Exception) { + sound = Sound.valueOf(latestId) + volume = latestVolume + pitch = latestPitch + } + } + + fun playSound(player: Player) { + player.playSound(player.location, sound, volume, pitch) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/TextSplitter.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/TextSplitter.kt new file mode 100644 index 0000000..48c5ee1 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/TextSplitter.kt @@ -0,0 +1,48 @@ +package net.evilblock.stark.util + +import java.util.ArrayList + +object TextSplitter { + + @JvmStatic fun split(length: Int, lines: List, linePrefix: String, wordSuffix: String): List { + val builder = StringBuilder() + + for (line in lines) { + builder.append(line.trim { it <= ' ' }) + builder.append(" ") + } + + return split(length, builder.substring(0, builder.length - 1), linePrefix, wordSuffix) + } + + @JvmStatic fun split(length: Int, text: String, linePrefix: String, wordSuffix: String): List { + if (text.length <= length) { + return arrayListOf(linePrefix + text) + } + + val lines = ArrayList() + val split = text.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + var builder = StringBuilder(linePrefix) + + for (i in split.indices) { + if (builder.length + split[i].length >= length) { + lines.add(builder.toString()) + builder = StringBuilder(linePrefix) + } + + builder.append(split[i]) + builder.append(wordSuffix) + + if (i == split.size - 1) { + builder.replace(builder.length - wordSuffix.length, builder.length, "") + } + } + + if (builder.isNotEmpty()) { + lines.add(builder.toString()) + } + + return lines + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/UUIDUtils.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/UUIDUtils.kt new file mode 100644 index 0000000..64a3799 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/UUIDUtils.kt @@ -0,0 +1,28 @@ +package net.evilblock.stark.util + +import net.evilblock.stark.Stark +import com.mongodb.BasicDBList +import java.util.* + +object UUIDUtils { + + @JvmStatic + fun formatPretty(uuid: UUID): String { + return Stark.instance.core.uuidCache.name(uuid) + " [" + uuid + "]" + } + + @JvmStatic + fun uuidsToStrings(toConvert: Collection?): BasicDBList { + if (toConvert == null || toConvert.isEmpty()) { + return BasicDBList() + } + + val dbList = BasicDBList() + for (uuid in toConvert) { + dbList.add(uuid.toString()) + } + + return dbList + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/cuboid/Cuboid.java b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/cuboid/Cuboid.java new file mode 100644 index 0000000..e664989 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/cuboid/Cuboid.java @@ -0,0 +1,723 @@ +package net.evilblock.stark.util.cuboid; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.configuration.serialization.ConfigurationSerializable; + +public class Cuboid implements Iterable, Cloneable, ConfigurationSerializable { + protected final String worldName; + protected final int x1, y1, z1; + protected final int x2, y2, z2; + + /** + * Construct a Cuboid given two Location objects which represent any two corners of the Cuboid. + * Note: The 2 locations must be on the same world. + * + * @param l1 - One of the corners + * @param l2 - The other corner + */ + public Cuboid(Location l1, Location l2) { + if (!l1.getWorld().equals(l2.getWorld())) + throw new IllegalArgumentException("Locations must be on the same world"); + this.worldName = l1.getWorld().getName(); + this.x1 = Math.min(l1.getBlockX(), l2.getBlockX()); + this.y1 = Math.min(l1.getBlockY(), l2.getBlockY()); + this.z1 = Math.min(l1.getBlockZ(), l2.getBlockZ()); + this.x2 = Math.max(l1.getBlockX(), l2.getBlockX()); + this.y2 = Math.max(l1.getBlockY(), l2.getBlockY()); + this.z2 = Math.max(l1.getBlockZ(), l2.getBlockZ()); + } + + /** + * Construct a one-block Cuboid at the given Location of the Cuboid. + * + * @param l1 location of the Cuboid + */ + public Cuboid(Location l1) { + this(l1, l1); + } + + /** + * Copy constructor. + * + * @param other - The Cuboid to copy + */ + public Cuboid(Cuboid other) { + this(other.getWorld().getName(), other.x1, other.y1, other.z1, other.x2, other.y2, other.z2); + } + + /** + * Construct a Cuboid in the given World and xyz co-ordinates + * + * @param world - The Cuboid's world + * @param x1 - X co-ordinate of corner 1 + * @param y1 - Y co-ordinate of corner 1 + * @param z1 - Z co-ordinate of corner 1 + * @param x2 - X co-ordinate of corner 2 + * @param y2 - Y co-ordinate of corner 2 + * @param z2 - Z co-ordinate of corner 2 + */ + public Cuboid(World world, int x1, int y1, int z1, int x2, int y2, int z2) { + this.worldName = world.getName(); + this.x1 = Math.min(x1, x2); + this.x2 = Math.max(x1, x2); + this.y1 = Math.min(y1, y2); + this.y2 = Math.max(y1, y2); + this.z1 = Math.min(z1, z2); + this.z2 = Math.max(z1, z2); + } + + /** + * Construct a Cuboid in the given world name and xyz co-ordinates. + * + * @param worldName - The Cuboid's world name + * @param x1 - X co-ordinate of corner 1 + * @param y1 - Y co-ordinate of corner 1 + * @param z1 - Z co-ordinate of corner 1 + * @param x2 - X co-ordinate of corner 2 + * @param y2 - Y co-ordinate of corner 2 + * @param z2 - Z co-ordinate of corner 2 + */ + private Cuboid(String worldName, int x1, int y1, int z1, int x2, int y2, int z2) { + this.worldName = worldName; + this.x1 = Math.min(x1, x2); + this.x2 = Math.max(x1, x2); + this.y1 = Math.min(y1, y2); + this.y2 = Math.max(y1, y2); + this.z1 = Math.min(z1, z2); + this.z2 = Math.max(z1, z2); + } + + /** + * Construct a Cuboid using a dyeMap with the following keys: worldName, x1, x2, y1, y2, z1, z2 + * + * @param map - The dyeMap of keys. + */ + public Cuboid(Map map) { + this.worldName = (String) map.get("worldName"); + this.x1 = (Integer) map.get("x1"); + this.x2 = (Integer) map.get("x2"); + this.y1 = (Integer) map.get("y1"); + this.y2 = (Integer) map.get("y2"); + this.z1 = (Integer) map.get("z1"); + this.z2 = (Integer) map.get("z2"); + } + + public Map serialize() { + Map map = new HashMap(); + map.put("worldName", this.worldName); + map.put("x1", this.x1); + map.put("y1", this.y1); + map.put("z1", this.z1); + map.put("x2", this.x2); + map.put("y2", this.y2); + map.put("z2", this.z2); + return map; + } + + /** + * Get the Location of the lower northeast corner of the Cuboid (minimum XYZ co-ordinates). + * + * @return Location of the lower northeast corner + */ + public Location getLowerNE() { + return new Location(this.getWorld(), this.x1, this.y1, this.z1); + } + + /** + * Get the Location of the upper southwest corner of the Cuboid (maximum XYZ co-ordinates). + * + * @return Location of the upper southwest corner + */ + public Location getUpperSW() { + return new Location(this.getWorld(), this.x2, this.y2, this.z2); + } + + /** + * Get the blocks in the Cuboid. + * + * @return The blocks in the Cuboid + */ + public List getBlocks() { + Iterator blockI = this.iterator(); + List copy = new ArrayList(); + while (blockI.hasNext()) + copy.add(blockI.next()); + return copy; + } + + /** + * Get the the centre of the Cuboid. + * + * @return Location at the centre of the Cuboid + */ + public Location getCenter() { + int x1 = this.getUpperX() + 1; + int y1 = this.getUpperY() + 1; + int z1 = this.getUpperZ() + 1; + return new Location(this.getWorld(), this.getLowerX() + (x1 - this.getLowerX()) / 2.0, this.getLowerY() + (y1 - this.getLowerY()) / 2.0, this.getLowerZ() + (z1 - this.getLowerZ()) / 2.0); + } + + /** + * Get the Cuboid's world. + * + * @return The World object representing this Cuboid's world + * @throws IllegalStateException if the world is not loaded + */ + public World getWorld() { + World world = Bukkit.getWorld(this.worldName); + if (world == null) throw new IllegalStateException("World '" + this.worldName + "' is not loaded"); + return world; + } + + /** + * Get the size of this Cuboid along the X axis + * + * @return Size of Cuboid along the X axis + */ + public int getSizeX() { + return (this.x2 - this.x1) + 1; + } + + /** + * Get the size of this Cuboid along the Y axis + * + * @return Size of Cuboid along the Y axis + */ + public int getSizeY() { + return (this.y2 - this.y1) + 1; + } + + /** + * Get the size of this Cuboid along the Z axis + * + * @return Size of Cuboid along the Z axis + */ + public int getSizeZ() { + return (this.z2 - this.z1) + 1; + } + + /** + * Get the minimum X co-ordinate of this Cuboid + * + * @return the minimum X co-ordinate + */ + public int getLowerX() { + return this.x1; + } + + /** + * Get the minimum Y co-ordinate of this Cuboid + * + * @return the minimum Y co-ordinate + */ + public int getLowerY() { + return this.y1; + } + + /** + * Get the minimum Z co-ordinate of this Cuboid + * + * @return the minimum Z co-ordinate + */ + public int getLowerZ() { + return this.z1; + } + + /** + * Get the maximum X co-ordinate of this Cuboid + * + * @return the maximum X co-ordinate + */ + public int getUpperX() { + return this.x2; + } + + /** + * Get the maximum Y co-ordinate of this Cuboid + * + * @return the maximum Y co-ordinate + */ + public int getUpperY() { + return this.y2; + } + + /** + * Get the maximum Z co-ordinate of this Cuboid + * + * @return the maximum Z co-ordinate + */ + public int getUpperZ() { + return this.z2; + } + + /** + * Get the Blocks at the eight corners of the Cuboid. + * + * @return array of Block objects representing the Cuboid corners + */ + public Block[] corners() { + Block[] res = new Block[8]; + World w = this.getWorld(); + res[0] = w.getBlockAt(this.x1, this.y1, this.z1); + res[1] = w.getBlockAt(this.x1, this.y1, this.z2); + res[2] = w.getBlockAt(this.x1, this.y2, this.z1); + res[3] = w.getBlockAt(this.x1, this.y2, this.z2); + res[4] = w.getBlockAt(this.x2, this.y1, this.z1); + res[5] = w.getBlockAt(this.x2, this.y1, this.z2); + res[6] = w.getBlockAt(this.x2, this.y2, this.z1); + res[7] = w.getBlockAt(this.x2, this.y2, this.z2); + return res; + } + + /** + * Expand the Cuboid in the given direction by the given amount. Negative amounts will shrink the Cuboid in the given direction. Shrinking a cuboid's face past the opposite face is not an error and will return a valid Cuboid. + * + * @param dir - The direction in which to expand + * @param amount - The number of blocks by which to expand + * @return A new Cuboid expanded by the given direction and amount + */ + public Cuboid expand(CuboidDirection dir, int amount) { + switch (dir) { + case North: + return new Cuboid(this.worldName, this.x1 - amount, this.y1, this.z1, this.x2, this.y2, this.z2); + case South: + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x2 + amount, this.y2, this.z2); + case East: + return new Cuboid(this.worldName, this.x1, this.y1, this.z1 - amount, this.x2, this.y2, this.z2); + case West: + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x2, this.y2, this.z2 + amount); + case Down: + return new Cuboid(this.worldName, this.x1, this.y1 - amount, this.z1, this.x2, this.y2, this.z2); + case Up: + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x2, this.y2 + amount, this.z2); + default: + throw new IllegalArgumentException("Invalid direction " + dir); + } + } + + /** + * Shift the Cuboid in the given direction by the given amount. + * + * @param dir - The direction in which to shift + * @param amount - The number of blocks by which to shift + * @return A new Cuboid shifted by the given direction and amount + */ + public Cuboid shift(CuboidDirection dir, int amount) { + return expand(dir, amount).expand(dir.opposite(), -amount); + } + + /** + * Outset (grow) the Cuboid in the given direction by the given amount. + * + * @param dir - The direction in which to outset (must be Horizontal, Vertical, or Both) + * @param amount - The number of blocks by which to outset + * @return A new Cuboid outset by the given direction and amount + */ + public Cuboid outset(CuboidDirection dir, int amount) { + Cuboid c; + switch (dir) { + case Horizontal: + c = expand(CuboidDirection.North, amount).expand(CuboidDirection.South, amount).expand(CuboidDirection.East, amount).expand(CuboidDirection.West, amount); + break; + case Vertical: + c = expand(CuboidDirection.Down, amount).expand(CuboidDirection.Up, amount); + break; + case Both: + c = outset(CuboidDirection.Horizontal, amount).outset(CuboidDirection.Vertical, amount); + break; + default: + throw new IllegalArgumentException("Invalid direction " + dir); + } + return c; + } + + /** + * Inset (shrink) the Cuboid in the given direction by the given amount. Equivalent + * to calling outset() with a negative amount. + * + * @param dir - The direction in which to inset (must be Horizontal, Vertical, or Both) + * @param amount - The number of blocks by which to inset + * @return A new Cuboid inset by the given direction and amount + */ + public Cuboid inset(CuboidDirection dir, int amount) { + return this.outset(dir, -amount); + } + + /** + * Return true if the point at (x,y,z) is contained within this Cuboid. + * + * @param x - The X co-ordinate + * @param y - The Y co-ordinate + * @param z - The Z co-ordinate + * @return true if the given point is within this Cuboid, false otherwise + */ + public boolean contains(int x, int y, int z) { + return x >= this.x1 && x <= this.x2 && y >= this.y1 && y <= this.y2 && z >= this.z1 && z <= this.z2; + } + + /** + * Check if the given Block is contained within this Cuboid. + * + * @param b - The Block to check for + * @return true if the Block is within this Cuboid, false otherwise + */ + public boolean contains(Block b) { + return this.contains(b.getLocation()); + } + + /** + * Check if the given Location is contained within this Cuboid. + * + * @param l - The Location to check for + * @return true if the Location is within this Cuboid, false otherwise + */ + public boolean contains(Location l) { + if (!this.worldName.equals(l.getWorld().getName())) return false; + return this.contains(l.getBlockX(), l.getBlockY(), l.getBlockZ()); + } + + /** + * Get the volume of this Cuboid. + * + * @return The Cuboid volume, in blocks + */ + public int getVolume() { + return this.getSizeX() * this.getSizeY() * this.getSizeZ(); + } + + /** + * Get the average light level of all empty (air) blocks in the Cuboid. Returns 0 if there are no empty blocks. + * + * @return The average light level of this Cuboid + */ + public byte getAverageLightLevel() { + long total = 0; + int n = 0; + for (Block b : this) { + if (b.isEmpty()) { + total += b.getLightLevel(); + ++n; + } + } + return n > 0 ? (byte) (total / n) : 0; + } + + /** + * Contract the Cuboid, returning a Cuboid with any air around the edges removed, just large enough to include all non-air blocks. + * + * @return A new Cuboid with no external air blocks + */ + public Cuboid contract() { + return this.contract(CuboidDirection.Down).contract(CuboidDirection.South).contract(CuboidDirection.East).contract(CuboidDirection.Up).contract(CuboidDirection.North).contract(CuboidDirection.West); + } + + /** + * Contract the Cuboid in the given direction, returning a new Cuboid which has no exterior empty space. + * E.g. A direction of Down will push the top face downwards as much as possible. + * + * @param dir - The direction in which to contract + * @return A new Cuboid contracted in the given direction + */ + public Cuboid contract(CuboidDirection dir) { + Cuboid face = getFace(dir.opposite()); + switch (dir) { + case Down: + while (face.containsOnly(0) && face.getLowerY() > this.getLowerY()) { + face = face.shift(CuboidDirection.Down, 1); + } + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x2, face.getUpperY(), this.z2); + case Up: + while (face.containsOnly(0) && face.getUpperY() < this.getUpperY()) { + face = face.shift(CuboidDirection.Up, 1); + } + return new Cuboid(this.worldName, this.x1, face.getLowerY(), this.z1, this.x2, this.y2, this.z2); + case North: + while (face.containsOnly(0) && face.getLowerX() > this.getLowerX()) { + face = face.shift(CuboidDirection.North, 1); + } + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, face.getUpperX(), this.y2, this.z2); + case South: + while (face.containsOnly(0) && face.getUpperX() < this.getUpperX()) { + face = face.shift(CuboidDirection.South, 1); + } + return new Cuboid(this.worldName, face.getLowerX(), this.y1, this.z1, this.x2, this.y2, this.z2); + case East: + while (face.containsOnly(0) && face.getLowerZ() > this.getLowerZ()) { + face = face.shift(CuboidDirection.East, 1); + } + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x2, this.y2, face.getUpperZ()); + case West: + while (face.containsOnly(0) && face.getUpperZ() < this.getUpperZ()) { + face = face.shift(CuboidDirection.West, 1); + } + return new Cuboid(this.worldName, this.x1, this.y1, face.getLowerZ(), this.x2, this.y2, this.z2); + default: + throw new IllegalArgumentException("Invalid direction " + dir); + } + } + + /** + * Get the Cuboid representing the face of this Cuboid. The resulting Cuboid will be one block thick in the axis perpendicular to the requested face. + * + * @param dir - which face of the Cuboid to get + * @return The Cuboid representing this Cuboid's requested face + */ + public Cuboid getFace(CuboidDirection dir) { + switch (dir) { + case Down: + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x2, this.y1, this.z2); + case Up: + return new Cuboid(this.worldName, this.x1, this.y2, this.z1, this.x2, this.y2, this.z2); + case North: + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x1, this.y2, this.z2); + case South: + return new Cuboid(this.worldName, this.x2, this.y1, this.z1, this.x2, this.y2, this.z2); + case East: + return new Cuboid(this.worldName, this.x1, this.y1, this.z1, this.x2, this.y2, this.z1); + case West: + return new Cuboid(this.worldName, this.x1, this.y1, this.z2, this.x2, this.y2, this.z2); + default: + throw new IllegalArgumentException("Invalid direction " + dir); + } + } + + /** + * Check if the Cuboid contains only blocks of the given type + * + * @param blockId - The block ID to check for + * @return true if this Cuboid contains only blocks of the given type + */ + public boolean containsOnly(int blockId) { + for (Block b : this) { + if (b.getTypeId() != blockId) return false; + } + return true; + } + + /** + * Get the Cuboid big enough to hold both this Cuboid and the given one. + * + * @param other - The other cuboid. + * @return A new Cuboid large enough to hold this Cuboid and the given Cuboid + */ + public Cuboid getBoundingCuboid(Cuboid other) { + if (other == null) return this; + + int xMin = Math.min(this.getLowerX(), other.getLowerX()); + int yMin = Math.min(this.getLowerY(), other.getLowerY()); + int zMin = Math.min(this.getLowerZ(), other.getLowerZ()); + int xMax = Math.max(this.getUpperX(), other.getUpperX()); + int yMax = Math.max(this.getUpperY(), other.getUpperY()); + int zMax = Math.max(this.getUpperZ(), other.getUpperZ()); + + return new Cuboid(this.worldName, xMin, yMin, zMin, xMax, yMax, zMax); + } + + /** + * Get a block relative to the lower NE point of the Cuboid. + * + * @param x - The X co-ordinate + * @param y - The Y co-ordinate + * @param z - The Z co-ordinate + * @return The block at the given position + */ + public Block getRelativeBlock(int x, int y, int z) { + return this.getWorld().getBlockAt(this.x1 + x, this.y1 + y, this.z1 + z); + } + + /** + * Get a block relative to the lower NE point of the Cuboid in the given World. This + * version of getRelativeBlock() should be used if being called many times, to avoid + * excessive calls to getWorld(). + * + * @param w - The world + * @param x - The X co-ordinate + * @param y - The Y co-ordinate + * @param z - The Z co-ordinate + * @return The block at the given position + */ + public Block getRelativeBlock(World w, int x, int y, int z) { + return w.getBlockAt(this.x1 + x, y1 + y, this.z1 + z); + } + + /** + * Get a list of the chunks which are fully or partially contained in this cuboid. + * + * @return A list of Chunk objects + */ + public List getChunks() { + List res = new ArrayList(); + + World w = this.getWorld(); + int x1 = this.getLowerX() & ~0xf; + int x2 = this.getUpperX() & ~0xf; + int z1 = this.getLowerZ() & ~0xf; + int z2 = this.getUpperZ() & ~0xf; + for (int x = x1; x <= x2; x += 16) { + for (int z = z1; z <= z2; z += 16) { + res.add(w.getChunkAt(x >> 4, z >> 4)); + } + } + return res; + } + + public Iterator iterator() { + return new CuboidIterator(this.getWorld(), this.x1, this.y1, this.z1, this.x2, this.y2, this.z2); + } + + @Override + public Cuboid clone() { + return new Cuboid(this); + } + + @Override + public String toString() { + return new String("Cuboid: " + this.worldName + "," + this.x1 + "," + this.y1 + "," + this.z1 + "=>" + this.x2 + "," + this.y2 + "," + this.z2); + } + + public List getWalls() { + List blocks = new ArrayList(); + + Location min = new Location(getWorld(), this.x1, this.y1, this.z1); + Location max = new Location(getWorld(), this.x2, this.y2, this.z2); + + int minX = min.getBlockX(); + int minY = min.getBlockY(); + int minZ = min.getBlockZ(); + int maxX = max.getBlockX(); + int maxY = max.getBlockY(); + int maxZ = max.getBlockZ(); + Location minLoc; + Location maxLoc; + for (int x = minX; x <= maxX; ++x) { + for (int y = minY; y <= maxY; ++y) { + minLoc = new Location(getWorld(), x, y, minZ); + maxLoc = new Location(getWorld(), x, y, maxZ); + + blocks.add(minLoc.getBlock()); + blocks.add(maxLoc.getBlock()); + } + } + for (int y = minY; y <= maxY; ++y) { + for (int z = minZ; z <= maxZ; ++z) { + minLoc = new Location(getWorld(), minX, y, z); + maxLoc = new Location(getWorld(), maxX, y, z); + + blocks.add(minLoc.getBlock()); + blocks.add(maxLoc.getBlock()); + } + } + + return blocks; + } + + public List getFaces() { + List blocks = new ArrayList(); + + Location min = new Location(getWorld(), this.x1, this.y1, this.z1); + Location max = new Location(getWorld(), this.x2, this.y2, this.z2); + + int minX = min.getBlockX(); + int minY = min.getBlockY(); + int minZ = min.getBlockZ(); + int maxX = max.getBlockX(); + int maxY = max.getBlockY(); + int maxZ = max.getBlockZ(); + + for (int x = minX; x <= maxX; ++x) { + for (int y = minY; y <= maxY; ++y) { + blocks.add(new Location(getWorld(), x, y, minZ).getBlock()); + blocks.add(new Location(getWorld(), x, y, maxZ).getBlock()); + } + } + for (int y = minY; y <= maxY; ++y) { + for (int z = minZ; z <= maxZ; ++z) { + blocks.add(new Location(getWorld(), minX, y, z).getBlock()); + blocks.add(new Location(getWorld(), maxX, y, z).getBlock()); + } + } + for (int z = minZ; z <= maxZ; ++z) { + for (int x = minX; x <= maxX; ++x) { + blocks.add(new Location(getWorld(), x, minY, z).getBlock()); + blocks.add(new Location(getWorld(), x, maxY, z).getBlock()); + } + } + + return blocks; + } + + public enum CuboidDirection { + North, East, South, West, Up, Down, Horizontal, Vertical, Both, Unknown; + + public CuboidDirection opposite() { + switch (this) { + case North: + return South; + case East: + return West; + case South: + return North; + case West: + return East; + case Horizontal: + return Vertical; + case Vertical: + return Horizontal; + case Up: + return Down; + case Down: + return Up; + case Both: + return Both; + default: + return Unknown; + } + } + } + + public class CuboidIterator implements Iterator { + private World w; + private int baseX, baseY, baseZ; + private int x, y, z; + private int sizeX, sizeY, sizeZ; + + public CuboidIterator(World w, int x1, int y1, int z1, int x2, int y2, int z2) { + this.w = w; + this.baseX = Math.min(x1, x2); + this.baseY = Math.min(y1, y2);; + this.baseZ = Math.min(z1, z2);; + this.sizeX = Math.abs(x2 - x1) + 1; + this.sizeY = Math.abs(y2 - y1) + 1; + this.sizeZ = Math.abs(z2 - z1) + 1; + this.x = this.y = this.z = 0; + } + + public boolean hasNext() { + return this.x < this.sizeX && this.y < this.sizeY && this.z < this.sizeZ; + } + + public Block next() { + Block b = this.w.getBlockAt(this.baseX + this.x, this.baseY + this.y, this.baseZ + this.z); + if (++x >= this.sizeX) { + this.x = 0; + if (++this.y >= this.sizeY) { + this.y = 0; + ++this.z; + } + } + return b; + } + + public void remove() { + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/cuboid/CuboidRegion.java b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/cuboid/CuboidRegion.java new file mode 100644 index 0000000..fef6183 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/cuboid/CuboidRegion.java @@ -0,0 +1,283 @@ +package net.evilblock.stark.util.cuboid; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +public class CuboidRegion implements Iterable { + + private int x1, x2, z1, z2, y1, y2; + private String world; + private String name; + private ArrayList tags = new ArrayList(); + + public CuboidRegion(String name, Location corner1, Location corner2) { + this(name, corner1.getWorld().getName(), corner1.getBlockX(), corner1.getBlockY(), corner1.getBlockZ(), corner2.getBlockX(), corner2.getBlockY(), corner2.getBlockZ()); + } + + public CuboidRegion(String name, String world, int x1, int y1, int z1, int x2, int y2, int z2) { + this.world = world; + this.name = name; + this.x1 = Math.min(x1, x2); + this.x2 = Math.max(x1, x2); + this.y1 = Math.min(y1, y2); + this.y2 = Math.max(y1, y2); + this.z1 = Math.min(z1, z2); + this.z2 = Math.max(z1, z2); + } + + /** + * Gets the name of the region + * + * @return name + */ + public String getName() { + return name; + } + + /** + * Gets the list of tags the rg has + * + * @return tags + */ + public ArrayList getTags() { + return tags; + } + + /** + * Adds a tag to the region + * + * @param tag + * the tag to add + */ + public void addTag(String tag) { + tags.add(tag.toLowerCase()); + } + + /** + * Checks if the region has a tag + * + * @param name + * the tag name + */ + public boolean hasTag(String name) { + return tags.contains(name.toLowerCase()); + } + + /** + * Removes a tag from the region + * + * @param tag + * the tag to remove + */ + public void removeTag(String tag) { + tags.remove(tag.toLowerCase()); + + } + + /** + * Gets the minimum point of the region + * + * @return minimum point + */ + public Location getMinimumPoint() { + return new Location(Bukkit.getWorld(world), Math.min(x1, x2), Math.min(y1, y2), Math.min(z1, z2)); + } + + /** + * Gets the maximum point of the region + * + * @return maximum point + */ + public Location getMaximumPoint() { + return new Location(Bukkit.getWorld(world), Math.max(x1, x2), Math.max(y1, y2), Math.max(z1, z2)); + } + + /** + * Sets the name of the region + * + * @param name + * name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Return true if the point at (x,y,z) is contained within this region. + * + * @param x + * the X coordinate + * @param y + * the Y coordinate + * @param z + * the Z coordinate + * @return true if the given point is within this region, false otherwise + */ + public boolean contains(int x, int y, int z) { + return x >= x1 && x <= x2 && y >= y1 && y <= y2 && z >= z1 && z <= z2; + } + + /** + * Check if the given location is contained within this region. + * + * @param l + * the location to check for + * @return true if the location is within this region, false otherwise + */ + public boolean contains(Location l) { + if (!world.equals(l.getWorld().getName())) { + return false; + } + return contains(l.getBlockX(), l.getBlockY(), l.getBlockZ()); + } + + /** + * Checks if the given Block is contained within the region + * + * @param b + * the block to check for + * @return true if the Location is within this Region, false otherwise + */ + public boolean contains(Block b) { + return contains(b.getLocation()); + } + + /** + * Checks if the given Player's location is contained within the region + * + * @param p + * the player to check for + * @return true if the Location is within this region, false otherwise + */ + public boolean contains(Player p) { + return contains(p.getLocation()); + } + + /** + * Gets a set of players that is in the region + * + * @return set of players in the region + */ + public HashSet getPlayers() { + HashSet players = new HashSet(); + for (Player p : Bukkit.getWorld(world).getPlayers()) { + if (contains(p)) + players.add(p); + } + return players; + } + + /** + * Sets the location of the region + *

+ * Handles min and max corner sorting automatically + * + * @param loc1 + * first corner + * @param loc2 + * second corner + */ + public void setLocation(Location loc1, Location loc2) { + setLocation(loc1.getWorld().getName(), loc1.getBlockX(), loc1.getBlockY(), loc1.getBlockZ(), loc2.getBlockX(), loc2.getBlockY(), loc2.getBlockZ()); + } + + /** + * Sets the location of the region + *

+ * Handles min and max corner sorting automatically + * + * @param world + * world name + * @param x1 + * first x coordinate + * @param y1 + * first y coordinate + * @param z1 + * first z coordinate + * @param x2 + * second x coordinate + * @param y2 + * second y coordinate + * @param z2 + * second z coordinate + */ + public void setLocation(String world, int x1, int y1, int z1, int x2, int y2, int z2) { + this.world = world; + this.x1 = Math.min(x1, x2); + this.x2 = Math.max(x1, x2); + this.y1 = Math.min(y1, y2); + this.y2 = Math.max(y1, y2); + this.z1 = Math.min(z1, z2); + this.z2 = Math.max(z1, z2); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof CuboidRegion) { + return ((CuboidRegion) obj).getName().equals(getName()); + } + return super.equals(obj); + } + + @Override + public String toString() { + return name; + } + + public Iterator iterator() { + return new CuboidIterator(world, x1, y1, z1, x2, y2, z2); + } + + /** + * Class to handle location iteration for CuboidRegions + * + * @author Kerem Celik + * + */ + public class CuboidIterator implements Iterator { + private int baseX, baseY, baseZ; + private int x, y, z; + private int sizeX, sizeY, sizeZ; + private String world; + + public CuboidIterator(String world, int x1, int y1, int z1, int x2, int y2, int z2) { + this.world = world; + baseX = x1; + baseY = y1; + baseZ = z1; + sizeX = Math.abs(x2 - x1) + 1; + sizeY = Math.abs(y2 - y1) + 1; + sizeZ = Math.abs(z2 - z1) + 1; + x = y = z = 0; + } + + public boolean hasNext() { + return x < sizeX && y < sizeY && z < sizeZ; + } + + public Location next() { + Location b = new Location(Bukkit.getWorld(world), baseX + x, baseY + y, baseZ + z); + if (++x >= sizeX) { + x = 0; + if (++y >= sizeY) { + y = 0; + ++z; + } + } + return b; + } + + public void remove() {} + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/enchantment/EnchantmentWrapper.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/enchantment/EnchantmentWrapper.kt new file mode 100644 index 0000000..d38e900 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/enchantment/EnchantmentWrapper.kt @@ -0,0 +1,107 @@ +package net.evilblock.stark.util.enchantment + +import org.bukkit.enchantments.Enchantment +import org.bukkit.enchantments.EnchantmentTarget +import org.bukkit.inventory.ItemStack + +enum class EnchantmentWrapper constructor(val friendlyName: String, parse: Array) { + + PROTECTION_ENVIRONMENTAL("Protection", arrayOf("p", "prot", "protect")), + PROTECTION_FIRE("Fire Protection", arrayOf("fp", "fprot", "fireprot", "fireprotection", "firep")), + PROTECTION_FALL("Feather Falling", arrayOf("ff", "featherf", "ffalling")), + PROTECTION_EXPLOSIONS("Blast Protection", arrayOf("explosionsprotection", "explosionprotection", "bprotection", "bprotect", "blastprotect", "pe", "bp")), + PROTECTION_PROJECTILE("Projectile Protection", arrayOf("pp", "projprot", "projprotection", "projp", "pprot")), + THORNS("Thorns", arrayOf()), + DURABILITY("Unbreaking", arrayOf("unbr", "unb", "dur", "dura")), + DAMAGE_ALL("Sharpness", arrayOf("s", "sharp")), + DAMAGE_UNDEAD("Smite", arrayOf("du", "dz")), + DAMAGE_ARTHROPODS("Bane of Arthropods", arrayOf("bane", "ardmg", "baneofarthropod", "arthropod", "dar", "dspider")), + KNOCKBACK("Knockback", arrayOf("k", "knock", "kb")), + FIRE_ASPECT("Fire Aspect", arrayOf("fire", "fa")), + OXYGEN("Respiration", arrayOf("oxygen", "breathing", "o", "breath")), + WATER_WORKER("Aqua Affinity", arrayOf("aa")), + LOOT_BONUS_MOBS("Looting", arrayOf("moblooting", "ml", "loot")), + DIG_SPEED("Efficiency", arrayOf("e", "eff", "digspeed", "ds")), + SILK_TOUCH("Silk Touch", arrayOf("silk", "st")), + LOOT_BONUS_BLOCKS("Fortune", arrayOf("fort", "lbm")), + ARROW_DAMAGE("Power", arrayOf("apower", "adamage", "admg")), + ARROW_KNOCKBACK("Punch", arrayOf("akb", "arrowkb", "arrowknockback", "aknockback")), + ARROW_FIRE("Fire", arrayOf("afire", "arrowfire")), + ARROW_INFINITE("Infinity", arrayOf("infinitearrows", "infinite", "inf", "infarrows", "unlimitedarrows", "ai", "uarrows", "unlimited")), + LUCK("Luck of the Sea", arrayOf("rodluck", "luckofsea", "los")), + LURE("Lure", arrayOf("rodlure")); + + var parse: Array + internal set + + val maxLevel: Int + get() = this.bukkitEnchantment.getMaxLevel() + + val startLevel: Int + get() = this.bukkitEnchantment.getStartLevel() + + val itemTarget: EnchantmentTarget + get() = this.bukkitEnchantment.getItemTarget() + + val bukkitEnchantment: Enchantment + get() = Enchantment.getByName(this.name) + + init { + this.parse = parse + } + + fun enchant(item: ItemStack, level: Int) { + item.addUnsafeEnchantment(this.bukkitEnchantment, level) + } + + fun conflictsWith(enchantment: Enchantment): Boolean { + return this.bukkitEnchantment.conflictsWith(enchantment) + } + + fun canEnchantItem(item: ItemStack): Boolean { + return this.bukkitEnchantment.canEnchantItem(item) + } + + override fun toString(): String { + return this.bukkitEnchantment.toString() + } + + companion object { + + @JvmStatic + fun parse(input: String): EnchantmentWrapper? { + for (enchantment in values()) { + for (str in enchantment.parse) { + if (str.equals(input, ignoreCase = true)) { + return enchantment + } + } + + if (enchantment.bukkitEnchantment.name.replace("_", "").equals(input, ignoreCase = true)) { + return enchantment + } + + if (enchantment.bukkitEnchantment.name.equals(input, ignoreCase = true)) { + return enchantment + } + + if (enchantment.friendlyName.equals(input, ignoreCase = true)) { + return enchantment + } + } + + return null + } + + @JvmStatic + fun parse(enchantment: Enchantment): EnchantmentWrapper { + for (possible in values()) { + if (possible.bukkitEnchantment === enchantment) { + return possible + } + } + throw IllegalArgumentException("Invalid enchantment given for parsing.") + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/event/HourEvent.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/event/HourEvent.kt new file mode 100644 index 0000000..1452c33 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/event/HourEvent.kt @@ -0,0 +1,17 @@ +package net.evilblock.stark.util.event + +import org.bukkit.event.Event +import org.bukkit.event.HandlerList + +class HourEvent constructor(val hour: Int) : Event() { + + override fun getHandlers(): HandlerList { + return handlerList + } + + companion object { + @JvmStatic + val handlerList: HandlerList = HandlerList() + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/pagination/PaginatedResult.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/pagination/PaginatedResult.kt new file mode 100644 index 0000000..ec829cb --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/pagination/PaginatedResult.kt @@ -0,0 +1,43 @@ +package net.evilblock.stark.util.pagination + +import java.util.ArrayList +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender + +abstract class PaginatedResult @JvmOverloads constructor(private val resultsPerPage: Int = 9) { + + init { + assert(resultsPerPage > 0) + } + + fun display(sender: CommandSender, results: Collection, page: Int) { + this.display(sender, ArrayList(results), page) + } + + fun display(sender: CommandSender, results: List, page: Int) { + if (results.isEmpty()) { + sender.sendMessage(ChatColor.RED.toString() + "No entries were found.") + return + } + + val maxPages = results.size / this.resultsPerPage + 1 + + if (page <= 0 || page > maxPages) { + sender.sendMessage(ChatColor.RED.toString() + "Page '" + page + "' not found. (" + ChatColor.YELLOW + "1 - " + maxPages + ChatColor.RED + ")") + return + } + + sender.sendMessage(this.getHeader(page, maxPages)) + + var i = this.resultsPerPage * (page - 1) + while (i < this.resultsPerPage * page && i < results.size) { + sender.sendMessage(this.format(results[i], i)) + ++i + } + } + + abstract fun getHeader(page: Int, maxPages: Int): String + + abstract fun format(result: T, resultIndex: Int): String + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/BlockVectorAdapter.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/BlockVectorAdapter.kt new file mode 100644 index 0000000..4c7bdb3 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/BlockVectorAdapter.kt @@ -0,0 +1,40 @@ +package net.evilblock.stark.util.serialization + +import com.google.gson.* +import org.bukkit.util.BlockVector +import java.lang.reflect.Type + +class BlockVectorAdapter : JsonDeserializer, JsonSerializer { + @Throws(JsonParseException::class) + override fun deserialize(src: JsonElement, type: Type, context: JsonDeserializationContext): BlockVector? { + return fromJson(src) + } + + override fun serialize(src: BlockVector, type: Type, context: JsonSerializationContext): JsonElement { + return toJson(src) as JsonElement + } + + companion object { + fun toJson(src: BlockVector?): JsonObject? { + if (src == null) { + return null + } + val `object` = JsonObject() + `object`.addProperty("x", src.x as Number) + `object`.addProperty("y", src.y as Number) + `object`.addProperty("z", src.z as Number) + return `object` + } + + fun fromJson(src: JsonElement?): BlockVector? { + if (src == null || !src.isJsonObject) { + return null + } + val json = src.asJsonObject + val x = json.get("x").asDouble + val y = json.get("y").asDouble + val z = json.get("z").asDouble + return BlockVector(x, y, z) + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/ItemStackAdapter.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/ItemStackAdapter.kt new file mode 100644 index 0000000..b5fb197 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/ItemStackAdapter.kt @@ -0,0 +1,190 @@ +package net.evilblock.stark.util.serialization + +import com.google.gson.* +import org.bukkit.Color +import org.bukkit.Material +import org.bukkit.enchantments.Enchantment +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.meta.* +import org.bukkit.potion.PotionEffect +import java.lang.reflect.Type +import java.util.* + +class ItemStackAdapter : JsonDeserializer, JsonSerializer { + override fun serialize(item: ItemStack, type: Type, context: JsonSerializationContext): JsonElement { + return serialize(item) + } + + @Throws(JsonParseException::class) + override fun deserialize(element: JsonElement, type: Type, context: JsonDeserializationContext): ItemStack { + return deserialize(element) + } + + companion object { + + fun serialize(item: ItemStack?): JsonElement { + var item = item + if (item == null) { + item = ItemStack(Material.AIR) + } + + val element = JsonObject() + element.addProperty("id", item.getTypeId() as Number) + element.addProperty(getDataKey(item), item.getDurability() as Number) + element.addProperty("count", item.getAmount() as Number) + + if (item.hasItemMeta()) { + val meta = item.getItemMeta() + if (meta.hasDisplayName()) { + element.addProperty("name", meta.getDisplayName()) + } + + if (meta.hasLore()) { + element.add("lore", convertStringList(meta.getLore()) as JsonElement) + } + + if (meta is LeatherArmorMeta) { + element.addProperty("color", meta.color.asRGB() as Number) + } else if (meta is SkullMeta) { + element.addProperty("skull", meta.owner) + } else if (meta is BookMeta) { + element.addProperty("title", meta.title) + element.addProperty("author", meta.author) + element.add("pages", convertStringList(meta.pages) as JsonElement) + } else if (meta is PotionMeta) { + if (meta.customEffects.isNotEmpty()) { + element.add("potion-effects", convertPotionEffectList(meta.customEffects) as JsonElement) + } + } else if (meta is MapMeta) { + element.addProperty("scaling", java.lang.Boolean.valueOf(meta.isScaling)) + } else if (meta is EnchantmentStorageMeta) { + val storedEnchantments = JsonObject() + for ((key, value) in meta.storedEnchants) { + storedEnchantments.addProperty(key.name, value as Number) + } + element.add("stored-enchants", storedEnchantments as JsonElement) + } + } + + if (item.getEnchantments().isNotEmpty()) { + val enchantments = JsonObject() + for (entry2 in item.getEnchantments().entries) { + enchantments.addProperty(entry2.key.getName(), entry2.value as Number) + } + element.add("enchants", enchantments as JsonElement) + } + + return element + } + + fun deserialize(`object`: JsonElement?): ItemStack { + if (`object` == null || `object` !is JsonObject) { + return ItemStack(Material.AIR) + } + val element = `object` as JsonObject? + val id = element!!.get("id").asInt + val data = (if (element.has("damage")) element.get("damage").asShort else if (element.has("data")) element.get("data").asShort else 0).toShort() + val count = element.get("count").asInt + val item = ItemStack(id, count, data) + val meta = item.itemMeta + + if (element.has("name")) { + meta.setDisplayName(element.get("name").asString) + } + + if (element.has("lore")) { + meta.setLore(convertStringList(element.get("lore"))) + } + + if (element.has("color")) { + (meta as LeatherArmorMeta).color = Color.fromRGB(element.get("color").asInt) + } else if (element.has("skull")) { + (meta as SkullMeta).owner = element.get("skull").asString + } else if (element.has("title")) { + (meta as BookMeta).title = element.get("title").asString + meta.author = element.get("author").asString + meta.setPages(convertStringList(element.get("pages"))) + } else if (element.has("potion-effects")) { + val potionMeta = meta as PotionMeta + for (effect in convertPotionEffectList(element.get("potion-effects"))!!) { + potionMeta.addCustomEffect(effect, false) + } + } else if (element.has("scaling")) { + (meta as MapMeta).isScaling = element.get("scaling").getAsBoolean() + } else if (element.has("stored-enchants")) { + val enchantments = element.get("stored-enchants") as JsonObject + for (enchantment in Enchantment.values()) { + if (enchantments.has(enchantment.getName())) { + (meta as EnchantmentStorageMeta).addStoredEnchant(enchantment, enchantments.get(enchantment.getName()).asInt, true) + } + } + } + + item.itemMeta = meta + + if (element.has("enchants")) { + val enchantments = element.get("enchants") as JsonObject + for (enchantment in Enchantment.values()) { + if (enchantments.has(enchantment.getName())) { + item.addUnsafeEnchantment(enchantment, enchantments.get(enchantment.getName()).asInt) + } + } + } + + return item + } + + private fun getDataKey(item: ItemStack): String { + if (item.getType() === Material.AIR) { + return "data" + } + return if (Enchantment.DURABILITY.canEnchantItem(item)) { + "damage" + } else "data" + } + + fun convertStringList(strings: Collection): JsonArray { + val ret = JsonArray() + for (string in strings) { + ret.add(JsonPrimitive(string) as JsonElement) + } + return ret + } + + fun convertStringList(jsonElement: JsonElement): List { + val array = jsonElement.getAsJsonArray() + val ret = ArrayList() + for (element in array) { + ret.add(element.asString) + } + return ret + } + + fun convertPotionEffectList(potionEffects: Collection): JsonArray { + val ret = JsonArray() + for (e in potionEffects) { + ret.add(PotionEffectAdapter.toJson(e) as JsonElement) + } + return ret + } + + fun convertPotionEffectList(jsonElement: JsonElement?): List? { + if (jsonElement == null) { + return null + } + + if (!jsonElement.isJsonArray()) { + return null + } + + val array = jsonElement.getAsJsonArray() + val ret = ArrayList() + for (element in array) { + val e = PotionEffectAdapter.fromJson(element) ?: continue + ret.add(e) + } + + return ret + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/ItemStackSerializer.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/ItemStackSerializer.kt new file mode 100644 index 0000000..63be43e --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/ItemStackSerializer.kt @@ -0,0 +1,84 @@ +package net.evilblock.stark.util.serialization + +import com.mongodb.BasicDBList +import com.mongodb.BasicDBObject +import org.bukkit.Material +import org.bukkit.enchantments.Enchantment +import org.bukkit.inventory.ItemStack + +object ItemStackSerializer { + + private val AIR: BasicDBObject = BasicDBObject() + + init { + AIR["type"] = "AIR" + AIR["amount"] = 1 + AIR["data"] = 0 + } + + @JvmStatic + fun serialize(itemStack: ItemStack?): BasicDBObject { + if (itemStack == null || itemStack.getType() === Material.AIR) { + return AIR + } + + val item = BasicDBObject("type", itemStack.type.toString()) + .append("amount", itemStack.amount) + .append("data", itemStack.durability) + + val enchants = BasicDBList() + for (entry in itemStack.enchantments.entries) { + enchants.add(BasicDBObject("enchantment", entry.key.name).append("level", entry.value)) + } + + if (itemStack.enchantments.isNotEmpty()) { + item.append("enchants", enchants) + } + + if (itemStack.hasItemMeta()) { + val m = itemStack.itemMeta + val meta = BasicDBObject("displayName", m.displayName) + item.append("meta", meta) + } + + return item + } + + @JvmStatic + fun deserialize(dbObject: BasicDBObject?): ItemStack { + if (dbObject == null || dbObject.isEmpty()) { + return ItemStack(Material.AIR) + } + + val type = Material.valueOf(dbObject.getString("type")) + val item = ItemStack(type, dbObject.getInt("amount")) + + item.durability = java.lang.Short.parseShort(dbObject.getString("data")) + + if (dbObject.containsField("enchants")) { + val enchantments = dbObject.get("enchants") as BasicDBList + + for (o in enchantments) { + val enchant = o as BasicDBObject + item.addUnsafeEnchantment(Enchantment.getByName(enchant.getString("enchantment")), enchant.getInt("level")) + } + } + + if (dbObject.containsField("meta")) { + val meta = dbObject.get("meta") as BasicDBObject + val m = item.itemMeta + + if (meta.containsField("displayName")) { + m.displayName = meta.getString("displayName") + } + + if (meta.containsField("lore")) { + m.lore = arrayListOf() + } + + item.itemMeta = m + } + return item + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/LocationAdapter.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/LocationAdapter.kt new file mode 100644 index 0000000..4b0e9f9 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/LocationAdapter.kt @@ -0,0 +1,50 @@ +package net.evilblock.stark.util.serialization + +import net.evilblock.stark.Stark +import com.google.gson.* +import org.bukkit.Location +import java.lang.reflect.Type + +class LocationAdapter : JsonDeserializer, JsonSerializer { + override fun serialize(src: Location, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { + return toJson(src) as JsonElement + } + + @Throws(JsonParseException::class) + override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Location? { + return fromJson(json) + } + + companion object { + fun toJson(location: Location?): JsonObject? { + if (location == null) { + return null + } + + val jsonObject = JsonObject() + jsonObject.addProperty("world", location.getWorld().getName()) + jsonObject.addProperty("x", location.getX() as Number) + jsonObject.addProperty("y", location.getY() as Number) + jsonObject.addProperty("z", location.getZ() as Number) + jsonObject.addProperty("yaw", location.getYaw() as Number) + jsonObject.addProperty("pitch", location.getPitch() as Number) + return jsonObject + } + + fun fromJson(jsonElement: JsonElement?): Location? { + if (jsonElement == null || !jsonElement.isJsonObject()) { + return null + } + + val jsonObject = jsonElement.getAsJsonObject() + val world = Stark.instance.server.getWorld(jsonObject.get("world").getAsString()) + val x = jsonObject.get("x").getAsDouble() + val y = jsonObject.get("y").getAsDouble() + val z = jsonObject.get("z").getAsDouble() + val yaw = jsonObject.get("yaw").getAsFloat() + val pitch = jsonObject.get("pitch").getAsFloat() + + return Location(world, x, y, z, yaw, pitch) + } + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/LocationSerializer.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/LocationSerializer.kt new file mode 100644 index 0000000..3bc898d --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/LocationSerializer.kt @@ -0,0 +1,40 @@ +package net.evilblock.stark.util.serialization + +import net.evilblock.stark.Stark +import com.mongodb.BasicDBObject +import org.bukkit.Location + +object LocationSerializer { + + @JvmStatic + fun serialize(location: Location?): BasicDBObject { + if (location == null) { + return BasicDBObject() + } + + val dbObject = BasicDBObject() + dbObject["world"] = location.world.name + dbObject["x"] = location.x + dbObject["y"] = location.y + dbObject["z"] = location.z + dbObject.append("yaw", location.yaw) + dbObject.append("pitch", location.pitch) + return dbObject + } + + @JvmStatic + fun deserialize(dbObject: BasicDBObject?): Location? { + if (dbObject == null || dbObject.isEmpty()) { + return null + } + + val world = Stark.instance.server.getWorld(dbObject.getString("world")) + val x = dbObject.getDouble("x") + val y = dbObject.getDouble("y") + val z = dbObject.getDouble("z") + val yaw = dbObject.getInt("yaw") + val pitch = dbObject.getInt("pitch") + return Location(world, x, y, z, yaw.toFloat(), pitch.toFloat()) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/PlayerInventorySerializer.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/PlayerInventorySerializer.kt new file mode 100644 index 0000000..52ff83f --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/PlayerInventorySerializer.kt @@ -0,0 +1,70 @@ +package net.evilblock.stark.util.serialization + +import net.evilblock.stark.Stark +import com.mongodb.BasicDBObject +import org.bukkit.Material +import org.bukkit.entity.Player +import org.bukkit.inventory.ItemStack +import org.bukkit.potion.PotionEffect + +object PlayerInventorySerializer { + + @JvmStatic + fun serialize(player: Player): String { + return Stark.plainGson.toJson(PlayerInventoryWrapper(player) as Any) + } + + @JvmStatic + fun deserialize(json: String): PlayerInventoryWrapper { + return Stark.plainGson.fromJson(json, PlayerInventorySerializer.PlayerInventoryWrapper::class.java) + } + + @JvmStatic + fun getInsertableObject(player: Player): BasicDBObject { + return BasicDBObject.parse(serialize(player)) + } + + class PlayerInventoryWrapper(player: Player) { + val effects: Array + val contents: Array = player.inventory.contents + val armor: Array + val health: Double + val hunger: Int + + init { + for (i in this.contents.indices) { + val stack = this.contents[i] + if (stack == null) { + this.contents[i] = ItemStack(Material.AIR, 0, 0.toShort()) + } + } + + this.armor = player.inventory.armorContents + + for (i in this.armor.indices) { + val stack = this.armor[i] + if (stack == null) { + this.armor[i] = ItemStack(Material.AIR, 0, 0.toShort()) + } + } + + this.effects = player.activePotionEffects.toTypedArray() + this.health = player.health + this.hunger = player.foodLevel + } + + fun apply(player: Player) { + player.inventory.contents = this.contents + player.inventory.armorContents = this.armor + + for (effect in player.activePotionEffects) { + player.removePotionEffect(effect.type) + } + + for (effect2 in this.effects) { + player.addPotionEffect(effect2) + } + } + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/PotionEffectAdapter.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/PotionEffectAdapter.kt new file mode 100644 index 0000000..6c80432 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/PotionEffectAdapter.kt @@ -0,0 +1,46 @@ +package net.evilblock.stark.util.serialization + +import com.google.gson.* +import org.bukkit.potion.PotionEffect +import org.bukkit.potion.PotionEffectType +import java.lang.reflect.Type + +class PotionEffectAdapter : JsonDeserializer, JsonSerializer { + override fun serialize(src: PotionEffect, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { + return toJson(src) as JsonElement + } + + @Throws(JsonParseException::class) + override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): PotionEffect? { + return fromJson(json) + } + + companion object { + fun toJson(potionEffect: PotionEffect?): JsonObject? { + if (potionEffect == null) { + return null + } + + val jsonObject = JsonObject() + jsonObject.addProperty("id", potionEffect.type.id as Number) + jsonObject.addProperty("duration", potionEffect.duration as Number) + jsonObject.addProperty("amplifier", potionEffect.amplifier as Number) + jsonObject.addProperty("ambient", java.lang.Boolean.valueOf(potionEffect.isAmbient)) + return jsonObject + } + + fun fromJson(jsonElement: JsonElement?): PotionEffect? { + if (jsonElement == null || !jsonElement!!.isJsonObject()) { + return null + } + + val jsonObject = jsonElement!!.getAsJsonObject() + val effectType = PotionEffectType.getById(jsonObject.get("id").getAsInt()) + val duration = jsonObject.get("duration").getAsInt() + val amplifier = jsonObject.get("amplifier").getAsInt() + val ambient = jsonObject.get("ambient").getAsBoolean() + + return PotionEffect(effectType, duration, amplifier, ambient) + } + } +} diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/VectorAdapter.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/VectorAdapter.kt new file mode 100644 index 0000000..b4e79b6 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/util/serialization/VectorAdapter.kt @@ -0,0 +1,41 @@ +package net.evilblock.stark.util.serialization + +import com.google.gson.* +import org.bukkit.util.Vector +import java.lang.reflect.Type + +class VectorAdapter : JsonDeserializer, JsonSerializer { + @Throws(JsonParseException::class) + override fun deserialize(src: JsonElement, type: Type, context: JsonDeserializationContext): Vector? { + return fromJson(src) + } + + override fun serialize(src: Vector, type: Type, context: JsonSerializationContext): JsonElement { + return toJson(src) as JsonElement + } + + companion object { + + fun toJson(src: Vector?): JsonObject? { + if (src == null) { + return null + } + val `object` = JsonObject() + `object`.addProperty("x", src.getX() as Number) + `object`.addProperty("y", src.getY() as Number) + `object`.addProperty("z", src.getZ() as Number) + return `object` + } + + fun fromJson(src: JsonElement?): Vector? { + if (src == null || !src.isJsonObject()) { + return null + } + val json = src.getAsJsonObject() + val x = json.get("x").getAsDouble() + val y = json.get("y").getAsDouble() + val z = json.get("z").getAsDouble() + return Vector(x, y, z) + } + } +} diff --git a/stark/bukkit/src/main/kotlin/net/evilblock/stark/uuid/UUIDListeners.kt b/stark/bukkit/src/main/kotlin/net/evilblock/stark/uuid/UUIDListeners.kt new file mode 100644 index 0000000..3303c64 --- /dev/null +++ b/stark/bukkit/src/main/kotlin/net/evilblock/stark/uuid/UUIDListeners.kt @@ -0,0 +1,15 @@ +package net.evilblock.stark.uuid + +import net.evilblock.stark.Stark +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.player.AsyncPlayerPreLoginEvent + +class UUIDListeners : Listener { + + @EventHandler + fun onAsyncPlayerPreLoginEvent(event: AsyncPlayerPreLoginEvent) { + Stark.instance.core.uuidCache.update(event.uniqueId, event.name) + } + +} \ No newline at end of file diff --git a/stark/bukkit/src/main/resources/config.yml b/stark/bukkit/src/main/resources/config.yml new file mode 100644 index 0000000..ecaf805 --- /dev/null +++ b/stark/bukkit/src/main/resources/config.yml @@ -0,0 +1,18 @@ +Mongo: + Host: "127.0.0.1" + Port: 27017 + Username: "admin" + Password: "" +LocalRedis: + Host: 'localhost' + Port: 6379 + Password: '' + DbId: 1 +BackboneRedis: + Host: 'localhost' + Port: 6379 + Password: '' + DbId: 0 +TimeZone: "EST" +RebootTimes: + - 24 \ No newline at end of file diff --git a/stark/bukkit/src/main/resources/items.csv b/stark/bukkit/src/main/resources/items.csv new file mode 100644 index 0000000..bbfdb06 --- /dev/null +++ b/stark/bukkit/src/main/resources/items.csv @@ -0,0 +1,7392 @@ +stone,1,0 +sstone,1,0 +smoothstone,1,0 +rock,1,0 +grass,2,0 +greendirt,2,0 +greenearth,2,0 +greenland,2,0 +dirt,3,0 +earth,3,0 +land,3,0 +cobblestone,4,0 +cstone,4,0 +cobble,4,0 +wood,5,0 +plank,5,0 +woodenplank,5,0 +woodplank,5,0 +wplank,5,0 +plankwooden,5,0 +plankwood,5,0 +plankw,5,0 +oakplank,5,0 +oakwoodenplank,5,0 +oakwoodplank,5,0 +oakwplank,5,0 +oakplankwooden,5,0 +oakplankwood,5,0 +oakplankw,5,0 +oplank,5,0 +owoodenplank,5,0 +owoodplank,5,0 +owplank,5,0 +oplankwooden,5,0 +oplankwood,5,0 +oplankw,5,0 +darkplank,5,1 +darkwoodenplank,5,1 +darkwoodplank,5,1 +darkwplank,5,1 +darkplankwooden,5,1 +darkplankwood,5,1 +darkplankw,5,1 +dplank,5,1 +dwoodenplank,5,1 +dwoodplank,5,1 +dwplank,5,1 +dplankwooden,5,1 +dplankwood,5,1 +dplankw,5,1 +spruceplank,5,1 +sprucewoodenplank,5,1 +sprucewoodplank,5,1 +sprucewplank,5,1 +spruceplankwooden,5,1 +spruceplankwood,5,1 +spruceplankw,5,1 +splank,5,1 +swoodenplank,5,1 +swoodplank,5,1 +swplank,5,1 +splankwooden,5,1 +splankwood,5,1 +splankw,5,1 +pineplank,5,1 +pinewoodenplank,5,1 +pinewoodplank,5,1 +pinewplank,5,1 +pineplankwooden,5,1 +pineplankwood,5,1 +pineplankw,5,1 +pplank,5,1 +pwoodenplank,5,1 +pwoodplank,5,1 +pwplank,5,1 +pplankwooden,5,1 +pplankwood,5,1 +pplankw,5,1 +lightplank,5,2 +lightwoodenplank,5,2 +lightwoodplank,5,2 +lightwplank,5,2 +lightplankwooden,5,2 +lightplankwood,5,2 +lightplankw,5,2 +lplank,5,2 +lwoodenplank,5,2 +lwoodplank,5,2 +lwplank,5,2 +lplankwooden,5,2 +lplankwood,5,2 +lplankw,5,2 +birchplank,5,2 +birchwoodenplank,5,2 +birchwoodplank,5,2 +birchwplank,5,2 +birchplankwooden,5,2 +birchplankwood,5,2 +birchplankw,5,2 +bplank,5,2 +bwoodenplank,5,2 +bwoodplank,5,2 +bwplank,5,2 +bplankwooden,5,2 +bplankwood,5,2 +bplankw,5,2 +whiteplank,5,2 +whitewoodenplank,5,2 +whitewoodplank,5,2 +whitewplank,5,2 +whiteplankwooden,5,2 +whiteplankwood,5,2 +whiteplankw,5,2 +wwoodenplank,5,2 +wwoodplank,5,2 +wwplank,5,2 +wplankwooden,5,2 +wplankwood,5,2 +wplankw,5,2 +jungleplank,5,3 +junglewoodenplank,5,3 +junglewoodplank,5,3 +junglewplank,5,3 +jungleplankwooden,5,3 +jungleplankwood,5,3 +jungleplankw,5,3 +jplank,5,3 +jwoodenplank,5,3 +jwoodplank,5,3 +jwplank,5,3 +jplankwooden,5,3 +jplankwood,5,3 +jplankw,5,3 +forestplank,5,3 +forestwoodenplank,5,3 +forestwoodplank,5,3 +forestwplank,5,3 +forestplankwooden,5,3 +forestplankwood,5,3 +forestplankw,5,3 +fplank,5,3 +fwoodenplank,5,3 +fwoodplank,5,3 +fwplank,5,3 +fplankwooden,5,3 +fplankwood,5,3 +fplankw,5,3 +sapling,6,0 +treesapling,6,0 +logsapling,6,0 +trunksapling,6,0 +woodsapling,6,0 +oaktreesapling,6,0 +oaklogsapling,6,0 +oaktrunksapling,6,0 +oakwoodsapling,6,0 +osapling,6,0 +otreesapling,6,0 +ologsapling,6,0 +otrunksapling,6,0 +owoodsapling,6,0 +darksapling,6,1 +darktreesapling,6,1 +darklogsapling,6,1 +darktrunksapling,6,1 +darkwoodsapling,6,1 +sprucesapling,6,1 +sprucetreesapling,6,1 +sprucelogsapling,6,1 +sprucetrunksapling,6,1 +sprucewoodsapling,6,1 +pinesapling,6,1 +pinetreesapling,6,1 +pinelogsapling,6,1 +pinetrunksapling,6,1 +pinewoodsapling,6,1 +dsapling,6,1 +dtreesapling,6,1 +dlogsapling,6,1 +dtrunksapling,6,1 +dwoodsapling,6,1 +ssapling,6,1 +streesapling,6,1 +slogsapling,6,1 +strunksapling,6,1 +swoodsapling,6,1 +psapling,6,1 +ptreesapling,6,1 +plogsapling,6,1 +ptrunksapling,6,1 +pwoodsapling,6,1 +birchsapling,6,2 +birchtreesapling,6,2 +birchlogsapling,6,2 +birchtrunksapling,6,2 +birchwoodsapling,6,2 +lightsapling,6,2 +lighttreesapling,6,2 +lightlogsapling,6,2 +lighttrunksapling,6,2 +lightwoodsapling,6,2 +whitesapling,6,2 +whitetreesapling,6,2 +whitelogsapling,6,2 +whitetrunksapling,6,2 +whitewoodsapling,6,2 +bsapling,6,2 +btreesapling,6,2 +blogsapling,6,2 +btrunksapling,6,2 +bwoodsapling,6,2 +lsapling,6,2 +ltreesapling,6,2 +llogsapling,6,2 +ltrunksapling,6,2 +lwoodsapling,6,2 +wsapling,6,2 +wtreesapling,6,2 +wlogsapling,6,2 +wtrunksapling,6,2 +wwoodsapling,6,2 +junglesapling,6,3 +jungletreesapling,6,3 +junglelogsapling,6,3 +jungletrunksapling,6,3 +junglewoodsapling,6,3 +forestsapling,6,3 +foresttreesapling,6,3 +forestlogsapling,6,3 +foresttrunksapling,6,3 +forestwoodsapling,6,3 +jsapling,6,3 +jtreesapling,6,3 +jlogsapling,6,3 +jtrunksapling,6,3 +jwoodsapling,6,3 +fsapling,6,3 +ftreesapling,6,3 +flogsapling,6,3 +ftrunksapling,6,3 +fwoodsapling,6,3 +bedrock,7,0 +oprock,7,0 +opblock,7,0 +adminblock,7,0 +adminrock,7,0 +adminium,7,0 +water,8,0 +stationarywater,9,0 +swater,9,0 +lava,10,0 +stationarylava,11,0 +slava,11,0 +sand,12,0 +gravel,13,0 +goldore,14,0 +oregold,14,0 +goldo,14,0 +ogold,14,0 +gore,14,0 +oreg,14,0 +ironore,15,0 +oreiron,15,0 +irono,15,0 +oiron,15,0 +steelore,15,0 +oresteel,15,0 +steelo,15,0 +osteel,15,0 +iore,15,0 +orei,15,0 +sore,15,0 +ores,15,0 +coalore,16,0 +orecoal,16,0 +coalo,16,0 +ocoal,16,0 +core,16,0 +tree,17,0 +log,17,0 +trunk,17,0 +oak,17,0 +oaktree,17,0 +oaklog,17,0 +oaktrunk,17,0 +oakwood,17,0 +otree,17,0 +olog,17,0 +otrunk,17,0 +owood,17,0 +darktree,17,1 +darklog,17,1 +darktrunk,17,1 +darkwood,17,1 +spruce,17,1 +sprucetree,17,1 +sprucelog,17,1 +sprucetrunk,17,1 +sprucewood,17,1 +pine,17,1 +pinetree,17,1 +pinelog,17,1 +pinetrunk,17,1 +pinewood,17,1 +dtree,17,1 +dlog,17,1 +dtrunk,17,1 +dwood,17,1 +stree,17,1 +slog,17,1 +strunk,17,1 +swood,17,1 +ptree,17,1 +plog,17,1 +ptrunk,17,1 +pwood,17,1 +birch,17,2 +birchtree,17,2 +birchlog,17,2 +birchtrunk,17,2 +birchwood,17,2 +whitetree,17,2 +whitelog,17,2 +whitetrunk,17,2 +whitewood,17,2 +lighttree,17,2 +lightlog,17,2 +lighttrunk,17,2 +lightwood,17,2 +btree,17,2 +blog,17,2 +btrunk,17,2 +bwood,17,2 +wtree,17,2 +wlog,17,2 +wtrunk,17,2 +wwood,17,2 +ltree,17,2 +llog,17,2 +ltrunk,17,2 +lwood,17,2 +jungle,17,3 +jungletree,17,3 +junglelog,17,3 +jungletrunk,17,3 +junglewood,17,3 +forest,17,3 +foresttree,17,3 +forestlog,17,3 +foresttrunk,17,3 +forestwood,17,3 +jtree,17,3 +jlog,17,3 +jtrunk,17,3 +jwood,17,3 +ftree,17,3 +flog,17,3 +ftrunk,17,3 +fwood,17,3 +leaves,18,0 +leaf,18,0 +treeleaves,18,0 +logleaves,18,0 +trunkleaves,18,0 +woodleaves,18,0 +oakleaves,18,0 +oakleaf,18,0 +oleaves,18,0 +oleaf,18,0 +oaktreeleaves,18,0 +oaklogleaves,18,0 +oaktrunkleaves,18,0 +oakwoodleaves,18,0 +otreeleaves,18,0 +ologleaves,18,0 +otrunkleaves,18,0 +owoodleaves,18,0 +treeleaf,18,0 +logleaf,18,0 +trunkleaf,18,0 +woodleaf,18,0 +oaktreeleaf,18,0 +oaklogleaf,18,0 +oaktrunkleaf,18,0 +oakwoodleaf,18,0 +otreeleaf,18,0 +ologleaf,18,0 +otrunkleaf,18,0 +owoodleaf,18,0 +spruceleaves,18,1 +spruceleaf,18,1 +sleaves,18,1 +sleaf,18,1 +sprucetreeleaves,18,1 +sprucelogleaves,18,1 +sprucetrunkleaves,18,1 +sprucewoodleaves,18,1 +streeleaves,18,1 +slogleaves,18,1 +strunkleaves,18,1 +swoodleaves,18,1 +pineleaves,18,1 +pineleaf,18,1 +pleaves,18,1 +pleaf,18,1 +pinetreeleaves,18,1 +pinelogleaves,18,1 +pinetrunkleaves,18,1 +pinewoodleaves,18,1 +ptreeleaves,18,1 +plogleaves,18,1 +ptrunkleaves,18,1 +pwoodleaves,18,1 +darkleaves,18,1 +darkleaf,18,1 +dleaves,18,1 +dleaf,18,1 +darktreeleaves,18,1 +darklogleaves,18,1 +darktrunkleaves,18,1 +darkwoodleaves,18,1 +dtreeleaves,18,1 +dlogleaves,18,1 +dtrunkleaves,18,1 +dwoodleaves,18,1 +sprucetreeleaf,18,1 +sprucelogleaf,18,1 +sprucetrunkleaf,18,1 +sprucewoodleaf,18,1 +streeleaf,18,1 +slogleaf,18,1 +strunkleaf,18,1 +swoodleaf,18,1 +pinetreeleaf,18,1 +pinelogleaf,18,1 +pinetrunkleaf,18,1 +pinewoodleaf,18,1 +ptreeleaf,18,1 +plogleaf,18,1 +ptrunkleaf,18,1 +pwoodleaf,18,1 +darktreeleaf,18,1 +darklogleaf,18,1 +darktrunkleaf,18,1 +darkwoodleaf,18,1 +dtreeleaf,18,1 +dlogleaf,18,1 +dtrunkleaf,18,1 +dwoodleaf,18,1 +birchleaves,18,2 +birchleaf,18,2 +bleaves,18,2 +bleaf,18,2 +birchtreeleaves,18,2 +birchlogleaves,18,2 +birchtrunkleaves,18,2 +birchwoodleaves,18,2 +btreeleaves,18,2 +blogleaves,18,2 +btrunkleaves,18,2 +bwoodleaves,18,2 +lightleaves,18,2 +lightleaf,18,2 +lleaves,18,2 +lleaf,18,2 +lighttreeleaves,18,2 +lightlogleaves,18,2 +lighttrunkleaves,18,2 +lightwoodleaves,18,2 +ltreeleaves,18,2 +llogleaves,18,2 +ltrunkleaves,18,2 +lwoodleaves,18,2 +whiteleaves,18,2 +whiteleaf,18,2 +wleaves,18,2 +wleaf,18,2 +whitetreeleaves,18,2 +whitelogleaves,18,2 +whitetrunkleaves,18,2 +whitewoodleaves,18,2 +wtreeleaves,18,2 +wlogleaves,18,2 +wtrunkleaves,18,2 +wwoodleaves,18,2 +birchtreeleaf,18,2 +birchlogleaf,18,2 +birchtrunkleaf,18,2 +birchwoodleaf,18,2 +btreeleaf,18,2 +blogleaf,18,2 +btrunkleaf,18,2 +bwoodleaf,18,2 +lighttreeleaf,18,2 +lightlogleaf,18,2 +lighttrunkleaf,18,2 +lightwoodleaf,18,2 +ltreeleaf,18,2 +llogleaf,18,2 +ltrunkleaf,18,2 +lwoodleaf,18,2 +whitetreeleaf,18,2 +whitelogleaf,18,2 +whitetrunkleaf,18,2 +whitewoodleaf,18,2 +wtreeleaf,18,2 +wlogleaf,18,2 +wtrunkleaf,18,2 +wwoodleaf,18,2 +jungleleaves,18,3 +jungleleaf,18,3 +jleaves,18,3 +jleaf,18,3 +jungletreeleaves,18,3 +junglelogleaves,18,3 +jungletrunkleaves,18,3 +junglewoodleaves,18,3 +jtreeleaves,18,3 +jlogleaves,18,3 +jtrunkleaves,18,3 +jwoodleaves,18,3 +forestleaves,18,3 +forestleaf,18,3 +fleaves,18,3 +fleaf,18,3 +foresttreeleaves,18,3 +forestlogleaves,18,3 +foresttrunkleaves,18,3 +forestwoodleaves,18,3 +ftreeleaves,18,3 +flogleaves,18,3 +ftrunkleaves,18,3 +fwoodleaves,18,3 +jungletreeleaf,18,3 +junglelogleaf,18,3 +jungletrunkleaf,18,3 +junglewoodleaf,18,3 +jtreeleaf,18,3 +jlogleaf,18,3 +jtrunkleaf,18,3 +jwoodleaf,18,3 +foresttreeleaf,18,3 +forestlogleaf,18,3 +foresttrunkleaf,18,3 +forestwoodleaf,18,3 +ftreeleaf,18,3 +flogleaf,18,3 +ftrunkleaf,18,3 +fwoodleaf,18,3 +sponge,19,0 +glass,20,0 +lapislazuliore,21,0 +lapislazulio,21,0 +orelapislazuli,21,0 +olapislazuli,21,0 +lapisore,21,0 +lapiso,21,0 +orelapis,21,0 +olapis,21,0 +lore,21,0 +orel,21,0 +lapislazuliblock,22,0 +blocklapislazuli,22,0 +lapisblock,22,0 +blocklapis,22,0 +lblock,22,0 +blockl,22,0 +dispenser,23,0 +dispense,23,0 +sandstone,24,0 +sastone,24,0 +csandstone,24,1 +csastone,24,1 +creepsandstone,24,1 +creepsastone,24,1 +creepersandstone,24,1 +creepersastone,24,1 +hieroglyphicsandstone,24,1 +hieroglyphicsastone,24,1 +hieroglyphsandstone,24,1 +hieroglyphsastone,24,1 +hsandstone,24,1 +hsastone,24,1 +pyramidsandstone,24,1 +pyramidsastone,24,1 +psandstone,24,1 +psastone,24,1 +smoothsandstone,24,2 +smoothsastone,24,2 +ssandstone,24,2 +smsastone,24,2 +ssastone,24,2 +noteblock,25,0 +musicblock,25,0 +nblock,25,0 +mblock,25,0 +bedblock,26,0 +poweredtrack,27,0 +poweredrails,27,0 +poweredrail,27,0 +boostertrack,27,0 +boosterrails,27,0 +boosterrail,27,0 +powertrack,27,0 +powerrails,27,0 +powerrail,27,0 +boosttrack,27,0 +boostrails,27,0 +boostrail,27,0 +ptrack,27,0 +prails,27,0 +prail,27,0 +btrack,27,0 +brails,27,0 +brail,27,0 +detectortrack,28,0 +detectorrails,28,0 +detectorrail,28,0 +detectingtrack,28,0 +detectingrails,28,0 +detectingrail,28,0 +detecttrack,28,0 +detectrails,28,0 +detectrail,28,0 +dtrack,28,0 +drails,28,0 +drail,28,0 +stickypistonbase,29,0 +stickypiston,29,0 +stickpistonbase,29,0 +stickpiston,29,0 +stickyp,29,0 +spistonbase,29,0 +spiston,29,0 +pistonstickybase,29,0 +pistonsticky,29,0 +pistonstickbase,29,0 +pistonstick,29,0 +pistonsbase,29,0 +pistons,29,0 +psticky,29,0 +pstick,29,0 +spiderweb,30,0 +sweb,30,0 +web,30,0 +longgrass,31,1 +tallgrass,31,1 +wildgrass,31,1 +grasslong,31,1 +grasstall,31,1 +grasswild,31,1 +lgrass,31,1 +tgrass,31,1 +wgrass,31,1 +fern,31,2 +bush,31,2 +deadshrub,32,0 +dshrub,32,0 +deadbush,32,0 +dbush,32,0 +deadsapling,32,0 +normalpistonbase,33,0 +normalpiston,33,0 +normpistonbase,33,0 +normpiston,33,0 +npistonbase,33,0 +npiston,33,0 +pistonnormalbase,33,0 +pistonnormal,33,0 +pistonnormbase,33,0 +pistonnorm,33,0 +pistonnbase,33,0 +pistonn,33,0 +pistonbase,33,0 +piston,33,0 +pistonblock,33,0 +pistonextensionnormal,34,0 +pistonextension,34,0 +pistonextnormal,34,0 +pistonext,34,0 +pistonenormal,34,0 +pistone,34,0 +extensionpistonnormal,34,0 +extensionpiston,34,0 +extpistonnormal,34,0 +extpiston,34,0 +epistonnormal,34,0 +episton,34,0 +whitecloth,35,0 +whitewool,35,0 +whitecotton,35,0 +wcloth,35,0 +wwool,35,0 +wcotton,35,0 +cloth,35,0 +wool,35,0 +cotton,35,0 +orangecloth,35,1 +orangewool,35,1 +orangecotton,35,1 +ocloth,35,1 +owool,35,1 +ocotton,35,1 +magentacloth,35,2 +magentawool,35,2 +magentacotton,35,2 +mcloth,35,2 +mwool,35,2 +mcotton,35,2 +lightbluecloth,35,3 +lightbluewool,35,3 +lightbluecotton,35,3 +lbluecloth,35,3 +lbluewool,35,3 +lbluecotton,35,3 +lightblucloth,35,3 +lightbluwool,35,3 +lightblucotton,35,3 +lblucloth,35,3 +lbluwool,35,3 +lblucotton,35,3 +yellowcloth,35,4 +yellowwool,35,4 +yellowcotton,35,4 +ycloth,35,4 +ywool,35,4 +ycotton,35,4 +lightgreencloth,35,5 +lightgreenwool,35,5 +lightgreencotton,35,5 +lgreencloth,35,5 +lgreenwool,35,5 +lgreencotton,35,5 +lightgrecloth,35,5 +lightgrewool,35,5 +lightgrecotton,35,5 +lgrecloth,35,5 +lgrewool,35,5 +lgrecotton,35,5 +limecloth,35,5 +limewool,35,5 +limecotton,35,5 +lcloth,35,5 +lwool,35,5 +lcotton,35,5 +pinkcloth,35,6 +pinkwool,35,6 +pinkcotton,35,6 +picloth,35,6 +piwool,35,6 +picotton,35,6 +darkgraycloth,35,7 +darkgraywool,35,7 +darkgraycotton,35,7 +dgraycloth,35,7 +dgraywool,35,7 +dgraycotton,35,7 +darkgracloth,35,7 +darkgrawool,35,7 +darkgracotton,35,7 +dgracloth,35,7 +dgrawool,35,7 +dgracotton,35,7 +graycloth,35,7 +graywool,35,7 +graycotton,35,7 +gracloth,35,7 +grawool,35,7 +gracotton,35,7 +lightgraycloth,35,8 +lightgraywool,35,8 +lightgraycotton,35,8 +lgraycloth,35,8 +lgraywool,35,8 +lgraycotton,35,8 +lightgracloth,35,8 +lightgrawool,35,8 +lightgracotton,35,8 +lgracloth,35,8 +lgrawool,35,8 +lgracotton,35,8 +silvercloth,35,8 +silverwool,35,8 +silvercotton,35,8 +sicloth,35,8 +siawool,35,8 +siacotton,35,8 +cyancloth,35,9 +cyanwool,35,9 +cyancotton,35,9 +ccloth,35,9 +cwool,35,9 +ccotton,35,9 +purplecloth,35,10 +purplewool,35,10 +purplecotton,35,10 +pucloth,35,10 +puwool,35,10 +pucotton,35,10 +bluecloth,35,11 +bluewool,35,11 +bluecotton,35,11 +blucloth,35,11 +bluwool,35,11 +blucotton,35,11 +browncloth,35,12 +brownwool,35,12 +browncotton,35,12 +brocloth,35,12 +browool,35,12 +brocotton,35,12 +darkgreencloth,35,13 +darkgreenwool,35,13 +darkgreencotton,35,13 +dgreencloth,35,13 +dgreenwool,35,13 +dgreencotton,35,13 +greencloth,35,13 +greenwool,35,13 +greencotton,35,13 +darkgrecloth,35,13 +darkgrewool,35,13 +darkgrecotton,35,13 +dgrecloth,35,13 +dgrewool,35,13 +dgrecotton,35,13 +grecloth,35,13 +grewool,35,13 +grecotton,35,13 +redcloth,35,14 +redwool,35,14 +redcotton,35,14 +rcloth,35,14 +rwool,35,14 +rcotton,35,14 +blackcloth,35,15 +blackwool,35,15 +blackcotton,35,15 +blacloth,35,15 +blawool,35,15 +blacotton,35,15 +pistonmovingpiece,36,0 +pistonmp,36,0 +pistontop,36,0 +yellowflower,37,0 +yflower,37,0 +flower,37,0 +redrose,38,0 +rrose,38,0 +rose,38,0 +redflower,38,0 +rflower,38,0 +brownmushroom,39,0 +brownshroom,39,0 +brownmush,39,0 +bmushroom,39,0 +bshroom,39,0 +bmush,39,0 +redmushroom,40,0 +redshroom,40,0 +redmush,40,0 +rmushroom,40,0 +rshroom,40,0 +rmush,40,0 +goldblock,41,0 +blockgold,41,0 +gblock,41,0 +blockg,41,0 +ironblock,42,0 +steelblock,42,0 +blockiron,42,0 +blocksteel,42,0 +iblock,42,0 +stblock,42,0 +blocki,42,0 +blockst,42,0 +stonedoublestep,43,0 +stonedstep,43,0 +sdoublestep,43,0 +sdstep,43,0 +doublestonestep,43,0 +dstonestep,43,0 +doublesstep,43,0 +doublestep,43,0 +dstep,43,0 +stonedoubleslab,43,0 +stonedslab,43,0 +sdoubleslab,43,0 +sdslab,43,0 +doublestoneslab,43,0 +dstoneslab,43,0 +doublesslab,43,0 +doubleslab,43,0 +dslab,43,0 +stonedoublehalfblock,43,0 +stonedhalfblock,43,0 +sdoublehalfblock,43,0 +sdhalfblock,43,0 +doublestonehalfblock,43,0 +dstonehalfblock,43,0 +doubleshalfblock,43,0 +doublehalfblock,43,0 +dhalfblock,43,0 +sandstonedoublestep,43,1 +sandstonedstep,43,1 +sstonedoublestep,43,1 +sstonedstep,43,1 +ssdoublestep,43,1 +ssdstep,43,1 +doublesandstonestep,43,1 +dsandstonestep,43,1 +doublesstonestep,43,1 +dsstonestep,43,1 +doublessstep,43,1 +dsstep,43,1 +sandstonedoubleslab,43,1 +sandstonedslab,43,1 +sstonedoubleslab,43,1 +sstonedslab,43,1 +ssdoubleslab,43,1 +ssdslab,43,1 +doublesandstoneslab,43,1 +dsandstoneslab,43,1 +doublesstoneslab,43,1 +dsstoneslab,43,1 +doublessslab,43,1 +dsslab,43,1 +sandstonedoublehalfblock,43,1 +sandstonedhalfblock,43,1 +sstonedoublehalfblock,43,1 +sstonedhalfblock,43,1 +ssdoublehalfblock,43,1 +ssdhalfblock,43,1 +doublesandstonehalfblock,43,1 +dsandstonehalfblock,43,1 +doublesstonehalfblock,43,1 +dsstonehalfblock,43,1 +doublesshalfblock,43,1 +dsshalfblock,43,1 +woodenplankstonedoublehalfblock,43,2 +woodenplankstonedhalfblock,43,2 +woodplankstonedoublehalfblock,43,2 +woodplankstonedhalfblock,43,2 +wplankstonedoublehalfblock,43,2 +wplankstonedhalfblock,43,2 +plankstonedoublehalfblock,43,2 +planstonekdhalfblock,43,2 +woodenstonedoublehalfblock,43,2 +woodenstonedhalfblock,43,2 +woodstonedoublehalfblock,43,2 +woodstonedhalfblock,43,2 +wstonedoublehalfblock,43,2 +wstonedhalfblock,43,2 +doublewoodenplankstonehalfblock,43,2 +dwoodenplankstonehalfblock,43,2 +doublewoodplankstonehalfblock,43,2 +dwoodplankstonehalfblock,43,2 +doublewplankstonehalfblock,43,2 +dwplankstonehalfblock,43,2 +doubleplankstonehalfblock,43,2 +dplankstonehalfblock,43,2 +doublewoodenstonehalfblock,43,2 +dwoodenstonehalfblock,43,2 +doublewoodstonehalfblock,43,2 +dwoodstonehalfblock,43,2 +doublewstonehalfblock,43,2 +dwstonehalfblock,43,2 +woodenplankstonedoublestep,43,2 +woodenplankstonedstep,43,2 +woodplankstonedoublestep,43,2 +woodplankstonedstep,43,2 +wplankstonedoublestep,43,2 +wplankstonedstep,43,2 +plankstonedoublestep,43,2 +plankstonedstep,43,2 +woodenstonedoublestep,43,2 +woodenstonedstep,43,2 +woodstonedoublestep,43,2 +woodstonedstep,43,2 +wstonedoublestep,43,2 +wstonedstep,43,2 +doublewoodenplankstonestep,43,2 +dwoodenplankstonestep,43,2 +doublewoodplankstonestep,43,2 +dwoodplankstonestep,43,2 +doublewplankstonestep,43,2 +dwplankstonestep,43,2 +doubleplankstonestep,43,2 +dplankstonestep,43,2 +doublewoodenstonestep,43,2 +dwoodenstonestep,43,2 +doublewoodstonestep,43,2 +dwoodstonestep,43,2 +doublewstonestep,43,2 +dwstonestep,43,2 +woodenplankstonedoubleslab,43,2 +woodenplankstonedslab,43,2 +woodplankstonedoubleslab,43,2 +woodplankstonedslab,43,2 +wplankstonedoubleslab,43,2 +wplankstonedslab,43,2 +plankstonedoubleslab,43,2 +plankstonedslab,43,2 +woodenstonedoubleslab,43,2 +woodenstonedslab,43,2 +woodstonedoubleslab,43,2 +woodstonedslab,43,2 +wstonedoubleslab,43,2 +wstonedslab,43,2 +doublewoodenplankstoneslab,43,2 +dwoodenplankstoneslab,43,2 +doublewoodplankstoneslab,43,2 +dwoodplankstoneslab,43,2 +doublewplankstoneslab,43,2 +dwplankstoneslab,43,2 +doubleplankstoneslab,43,2 +dplankstoneslab,43,2 +doublewoodenstoneslab,43,2 +dwoodenstoneslab,43,2 +doublewoodstoneslab,43,2 +dwoodstoneslab,43,2 +doublewstoneslab,43,2 +dwstoneslab,43,2 +cobblestonedoublestep,43,3 +cobblestonedstep,43,3 +cobbledoublestep,43,3 +cobbledstep,43,3 +cstonedoublestep,43,3 +cstonedstep,43,3 +csdoublestep,43,3 +csdstep,43,3 +doublecobblestonestep,43,3 +dcobblestonestep,43,3 +doublecobblestep,43,3 +dcobblestep,43,3 +doublecstonestep,43,3 +dcstonestep,43,3 +doublecsstep,43,3 +dcsstep,43,3 +cobblestonedoubleslab,43,3 +cobblestonedslab,43,3 +cobbledoubleslab,43,3 +cobbledslab,43,3 +cstonedoubleslab,43,3 +cstonedslab,43,3 +csdoubleslab,43,3 +csdslab,43,3 +doublecobblestoneslab,43,3 +dcobblestoneslab,43,3 +doublecobbleslab,43,3 +dcobbleslab,43,3 +doublecstoneslab,43,3 +dcstoneslab,43,3 +doublecsslab,43,3 +dcsslab,43,3 +cobblestonedoublehalfblock,43,3 +cobblestonedhalfblock,43,3 +cobbledoublehalfblock,43,3 +cobbledhalfblock,43,3 +cstonedoublehalfblock,43,3 +cstonedhalfblock,43,3 +csdoublehalfblock,43,3 +csdhalfblock,43,3 +doublecobblestonehalfblock,43,3 +dcobblestonehalfblock,43,3 +doublecobblehalfblock,43,3 +dcobblehalfblock,43,3 +doublecstonehalfblock,43,3 +dcstonehalfblock,43,3 +doublecshalfblock,43,3 +dcshalfblock,43,3 +brickblockdoublestep,43,4 +brickblockdstep,43,4 +brickbdoublestep,43,4 +brickbdstep,43,4 +brickdoublestep,43,4 +brickdstep,43,4 +bdoublestep,43,4 +bdstep,43,4 +brickblockdoubleslab,43,4 +brickblockdslab,43,4 +brickbdoubleslab,43,4 +brickbdslab,43,4 +brickdoubleslab,43,4 +brickdslab,43,4 +bdoubleslab,43,4 +bdslab,43,4 +doublebrickblockstep,43,4 +dbrickblockstep,43,4 +doublebrickbstep,43,4 +dbrickbstep,43,4 +doublebrickstep,43,4 +dbrickstep,43,4 +doublebstep,43,4 +dbstep,43,4 +doublebrickblockslab,43,4 +dbrickblockslab,43,4 +doublebrickbslab,43,4 +dbrickbslab,43,4 +doublebrickslab,43,4 +dbrickslab,43,4 +doublebslab,43,4 +dbslab,43,4 +brickblockdoublehalfblock,43,4 +brickblockdhalfblock,43,4 +brickbdoublehalfblock,43,4 +brickbdhalfblock,43,4 +brickdoublehalfblock,43,4 +brickdhalfblock,43,4 +bdoublehalfblock,43,4 +bdhalfblock,43,4 +doublebrickblockhalfblock,43,4 +dbrickblockhalfblock,43,4 +doublebrickbhalfblock,43,4 +dbrickbhalfblock,43,4 +doublebrickhalfblock,43,4 +dbrickhalfblock,43,4 +doublebhalfblock,43,4 +dbhalfblock,43,4 +stonebrickdoublestep,43,5 +stonebrickdstep,43,5 +stonebdoublestep,43,5 +stonebdstep,43,5 +sbrickdoublestep,43,5 +sbrickdstep,43,5 +sbdoublestep,43,5 +sbdstep,43,5 +stonebrickdoubleslab,43,5 +stonebrickdslab,43,5 +stonebdoubleslab,43,5 +stonebdslab,43,5 +sbrickdoubleslab,43,5 +sbrickdslab,43,5 +sbdoubleslab,43,5 +sbdslab,43,5 +doublestonebrickstep,43,5 +dstonebrickstep,43,5 +doublestonebstep,43,5 +dstonebstep,43,5 +doublesbrickstep,43,5 +dsbrickstep,43,5 +doublesbstep,43,5 +dsbstep,43,5 +doublestonebrickslab,43,5 +dstonebrickslab,43,5 +doublestonebslab,43,5 +dstonebslab,43,5 +doublesbrickslab,43,5 +dsbrickdslab,43,5 +doublesbslab,43,5 +dsbslab,43,5 +stonebrickdoublehalfblock,43,5 +stonebrickdhalfblock,43,5 +stonebdoublehalfblock,43,5 +stonebdhalfblock,43,5 +sbrickdoublehalfblock,43,5 +sbrickdhalfblock,43,5 +sbdoublehalfblock,43,5 +sbdhalfblock,43,5 +doublestonebrickhalfblock,43,5 +dstonebrickhalfblock,43,5 +doublestonebhalfblock,43,5 +dstonebhalfblock,43,5 +doublesbrickhalfblock,43,5 +dsbrickhalfblock,43,5 +doublesbhalfblock,43,5 +dsbhalfblock,43,5 +netherbrickdoubleslab,43,6 +hellbrickdoubleslab,43,6 +nbrickdoubleslab,43,6 +hbrickdoubleslab,43,6 +netherdoubleslab,43,6 +helldoubleslab,43,6 +nbdoubleslab,43,6 +hbdoubleslab,43,6 +hdoubleslab,43,6 +ndoubleslab,43,6 +netherbrickdoublestep,43,6 +hellbrickdoublestep,43,6 +nbrickdoublestep,43,6 +hbrickdoublestep,43,6 +netherdoublestep,43,6 +helldoublestep,43,6 +nbdoublestep,43,6 +hbdoublestep,43,6 +ndoublestep,43,6 +hdoublestep,43,6 +netherbrickdoublehalfblock,43,6 +hellbrickdoublehalfblock,43,6 +nbrickdoublehalfblock,43,6 +hbrickdoublehalfblock,43,6 +netherdoublehalfblock,43,6 +helldoublehalfblock,43,6 +nbdoublehalfblock,43,6 +hbdoublehalfblock,43,6 +ndoublehalfblock,43,6 +hdoublehalfblock,43,6 +netherbrickdslab,43,6 +hellbrickdslab,43,6 +nbrickdslab,43,6 +hbrickdslab,43,6 +netherdslab,43,6 +helldslab,43,6 +nbdslab,43,6 +hbdslab,43,6 +hdslab,43,6 +ndslab,43,6 +netherbrickdstep,43,6 +hellbrickdstep,43,6 +nbrickdstep,43,6 +hbrickdstep,43,6 +netherdstep,43,6 +helldstep,43,6 +nbdstep,43,6 +hbdstep,43,6 +ndstep,43,6 +hdstep,43,6 +netherbrickdhalfblock,43,6 +hellbrickdhalfblock,43,6 +nbrickdhalfblock,43,6 +hbrickdhalfblock,43,6 +netherdhalfblock,43,6 +helldhalfblock,43,6 +nbdhalfblock,43,6 +hbdhalfblock,43,6 +ndhalfblock,43,6 +hdhalfblock,43,6 +doublenetherbrickslab,43,6 +doublehellbrickslab,43,6 +doublenbrickslab,43,6 +doublehbrickslab,43,6 +doublenetherslab,43,6 +doublehellslab,43,6 +doublenbslab,43,6 +doublehbslab,43,6 +doublehslab,43,6 +doublenslab,43,6 +doublenetherbrickstep,43,6 +doublehellbrickstep,43,6 +doublenbrickstep,43,6 +doublehbrickstep,43,6 +doublenetherstep,43,6 +doublehellstep,43,6 +doublenbstep,43,6 +doublehbstep,43,6 +doublenstep,43,6 +doublehstep,43,6 +doublenetherbrickhalfblock,43,6 +doublehellbrickhalfblock,43,6 +doublenbrickhalfblock,43,6 +doublehbrickhalfblock,43,6 +doublenetherhalfblock,43,6 +doublehellhalfblock,43,6 +doublenbhalfblock,43,6 +doublehbhalfblock,43,6 +doublenhalfblock,43,6 +doublehhalfblock,43,6 +dnetherbrickslab,43,6 +dhellbrickslab,43,6 +dnbrickslab,43,6 +dhbrickslab,43,6 +dnetherslab,43,6 +dhellslab,43,6 +dnbslab,43,6 +dhbslab,43,6 +dhslab,43,6 +dnslab,43,6 +dnetherbrickstep,43,6 +dhellbrickstep,43,6 +dnbrickstep,43,6 +dhbrickstep,43,6 +dnetherstep,43,6 +dhellstep,43,6 +dnbstep,43,6 +dhbstep,43,6 +dnstep,43,6 +dhstep,43,6 +dnetherbrickhalfblock,43,6 +dhellbrickhalfblock,43,6 +dnbrickhalfblock,43,6 +dhbrickhalfblock,43,6 +dnetherhalfblock,43,6 +dhellhalfblock,43,6 +dnbhalfblock,43,6 +dhbhalfblock,43,6 +dnhalfblock,43,6 +dhhalfblock,43,6 +netherquartzdoubleslab,43,7 +hellquartzdoubleslab,43,7 +deathquartzdoubleslab,43,7 +nquartzdoubleslab,43,7 +hquartzdoubleslab,43,7 +dquartzdoubleslab,43,7 +quartzdoubleslab,43,7 +nqdoubleslab,43,7 +hqdoubleslab,43,7 +dqdoubleslab,43,7 +qdoubleslab,43,7 +netherquartzdoublestep,43,7 +hellquartzdoublestep,43,7 +deathquartzdoublestep,43,7 +nquartzdoublestep,43,7 +hquartzdoublestep,43,7 +dquartzdoublestep,43,7 +quartzdoublestep,43,7 +nqdoublestep,43,7 +hqdoublestep,43,7 +dqdoublestep,43,7 +qdoublestep,43,7 +netherquartzdoublehalfblock,43,7 +hellquartzdoublehalfblock,43,7 +deathquartzdoublehalfblock,43,7 +nquartzdoublehalfblock,43,7 +hquartzdoublehalfblock,43,7 +dquartzdoublehalfblock,43,7 +quartzdoublehalfblock,43,7 +nqdoublehalfblock,43,7 +hqdoublehalfblock,43,7 +dqdoublehalfblock,43,7 +qdoublehalfblock,43,7 +netherquartzdslab,43,7 +hellquartzdslab,43,7 +deathquartzdslab,43,7 +nquartzdslab,43,7 +hquartzdslab,43,7 +dquartzdslab,43,7 +quartzdslab,43,7 +nqdslab,43,7 +hqdslab,43,7 +dqdslab,43,7 +qdslab,43,7 +netherquartzdstep,43,7 +hellquartzdstep,43,7 +deathquartzdstep,43,7 +nquartzdstep,43,7 +hquartzdstep,43,7 +dquartzdstep,43,7 +quartzdstep,43,7 +nqdstep,43,7 +hqdstep,43,7 +dqdstep,43,7 +qdstep,43,7 +netherquartzdhalfblock,43,7 +hellquartzdhalfblock,43,7 +deathquartzdhalfblock,43,7 +nquartzdhalfblock,43,7 +hquartzdhalfblock,43,7 +dquartzdhalfblock,43,7 +quartzdhalfblock,43,7 +nqdhalfblock,43,7 +hqdhalfblock,43,7 +dqdhalfblock,43,7 +qdhalfblock,43,7 +doublenetherquartzslab,43,7 +doublehellquartzslab,43,7 +doubledeathquartzslab,43,7 +doublenquartzslab,43,7 +doublehquartzslab,43,7 +doubledquartzslab,43,7 +doublequartzslab,43,7 +doublenqslab,43,7 +doublehqslab,43,7 +doubledqslab,43,7 +doubleqslab,43,7 +doublenetherquartzstep,43,7 +doublehellquartzstep,43,7 +doubledeathquartzstep,43,7 +doublenquartzstep,43,7 +doublehquartzstep,43,7 +doubledquartzstep,43,7 +doublequartzstep,43,7 +doublenqstep,43,7 +doublehqstep,43,7 +doubledqstep,43,7 +doubleqstep,43,7 +doublenetherquartzhalfblock,43,7 +doublehellquartzhalfblock,43,7 +doubledeathquartzhalfblock,43,7 +doublenquartzhalfblock,43,7 +doublehquartzhalfblock,43,7 +doubledquartzhalfblock,43,7 +doublequartzhalfblock,43,7 +doublenqhalfblock,43,7 +doublehqhalfblock,43,7 +doubledqhalfblock,43,7 +doubleqhalfblock,43,7 +dnetherquartzslab,43,7 +dhellquartzslab,43,7 +ddeathquartzslab,43,7 +dnquartzslab,43,7 +dhquartzslab,43,7 +ddquartzslab,43,7 +dnqslab,43,7 +dhqslab,43,7 +ddqslab,43,7 +dnetherquartzstep,43,7 +dhellquartzstep,43,7 +ddeathquartzstep,43,7 +dnquartzstep,43,7 +dhquartzstep,43,7 +ddquartzstep,43,7 +dnqstep,43,7 +dhqstep,43,7 +ddqstep,43,7 +dnetherquartzhalfblock,43,7 +dhellquartzhalfblock,43,7 +ddeathquartzhalfblock,43,7 +dnquartzhalfblock,43,7 +dhquartzhalfblock,43,7 +ddquartzhalfblock,43,7 +dnqhalfblock,43,7 +dhqhalfblock,43,7 +ddqhalfblock,43,7 +smoothstonedoubleslab,43,8 +smoothstonedoublestep,43,8 +smoothstonedoublehalfblock,43,8 +smoothstonedslab,43,8 +smoothstonedstep,43,8 +smoothstonedhalfblock,43,8 +doublesmoothstoneslab,43,8 +doublesmoothstonestep,43,8 +doublesmoothstonehalfblock,43,8 +dsmoothstoneslab,43,8 +dsmoothstonestep,43,8 +dsmoothstonehalfblock,43,8 +smoothsandstonedoubleslab,43,9 +ssandstonedoubleslab,43,9 +ssstonedoubleslab,43,9 +sssdoubleslab,43,9 +smoothsandstonedoublestep,43,9 +ssandstonedoublestep,43,9 +ssstonedoublestep,43,9 +sssdoublestep,43,9 +smoothsandstonedoublehalfblock,43,9 +ssandstonedoublehalfblock,43,9 +ssstonedoublehalfblock,43,9 +sssdoublehalfblock,43,9 +smoothsandstonedslab,43,9 +ssandstonedslab,43,9 +ssstonedslab,43,9 +sssdslab,43,9 +smoothsandstonedstep,43,9 +ssandstonedstep,43,9 +ssstonedstep,43,9 +sssdstep,43,9 +smoothsandstonedhalfblock,43,9 +ssandstonedhalfblock,43,9 +ssstonedhalfblock,43,9 +sssdhalfblock,43,9 +doublesmoothsandstoneslab,43,9 +doublessandstoneslab,43,9 +doublessstoneslab,43,9 +doublesssslab,43,9 +doublesmoothsandstonestep,43,9 +doublessandstonestep,43,9 +doublessstonestep,43,9 +doublesssstep,43,9 +doublesmoothsandstonehalfblock,43,9 +doublessandstonehalfblock,43,9 +doublessstonehalfblock,43,9 +doublessshalfblock,43,9 +dsmoothsandstoneslab,43,9 +dssandstoneslab,43,9 +dssstoneslab,43,9 +dsssslab,43,9 +dsmoothsandstonestep,43,9 +dssandstonestep,43,9 +dssstonestep,43,9 +dsssstep,43,9 +dsmoothsandstonehalfblock,43,9 +dssandstonehalfblock,43,9 +dssstonehalfblock,43,9 +dssshalfblock,43,9 +smoothstonestep,44,0 +stonestep,44,0 +sstep,44,0 +step,44,0 +smoothstoneslab,44,0 +stoneslab,44,0 +sslab,44,0 +slab,44,0 +smoothstonehalfblock,44,0 +stonehalfblock,44,0 +shalfblock,44,0 +halfblock,44,0 +sandstonestep,44,1 +sstonestep,44,1 +ssstep,44,1 +sandstoneslab,44,1 +sstoneslab,44,1 +ssslab,44,1 +sandstonehalfblock,44,1 +sstonehalfblock,44,1 +sshalfblock,44,1 +woodenplankstonestep,44,2 +woodplankstonestep,44,2 +wplankstonestep,44,2 +plankstonestep,44,2 +woodenstonestep,44,2 +woodstonestep,44,2 +wstonestep,44,2 +woodenplankstoneslab,44,2 +woodplankstoneslab,44,2 +wplankstoneslab,44,2 +plankstoneslab,44,2 +woodenstoneslab,44,2 +woodstoneslab,44,2 +wstoneslab,44,2 +woodenplankstonehalfblock,44,2 +woodplankstonehalfblock,44,2 +wplankstonehalfblock,44,2 +plankstonehalfblock,44,2 +woodenstonehalfblock,44,2 +woodstonehalfblock,44,2 +wstonehalfblock,44,2 +cobblestonestep,44,3 +cobblestep,44,3 +cstonestep,44,3 +csstep,44,3 +cobblestoneslab,44,3 +cobbleslab,44,3 +cstoneslab,44,3 +csslab,44,3 +cobblestonehalfblock,44,3 +cobblehalfblock,44,3 +cstonehalfblock,44,3 +cshalfblock,44,3 +brickblockstep,44,4 +brickbstep,44,4 +brickstep,44,4 +bstep,44,4 +brickblockslab,44,4 +brickbslab,44,4 +brickslab,44,4 +bslab,44,4 +brickblockhalfblock,44,4 +brickbhalfblock,44,4 +brickhalfblock,44,4 +bhalfblock,44,4 +stonebrickstep,44,5 +stonebstep,44,5 +sbrickstep,44,5 +sbstep,44,5 +stonebrickslab,44,5 +stonebslab,44,5 +sbrickslab,44,5 +sbslab,44,5 +stonebrickhalfblock,44,5 +stonebhalfblock,44,5 +sbrickhalfblock,44,5 +sbhalfblock,44,5 +hellbrickslab,44,6 +nbrickslab,44,6 +hbrickslab,44,6 +netherslab,44,6 +hellslab,44,6 +nbslab,44,6 +hbslab,44,6 +hslab,44,6 +nslab,44,6 +netherbrickstep,44,6 +hellbrickstep,44,6 +nbrickstep,44,6 +hbrickstep,44,6 +netherstep,44,6 +hellstep,44,6 +nbstep,44,6 +hbstep,44,6 +nstep,44,6 +hstep,44,6 +netherbrickhalfblock,44,6 +hellbrickhalfblock,44,6 +nbrickhalfblock,44,6 +hbrickhalfblock,44,6 +netherhalfblock,44,6 +hellhalfblock,44,6 +nbhalfblock,44,6 +hbhalfblock,44,6 +nhalfblock,44,6 +hhalfblock,44,6 +netherquartzslab,44,7 +hellquartzslab,44,7 +deathquartzslab,44,7 +nquartzslab,44,7 +hquartzslab,44,7 +dquartzslab,44,7 +quartzslab,44,7 +nqslab,44,7 +hqslab,44,7 +dqslab,44,7 +qslab,44,7 +netherquartzstep,44,7 +hellquartzstep,44,7 +deathquartzstep,44,7 +nquartzstep,44,7 +hquartzstep,44,7 +dquartzstep,44,7 +quartzstep,44,7 +nqstep,44,7 +hqstep,44,7 +dqstep,44,7 +qstep,44,7 +netherquartzhalfblock,44,7 +hellquartzhalfblock,44,7 +deathquartzhalfblock,44,7 +nquartzhalfblock,44,7 +hquartzhalfblock,44,7 +dquartzhalfblock,44,7 +quartzhalfblock,44,7 +nqhalfblock,44,7 +hqhalfblock,44,7 +dqhalfblock,44,7 +qhalfblock,44,7 +brickblock,45,0 +blockbrick,45,0 +bblock,45,0 +blockb,45,0 +tntblock,46,0 +blocktnt,46,0 +bombblock,46,0 +blockbomb,46,0 +dynamiteblock,46,0 +blockdynamite,46,0 +tnt,46,0 +bomb,46,0 +dynamite,46,0 +bookcase,47,0 +casebook,47,0 +bookshelf,47,0 +shelfbook,47,0 +bookblock,47,0 +blockbook,47,0 +mossycobblestone,48,0 +mosscobblestone,48,0 +mcobblestone,48,0 +mossycobble,48,0 +mosscobble,48,0 +mcobble,48,0 +mossstone,48,0 +mossystone,48,0 +mstone,48,0 +obsidian,49,0 +obsi,49,0 +obby,49,0 +torch,50,0 +burningstick,50,0 +burnstick,50,0 +fire,51,0 +flame,51,0 +flames,51,0 +mobspawner,52,0 +mobcage,52,0 +monsterspawner,52,0 +monstercage,52,0 +mspawner,52,0 +mcage,52,0 +spawner,52,0 +cage,52,0 +woodenstairs,53,0 +woodstairs,53,0 +wstairs,53,0 +woodenstair,53,0 +woodstair,53,0 +wstair,53,0 +chest,54,0 +container,54,0 +redstonewireblock,55,0 +rstonewireblock,55,0 +redswireblock,55,0 +redwireblock,55,0 +rswireblock,55,0 +rwireblock,55,0 +redstonewire,55,0 +rstonewire,55,0 +redswire,55,0 +redwire,55,0 +rswire,55,0 +rwire,55,0 +wire,55,0 +diamondore,56,0 +crystalore,56,0 +diamondo,56,0 +crystalo,56,0 +orediamond,56,0 +odiamond,56,0 +orecrystal,56,0 +ocrystal,56,0 +dore,56,0 +ored,56,0 +diamondblock,57,0 +blockdiamond,57,0 +crystalblock,57,0 +blockcrystal,57,0 +dblock,57,0 +blockd,57,0 +workbench,58,0 +craftingbench,58,0 +crafterbench,58,0 +craftbench,58,0 +worktable,58,0 +craftingtable,58,0 +craftertable,58,0 +crafttable,58,0 +wbench,58,0 +cbench,58,0 +crops,59,0 +crop,59,0 +soil,60,0 +furnace,61,0 +burningfurnace,62,0 +bfurnace,62,0 +signpost,63,0 +spost,63,0 +woodendoorhalf,64,0 +wooddoorhalf,64,0 +wdoorhalf,64,0 +woodendoorbottom,64,0 +wooddoorbottom,64,0 +wdoorbottom,64,0 +woodendoorblock,64,0 +wooddoorblock,64,0 +wdoorblock,64,0 +ladder,65,0 +minecarttrack,66,0 +minecartrails,66,0 +minecartrail,66,0 +mcarttrack,66,0 +mcartrails,66,0 +mcartrail,66,0 +mctrack,66,0 +mcrails,66,0 +mcrail,66,0 +track,66,0 +rails,66,0 +rail,66,0 +cobblestonestairs,67,0 +cstonestairs,67,0 +stonestairs,67,0 +cobblestairs,67,0 +csstairs,67,0 +sstairs,67,0 +cstairs,67,0 +cobblestonestair,67,0 +cstonestair,67,0 +stonestair,67,0 +cobblestair,67,0 +csstair,67,0 +sstair,67,0 +cstair,67,0 +wallsign,68,0 +wsign,68,0 +lever,69,0 +smoothstonepressureplate,70,0 +smoothstonepressplate,70,0 +smoothstonepplate,70,0 +smoothstoneplate,70,0 +sstonepressureplate,70,0 +sstonepressplate,70,0 +sstonepplate,70,0 +sstoneplate,70,0 +stonepressureplate,70,0 +stonepressplate,70,0 +stonepplate,70,0 +stoneplate,70,0 +spressureplate,70,0 +spressplate,70,0 +spplate,70,0 +splate,70,0 +irondoorhalf,71,0 +idoorhalf,71,0 +irondoorbottom,71,0 +idoorbottom,71,0 +irondoorblock,71,0 +idoorblock,71,0 +steeldoorhalf,71,0 +sdoorhalf,71,0 +steeldoorbottom,71,0 +sdoorbottom,71,0 +steeldoorblock,71,0 +sdoorblock,71,0 +woodenpressureplate,72,0 +woodenpressplate,72,0 +woodenpplate,72,0 +woodenplate,72,0 +woodpressureplate,72,0 +woodpressplate,72,0 +woodpplate,72,0 +woodplate,72,0 +wpressureplate,72,0 +wpressplate,72,0 +wpplate,72,0 +wplate,72,0 +redstoneore,73,0 +redsore,73,0 +redore,73,0 +rstoneore,73,0 +rsore,73,0 +rore,73,0 +oreredstone,73,0 +orereds,73,0 +orered,73,0 +orerstone,73,0 +orers,73,0 +orer,73,0 +glowingredstoneore,74,0 +glowredstoneore,74,0 +gredstoneore,74,0 +glowingrstoneore,74,0 +glowrstoneore,74,0 +grstoneore,74,0 +glowingredsore,74,0 +glowredsore,74,0 +gredsore,74,0 +glowingredore,74,0 +glowredore,74,0 +gredore,74,0 +glowingrsore,74,0 +glowrsore,74,0 +grsore,74,0 +grore,74,0 +oreglowingredstone,74,0 +oreglowredstone,74,0 +oregredstone,74,0 +oreglowingrstone,74,0 +oreglowrstone,74,0 +oregrstone,74,0 +oreglowingreds,74,0 +oreglowreds,74,0 +oregreds,74,0 +oreglowingred,74,0 +oreglowred,74,0 +oregred,74,0 +oreglowingrs,74,0 +oreglowrs,74,0 +oregrs,74,0 +oregr,74,0 +redstonetorchoff,75,0 +rstonetorchoff,75,0 +redstorchoff,75,0 +redtorchoff,75,0 +rstorchoff,75,0 +redstonetorchon,76,0 +redstonetorch,76,0 +rstonetorchon,76,0 +rstonetorch,76,0 +redstorchon,76,0 +redstorch,76,0 +redtorchon,76,0 +redtorch,76,0 +rstorchon,76,0 +rstorch,76,0 +smoothstonebutton,77,0 +sstonebutton,77,0 +stonebutton,77,0 +sbutton,77,0 +button,77,0 +snowcovering,78,0 +snowcover,78,0 +scover,78,0 +ice,79,0 +frozenwater,79,0 +waterfrozen,79,0 +freezewater,79,0 +waterfreeze,79,0 +snowblock,80,0 +blocksnow,80,0 +sblock,80,0 +blocks,80,0 +cactus,81,0 +cactuses,81,0 +cacti,81,0 +clayblock,82,0 +blockclay,82,0 +cblock,82,0 +blockc,82,0 +reedblock,83,0 +reedsblock,83,0 +sugarcaneblock,83,0 +scaneblock,83,0 +bambooblock,83,0 +blockreed,83,0 +blockreeds,83,0 +blocksugarcane,83,0 +blockscane,83,0 +blockbamboo,83,0 +jukebox,84,0 +jbox,84,0 +fence,85,0 +woodenfence,85,0 +woodfence,85,0 +wfence,85,0 +fencewooden,85,0 +fencewood,85,0 +fencew,85,0 +pumpkin,86,0 +netherrack,87,0 +netherrock,87,0 +netherstone,87,0 +hellstone,87,0 +nstone,87,0 +hstone,87,0 +soulsand,88,0 +slowsand,88,0 +slowmud,88,0 +ssand,88,0 +smud,88,0 +mud,88,0 +glowingstoneblock,89,0 +lightstoneblock,89,0 +glowstoneblock,89,0 +blockglowingstone,89,0 +blocklightstone,89,0 +blockglowstone,89,0 +glowingstoneb,89,0 +lightstoneb,89,0 +glowstoneb,89,0 +bglowingstone,89,0 +blightstone,89,0 +bglowstone,89,0 +glowingstone,89,0 +lightstone,89,0 +glowstone,89,0 +glowingblock,89,0 +lightblock,89,0 +glowblock,89,0 +lstone,89,0 +gstone,89,0 +portal,90,0 +jackolantern,91,0 +pumpkinlantern,91,0 +glowingpumpkin,91,0 +lightpumpkin,91,0 +jpumpkin,91,0 +plantren,91,0 +glowpumpkin,91,0 +gpumpkin,91,0 +lpumpkin,91,0 +cakeblock,92,0 +repeateroff,93,0 +repeatoff,93,0 +delayeroff,93,0 +delayoff,93,0 +dioderoff,93,0 +diodeoff,93,0 +repeaterblockoff,93,0 +repeatblockoff,93,0 +delayerblockoff,93,0 +delayblockoff,93,0 +dioderblockoff,93,0 +diodeblockoff,93,0 +repeateron,94,0 +repeaton,94,0 +delayeron,94,0 +delayon,94,0 +dioderon,94,0 +diodeon,94,0 +repeaterblockon,94,0 +repeatblockon,94,0 +delayerblockon,94,0 +delayblockon,94,0 +dioderblockon,94,0 +diodeblockon,94,0 +lockedchest,95,0 +lockchest,95,0 +jokechest,95,0 +aprilfoolschest,95,0 +aprilchest,95,0 +lootchest,95,0 +trapdoor,96,0 +doortrap,96,0 +hatch,96,0 +tdoor,96,0 +doort,96,0 +trapd,96,0 +dtrap,96,0 +silverfish,97,0 +monsteregg,97,0 +monstereggsmoothstone,97,0 +monstereggsstone,97,0 +meggsmoothstone,97,0 +meggsstone,97,0 +mesmoothstone,97,0 +messtone,97,0 +silverfishsmoothstone,97,0 +silverfishsstone,97,0 +silverfishstone,97,0 +sfishsmoothstone,97,0 +sfishsstone,97,0 +sfishstone,97,0 +fishsmoothstone,97,0 +fishsstone,97,0 +fishstone,97,0 +sfsmoothstone,97,0 +sfsstone,97,0 +sfstone,97,0 +trapsmoothstone,97,0 +trapsstone,97,0 +trapstone,97,0 +monstereggcobblestone,97,1 +monstereggcstone,97,1 +meggcobblestone,97,1 +meggcstone,97,1 +mecobblestone,97,1 +mecstone,97,1 +silverfishcobblestone,97,1 +silverfishcstone,97,1 +sfishcobblestone,97,1 +sfishcstone,97,1 +fishcobblestone,97,1 +fishcstone,97,1 +sfcobblestone,97,1 +sfcstone,97,1 +trapcobblestone,97,1 +trapcstone,97,1 +monstereggstonebrick,97,2 +monstereggsbrick,97,2 +meggstonebrick,97,2 +meggsbrick,97,2 +mestonebrick,97,2 +mesbrick,97,2 +silverfishstonebrick,97,2 +silverfishsbrick,97,2 +sfishstonebrick,97,2 +sfishsbrick,97,2 +fishstonebrick,97,2 +fishsbrick,97,2 +sfstonebrick,97,2 +sfsbrick,97,2 +trapstonebrick,97,2 +trapsbrick,97,2 +stonebrick,98,0 +stonebricks,98,0 +stonebrickblock,98,0 +stonebb,98,0 +sbrick,98,0 +mossystonebrick,98,1 +mossystonebricks,98,1 +mossystonebrickblock,98,1 +mossystonebb,98,1 +mossstonebrick,98,1 +mossstonebricks,98,1 +mossstonebrickblock,98,1 +mossstonebb,98,1 +mstonebrick,98,1 +mstonebricks,98,1 +mstonebrickblock,98,1 +mstonebb,98,1 +mosssbrick,98,1 +mosssbricks,98,1 +mosssbrickblock,98,1 +mosssbb,98,1 +msbrick,98,1 +msbricks,98,1 +msbrickblock,98,1 +crackedstone,98,2 +crackedstonebrick,98,2 +crackedstonebricks,98,2 +crackedstonebrickblock,98,2 +crackedstonebb,98,2 +crackstonebrick,98,2 +crackstonebricks,98,2 +crackstonebrickblock,98,2 +crackstonebb,98,2 +crstonebrick,98,2 +crstonebricks,98,2 +crstonebrickblock,98,2 +crstonebb,98,2 +cracksbrick,98,2 +cracksbricks,98,2 +cracksbrickblock,98,2 +cracksbb,98,2 +crsbrick,98,2 +crsbricks,98,2 +crsbrickblock,98,2 +circlestone,98,3 +circlestonebrick,98,3 +circlestonebricks,98,3 +circlestonebrickblock,98,3 +circlestonebb,98,3 +cistonebrick,98,3 +cistonebricks,98,3 +cistonebrickblock,98,3 +cistonebb,98,3 +circlesbrick,98,3 +circlesbricks,98,3 +circlesbrickblock,98,3 +circlesbb,98,3 +cisbrick,98,3 +cisbricks,98,3 +cisbrickblock,98,3 +giantredmushroom,99,0 +hugeredmushroom,99,0 +bigredmushroom,99,0 +gredmushroom,99,0 +hredmushroom,99,0 +bredmushroom,99,0 +giantrmushroom,99,0 +hugermushroom,99,0 +bigrmushroom,99,0 +grmushroom,99,0 +hrmushroom,99,0 +brmushroom,99,0 +giantbrownmushroom,100,0 +hugebrownmushroom,100,0 +bigbrownmushroom,100,0 +gbrownmushroom,100,0 +hbrownmushroom,100,0 +bbrownmushroom,100,0 +giantbmushroom,100,0 +hugebmushroom,100,0 +bigbmushroom,100,0 +gbmushroom,100,0 +hbmushroom,100,0 +bbmushroom,100,0 +ironbars,101,0 +ironbarsb,101,0 +ironbarsblock,101,0 +ironfence,101,0 +metalbars,101,0 +metalbarsb,101,0 +metalbarsblock,101,0 +metalfence,101,0 +jailbars,101,0 +jailbarsb,101,0 +jailbarsblock,101,0 +jailfence,101,0 +mbars,101,0 +mbarsb,101,0 +mbarsblock,101,0 +mfence,101,0 +jbars,101,0 +jbarsb,101,0 +jbarsblock,101,0 +jfence,101,0 +ibars,101,0 +ibarsb,101,0 +ibarsblock,101,0 +ifence,101,0 +glasspane,102,0 +glassp,102,0 +paneglass,102,0 +pglass,102,0 +flatglass,102,0 +fglass,102,0 +skinnyglass,102,0 +sglass,102,0 +glassflat,102,0 +glassf,102,0 +glassskinny,102,0 +glasss,102,0 +melon,360,0 +watermelon,360,0 +greenmelon,360,0 +melongreen,360,0 +melonblock,103,0 +watermelonblock,103,0 +greenmelonblock,103,0 +pumpkinstem,104,0 +stempumpkin,104,0 +pumpstem,104,0 +stempump,104,0 +melonstem,105,0 +watermelonstem,105,0 +greenmelonstem,105,0 +stemmelon,105,0 +stemwatermelon,105,0 +stemgreenmelon,105,0 +vines,106,0 +vine,106,0 +greenvines,106,0 +greenvine,106,0 +gardenvines,106,0 +gardenvine,106,0 +vinesgreen,106,0 +vinegreen,106,0 +vinesgarden,106,0 +vinegarden,106,0 +vinesg,106,0 +vineg,106,0 +gvines,106,0 +gvine,106,0 +wfencegate,107,0 +woodfencegate,107,0 +woodenfencegate,107,0 +woodengate,107,0 +woodgate,107,0 +wgate,107,0 +gate,107,0 +gardengate,107,0 +ggate,107,0 +fencegate,107,0 +fencegatewooden,107,0 +fencegatewood,107,0 +brickstairs,108,0 +redbrickstairs,108,0 +redbstairs,108,0 +rbrickstairs,108,0 +bstairs,108,0 +redstairs,108,0 +brickstair,108,0 +redbrickstair,108,0 +redbstair,108,0 +rbrickstair,108,0 +bstair,108,0 +redstair,108,0 +stonebrickstairs,109,0 +stonebstairs,109,0 +sbstairs,109,0 +cementbrickstairs,109,0 +cementstairs,109,0 +cementbstairs,109,0 +cbstairs,109,0 +greybrickstairs,109,0 +greybstairs,109,0 +greystairs,109,0 +purplegrass,110,0 +pinkgrass,110,0 +mycel,110,0 +mycelium,110,0 +swampgrass,110,0 +sgrass,110,0 +mushroomgrass,110,0 +mushgrass,110,0 +waterlily,111,0 +lilypad,111,0 +lily,111,0 +swamppad,111,0 +lpad,111,0 +wlily,111,0 +netherbrickblock,112,0 +nbrickblock,112,0 +hellbrickblock,112,0 +deathbrickblock,112,0 +dbrickblock,112,0 +hbrickblock,112,0 +blocknetherbrick,112,0 +blocknbrick,112,0 +blockhellbrick,112,0 +blockdeathbrick,112,0 +blockdbrick,112,0 +blockhbrick,112,0 +netherbrickfence,113,0 +hellbrickfence,113,0 +nbrickfence,113,0 +hbrickfence,113,0 +netherbfence,113,0 +hellbfence,113,0 +netherfence,113,0 +hellfence,113,0 +nbfence,113,0 +hbfence,113,0 +nfence,113,0 +hfence,113,0 +netherbrickstairs,114,0 +hellbrickstairs,114,0 +nbrickstairs,114,0 +hbrickstairs,114,0 +netherbstairs,114,0 +hellbstairs,114,0 +netherstairs,114,0 +hellstairs,114,0 +nbstairs,114,0 +hbstairs,114,0 +nstairs,114,0 +hstairs,114,0 +netherbrickstair,114,0 +hellbrickstair,114,0 +nbrickstair,114,0 +hbrickstair,114,0 +netherbstair,114,0 +hellbstair,114,0 +netherstair,114,0 +hellstair,114,0 +nbstair,114,0 +hbstair,114,0 +nstair,114,0 +hstair,114,0 +netherwarts,115,0 +netherwart,115,0 +netherplant,115,0 +nethercrop,115,0 +hellwarts,115,0 +hellwart,115,0 +hellplant,115,0 +hellcrop,115,0 +deathwarts,115,0 +deathwart,115,0 +deathplant,115,0 +deathcrop,115,0 +nwarts,115,0 +nwart,115,0 +ncrop,115,0 +nplant,115,0 +hwarts,115,0 +hwart,115,0 +hplant,115,0 +hcrop,115,0 +dwarts,115,0 +dwart,115,0 +dplant,115,0 +dcrop,115,0 +enchantmenttable,116,0 +enchantingtable,116,0 +enchanttable,116,0 +etable,116,0 +magicaltable,116,0 +magictable,116,0 +mtable,116,0 +enchantmentdesk,116,0 +enchantingdesk,116,0 +enchantdesk,116,0 +edesk,116,0 +magicaldesk,116,0 +magicdesk,116,0 +mdesk,116,0 +booktable,116,0 +bookdesk,116,0 +btable,116,0 +bdesk,116,0 +brewingstandblock,117,0 +brewerblock,117,0 +potionstandblock,117,0 +potionbrewerblock,117,0 +pstandblock,117,0 +bstandblock,117,0 +pbrewerblock,117,0 +cauldron,118,0 +steelcauldron,118,0 +ironcauldron,118,0 +icauldron,118,0 +scauldron,118,0 +potioncauldron,118,0 +pcauldron,118,0 +enderportal,119,0 +endergoo,119,0 +endgoo,119,0 +endportal,119,0 +egoo,119,0 +eportal,119,0 +enderportalframe,120,0 +endportalframe,120,0 +endgooframe,120,0 +endergooframe,120,0 +egooframe,120,0 +eportalframe,120,0 +enderframe,120,0 +endframe,120,0 +enderstone,121,0 +endstone,121,0 +endrock,121,0 +enderrock,121,0 +erock,121,0 +estone,121,0 +enderdragonegg,122,0 +endegg,122,0 +dragonegg,122,0 +degg,122,0 +bossegg,122,0 +begg,122,0 +redstonelamp,123,0 +redstonelampoff,123,0 +redlamp,123,0 +redlampoff,123,0 +rslamp,123,0 +rslampoff,123,0 +redstonelampon,124,0 +redlampon,124,0 +rslampon,124,0 +woodenplankdoublehalfblock,125,0 +woodenplankdhalfblock,125,0 +woodplankdoublehalfblock,125,0 +woodplankdhalfblock,125,0 +wplankdoublehalfblock,125,0 +wplankdhalfblock,125,0 +plankdoublehalfblock,125,0 +plankdhalfblock,125,0 +woodendoublehalfblock,125,0 +woodendhalfblock,125,0 +wooddoublehalfblock,125,0 +wooddhalfblock,125,0 +wdoublehalfblock,125,0 +wdhalfblock,125,0 +doublewoodenplankhalfblock,125,0 +dwoodenplankhalfblock,125,0 +doublewoodplankhalfblock,125,0 +dwoodplankhalfblock,125,0 +doublewplankhalfblock,125,0 +dwplankhalfblock,125,0 +doubleplankhalfblock,125,0 +dplankhalfblock,125,0 +doublewoodenhalfblock,125,0 +dwoodenhalfblock,125,0 +doublewoodhalfblock,125,0 +dwoodhalfblock,125,0 +doublewhalfblock,125,0 +dwhalfblock,125,0 +woodenplankdoublestep,125,0 +woodenplankdstep,125,0 +woodplankdoublestep,125,0 +woodplankdstep,125,0 +wplankdoublestep,125,0 +wplankdstep,125,0 +plankdoublestep,125,0 +plankdstep,125,0 +woodendoublestep,125,0 +woodendstep,125,0 +wooddoublestep,125,0 +wooddstep,125,0 +wdoublestep,125,0 +wdstep,125,0 +doublewoodenplankstep,125,0 +dwoodenplankstep,125,0 +doublewoodplankstep,125,0 +dwoodplankstep,125,0 +doublewplankstep,125,0 +dwplankstep,125,0 +doubleplankstep,125,0 +dplankstep,125,0 +doublewoodenstep,125,0 +dwoodenstep,125,0 +doublewoodstep,125,0 +dwoodstep,125,0 +doublewstep,125,0 +dwstep,125,0 +woodenplankdoubleslab,125,0 +woodenplankdslab,125,0 +woodplankdoubleslab,125,0 +woodplankdslab,125,0 +wplankdoubleslab,125,0 +wplankdslab,125,0 +plankdoubleslab,125,0 +plankdslab,125,0 +woodendoubleslab,125,0 +woodendslab,125,0 +wooddoubleslab,125,0 +wooddslab,125,0 +wdoubleslab,125,0 +wdslab,125,0 +doublewoodenplankslab,125,0 +dwoodenplankslab,125,0 +doublewoodplankslab,125,0 +dwoodplankslab,125,0 +doublewplankslab,125,0 +dwplankslab,125,0 +doubleplankslab,125,0 +dplankslab,125,0 +doublewoodenslab,125,0 +dwoodenslab,125,0 +doublewoodslab,125,0 +dwoodslab,125,0 +doublewslab,125,0 +dwslab,125,0 +oakwoodenplankdoublehalfblock,125,0 +oakwoodenplankdhalfblock,125,0 +oakwoodplankdoublehalfblock,125,0 +oakwoodplankdhalfblock,125,0 +oakwplankdoublehalfblock,125,0 +oakwplankdhalfblock,125,0 +oakplankdoublehalfblock,125,0 +oakplankdhalfblock,125,0 +oakwoodendoublehalfblock,125,0 +oakwoodendhalfblock,125,0 +oakwooddoublehalfblock,125,0 +oakwooddhalfblock,125,0 +oakwdoublehalfblock,125,0 +oakwdhalfblock,125,0 +oakdoublewoodenplankhalfblock,125,0 +oakdwoodenplankhalfblock,125,0 +oakdoublewoodplankhalfblock,125,0 +oakdwoodplankhalfblock,125,0 +oakdoublewplankhalfblock,125,0 +oakdwplankhalfblock,125,0 +oakdoubleplankhalfblock,125,0 +oakdplankhalfblock,125,0 +oakdoublewoodenhalfblock,125,0 +oakdwoodenhalfblock,125,0 +oakdoublewoodhalfblock,125,0 +oakdwoodhalfblock,125,0 +oakdoublewhalfblock,125,0 +oakdwhalfblock,125,0 +oakdoublehalfblock,125,0 +oakdhalfblock,125,0 +oakwoodenplankdoublestep,125,0 +oakwoodenplankdstep,125,0 +oakwoodplankdoublestep,125,0 +oakwoodplankdstep,125,0 +oakwplankdoublestep,125,0 +oakwplankdstep,125,0 +oakplankdoublestep,125,0 +oakplankdstep,125,0 +oakwoodendoublestep,125,0 +oakwoodendstep,125,0 +oakwooddoublestep,125,0 +oakwooddstep,125,0 +oakwdoublestep,125,0 +oakwdstep,125,0 +oakdoublewoodenplankstep,125,0 +oakdwoodenplankstep,125,0 +oakdoublewoodplankstep,125,0 +oakdwoodplankstep,125,0 +oakdoublewplankstep,125,0 +oakdwplankstep,125,0 +oakdoubleplankstep,125,0 +oakdplankstep,125,0 +oakdoublewoodenstep,125,0 +oakdwoodenstep,125,0 +oakdoublewoodstep,125,0 +oakdwoodstep,125,0 +oakdoublewstep,125,0 +oakdwstep,125,0 +oakdoublestep,125,0 +oakdstep,125,0 +oakwoodenplankdoubleslab,125,0 +oakwoodenplankdslab,125,0 +oakwoodplankdoubleslab,125,0 +oakwoodplankdslab,125,0 +oakwplankdoubleslab,125,0 +oakwplankdslab,125,0 +oakplankdoubleslab,125,0 +oakplankdslab,125,0 +oakwoodendoubleslab,125,0 +oakwoodendslab,125,0 +oakwooddoubleslab,125,0 +oakwooddslab,125,0 +oakwdoubleslab,125,0 +oakwdslab,125,0 +oakdoublewoodenplankslab,125,0 +oakdwoodenplankslab,125,0 +oakdoublewoodplankslab,125,0 +oakdwoodplankslab,125,0 +oakdoublewplankslab,125,0 +oakdwplankslab,125,0 +oakdoubleplankslab,125,0 +oakdplankslab,125,0 +oakdoublewoodenslab,125,0 +oakdwoodenslab,125,0 +oakdoublewoodslab,125,0 +oakdwoodslab,125,0 +oakdoublewslab,125,0 +oakdwslab,125,0 +oakdoubleslab,125,0 +oakdslab,125,0 +sprucewoodenplankdoublehalfblock,125,1 +sprucewoodenplankdhalfblock,125,1 +sprucewoodplankdoublehalfblock,125,1 +sprucewoodplankdhalfblock,125,1 +sprucewplankdoublehalfblock,125,1 +sprucewplankdhalfblock,125,1 +spruceplankdoublehalfblock,125,1 +spruceplankdhalfblock,125,1 +sprucewoodendoublehalfblock,125,1 +sprucewoodendhalfblock,125,1 +sprucewooddoublehalfblock,125,1 +sprucewooddhalfblock,125,1 +sprucewdoublehalfblock,125,1 +sprucewdhalfblock,125,1 +sprucedoublewoodenplankhalfblock,125,1 +sprucedwoodenplankhalfblock,125,1 +sprucedoublewoodplankhalfblock,125,1 +sprucedwoodplankhalfblock,125,1 +sprucedoublewplankhalfblock,125,1 +sprucedwplankhalfblock,125,1 +sprucedoubleplankhalfblock,125,1 +sprucedplankhalfblock,125,1 +sprucedoublewoodenhalfblock,125,1 +sprucedwoodenhalfblock,125,1 +sprucedoublewoodhalfblock,125,1 +sprucedwoodhalfblock,125,1 +sprucedoublewhalfblock,125,1 +sprucedwhalfblock,125,1 +sprucedoublehalfblock,125,1 +sprucedhalfblock,125,1 +sprucewoodenplankdoublestep,125,1 +sprucewoodenplankdstep,125,1 +sprucewoodplankdoublestep,125,1 +sprucewoodplankdstep,125,1 +sprucewplankdoublestep,125,1 +sprucewplankdstep,125,1 +spruceplankdoublestep,125,1 +spruceplankdstep,125,1 +sprucewoodendoublestep,125,1 +sprucewoodendstep,125,1 +sprucewooddoublestep,125,1 +sprucewooddstep,125,1 +sprucewdoublestep,125,1 +sprucewdstep,125,1 +sprucedoublewoodenplankstep,125,1 +sprucedwoodenplankstep,125,1 +sprucedoublewoodplankstep,125,1 +sprucedwoodplankstep,125,1 +sprucedoublewplankstep,125,1 +sprucedwplankstep,125,1 +sprucedoubleplankstep,125,1 +sprucedplankstep,125,1 +sprucedoublewoodenstep,125,1 +sprucedwoodenstep,125,1 +sprucedoublewoodstep,125,1 +sprucedwoodstep,125,1 +sprucedoublewstep,125,1 +sprucedwstep,125,1 +sprucedoublestep,125,1 +sprucedstep,125,1 +sprucewoodenplankdoubleslab,125,1 +sprucewoodenplankdslab,125,1 +sprucewoodplankdoubleslab,125,1 +sprucewoodplankdslab,125,1 +sprucewplankdoubleslab,125,1 +sprucewplankdslab,125,1 +spruceplankdoubleslab,125,1 +spruceplankdslab,125,1 +sprucewoodendoubleslab,125,1 +sprucewoodendslab,125,1 +sprucewooddoubleslab,125,1 +sprucewooddslab,125,1 +sprucewdoubleslab,125,1 +sprucewdslab,125,1 +sprucedoublewoodenplankslab,125,1 +sprucedwoodenplankslab,125,1 +sprucedoublewoodplankslab,125,1 +sprucedwoodplankslab,125,1 +sprucedoublewplankslab,125,1 +sprucedwplankslab,125,1 +sprucedoubleplankslab,125,1 +sprucedplankslab,125,1 +sprucedoublewoodenslab,125,1 +sprucedwoodenslab,125,1 +sprucedoublewoodslab,125,1 +sprucedwoodslab,125,1 +sprucedoublewslab,125,1 +sprucedwslab,125,1 +sprucedoubleslab,125,1 +sprucedslab,125,1 +darkwoodenplankdoublehalfblock,125,1 +darkwoodenplankdhalfblock,125,1 +darkwoodplankdoublehalfblock,125,1 +darkwoodplankdhalfblock,125,1 +darkwplankdoublehalfblock,125,1 +darkwplankdhalfblock,125,1 +darkplankdoublehalfblock,125,1 +darkplankdhalfblock,125,1 +darkwoodendoublehalfblock,125,1 +darkwoodendhalfblock,125,1 +darkwooddoublehalfblock,125,1 +darkwooddhalfblock,125,1 +darkwdoublehalfblock,125,1 +darkwdhalfblock,125,1 +darkdoublewoodenplankhalfblock,125,1 +darkdwoodenplankhalfblock,125,1 +darkdoublewoodplankhalfblock,125,1 +darkdwoodplankhalfblock,125,1 +darkdoublewplankhalfblock,125,1 +darkdwplankhalfblock,125,1 +darkdoubleplankhalfblock,125,1 +darkdplankhalfblock,125,1 +darkdoublewoodenhalfblock,125,1 +darkdwoodenhalfblock,125,1 +darkdoublewoodhalfblock,125,1 +darkdwoodhalfblock,125,1 +darkdoublewhalfblock,125,1 +darkdwhalfblock,125,1 +darkdoublehalfblock,125,1 +darkdhalfblock,125,1 +darkwoodenplankdoublestep,125,1 +darkwoodenplankdstep,125,1 +darkwoodplankdoublestep,125,1 +darkwoodplankdstep,125,1 +darkwplankdoublestep,125,1 +darkwplankdstep,125,1 +darkplankdoublestep,125,1 +darkplankdstep,125,1 +darkwoodendoublestep,125,1 +darkwoodendstep,125,1 +darkwooddoublestep,125,1 +darkwooddstep,125,1 +darkwdoublestep,125,1 +darkwdstep,125,1 +darkdoublewoodenplankstep,125,1 +darkdwoodenplankstep,125,1 +darkdoublewoodplankstep,125,1 +darkdwoodplankstep,125,1 +darkdoublewplankstep,125,1 +darkdwplankstep,125,1 +darkdoubleplankstep,125,1 +darkdplankstep,125,1 +darkdoublewoodenstep,125,1 +darkdwoodenstep,125,1 +darkdoublewoodstep,125,1 +darkdwoodstep,125,1 +darkdoublewstep,125,1 +darkdwstep,125,1 +darkdoublestep,125,1 +darkdstep,125,1 +darkwoodenplankdoubleslab,125,1 +darkwoodenplankdslab,125,1 +darkwoodplankdoubleslab,125,1 +darkwoodplankdslab,125,1 +darkwplankdoubleslab,125,1 +darkwplankdslab,125,1 +darkplankdoubleslab,125,1 +darkplankdslab,125,1 +darkwoodendoubleslab,125,1 +darkwoodendslab,125,1 +darkwooddoubleslab,125,1 +darkwooddslab,125,1 +darkwdoubleslab,125,1 +darkwdslab,125,1 +darkdoublewoodenplankslab,125,1 +darkdwoodenplankslab,125,1 +darkdoublewoodplankslab,125,1 +darkdwoodplankslab,125,1 +darkdoublewplankslab,125,1 +darkdwplankslab,125,1 +darkdoubleplankslab,125,1 +darkdplankslab,125,1 +darkdoublewoodenslab,125,1 +darkdwoodenslab,125,1 +darkdoublewoodslab,125,1 +darkdwoodslab,125,1 +darkdoublewslab,125,1 +darkdwslab,125,1 +darkdoubleslab,125,1 +darkdslab,125,1 +birchwoodenplankdoublehalfblock,125,2 +birchwoodenplankdhalfblock,125,2 +birchwoodplankdoublehalfblock,125,2 +birchwoodplankdhalfblock,125,2 +birchwplankdoublehalfblock,125,2 +birchwplankdhalfblock,125,2 +birchplankdoublehalfblock,125,2 +birchplankdhalfblock,125,2 +birchwoodendoublehalfblock,125,2 +birchwoodendhalfblock,125,2 +birchwooddoublehalfblock,125,2 +birchwooddhalfblock,125,2 +birchwdoublehalfblock,125,2 +birchwdhalfblock,125,2 +birchdoublewoodenplankhalfblock,125,2 +birchdwoodenplankhalfblock,125,2 +birchdoublewoodplankhalfblock,125,2 +birchdwoodplankhalfblock,125,2 +birchdoublewplankhalfblock,125,2 +birchdwplankhalfblock,125,2 +birchdoubleplankhalfblock,125,2 +birchdplankhalfblock,125,2 +birchdoublewoodenhalfblock,125,2 +birchdwoodenhalfblock,125,2 +birchdoublewoodhalfblock,125,2 +birchdwoodhalfblock,125,2 +birchdoublewhalfblock,125,2 +birchdwhalfblock,125,2 +birchdoublehalfblock,125,2 +birchdhalfblock,125,2 +birchwoodenplankdoublestep,125,2 +birchwoodenplankdstep,125,2 +birchwoodplankdoublestep,125,2 +birchwoodplankdstep,125,2 +birchwplankdoublestep,125,2 +birchwplankdstep,125,2 +birchplankdoublestep,125,2 +birchplankdstep,125,2 +birchwoodendoublestep,125,2 +birchwoodendstep,125,2 +birchwooddoublestep,125,2 +birchwooddstep,125,2 +birchwdoublestep,125,2 +birchwdstep,125,2 +birchdoublewoodenplankstep,125,2 +birchdwoodenplankstep,125,2 +birchdoublewoodplankstep,125,2 +birchdwoodplankstep,125,2 +birchdoublewplankstep,125,2 +birchdwplankstep,125,2 +birchdoubleplankstep,125,2 +birchdplankstep,125,2 +birchdoublewoodenstep,125,2 +birchdwoodenstep,125,2 +birchdoublewoodstep,125,2 +birchdwoodstep,125,2 +birchdoublewstep,125,2 +birchdwstep,125,2 +birchdoublestep,125,2 +birchdstep,125,2 +birchwoodenplankdoubleslab,125,2 +birchwoodenplankdslab,125,2 +birchwoodplankdoubleslab,125,2 +birchwoodplankdslab,125,2 +birchwplankdoubleslab,125,2 +birchwplankdslab,125,2 +birchplankdoubleslab,125,2 +birchplankdslab,125,2 +birchwoodendoubleslab,125,2 +birchwoodendslab,125,2 +birchwooddoubleslab,125,2 +birchwooddslab,125,2 +birchwdoubleslab,125,2 +birchwdslab,125,2 +birchdoublewoodenplankslab,125,2 +birchdwoodenplankslab,125,2 +birchdoublewoodplankslab,125,2 +birchdwoodplankslab,125,2 +birchdoublewplankslab,125,2 +birchdwplankslab,125,2 +birchdoubleplankslab,125,2 +birchdplankslab,125,2 +birchdoublewoodenslab,125,2 +birchdwoodenslab,125,2 +birchdoublewoodslab,125,2 +birchdwoodslab,125,2 +birchdoublewslab,125,2 +birchdwslab,125,2 +birchdoubleslab,125,2 +birchdslab,125,2 +lightwoodenplankdoublehalfblock,125,2 +lightwoodenplankdhalfblock,125,2 +lightwoodplankdoublehalfblock,125,2 +lightwoodplankdhalfblock,125,2 +lightwplankdoublehalfblock,125,2 +lightwplankdhalfblock,125,2 +lightplankdoublehalfblock,125,2 +lightplankdhalfblock,125,2 +lightwoodendoublehalfblock,125,2 +lightwoodendhalfblock,125,2 +lightwooddoublehalfblock,125,2 +lightwooddhalfblock,125,2 +lightwdoublehalfblock,125,2 +lightwdhalfblock,125,2 +lightdoublewoodenplankhalfblock,125,2 +lightdwoodenplankhalfblock,125,2 +lightdoublewoodplankhalfblock,125,2 +lightdwoodplankhalfblock,125,2 +lightdoublewplankhalfblock,125,2 +lightdwplankhalfblock,125,2 +lightdoubleplankhalfblock,125,2 +lightdplankhalfblock,125,2 +lightdoublewoodenhalfblock,125,2 +lightdwoodenhalfblock,125,2 +lightdoublewoodhalfblock,125,2 +lightdwoodhalfblock,125,2 +lightdoublewhalfblock,125,2 +lightdwhalfblock,125,2 +lightdoublehalfblock,125,2 +lightdhalfblock,125,2 +lightwoodenplankdoublestep,125,2 +lightwoodenplankdstep,125,2 +lightwoodplankdoublestep,125,2 +lightwoodplankdstep,125,2 +lightwplankdoublestep,125,2 +lightwplankdstep,125,2 +lightplankdoublestep,125,2 +lightplankdstep,125,2 +lightwoodendoublestep,125,2 +lightwoodendstep,125,2 +lightwooddoublestep,125,2 +lightwooddstep,125,2 +lightwdoublestep,125,2 +lightwdstep,125,2 +lightdoublewoodenplankstep,125,2 +lightdwoodenplankstep,125,2 +lightdoublewoodplankstep,125,2 +lightdwoodplankstep,125,2 +lightdoublewplankstep,125,2 +lightdwplankstep,125,2 +lightdoubleplankstep,125,2 +lightdplankstep,125,2 +lightdoublewoodenstep,125,2 +lightdwoodenstep,125,2 +lightdoublewoodstep,125,2 +lightdwoodstep,125,2 +lightdoublewstep,125,2 +lightdwstep,125,2 +lightdoublestep,125,2 +lightdstep,125,2 +lightwoodenplankdoubleslab,125,2 +lightwoodenplankdslab,125,2 +lightwoodplankdoubleslab,125,2 +lightwoodplankdslab,125,2 +lightwplankdoubleslab,125,2 +lightwplankdslab,125,2 +lightplankdoubleslab,125,2 +lightplankdslab,125,2 +lightwoodendoubleslab,125,2 +lightwoodendslab,125,2 +lightwooddoubleslab,125,2 +lightwooddslab,125,2 +lightwdoubleslab,125,2 +lightwdslab,125,2 +lightdoublewoodenplankslab,125,2 +lightdwoodenplankslab,125,2 +lightdoublewoodplankslab,125,2 +lightdwoodplankslab,125,2 +lightdoublewplankslab,125,2 +lightdwplankslab,125,2 +lightdoubleplankslab,125,2 +lightdplankslab,125,2 +lightdoublewoodenslab,125,2 +lightdwoodenslab,125,2 +lightdoublewoodslab,125,2 +lightdwoodslab,125,2 +lightdoublewslab,125,2 +lightdwslab,125,2 +lightdoubleslab,125,2 +lightdslab,125,2 +junglewoodenplankdoublehalfblock,125,3 +junglewoodenplankdhalfblock,125,3 +junglewoodplankdoublehalfblock,125,3 +junglewoodplankdhalfblock,125,3 +junglewplankdoublehalfblock,125,3 +junglewplankdhalfblock,125,3 +jungleplankdoublehalfblock,125,3 +jungleplankdhalfblock,125,3 +junglewoodendoublehalfblock,125,3 +junglewoodendhalfblock,125,3 +junglewooddoublehalfblock,125,3 +junglewooddhalfblock,125,3 +junglewdoublehalfblock,125,3 +junglewdhalfblock,125,3 +jungledoublewoodenplankhalfblock,125,3 +jungledwoodenplankhalfblock,125,3 +jungledoublewoodplankhalfblock,125,3 +jungledwoodplankhalfblock,125,3 +jungledoublewplankhalfblock,125,3 +jungledwplankhalfblock,125,3 +jungledoubleplankhalfblock,125,3 +jungledplankhalfblock,125,3 +jungledoublewoodenhalfblock,125,3 +jungledwoodenhalfblock,125,3 +jungledoublewoodhalfblock,125,3 +jungledwoodhalfblock,125,3 +jungledoublewhalfblock,125,3 +jungledwhalfblock,125,3 +jungledoublehalfblock,125,3 +jungledhalfblock,125,3 +junglewoodenplankdoublestep,125,3 +junglewoodenplankdstep,125,3 +junglewoodplankdoublestep,125,3 +junglewoodplankdstep,125,3 +junglewplankdoublestep,125,3 +junglewplankdstep,125,3 +jungleplankdoublestep,125,3 +jungleplankdstep,125,3 +junglewoodendoublestep,125,3 +junglewoodendstep,125,3 +junglewooddoublestep,125,3 +junglewooddstep,125,3 +junglewdoublestep,125,3 +junglewdstep,125,3 +jungledoublewoodenplankstep,125,3 +jungledwoodenplankstep,125,3 +jungledoublewoodplankstep,125,3 +jungledwoodplankstep,125,3 +jungledoublewplankstep,125,3 +jungledwplankstep,125,3 +jungledoubleplankstep,125,3 +jungledplankstep,125,3 +jungledoublewoodenstep,125,3 +jungledwoodenstep,125,3 +jungledoublewoodstep,125,3 +jungledwoodstep,125,3 +jungledoublewstep,125,3 +jungledwstep,125,3 +jungledoublestep,125,3 +jungledstep,125,3 +junglewoodenplankdoubleslab,125,3 +junglewoodenplankdslab,125,3 +junglewoodplankdoubleslab,125,3 +junglewoodplankdslab,125,3 +junglewplankdoubleslab,125,3 +junglewplankdslab,125,3 +jungleplankdoubleslab,125,3 +jungleplankdslab,125,3 +junglewoodendoubleslab,125,3 +junglewoodendslab,125,3 +junglewooddoubleslab,125,3 +junglewooddslab,125,3 +junglewdoubleslab,125,3 +junglewdslab,125,3 +jungledoublewoodenplankslab,125,3 +jungledwoodenplankslab,125,3 +jungledoublewoodplankslab,125,3 +jungledwoodplankslab,125,3 +jungledoublewplankslab,125,3 +jungledwplankslab,125,3 +jungledoubleplankslab,125,3 +jungledplankslab,125,3 +jungledoublewoodenslab,125,3 +jungledwoodenslab,125,3 +jungledoublewoodslab,125,3 +jungledwoodslab,125,3 +jungledoublewslab,125,3 +jungledwslab,125,3 +jungledoubleslab,125,3 +jungledslab,125,3 +forestwoodenplankdoublehalfblock,125,3 +forestwoodenplankdhalfblock,125,3 +forestwoodplankdoublehalfblock,125,3 +forestwoodplankdhalfblock,125,3 +forestwplankdoublehalfblock,125,3 +forestwplankdhalfblock,125,3 +forestplankdoublehalfblock,125,3 +forestplankdhalfblock,125,3 +forestwoodendoublehalfblock,125,3 +forestwoodendhalfblock,125,3 +forestwooddoublehalfblock,125,3 +forestwooddhalfblock,125,3 +forestwdoublehalfblock,125,3 +forestwdhalfblock,125,3 +forestdoublewoodenplankhalfblock,125,3 +forestdwoodenplankhalfblock,125,3 +forestdoublewoodplankhalfblock,125,3 +forestdwoodplankhalfblock,125,3 +forestdoublewplankhalfblock,125,3 +forestdwplankhalfblock,125,3 +forestdoubleplankhalfblock,125,3 +forestdplankhalfblock,125,3 +forestdoublewoodenhalfblock,125,3 +forestdwoodenhalfblock,125,3 +forestdoublewoodhalfblock,125,3 +forestdwoodhalfblock,125,3 +forestdoublewhalfblock,125,3 +forestdwhalfblock,125,3 +forestdoublehalfblock,125,3 +forestdhalfblock,125,3 +forestwoodenplankdoublestep,125,3 +forestwoodenplankdstep,125,3 +forestwoodplankdoublestep,125,3 +forestwoodplankdstep,125,3 +forestwplankdoublestep,125,3 +forestwplankdstep,125,3 +forestplankdoublestep,125,3 +forestplankdstep,125,3 +forestwoodendoublestep,125,3 +forestwoodendstep,125,3 +forestwooddoublestep,125,3 +forestwooddstep,125,3 +forestwdoublestep,125,3 +forestwdstep,125,3 +forestdoublewoodenplankstep,125,3 +forestdwoodenplankstep,125,3 +forestdoublewoodplankstep,125,3 +forestdwoodplankstep,125,3 +forestdoublewplankstep,125,3 +forestdwplankstep,125,3 +forestdoubleplankstep,125,3 +forestdplankstep,125,3 +forestdoublewoodenstep,125,3 +forestdwoodenstep,125,3 +forestdoublewoodstep,125,3 +forestdwoodstep,125,3 +forestdoublewstep,125,3 +forestdwstep,125,3 +forestdoublestep,125,3 +forestdstep,125,3 +forestwoodenplankdoubleslab,125,3 +forestwoodenplankdslab,125,3 +forestwoodplankdoubleslab,125,3 +forestwoodplankdslab,125,3 +forestwplankdoubleslab,125,3 +forestwplankdslab,125,3 +forestplankdoubleslab,125,3 +forestplankdslab,125,3 +forestwoodendoubleslab,125,3 +forestwoodendslab,125,3 +forestwooddoubleslab,125,3 +forestwooddslab,125,3 +forestwdoubleslab,125,3 +forestwdslab,125,3 +forestdoublewoodenplankslab,125,3 +forestdwoodenplankslab,125,3 +forestdoublewoodplankslab,125,3 +forestdwoodplankslab,125,3 +forestdoublewplankslab,125,3 +forestdwplankslab,125,3 +forestdoubleplankslab,125,3 +forestdplankslab,125,3 +forestdoublewoodenslab,125,3 +forestdwoodenslab,125,3 +forestdoublewoodslab,125,3 +forestdwoodslab,125,3 +forestdoublewslab,125,3 +forestdwslab,125,3 +forestdoubleslab,125,3 +forestdslab,125,3 +woodenplankstep,126,0 +woodplankstep,126,0 +wplankstep,126,0 +plankstep,126,0 +woodenstep,126,0 +woodstep,126,0 +wstep,126,0 +woodenplankslab,126,0 +woodplankslab,126,0 +wplankslab,126,0 +plankslab,126,0 +woodenslab,126,0 +woodslab,126,0 +wslab,126,0 +woodenplankhalfblock,126,0 +woodplankhalfblock,126,0 +wplankhalfblock,126,0 +plankhalfblock,126,0 +woodenhalfblock,126,0 +woodhalfblock,126,0 +whalfblock,126,0 +oakwoodenplankstep,126,0 +oakwoodplankstep,126,0 +oakwplankstep,126,0 +oakplankstep,126,0 +oakwoodenstep,126,0 +oakwoodstep,126,0 +oakwstep,126,0 +oakstep,126,0 +oakwoodenplankslab,126,0 +oakwoodplankslab,126,0 +oakwplankslab,126,0 +oakplankslab,126,0 +oakwoodenslab,126,0 +oakwoodslab,126,0 +oakwslab,126,0 +oakslab,126,0 +oakwoodenplankhalfblock,126,0 +oakwoodplankhalfblock,126,0 +oakwplankhalfblock,126,0 +oakplankhalfblock,126,0 +oakwoodenhalfblock,126,0 +oakwoodhalfblock,126,0 +oakwhalfblock,126,0 +oakhalfblock,126,0 +sprucewoodenplankstep,126,1 +sprucewoodplankstep,126,1 +sprucewplankstep,126,1 +spruceplankstep,126,1 +sprucewoodenstep,126,1 +sprucewoodstep,126,1 +sprucewstep,126,1 +sprucestep,126,1 +sprucewoodenplankslab,126,1 +sprucewoodplankslab,126,1 +sprucewplankslab,126,1 +spruceplankslab,126,1 +sprucewoodenslab,126,1 +sprucewoodslab,126,1 +sprucewslab,126,1 +spruceslab,126,1 +sprucewoodenplankhalfblock,126,1 +sprucewoodplankhalfblock,126,1 +sprucewplankhalfblock,126,1 +spruceplankhalfblock,126,1 +sprucewoodenhalfblock,126,1 +sprucewoodhalfblock,126,1 +sprucewhalfblock,126,1 +sprucehalfblock,126,1 +darkwoodenplankstep,126,1 +darkwoodplankstep,126,1 +darkwplankstep,126,1 +darkplankstep,126,1 +darkwoodenstep,126,1 +darkwoodstep,126,1 +darkwstep,126,1 +darkstep,126,1 +darkwoodenplankslab,126,1 +darkwoodplankslab,126,1 +darkwplankslab,126,1 +darkplankslab,126,1 +darkwoodenslab,126,1 +darkwoodslab,126,1 +darkwslab,126,1 +darkslab,126,1 +darkwoodenplankhalfblock,126,1 +darkwoodplankhalfblock,126,1 +darkwplankhalfblock,126,1 +darkplankhalfblock,126,1 +darkwoodenhalfblock,126,1 +darkwoodhalfblock,126,1 +darkwhalfblock,126,1 +darkhalfblock,126,1 +birchwoodenplankstep,126,2 +birchwoodplankstep,126,2 +birchwplankstep,126,2 +birchplankstep,126,2 +birchwoodenstep,126,2 +birchwoodstep,126,2 +birchwstep,126,2 +birchstep,126,2 +birchwoodenplankslab,126,2 +birchwoodplankslab,126,2 +birchwplankslab,126,2 +birchplankslab,126,2 +birchwoodenslab,126,2 +birchwoodslab,126,2 +birchwslab,126,2 +birchslab,126,2 +birchwoodenplankhalfblock,126,2 +birchwoodplankhalfblock,126,2 +birchwplankhalfblock,126,2 +birchplankhalfblock,126,2 +birchwoodenhalfblock,126,2 +birchwoodhalfblock,126,2 +birchwhalfblock,126,2 +birchhalfblock,126,2 +lightwoodenplankstep,126,2 +lightwoodplankstep,126,2 +lightwplankstep,126,2 +lightplankstep,126,2 +lightwoodenstep,126,2 +lightwoodstep,126,2 +lightwstep,126,2 +lightstep,126,2 +lightwoodenplankslab,126,2 +lightwoodplankslab,126,2 +lightwplankslab,126,2 +lightplankslab,126,2 +lightwoodenslab,126,2 +lightwoodslab,126,2 +lightwslab,126,2 +lightslab,126,2 +lightwoodenplankhalfblock,126,2 +lightwoodplankhalfblock,126,2 +lightwplankhalfblock,126,2 +lightplankhalfblock,126,2 +lightwoodenhalfblock,126,2 +lightwoodhalfblock,126,2 +lightwhalfblock,126,2 +lighthalfblock,126,2 +junglewoodenplankstep,126,3 +junglewoodplankstep,126,3 +junglewplankstep,126,3 +jungleplankstep,126,3 +junglewoodenstep,126,3 +junglewoodstep,126,3 +junglewstep,126,3 +junglestep,126,3 +junglewoodenplankslab,126,3 +junglewoodplankslab,126,3 +junglewplankslab,126,3 +jungleplankslab,126,3 +junglewoodenslab,126,3 +junglewoodslab,126,3 +junglewslab,126,3 +jungleslab,126,3 +junglewoodenplankhalfblock,126,3 +junglewoodplankhalfblock,126,3 +junglewplankhalfblock,126,3 +jungleplankhalfblock,126,3 +junglewoodenhalfblock,126,3 +junglewoodhalfblock,126,3 +junglewhalfblock,126,3 +junglehalfblock,126,3 +forestwoodenplankstep,126,3 +forestwoodplankstep,126,3 +forestwplankstep,126,3 +forestplankstep,126,3 +forestwoodenstep,126,3 +forestwoodstep,126,3 +forestwstep,126,3 +foreststep,126,3 +forestwoodenplankslab,126,3 +forestwoodplankslab,126,3 +forestwplankslab,126,3 +forestplankslab,126,3 +forestwoodenslab,126,3 +forestwoodslab,126,3 +forestwslab,126,3 +forestslab,126,3 +forestwoodenplankhalfblock,126,3 +forestwoodplankhalfblock,126,3 +forestwplankhalfblock,126,3 +forestplankhalfblock,126,3 +forestwoodenhalfblock,126,3 +forestwoodhalfblock,126,3 +forestwhalfblock,126,3 +foresthalfblock,126,3 +cocoaplant,127,0 +cocoplant,127,0 +cplant,127,0 +cocoafruit,127,0 +cocofruit,127,0 +cfruit,127,0 +cocoapod,127,0 +cocopod,127,0 +cpod,127,0 +sandstairs,128,0 +sandstonestairs,128,0 +sandsstairs,128,0 +sstonestairs,128,0 +ssstairs,128,0 +sandstair,128,0 +sandstonestair,128,0 +sandsstair,128,0 +sstonestair,128,0 +ssstair,128,0 +emeraldore,129,0 +eore,129,0 +oreemerald,129,0 +oree,129,0 +enderchest,130,0 +endchest,130,0 +echest,130,0 +chestender,130,0 +chestend,130,0 +cheste,130,0 +endercontainer,130,0 +endcontainer,130,0 +econtainer,130,0 +tripwirehook,131,0 +tripwire,131,0 +trip,131,0 +tripwirelever,131,0 +triphook,131,0 +tripwireblock,132,0 +tripblock,132,0 +blocktrip,132,0 +blocktripwire,132,0 +emeraldblock,133,0 +blockemerald,133,0 +eblock,133,0 +blocke,133,0 +sprucewoodenplankstairs,134,0 +sprucewoodplankstairs,134,0 +sprucewplankstairs,134,0 +spruceplankstairs,134,0 +sprucewoodenstairs,134,0 +sprucewoodstairs,134,0 +sprucewstairs,134,0 +sprucestairs,134,0 +darkwoodenplankstairs,134,0 +darkwoodplankstairs,134,0 +darkwplankstairs,134,0 +darkplankstairs,134,0 +darkwoodenstairs,134,0 +darkwoodstairs,134,0 +darkwstairs,134,0 +darkstairs,134,0 +sprucewoodenplankstair,134,0 +sprucewoodplankstair,134,0 +sprucewplankstair,134,0 +spruceplankstair,134,0 +sprucewoodenstair,134,0 +sprucewoodstair,134,0 +sprucewstair,134,0 +sprucestair,134,0 +darkwoodenplankstair,134,0 +darkwoodplankstair,134,0 +darkwplankstair,134,0 +darkplankstair,134,0 +darkwoodenstair,134,0 +darkwoodstair,134,0 +darkwstair,134,0 +darkstair,134,0 +birchwoodenplankstairs,135,0 +birchwoodplankstairs,135,0 +birchwplankstairs,135,0 +birchplankstairs,135,0 +birchwoodenstairs,135,0 +birchwoodstairs,135,0 +birchwstairs,135,0 +birchstairs,135,0 +lightwoodenplankstairs,135,0 +lightwoodplankstairs,135,0 +lightwplankstairs,135,0 +lightplankstairs,135,0 +lightwoodenstairs,135,0 +lightwoodstairs,135,0 +lightwstairs,135,0 +lightstairs,135,0 +birchwoodenplankstair,135,0 +birchwoodplankstair,135,0 +birchwplankstair,135,0 +birchplankstair,135,0 +birchwoodenstair,135,0 +birchwoodstair,135,0 +birchwstair,135,0 +birchstair,135,0 +lightwoodenplankstair,135,0 +lightwoodplankstair,135,0 +lightwplankstair,135,0 +lightplankstair,135,0 +lightwoodenstair,135,0 +lightwoodstair,135,0 +lightwstair,135,0 +lightstair,135,0 +junglewoodenplankstairs,136,0 +junglewoodplankstairs,136,0 +junglewplankstairs,136,0 +jungleplankstairs,136,0 +junglewoodenstairs,136,0 +junglewoodstairs,136,0 +junglewstairs,136,0 +junglestairs,136,0 +forestwoodenplankstairs,136,0 +forestwoodplankstairs,136,0 +forestwplankstairs,136,0 +forestplankstairs,136,0 +forestwoodenstairs,136,0 +forestwoodstairs,136,0 +forestwstairs,136,0 +foreststairs,136,0 +junglewoodenplankstair,136,0 +junglewoodplankstair,136,0 +junglewplankstair,136,0 +jungleplankstair,136,0 +junglewoodenstair,136,0 +junglewoodstair,136,0 +junglewstair,136,0 +junglestair,136,0 +forestwoodenplankstair,136,0 +forestwoodplankstair,136,0 +forestwplankstair,136,0 +forestplankstair,136,0 +forestwoodenstair,136,0 +forestwoodstair,136,0 +forestwstair,136,0 +foreststair,136,0 +commandblock,137,0 +blockcommand,137,0 +cmdblock,137,0 +blockcmd,137,0 +macroblock,137,0 +blockmacro,137,0 +beacon,138,0 +beaconblock,138,0 +cobblestonewall,139,0 +cstonewall,139,0 +cobblewall,139,0 +cobblestonefence,139,0 +cstonefence,139,0 +cobblefence,139,0 +mosscobblestonewall,139,1 +mosscstonewall,139,1 +mosscobblewall,139,1 +mcobblestonewall,139,1 +mcstonewall,139,1 +mcobblewall,139,1 +mosscobblestonefence,139,1 +mosscstonefence,139,1 +mosscobblefence,139,1 +mcobblestonefence,139,1 +mcstonefence,139,1 +mcobblefence,139,1 +emptyflowerpot,140,0 +emptypot,140,0 +flowerpotblock,140,0 +potblock,140,0 +roseflowerpot,140,1 +rosepot,140,1 +dandelionflowerpot,140,2 +dandelionpot,140,2 +oaksaplingflowerpot,140,3 +saplingflowerpot,140,3 +oakflowerpot,140,3 +oaksaplingpot,140,3 +saplingpot,140,3 +oakpot,140,3 +sprucesaplingflowerpot,140,4 +spruceflowerpot,140,4 +darksaplingflowerpot,140,4 +darkflowerpot,140,4 +pinesaplingflowerpot,140,4 +pineflowerpot,140,4 +sprucesaplingpot,140,4 +sprucepot,140,4 +darksaplingpot,140,4 +darkpot,140,4 +pinesaplingpot,140,4 +pinepot,140,4 +birchsaplingflowerpot,140,5 +birchflowerpot,140,5 +lightsaplingflowerpot,140,5 +lightflowerpot,140,5 +whitesaplingflowerpot,140,5 +whiteflowerpot,140,5 +birchsaplingpot,140,5 +birchpot,140,5 +lightsaplingpot,140,5 +lightpot,140,5 +whitesaplingpot,140,5 +whitepot,140,5 +forestsaplingflowerpot,140,6 +forestflowerpot,140,6 +junglesaplingflowerpot,140,6 +jungleflowerpot,140,6 +forestsaplingpot,140,6 +forestpot,140,6 +junglesaplingpot,140,6 +junglepot,140,6 +redmushroomflowerpot,140,7 +redshroomflowerpot,140,7 +redmushflowerpot,140,7 +rmushroomflowerpot,140,7 +rshroomflowerpot,140,7 +rmushflowerpot,140,7 +redmushroompot,140,7 +redshroompot,140,7 +redmushpot,140,7 +rmushroompot,140,7 +rshroompot,140,7 +rmushpot,140,7 +brownmushroomflowerpot,140,8 +brownshroomflowerpot,140,8 +brownmushflowerpot,140,8 +bmushroomflowerpot,140,8 +bshroomflowerpot,140,7 +bmushflowerpot,140,8 +brownmushroompot,140,8 +brownshroompot,140,8 +brownmushpot,140,8 +bmushroompot,140,8 +bshroompot,140,8 +bmushpot,140,8 +cactusflowerpot,140,9 +cactuspot,140,9 +deadbushflowerpot,140,10 +dbushflowerpot,140,10 +deadsaplingflowerpot,140,10 +dsaplingflowerpot,140,10 +deadshrubflowerpot,140,10 +dshrubflowerpot,140,10 +deadbushpot,140,10 +dbushpot,140,10 +deadsaplingpot,140,10 +dsaplingpot,140,10 +deadshrubpot,140,10 +dshrubpot,140,10 +fernflowerpot,140,11 +bushflowerpot,140,11 +fernpot,140,11 +bushpot,140,11 +carrots,141,0 +plantedcarrot,141,0 +plantcarrot,141,0 +growingcarrot,141,0 +potatoes,142,0 +plantedpotato,142,0 +plantpotato,142,0 +growingpotato,142,0 +woodenplankbutton,143,0 +woodplankbutton,143,0 +wplankbutton,143,0 +plankbutton,143,0 +woodenbutton,143,0 +woodbutton,143,0 +wbutton,143,0 +headblock,144,0 +anvil,145,0 +slightlydamagedanvil,145,1 +slightdamageanvil,145,1 +damagedanvil,145,1 +verydamagedanvil,145,2 +trappedchest,146,0 +trapchest,146,0 +chesttrapped,146,0 +chesttrap,146,0 +weightedgoldpressureplate,147,0 +weightgoldpressureplate,147,0 +wgoldpressureplate,147,0 +goldpressureplate,147,0 +weightedgoldpressplate,147,0 +weightgoldpressplate,147,0 +wgoldpressplate,147,0 +goldpressplate,147,0 +weightedgoldpplate,147,0 +weightgoldpplate,147,0 +wgoldpplate,147,0 +goldpplate,147,0 +weightedgoldplate,147,0 +weightgoldplate,147,0 +wgoldplate,147,0 +goldplate,147,0 +weightedgpressureplate,147,0 +weightgpressureplate,147,0 +wgpressureplate,147,0 +gpressureplate,147,0 +weightedgpressplate,147,0 +weightgpressplate,147,0 +wgpressplate,147,0 +gpressplate,147,0 +weightedgpplate,147,0 +weightgpplate,147,0 +wgpplate,147,0 +gpplate,147,0 +weightedgplate,147,0 +weightgplate,147,0 +wgplate,147,0 +gplate,147,0 +weightedironpressureplate,148,0 +weightironpressureplate,148,0 +wironpressureplate,148,0 +ironpressureplate,148,0 +weightedironpressplate,148,0 +weightironpressplate,148,0 +wironpressplate,148,0 +ironpressplate,148,0 +weightedironpplate,148,0 +weightironpplate,148,0 +wironpplate,148,0 +ironpplate,148,0 +weightedironplate,148,0 +weightironplate,148,0 +wironplate,148,0 +ironplate,148,0 +weightedipressureplate,148,0 +weightipressureplate,148,0 +wipressureplate,148,0 +ipressureplate,148,0 +weightedipressplate,148,0 +weightipressplate,148,0 +wipressplate,148,0 +ipressplate,148,0 +weightedipplate,148,0 +weightipplate,148,0 +wipplate,148,0 +ipplate,148,0 +weightediplate,148,0 +weightiplate,148,0 +wiplate,148,0 +iplate,148,0 +redstonecomparatorblockoff,149,0 +redstonecompererblockoff,149,0 +redstonecompareblockoff,149,0 +rstonecomparatorblockoff,149,0 +rstonecompererblockoff,149,0 +rstonecompareblockoff,149,0 +redscomparatorblockoff,149,0 +redscompererblockoff,149,0 +redscompareblockoff,149,0 +rscomparatorblockoff,149,0 +rscompererblockoff,149,0 +rscompareblockoff,149,0 +comparatorblockoff,149,0 +compererblockoff,149,0 +compareblockoff,149,0 +redstonecomparatoroff,149,0 +redstonecompereroff,149,0 +redstonecompareoff,149,0 +rstonecomparatoroff,149,0 +rstonecompereroff,149,0 +rstonecompareoff,149,0 +redscomparatoroff,149,0 +redscompereroff,149,0 +redscompareoff,149,0 +rscomparatoroff,149,0 +rscompereroff,149,0 +rscompareoff,149,0 +comparatoroff,149,0 +compereroff,149,0 +compareoff,149,0 +redstonecomparatorblockon,150,0 +redstonecompererblockon,150,0 +redstonecompareblockon,150,0 +rstonecomparatorblockon,150,0 +rstonecompererblockon,150,0 +rstonecompareblockon,150,0 +redscomparatorblockon,150,0 +redscompererblockon,150,0 +redscompareblockon,150,0 +rscomparatorblockon,150,0 +rscompererblockon,150,0 +rscompareblockon,150,0 +comparatorblockon,150,0 +compererblockon,150,0 +compareblockon,150,0 +redstonecomparatoron,150,0 +redstonecompereron,150,0 +redstonecompareon,150,0 +rstonecomparatoron,150,0 +rstonecompereron,150,0 +rstonecompareon,150,0 +redscomparatoron,150,0 +redscompereron,150,0 +redscompareon,150,0 +rscomparatoron,150,0 +rscompereron,150,0 +rscompareon,150,0 +comparatoron,150,0 +compereron,150,0 +compareon,150,0 +daylightsensor,151,0 +daylightsense,151,0 +lightsensor,151,0 +lightsense,151,0 +daysensor,151,0 +daysense,151,0 +timesensor,151,0 +timesense,151,0 +redstoneblock,152,0 +rstoneblock,152,0 +redsblock,152,0 +rsblock,152,0 +blockredstone,152,0 +blockrstone,152,0 +blockreds,152,0 +blockrs,152,0 +netherquartzore,153,0 +hellquartzore,153,0 +deathquartzore,153,0 +nquartzore,153,0 +hquartzore,153,0 +dquartzore,153,0 +quartzore,153,0 +netherqore,153,0 +hellqore,153,0 +deathqore,153,0 +nqore,153,0 +hqore,153,0 +dqore,153,0 +qore,153,0 +hopper,154,0 +chestpuller,154,0 +chestpull,154,0 +cheststorer,154,0 +cheststore,154,0 +itempuller,154,0 +itempull,154,0 +itemstorer,154,0 +itemstore,154,0 +normalquartzblock,155,0 +nquartzblock,155,0 +quartzblock,155,0 +normalqblock,155,0 +nqblock,155,0 +qblock,155,0 +blocknormalquartz,155,0 +blocknquartz,155,0 +blockquartz,155,0 +blocknormalq,155,0 +blocknq,155,0 +blockq,155,0 +chiseledquartzblock,155,1 +chiselquartzblock,155,1 +cquartzblock,155,1 +chiseledqblock,155,1 +chiselqblock,155,1 +cqblock,155,1 +blockchiseledquartz,155,1 +blockchiselquartz,155,1 +blockcquartz,155,1 +blockchiseledq,155,1 +blockchiselq,155,1 +blockcq,155,1 +pillarquartzblock,155,2 +pquartzblock,155,2 +pillarqblock,155,2 +pqblock,155,2 +blockpillarquartz,155,2 +blockpquartz,155,2 +blockpillarq,155,2 +blockpq,155,2 +quartzstairs,156,0 +qstairs,156,0 +quartzstair,156,0 +qstair,156,0 +activatorrails,157,0 +activaterails,157,0 +triggerrails,157,0 +arails,157,0 +trails,157,0 +activatorrail,157,0 +activaterail,157,0 +triggerrail,157,0 +arail,157,0 +trail,157,0 +activatortrack,157,0 +activatetrack,157,0 +triggertrack,157,0 +atrack,157,0 +ttrack,157,0 +dropper,158,0 +drop,158,0 +chestdispenser,158,0 +chestdispense,158,0 +chestdropper,158,0 +chestdrop,158,0 +whiteclay,159,0 +whitesclay,159,0 +whitestainedclay,159,0 +wclay,159,0 +wsclay,159,0 +wstainedclay,159,0 +sclay,159,0 +stainedclay,159,0 +orangeclay,159,1 +orangesclay,159,1 +orangestainedclay,159,1 +oclay,159,1 +osclay,159,1 +ostainedclay,159,1 +magentaclay,159,2 +magentasclay,159,2 +magentastainedclay,159,2 +mclay,159,2 +msclay,159,2 +mstainedclay,159,2 +lightblueclay,159,3 +lightbluesclay,159,3 +lightbluestainedclay,159,3 +lblueclay,159,3 +lbluesclay,159,3 +lbluestainedclay,159,3 +lightbluclay,159,3 +lightblusclay,159,3 +lightblustainedclay,159,3 +lbluclay,159,3 +lblusclay,159,3 +lblustainedclay,159,3 +yellowclay,159,4 +yellowsclay,159,4 +yellowstainedclay,159,4 +yclay,159,4 +ysclay,159,4 +ystainedclay,159,4 +lightgreenclay,159,5 +lightgreensclay,159,5 +lightgreenstainedclay,159,5 +lgreenclay,159,5 +lgreensclay,159,5 +lgreenstainedclay,159,5 +lightgreclay,159,5 +lightgresclay,159,5 +lightgrestainedclay,159,5 +lgreclay,159,5 +lgresclay,159,5 +lgrestainedclay,159,5 +limeclay,159,5 +limesclay,159,5 +limestainedclay,159,5 +lclay,159,5 +lsclay,159,5 +lstainedclay,159,5 +pinkclay,159,6 +pinksclay,159,6 +pinkstainedclay,159,6 +piclay,159,6 +pisclay,159,6 +pistainedclay,159,6 +darkgrayclay,159,7 +darkgraysclay,159,7 +darkgraystainedclay,159,7 +dgrayclay,159,7 +dgraysclay,159,7 +dgraystainedclay,159,7 +darkgraclay,159,7 +darkgrasclay,159,7 +darkgrastainedclay,159,7 +dgraclay,159,7 +dgrasclay,159,7 +dgrastainedclay,159,7 +grayclay,159,7 +graysclay,159,7 +graystainedclay,159,7 +graclay,159,7 +grasclay,159,7 +grastainedclay,159,7 +lightgrayclay,159,8 +lightgraysclay,159,8 +lightgraystainedclay,159,8 +lgrayclay,159,8 +lgraysclay,159,8 +lgraystainedclay,159,8 +lightgraclay,159,8 +lightgrasclay,159,8 +lightgrastainedclay,159,8 +lgraclay,159,8 +lgrasclay,159,8 +lgrastainedclay,159,8 +silverclay,159,8 +silversclay,159,8 +silverstainedclay,159,8 +siclay,159,8 +siasclay,159,8 +siastainedclay,159,8 +cyanclay,159,9 +cyansclay,159,9 +cyanstainedclay,159,9 +cclay,159,9 +csclay,159,9 +cstainedclay,159,9 +purpleclay,159,10 +purplesclay,159,10 +purplestainedclay,159,10 +puclay,159,10 +pusclay,159,10 +pustainedclay,159,10 +blueclay,159,11 +bluesclay,159,11 +bluestainedclay,159,11 +bluclay,159,11 +blusclay,159,11 +blustainedclay,159,11 +brownclay,159,12 +brownsclay,159,12 +brownstainedclay,159,12 +broclay,159,12 +brosclay,159,12 +brostainedclay,159,12 +darkgreenclay,159,13 +darkgreensclay,159,13 +darkgreenstainedclay,159,13 +dgreenclay,159,13 +dgreensclay,159,13 +dgreenstainedclay,159,13 +greenclay,159,13 +greensclay,159,13 +greenstainedclay,159,13 +darkgreclay,159,13 +darkgresclay,159,13 +darkgrestainedclay,159,13 +dgreclay,159,13 +dgresclay,159,13 +dgrestainedclay,159,13 +greclay,159,13 +gresclay,159,13 +grestainedclay,159,13 +redclay,159,14 +redsclay,159,14 +redstainedclay,159,14 +rclay,159,14 +rsclay,159,14 +rstainedclay,159,14 +blackclay,159,15 +blacksclay,159,15 +blackstainedclay,159,15 +blaclay,159,15 +blasclay,159,15 +blastainedclay,159,15 +hayblock,170,0 +haybale,170,0 +hay,170,0 +whitecarpet,171,0 +whitefloor,171,0 +wcarpet,171,0 +wfloor,171,0 +carpet,171,0 +floor,171,0 +orangecarpet,171,1 +orangefloor,171,1 +ocarpet,171,1 +ofloor,171,1 +magentacarpet,171,2 +magentafloor,171,2 +mcarpet,171,2 +mfloor,171,2 +lightbluecarpet,171,3 +lightbluefloor,171,3 +lbluecarpet,171,3 +lbluefloor,171,3 +lightblucarpet,171,3 +lightblufloor,171,3 +lblucarpet,171,3 +lblufloor,171,3 +yellowcarpet,171,4 +yellowfloor,171,4 +ycarpet,171,4 +yfloor,171,4 +lightgreencarpet,171,5 +lightgreenfloor,171,5 +lgreencarpet,171,5 +lgreenfloor,171,5 +lightgrecarpet,171,5 +lightgrefloor,171,5 +lgrecarpet,171,5 +lgrefloor,171,5 +limecarpet,171,5 +limefloor,171,5 +lcarpet,171,5 +lfloor,171,5 +pinkcarpet,171,6 +pinkfloor,171,6 +picarpet,171,6 +pifloor,171,6 +darkgraycarpet,171,7 +darkgrayfloor,171,7 +dgraycarpet,171,7 +dgrayfloor,171,7 +darkgracarpet,171,7 +darkgrafloor,171,7 +dgracarpet,171,7 +dgrafloor,171,7 +graycarpet,171,7 +grayfloor,171,7 +gracarpet,171,7 +grafloor,171,7 +lightgraycarpet,171,8 +lightgrayfloor,171,8 +lgraycarpet,171,8 +lgrayfloor,171,8 +lightgracarpet,171,8 +lightgrafloor,171,8 +lgracarpet,171,8 +lgrafloor,171,8 +silvercarpet,171,8 +silverfloor,171,8 +sicarpet,171,8 +siafloor,171,8 +cyancarpet,171,9 +cyanfloor,171,9 +ccarpet,171,9 +cfloor,171,9 +purplecarpet,171,10 +purplefloor,171,10 +pucarpet,171,10 +pufloor,171,10 +bluecarpet,171,11 +bluefloor,171,11 +blucarpet,171,11 +blufloor,171,11 +browncarpet,171,12 +brownfloor,171,12 +brocarpet,171,12 +brofloor,171,12 +darkgreencarpet,171,13 +darkgreenfloor,171,13 +dgreencarpet,171,13 +dgreenfloor,171,13 +greencarpet,171,13 +greenfloor,171,13 +darkgrecarpet,171,13 +darkgrefloor,171,13 +dgrecarpet,171,13 +dgrefloor,171,13 +grecarpet,171,13 +grefloor,171,13 +redcarpet,171,14 +redfloor,171,14 +rcarpet,171,14 +rfloor,171,14 +blackcarpet,171,15 +blackfloor,171,15 +blacarpet,171,15 +blafloor,171,15 +hardenedclay,172,0 +hardclay,172,0 +hclay,172,0 +coalblock,173,0 +blockcoal,173,0 +ironshovel,256,0 +ironspade,256,0 +ishovel,256,0 +ispade,256,0 +steelshovel,256,0 +steelspade,256,0 +ironpickaxe,257,0 +ironpick,257,0 +steelpickaxe,257,0 +steelpick,257,0 +ipickaxe,257,0 +ipick,257,0 +ironaxe,258,0 +iaxe,258,0 +steelaxe,258,0 +flintandsteel,259,0 +flintandiron,259,0 +flintandtinder,259,0 +flintnsteel,259,0 +flintniron,259,0 +flintntinder,259,0 +flintsteel,259,0 +flintiron,259,0 +flinttinder,259,0 +lighter,259,0 +apple,260,0 +normalapple,260,0 +redapple,260,0 +bow,261,0 +arrow,262,0 +coal,263,0 +charcoal,263,1 +ccoal,263,1 +diamond,264,0 +crystal,264,0 +iron,265,0 +ironingot,265,0 +ironbar,265,0 +ironi,265,0 +steelingot,265,0 +steelbar,265,0 +steeli,265,0 +iingot,265,0 +ibar,265,0 +ingotiron,265,0 +bariron,265,0 +iiron,265,0 +ingotsteel,265,0 +barsteel,265,0 +isteel,265,0 +ingoti,265,0 +bari,265,0 +gold,266,0 +goldingot,266,0 +goldbar,266,0 +goldi,266,0 +gingot,266,0 +gbar,266,0 +ingotgold,266,0 +bargold,266,0 +igold,266,0 +ingotg,266,0 +barg,266,0 +ironsword,267,0 +steelsword,267,0 +isword,267,0 +woodensword,268,0 +woodsword,268,0 +wsword,268,0 +woodenshovel,269,0 +woodenspade,269,0 +woodshovel,269,0 +woodspade,269,0 +wshovel,269,0 +wspade,269,0 +woodenpickaxe,270,0 +woodenpick,270,0 +woodpickaxe,270,0 +woodpick,270,0 +wpickaxe,270,0 +wpick,270,0 +woodenaxe,271,0 +woodaxe,271,0 +waxe,271,0 +smoothstonesword,272,0 +cobblestonesword,272,0 +sstonesword,272,0 +cstonesword,272,0 +stonesword,272,0 +sssword,272,0 +cssword,272,0 +ssword,272,0 +smoothstoneshovel,273,0 +smoothstonespade,273,0 +cobblestoneshovel,273,0 +cobblestonespade,273,0 +sstoneshovel,273,0 +sstonespade,273,0 +cstoneshovel,273,0 +cstonespade,273,0 +stoneshovel,273,0 +stonespade,273,0 +ssshovel,273,0 +csshovel,273,0 +ssspade,273,0 +csspade,273,0 +sshovel,273,0 +sspade,273,0 +smoothstonepickaxe,274,0 +cobblestonepickaxe,274,0 +smoothstonepick,274,0 +cobblestonepick,274,0 +sstonepickaxe,274,0 +sstonepick,274,0 +cstonepickaxe,274,0 +cstonepick,274,0 +stonepickaxe,274,0 +stonepick,274,0 +sspickaxe,274,0 +sspick,274,0 +cspickaxe,274,0 +cspick,274,0 +spickaxe,274,0 +spick,274,0 +smoothstoneaxe,275,0 +cobblestoneaxe,275,0 +sstoneaxe,275,0 +cstoneaxe,275,0 +stoneaxe,275,0 +ssaxe,275,0 +csaxe,275,0 +saxe,275,0 +diamondsword,276,0 +crystalsword,276,0 +dsword,276,0 +diamondshovel,277,0 +diamondspade,277,0 +crystalshovel,277,0 +crystalspade,277,0 +dshovel,277,0 +dspade,277,0 +diamondpickaxe,278,0 +diamondpick,278,0 +crystalpickaxe,278,0 +crystalpick,278,0 +dpickaxe,278,0 +dpick,278,0 +diamondaxe,279,0 +crystalaxe,279,0 +daxe,279,0 +stick,280,0 +twig,280,0 +branch,280,0 +bowl,281,0 +mushroomsoup,282,0 +mrsoup,282,0 +soup,282,0 +goldsword,283,0 +gsword,283,0 +goldshovel,284,0 +goldspade,284,0 +gshovel,284,0 +gspade,284,0 +goldpickaxe,285,0 +goldpick,285,0 +gpickaxe,285,0 +gpick,285,0 +goldaxe,286,0 +gaxe,286,0 +string,287,0 +feather,288,0 +gunpowder,289,0 +sulfur,289,0 +woodenhoe,290,0 +woodhoe,290,0 +whoe,290,0 +smoothstonehoe,291,0 +cobblestonehoe,291,0 +sstonehoe,291,0 +cstonehoe,291,0 +stonehoe,291,0 +sshoe,291,0 +cshoe,291,0 +shoe,291,0 +ironhoe,292,0 +steelhoe,292,0 +ihoe,292,0 +diamondhoe,293,0 +crystalhoe,293,0 +dhoe,293,0 +goldhoe,294,0 +ghoe,294,0 +seeds,295,0 +seed,295,0 +wheat,296,0 +bread,297,0 +leatherhelmet,298,0 +leatherhelm,298,0 +leatherhat,298,0 +leathercoif,298,0 +lhelmet,298,0 +lhelm,298,0 +lhat,298,0 +lcoif,298,0 +leatherchestplate,299,0 +leatherplatebody,299,0 +leatherplate,299,0 +leathershirt,299,0 +leathertunic,299,0 +lchestplate,299,0 +lplatebody,299,0 +lplate,299,0 +lshirt,299,0 +ltunic,299,0 +leatherleggings,300,0 +leatherlegs,300,0 +leatherpants,300,0 +lleggings,300,0 +llegs,300,0 +lpants,300,0 +leatherboots,301,0 +leathershoes,301,0 +lboots,301,0 +lshoes,301,0 +chainmailhelmet,302,0 +chainmailhelm,302,0 +chainmailhat,302,0 +chainmailcoif,302,0 +chainmhelmet,302,0 +chainmhelm,302,0 +chainmhat,302,0 +chainmcoif,302,0 +cmailhelmet,302,0 +cmailhelm,302,0 +cmailhat,302,0 +cmailcoif,302,0 +chainhelmet,302,0 +chainhelm,302,0 +chainhat,302,0 +chaincoif,302,0 +cmhelmet,302,0 +cmhelm,302,0 +cmhat,302,0 +cmcoif,302,0 +chainmailchestplate,303,0 +chainmailplatebody,303,0 +chainmailplate,303,0 +chainmailshirt,303,0 +chainmailtunic,303,0 +chainmchestplate,303,0 +chainmplatebody,303,0 +chainmplate,303,0 +chainmshirt,303,0 +chainmtunic,303,0 +cmailchestplate,303,0 +cmailplatebody,303,0 +cmailplate,303,0 +cmailshirt,303,0 +cmailtunic,303,0 +chainchestplate,303,0 +chainplatebody,303,0 +chainplate,303,0 +chainshirt,303,0 +chaintunic,303,0 +cmchestplate,303,0 +cmplatebody,303,0 +cmplate,303,0 +cmshirt,303,0 +cmtunic,303,0 +chainmailleggings,304,0 +chainmaillegs,304,0 +chainmailpants,304,0 +chainmleggings,304,0 +chainmlegs,304,0 +chainmpants,304,0 +cmailleggings,304,0 +cmaillegs,304,0 +cmailpants,304,0 +chainleggings,304,0 +chainlegs,304,0 +chainpants,304,0 +cmleggings,304,0 +cmlegs,304,0 +cmpants,304,0 +chainmailboots,305,0 +chainmailshoes,305,0 +chainmboots,305,0 +chainmshoes,305,0 +cmailboots,305,0 +cmailshoes,305,0 +chainboots,305,0 +chainshoes,305,0 +cmboots,305,0 +cmshoes,305,0 +ironhelmet,306,0 +ironhelm,306,0 +ironhat,306,0 +ironcoif,306,0 +ihelmet,306,0 +ihelm,306,0 +ihat,306,0 +icoif,306,0 +steelhelmet,306,0 +steelhelm,306,0 +steelhat,306,0 +steelcoif,306,0 +shelmet,306,0 +shelm,306,0 +shat,306,0 +scoif,306,0 +ironchestplate,307,0 +ironplatebody,307,0 +ironshirt,307,0 +irontunic,307,0 +ichestplate,307,0 +iplatebody,307,0 +ishirt,307,0 +itunic,307,0 +steelchestplate,307,0 +steelplatebody,307,0 +steelplate,307,0 +steelshirt,307,0 +steeltunic,307,0 +schestplate,307,0 +splatebody,307,0 +sshirt,307,0 +stunic,307,0 +ironleggings,308,0 +ironlegs,308,0 +ironpants,308,0 +ileggings,308,0 +ilegs,308,0 +ipants,308,0 +steelleggings,308,0 +steellegs,308,0 +steelpants,308,0 +sleggings,308,0 +slegs,308,0 +spants,308,0 +ironboots,309,0 +ironshoes,309,0 +iboots,309,0 +ishoes,309,0 +steelboots,309,0 +steelshoes,309,0 +sboots,309,0 +sshoes,309,0 +diamondhelmet,310,0 +diamondhelm,310,0 +diamondhat,310,0 +diamondcoif,310,0 +dhelmet,310,0 +dhelm,310,0 +dhat,310,0 +dcoif,310,0 +crystalhelmet,310,0 +crystalhelm,310,0 +crystalhat,310,0 +crystalcoif,310,0 +chelmet,310,0 +chelm,310,0 +chat,310,0 +ccoif,310,0 +diamondchestplate,311,0 +diamondplatebody,311,0 +diamondplate,311,0 +diamondshirt,311,0 +diamondtunic,311,0 +dchestplate,311,0 +dchest,311,0 +dplatebody,311,0 +dplate,311,0 +dshirt,311,0 +dtunic,311,0 +crystalchestplate,311,0 +crystalplatebody,311,0 +crystalplate,311,0 +crystalshirt,311,0 +crystaltunic,311,0 +cchestplate,311,0 +cplatebody,311,0 +cplate,311,0 +cshirt,311,0 +ctunic,311,0 +diamondleggings,312,0 +diamondlegs,312,0 +diamondpants,312,0 +dleggings,312,0 +dlegs,312,0 +dpants,312,0 +crystalleggings,312,0 +crystallegs,312,0 +crystalpants,312,0 +cleggings,312,0 +clegs,312,0 +cpants,312,0 +diamondboots,313,0 +diamondshoes,313,0 +dboots,313,0 +dshoes,313,0 +crystalboots,313,0 +crystalshoes,313,0 +cboots,313,0 +cshoes,313,0 +goldhelmet,314,0 +goldhelm,314,0 +goldhat,314,0 +goldcoif,314,0 +ghelmet,314,0 +ghelm,314,0 +ghat,314,0 +gcoif,314,0 +goldchestplate,315,0 +goldplatebody,315,0 +goldshirt,315,0 +goldtunic,315,0 +gchestplate,315,0 +gplatebody,315,0 +gplateplate,315,0 +gshirt,315,0 +gtunic,315,0 +goldleggings,316,0 +goldlegs,316,0 +goldpants,316,0 +gleggings,316,0 +glegs,316,0 +gpants,316,0 +goldboots,317,0 +goldshoes,317,0 +gboots,317,0 +gshoes,317,0 +flint,318,0 +pork,319,0 +porkchop,319,0 +rawpork,319,0 +rpork,319,0 +rawporkchop,319,0 +rporkchop,319,0 +grilledpork,320,0 +grillpork,320,0 +gpork,320,0 +cookedpork,320,0 +cookpork,320,0 +cpork,320,0 +grilledporkchop,320,0 +grillporkchop,320,0 +gporkchop,320,0 +cookedporkchop,320,0 +cookporkchop,320,0 +cporkchop,320,0 +bacon,320,0 +painting,321,0 +picture,321,0 +goldenapple,322,0 +goldapple,322,0 +gapple,322,0 +enchantedgoldenapple,322,1 +enchantedgoldapple,322,1 +enchantedgapple,322,1 +supergoldenapple,322,1 +supergoldapple,322,1 +supergapple,322,1 +magicalgoldenapple,322,1 +magicalgoldapple,322,1 +magicalgapple,322,1 +magicgoldenapple,322,1 +magicgoldapple,322,1 +magicgapple,322,1 +egoldenapple,322,1 +egoldapple,322,1 +egapple,322,1 +sgoldenapple,322,1 +sgoldapple,322,1 +sgapple,322,1 +mgoldenapple,322,1 +mgoldapple,322,1 +mgapple,322,1 +sign,323,0 +woodendoor,324,0 +wooddoor,324,0 +wdoor,324,0 +door,324,0 +bucket,325,0 +bukkit,325,0 +waterbucket,326,0 +waterbukkit,326,0 +wbucket,326,0 +wbukkit,326,0 +magmabucket,327,0 +magmabukkit,327,0 +lavabucket,327,0 +lavabukkit,327,0 +lbucket,327,0 +lbukkit,327,0 +minecart,328,0 +mcart,328,0 +cart,328,0 +saddle,329,0 +irondoor,330,0 +idoor,330,0 +steeldoor,330,0 +sdoor,330,0 +dooriron,330,0 +doori,330,0 +doorsteel,330,0 +doors,330,0 +redstonedust,331,0 +redstone,331,0 +rstonedust,331,0 +rstone,331,0 +redsdust,331,0 +reddust,331,0 +rsdust,331,0 +rdust,331,0 +snow,332,0 +snowball,332,0 +snball,332,0 +boat,333,0 +leather,334,0 +milkbucket,335,0 +milkbukkit,335,0 +mbucket,335,0 +mbukkit,335,0 +claybrick,336,0 +brick,336,0 +clayball,337,0 +cball,337,0 +clay,337,0 +reeds,338,0 +reed,338,0 +sugarcane,338,0 +scane,338,0 +bamboo,338,0 +paper,339,0 +papyrus,339,0 +book,340,0 +slimeball,341,0 +slball,341,0 +storageminecart,342,0 +chestminecart,342,0 +storagemcart,342,0 +chestmcart,342,0 +storagecart,342,0 +chestcart,342,0 +sminecart,342,0 +cminecart,342,0 +smcart,342,0 +cmcart,342,0 +scart,342,0 +ccart,342,0 +engineminecart,343,0 +poweredminecart,343,0 +powerminecart,343,0 +furnaceminecart,343,0 +enginemcart,343,0 +poweredmcart,343,0 +powermcart,343,0 +furnacemcart,343,0 +enginecart,343,0 +poweredcart,343,0 +powercart,343,0 +furnacecart,343,0 +eminecart,343,0 +pminecart,343,0 +fminecart,343,0 +emcart,343,0 +pmcart,343,0 +fmcart,343,0 +ecart,343,0 +pcart,343,0 +fcart,343,0 +egg,344,0 +compass,345,0 +fishingrod,346,0 +fishrod,346,0 +frod,346,0 +rod,346,0 +goldwatch,347,0 +goldclock,347,0 +gwatch,347,0 +gclock,347,0 +watch,347,0 +clock,347,0 +glowingstoneblockdust,348,0 +lightstoneblockdust,348,0 +glowstoneblockdust,348,0 +blockglowingstonedust,348,0 +blocklightstonedust,348,0 +blockglowstonedust,348,0 +glowingstonebdust,348,0 +lightstonebdust,348,0 +glowstonebdust,348,0 +bglowingstonedust,348,0 +blightstonedust,348,0 +bglowstonedust,348,0 +glowingstonedust,348,0 +lightstonedust,348,0 +glowstonedust,348,0 +glowingblockdust,348,0 +lightblockdust,348,0 +glowblockdust,348,0 +lbdust,348,0 +gbdust,348,0 +lsdust,348,0 +gsdust,348,0 +rawfish,349,0 +rafish,349,0 +fish,349,0 +cookedfish,350,0 +cookfish,350,0 +cfish,350,0 +grilledfish,350,0 +grillfish,350,0 +gfish,350,0 +roastedfish,350,0 +roastfish,350,0 +rofish,350,0 +dye,351,0 +inksack,351,0 +inksac,351,0 +isack,351,0 +isac,351,0 +sack,351,0 +sac,351,0 +blackinksack,351,0 +blackinksac,351,0 +blackisack,351,0 +blackisac,351,0 +blacksack,351,0 +blacksac,351,0 +inksackblack,351,0 +inksacblack,351,0 +isackblack,351,0 +isacclack,351,0 +sackblack,351,0 +sacblack,351,0 +blackinksackcolour,351,0 +blackinksaccolour,351,0 +blackisackcolour,351,0 +blackisaccolour,351,0 +blacksackcolour,351,0 +blacksaccolour,351,0 +inksackblackcolour,351,0 +inksacblackcolour,351,0 +isackblackcolour,351,0 +isacclackcolour,351,0 +sackblackcolour,351,0 +sacblackcolour,351,0 +blackinksackcolor,351,0 +blackinksaccolor,351,0 +blackisackcolor,351,0 +blackisaccolor,351,0 +blacksackcolor,351,0 +blacksaccolor,351,0 +inksackblackcolor,351,0 +inksacblackcolor,351,0 +isackblackcolor,351,0 +isacblackcolor,351,0 +sackblackcolor,351,0 +sacblackcolor,351,0 +blackinksackdye,351,0 +blackinksacdye,351,0 +blackisackdye,351,0 +blackisacdye,351,0 +blacksackdye,351,0 +blacksacdye,351,0 +inksackblackdye,351,0 +inksacblackdye,351,0 +isackblackdye,351,0 +isacclackdye,351,0 +sackblackdye,351,0 +sacblackdye,351,0 +blackcolor,351,0 +blackdye,351,0 +rosered,351,1 +roseredcolor,351,1 +roseredcolour,351,1 +rosereddye,351,1 +redrosecolor,351,1 +redrosecolour,351,1 +redrosedye,351,1 +redr,351,1 +redrcolor,351,1 +redrcolour,351,1 +redrdye,351,1 +redcolor,351,1 +redcolour,351,1 +reddye,351,1 +cactusgreen,351,2 +greencactus,351,2 +cactusgreencolour,351,2 +greencactuscolour,351,2 +cactusgreencolor,351,2 +greencactuscolor,351,2 +cactusgreendye,351,2 +greencactusdye,351,2 +greencolour,351,2 +greencolor,351,2 +greendye,351,2 +cocoabeans,351,3 +cocoabean,351,3 +cocobeans,351,3 +cocobean,351,3 +cbeans,351,3 +cbean,351,3 +beans,351,3 +bean,351,3 +browncocoabeans,351,3 +browncocoabean,351,3 +browncocobeans,351,3 +browncocobean,351,3 +browncbeans,351,3 +browncbean,351,3 +brownbeans,351,3 +brownbean,351,3 +brownb,351,3 +cocoabeanscolour,351,3 +cocoabeancolour,351,3 +cocobeanscolour,351,3 +cocobeancolour,351,3 +cbeanscolour,351,3 +cbeancolour,351,3 +beanscolour,351,3 +beancolour,351,3 +browncocoabeanscolour,351,3 +browncocoabeancolour,351,3 +browncocobeanscolour,351,3 +browncocobeancolour,351,3 +browncbeanscolour,351,3 +browncbeancolour,351,3 +brownbeanscolour,351,3 +brownbeancolour,351,3 +brownbcolour,351,3 +cocoabeanscolor,351,3 +cocoabeancolor,351,3 +cocobeanscolor,351,3 +cocobeancolor,351,3 +cbeanscolor,351,3 +cbeancolor,351,3 +beanscolor,351,3 +beancolor,351,3 +browncocoabeanscolor,351,3 +browncocoabeancolor,351,3 +browncocobeanscolor,351,3 +browncocobeancolor,351,3 +browncbeanscolor,351,3 +browncbeancolor,351,3 +brownbeanscolor,351,3 +brownbeancolor,351,3 +brownbcolor,351,3 +cocoabeansdye,351,3 +cocoabeandye,351,3 +cocobeansdye,351,3 +cocobeandye,351,3 +cbeansdye,351,3 +cbeandye,351,3 +beansdye,351,3 +beandye,351,3 +browncocoabeansdye,351,3 +browncocoabeandye,351,3 +browncocobeansdye,351,3 +browncocobeandye,351,3 +browncbeansdye,351,3 +browncbeandye,351,3 +brownbeansdye,351,3 +brownbeandye,351,3 +brownbdye,351,3 +browncolour,351,3 +browncolor,351,3 +browndye,351,3 +bluelapislzuli,351,4 +bluelapisl,351,4 +bluelapis,351,4 +bluel,351,4 +lapislazuliblue,351,4 +lapislblue,351,4 +lapisblue,351,4 +lapislazuli,351,4 +lapisl,351,4 +lapis,351,4 +bluelapislazulicolour,351,4 +bluelapislcolour,351,4 +bluelapiscolour,351,4 +lapislazulibluecolour,351,4 +lapislbluecolour,351,4 +lapisbluecolour,351,4 +lapislazulicolour,351,4 +lapislcolour,351,4 +lapiscolour,351,4 +bluelapislazulicolor,351,4 +bluelapislcolor,351,4 +bluelapiscolor,351,4 +lapislazulibluecolor,351,4 +lapislbluecolor,351,4 +lapisbluecolor,351,4 +lapislazulicolor,351,4 +lapislcolor,351,4 +lapiscolor,351,4 +bluelapislazulidye,351,4 +bluelapisldye,351,4 +bluelapisdye,351,4 +lapislazulibluedye,351,4 +lapislbluedye,351,4 +lapisbluedye,351,4 +lapislazulidye,351,4 +lapisldye,351,4 +lapisdye,351,4 +bluecolour,351,4 +bluecolor,351,4 +bluedye,351,4 +purplecolour,351,5 +purplecolor,351,5 +purpledye,351,5 +cyancolour,351,6 +cyancolor,351,6 +cyandye,351,6 +lightgraycolour,351,7 +lightgraycolor,351,7 +lightgraydye,351,7 +lgraycolour,351,7 +lgraycolor,351,7 +lgraydye,351,7 +silvercolour,351,7 +silvercolor,351,7 +silverdye,351,7 +darkgraycolour,351,8 +darkgraycolor,351,8 +darkgraydye,351,8 +dgraycolour,351,8 +dgraycolor,351,8 +dgraydye,351,8 +graycolour,351,8 +graycolor,351,8 +graydye,351,8 +pinkcolour,351,9 +pinkcolor,351,9 +pinkdye,351,9 +limecolour,351,10 +limecolor,351,10 +limedye,351,10 +dandelionyellow,351,11 +dandelionyellowcolour,351,11 +dandelionyellowcolor,351,11 +dandelionyellowdye,351,11 +yellowdandelion,351,11 +yellowdandelioncolour,351,11 +yellowdandelioncolor,351,11 +yellowdandeliondye,351,11 +yellowd,351,11 +yellowdcolour,351,11 +yellowdcolor,351,11 +yellowddye,351,11 +dyellow,351,11 +dyellowcolour,351,11 +dyellowcolor,351,11 +dyellowdye,351,11 +yellowcolour,351,11 +yellowcolor,351,11 +yellowdye,351,11 +lightbluecolour,351,12 +lightbluecolor,351,12 +lightbluedye,351,12 +lbluecolour,351,12 +lbluecolor,351,12 +lbluedye,351,12 +magentacolour,351,13 +magentacolor,351,13 +magentadye,351,13 +orangecolour,351,14 +orangecolor,351,14 +orangedye,351,14 +whitebonemeal,351,15 +whitebonemealcolour,351,15 +whitebonemealcolor,351,15 +whitebonemealdye,351,15 +bonemealwhite,351,15 +bonemealwhitecolour,351,15 +bonemealwhitecolor,351,15 +bonemealwhitedye,351,15 +whitebonem,351,15 +whitebonemcolour,351,15 +whitebonemcolor,351,15 +whitebonemdye,351,15 +bonemwhite,351,15 +bonemwhitecolour,351,15 +bonemwhitecolor,351,15 +bonemwhitedye,351,15 +bonemeal,351,15 +bonemealcolour,351,15 +bonemealcolor,351,15 +bonemealdye,351,15 +bonem,351,15 +bonemcolour,351,15 +bonemcolor,351,15 +bonemdye,351,15 +whitecolour,351,15 +whitecolor,351,15 +whitedye,351,15 +bone,352,0 +sugar,353,0 +whitedust,353,0 +cake,354,0 +bed,355,0 +redstonerepeater,356,0 +redstonerepeat,356,0 +redstonedelayer,356,0 +redstonedelay,356,0 +redstonedioder,356,0 +redstonediode,356,0 +rstonerepeater,356,0 +rstonerepeat,356,0 +rstonedelayer,356,0 +rstonedelay,356,0 +rstonedioder,356,0 +rstonediode,356,0 +redsrepeater,356,0 +redsrepeat,356,0 +redsdelayer,356,0 +redsdelay,356,0 +redsdioder,356,0 +redsdiode,356,0 +rsrepeater,356,0 +rsrepeat,356,0 +rsdelayer,356,0 +rsdelay,356,0 +rsdioder,356,0 +rsdiode,356,0 +repeater,356,0 +repeat,356,0 +delayer,356,0 +delay,356,0 +dioder,356,0 +diode,356,0 +cookie,357,0 +chart,358,0 +map0,358,0 +map1,358,1 +map2,358,2 +map3,358,3 +map4,358,4 +map5,358,5 +map6,358,6 +map7,358,7 +map8,358,8 +map9,358,9 +map10,358,10 +map11,358,11 +map12,358,12 +map13,358,13 +map14,358,14 +map15,358,15 +shears,359,0 +shear,359,0 +sheers,359,0 +sheer,359,0 +woolcutters,359,0 +woolcutter,359,0 +cutterswool,359,0 +cutterwool,359,0 +melonslice,360,0 +mslice,360,0 +slicemelon,360,0 +watermelonslice,360,0 +greenmelonslice,360,0 +melongreenslice,360,0 +pumpkinseeds,361,0 +pseeds,361,0 +seedsp,361,0 +seedspumpkin,361,0 +pumpseeds,361,0 +seedspump,361,0 +melonseeds,362,0 +mseeds,362,0 +watermelonseeds,362,0 +greenmelonseeds,362,0 +gmelonseeds,362,0 +seedsmelon,362,0 +seedswatermelon,362,0 +rawbeef,363,0 +rawsteak,363,0 +uncookedbeef,363,0 +uncookedsteak,363,0 +cowmeat,363,0 +plainbeef,363,0 +beef,364,0 +steak,364,0 +cookedbeef,364,0 +grilledbeef,364,0 +cookedsteak,364,0 +grilledsteak,364,0 +cookedcowmeat,364,0 +rawchicken,365,0 +uncookedchicken,365,0 +plainchicken,365,0 +chickenplain,365,0 +chickenuncooked,365,0 +chickenraw,365,0 +cookedchicken,366,0 +grilledchicken,366,0 +toastedchicken,366,0 +gchicken,366,0 +bbqchicken,366,0 +friedchicken,366,0 +cchicken,366,0 +rottenflesh,367,0 +zombieflesh,367,0 +rottenmeat,367,0 +zombiemeat,367,0 +badflesh,367,0 +poisonflesh,367,0 +zombieremains,367,0 +enderpearl,368,0 +endpearl,368,0 +pearl,368,0 +epearl,368,0 +bluepearl,368,0 +endergem,368,0 +blazerod,369,0 +goldenrod,369,0 +goldrod,369,0 +blazestick,369,0 +goldstick,369,0 +brod,369,0 +grod,369,0 +bstick,369,0 +gstick,369,0 +ghasttear,370,0 +ghastdrop,370,0 +ghosttear,370,0 +ghostdrop,370,0 +gtear,370,0 +gdrop,370,0 +tear,370,0 +goldnugget,371,0 +gnugget,371,0 +goldball,371,0 +goldpebble,371,0 +gball,371,0 +gpebble,371,0 +pigzombienugget,371,0 +pigznugget,371,0 +pzombienugget,371,0 +pznugget,371,0 +pigzombieball,371,0 +pigzball,371,0 +pzombieball,371,0 +pzball,371,0 +pigzombiepebble,371,0 +pigzpebble,371,0 +pzombiepebble,371,0 +pzpebble,371,0 +netherstalk,372,0 +deathstalk,372,0 +hellstalk,372,0 +nstalk,372,0 +dstalk,372,0 +hstalk,372,0 +potion,373,0 +mixture,373,0 +potions,373,0 +waterbottle,373,0 +fullbottle,373,0 +watervase,373,0 +fullvase,373,0 +clearpotion,373,6 +clearpot,373,6 +clearextendedpotion,373,7 +clearexpotion,373,7 +clear2potion,373,7 +clearextendedpot,373,7 +clearexpot,373,7 +clear2pot,373,7 +diffusepotion,373,11 +diffusepot,373,11 +artlesspotion,373,13 +artlesspot,373,13 +thinpotion,373,14 +thinpot,373,14 +thinextendedpotion,373,15 +thinexpotion,373,15 +thin2potion,373,15 +thinextendedpot,373,15 +thinexpot,373,15 +thin2pot,373,15 +awkwardpotion,373,16 +awkwardpot,373,16 +bunglingpotion,373,22 +bunglingpot,373,22 +bunglingextendedpotion,373,23 +bunglingexpotion,373,23 +bungling2potion,373,23 +bunglingextendedpot,373,23 +bunglingexpot,373,23 +bungling2pot,373,23 +smoothpotion,373,27 +smoothpot,373,27 +suavepotion,373,29 +suavepot,373,29 +debonairpotion,373,30 +debonairpot,373,30 +debonairextendedpotion,373,31 +debonairexpotion,373,31 +debonair2potion,373,31 +debonairextendedpot,373,31 +debonairexpot,373,31 +debonair2pot,373,31 +thickpotion,373,32 +thickpot,373,32 +charmingpotion,373,38 +charmingpot,373,38 +charmingextendedpotion,373,39 +charmingexpotion,373,39 +charming2potion,373,39 +charmingextendedpot,373,39 +charmingexpot,373,39 +charming2pot,373,39 +refinedpotion,373,43 +refinedpot,373,43 +cordialpotion,373,45 +cordialpot,373,45 +sparklingpotion,373,46 +sparklingpot,373,46 +sparklingextendedpotion,373,47 +sparklingexpotion,373,47 +sparkling2potion,373,47 +sparklingextendedpot,373,47 +sparklingexpot,373,47 +sparkling2pot,373,47 +potentpotion,373,48 +potentpot,373,48 +rankpotion,373,54 +rankpot,373,54 +rankextendedpotion,373,55 +rankexpotion,373,55 +rank2potion,373,55 +rankextendedpot,373,55 +rankexpot,373,55 +rank2pot,373,55 +acridpotion,373,59 +acridpot,373,59 +grosspotion,373,61 +grosspot,373,61 +stinkypotion,373,62 +stinkypot,373,62 +stinkyextendedpotion,373,63 +stinkyexpotion,373,63 +stinky2potion,373,63 +stinkyextendedpot,373,63 +stinkyexpot,373,63 +stinky2pot,373,63 +mundaneextendedpotion,373,64 +mundaneexpotion,373,64 +mundane2potion,373,64 +mundaneextendedpot,373,64 +mundaneexpot,373,64 +mundane2pot,373,64 +mundanepotion,373,8192 +mundanepot,373,8192 +regenerationpotion,373,8193 +regeneratepotion,373,8193 +regenpotion,373,8193 +regenerationpot,373,8193 +regeneratepot,373,8193 +regenpot,373,8193 +rpot,373,8193 +swiftnesspotion,373,8194 +swiftpotion,373,8194 +speedpotion,373,8194 +swiftnesspot,373,8194 +swiftpot,373,8194 +speedpot,373,8194 +swpot,373,8194 +fireresistancepotion,373,8195 +fireresistpotion,373,8195 +firerespotion,373,8195 +fireresistancepot,373,8195 +fireresistpot,373,8195 +firerespot,373,8195 +fpot,373,8195 +poisonpotion,373,8196 +acidpotion,373,8196 +poisonpot,373,8196 +acidpot,373,8196 +ppot,373,8196 +healingpotion,373,8197 +healpotion,373,8197 +lifepotion,373,8197 +healingpot,373,8197 +healpot,373,8197 +lifepot,373,8197 +hpot,373,8197 +nightvisionpotion,373,8198 +nvisionpotion,373,8198 +nightvpotion,373,8198 +darkvisionpotion,373,8198 +dvisionpotion,373,8198 +darkvpotion,373,8198 +nightvisionpot,373,8198 +nvisionpot,373,8198 +nightvpot,373,8198 +darkvisionpot,373,8198 +dvisionpot,373,8198 +darkvpot,373,8198 +npot,373,8198 +weaknesspotion,373,8200 +weakpotion,373,8200 +weaknesspot,373,8200 +weakpot,373,8200 +wpot,373,8200 +strengthpotion,373,8201 +strongpotion,373,8201 +strpotion,373,8201 +strengthpot,373,8201 +strongpot,373,8201 +strpot,373,8201 +stpot,373,8201 +slownesspotion,373,8202 +slowpotion,373,8202 +slownesspot,373,8202 +slowpot,373,8202 +slpot,373,8202 +harmingpotion,373,8204 +damagepotion,373,8204 +dmgpotion,373,8204 +harmingpot,373,8204 +damagepot,373,8204 +dmgpot,373,8204 +dpot,373,8204 +invisibilitypotion,373,8206 +invisiblepotion,373,8206 +invpotion,373,8206 +invisibilitypot,373,8206 +invisiblepot,373,8206 +invpot,373,8206 +ipot,373,8206 +regenerationleveliipotion,373,8225 +regenerateleveliipotion,373,8225 +regenleveliipotion,373,8225 +regenerationlevel2potion,373,8225 +regeneratelevel2potion,373,8225 +regenlevel2potion,373,8225 +regenerationiipotion,373,8225 +regenerateiipotion,373,8225 +regeniipotion,373,8225 +regenerationleveliipot,373,8225 +regenerateleveliipot,373,8225 +regenleveliipot,373,8225 +regenerationlevel2pot,373,8225 +regeneratelevel2pot,373,8225 +regenlevel2pot,373,8225 +regenerationiipot,373,8225 +regenerateiipot,373,8225 +regeniipot,373,8225 +r2pot,373,8225 +swiftnessleveliipotion,373,8226 +swiftleveliipotion,373,8226 +speedleveliipotion,373,8226 +swiftnesslevel2potion,373,8226 +swiftlevel2potion,373,8226 +speedlevel2potion,373,8226 +swiftnessiipotion,373,8226 +swiftiipotion,373,8226 +speediipotion,373,8226 +swiftnessleveliipot,373,8226 +swiftleveliipot,373,8226 +speedleveliipot,373,8226 +swiftnesslevel2pot,373,8226 +swiftlevel2pot,373,8226 +speedlevel2pot,373,8226 +swiftnessiipot,373,8226 +swiftiipot,373,8226 +speediipot,373,8226 +sw2pot,373,8226 +poisonleveliipotion,373,8228 +acidleveliipotion,373,8228 +poisonlevel2potion,373,8228 +acidlevel2potion,373,8228 +poisoniipotion,373,8228 +acidiipotion,373,8228 +poisonleveliipot,373,8228 +acidleveliipot,373,8228 +poisonlevel2pot,373,8228 +acidlevel2pot,373,8228 +poisoniipot,373,8228 +acidiipot,373,8228 +p2pot,373,8228 +healingleveliipotion,373,8229 +healleveliipotion,373,8229 +healinglevel2potion,373,8229 +heallevel2potion,373,8229 +healingiipotion,373,8229 +healiipotion,373,8229 +healingleveliipot,373,8229 +healleveliipot,373,8229 +healinglevel2pot,373,8229 +heallevel2pot,373,8229 +healingiipot,373,8229 +healiipot,373,8229 +h2pot,373,8229 +strengthleveliipotion,373,8233 +strongleveliipotion,373,8233 +strleveliipotion,373,8233 +strengthlevel2potion,373,8233 +stronglevel2potion,373,8233 +strlevel2potion,373,8233 +strengthiipotion,373,8233 +strongiipotion,373,8233 +striipotion,373,8233 +strengthleveliipot,373,8233 +strongleveliipot,373,8233 +strleveliipot,373,8233 +strengthlevel2pot,373,8233 +stronglevel2pot,373,8233 +strlevel2pot,373,8233 +strengthiipot,373,8233 +strongiipot,373,8233 +striipot,373,8233 +st2pot,373,8233 +harmingleveliipotion,373,8236 +damageleveliipotion,373,8236 +dmgleveliipotion,373,8236 +harminglevel2potion,373,8236 +damagelevel2potion,373,8236 +dmglevel2potion,373,8236 +harmingiipotion,373,8236 +damageiipotion,373,8236 +dmgiipotion,373,8236 +harmingleveliipot,373,8236 +damageleveliipot,373,8236 +dmgleveliipot,373,8236 +harminglevel2pot,373,8236 +damagelevel2pot,373,8236 +dmglevel2pot,373,8236 +harmingiipot,373,8236 +damageiipot,373,8236 +dmgiipot,373,8236 +d2pot,373,8236 +regenerationextendedpotion,373,8257 +regenerateextendedpotion,373,8257 +regenextendepotion,373,8257 +regenerationexpotion,373,8257 +regenerateexpotion,373,8257 +regenexpotion,373,8257 +regenerationextendedpot,373,8257 +regenerateextendedpot,373,8257 +regenextendepot,373,8257 +regenerationexpot,373,8257 +regenerateexpot,373,8257 +regenexpot,373,8257 +repot,373,8257 +swiftnessextendedpotion,373,8258 +swiftextendedpotion,373,8258 +speedextendedpotion,373,8258 +swiftnessexpotion,373,8258 +swiftexpotion,373,8258 +speedexpotion,373,8258 +swiftnessextendedpot,373,8258 +swiftextendedpot,373,8258 +speedextendedpot,373,8258 +swiftnessexpot,373,8258 +swiftexpot,373,8258 +speedexpot,373,8258 +swepot,373,8258 +fireresistanceextendedpotion,373,8259 +fireresistextendedpotion,373,8259 +fireresextendedpotion,373,8259 +fireresistanceexpotion,373,8259 +fireresistexpotion,373,8259 +fireresexpotion,373,8259 +fireresistanceextendedpot,373,8259 +fireresistextendedpot,373,8259 +fireresextendedpot,373,8259 +fireresistanceexpot,373,8259 +fireresistexpot,373,8259 +fireresexpot,373,8259 +fepot,373,8259 +poisonextendedpotion,373,8260 +acidextendedpotion,373,8260 +poisonexpotion,373,8260 +acidexpotion,373,8260 +poisonextendedpot,373,8260 +acidextendedpot,373,8260 +poisonexpot,373,8260 +acidexpot,373,8260 +pepot,373,8260 +nightvisionextendedpotion,373,8262 +nvisionextendedpotion,373,8262 +nightvextendedpotion,373,8262 +darkvisionextendedpotion,373,8262 +dvisionextendedpotion,373,8262 +darkvextendedpotion,373,8262 +nightvisionexpotion,373,8262 +nvisionexpotion,373,8262 +nightvexpotion,373,8262 +darkvisionexpotion,373,8262 +dvisionexpotion,373,8262 +darkvexpotion,373,8262 +nightvisionextendedpot,373,8262 +nvisionextendedpot,373,8262 +nightvextendedpot,373,8262 +darkvisionextendedpot,373,8262 +dvisionextendedpot,373,8262 +darkvextendedpot,373,8262 +nightvisionexpot,373,8262 +nvisionexpot,373,8262 +nightvexpot,373,8262 +darkvisionexpot,373,8262 +dvisionexpot,373,8262 +darkvexpot,373,8262 +nepot,373,8262 +weaknessextendedpotion,373,8264 +weakextendedpotion,373,8264 +weaknessexpotion,373,8264 +weakexpotion,373,8264 +weaknessextendedpot,373,8264 +weakextendedpot,373,8264 +weaknessexpot,373,8264 +weakexpot,373,8264 +wepot,373,8264 +strengthextendedpotion,373,8265 +strongextendedpotion,373,8265 +strextendedpotion,373,8265 +strengthexpotion,373,8265 +strongexpotion,373,8265 +strexpotion,373,8265 +strengthextendedpot,373,8265 +strongextendedpot,373,8265 +strextendedpot,373,8265 +strengthexpot,373,8265 +strongexpot,373,8265 +strexpot,373,8265 +stepot,373,8265 +slownessextendedpotion,373,8266 +slowextenedpotion,373,8266 +slownessexpotion,373,8266 +slowexpotion,373,8266 +slownessextendedpot,373,8266 +slowextenedpot,373,8266 +slownessexpot,373,8266 +slowexpot,373,8266 +slepot,373,8266 +invisibilityextendedpotion,373,8270 +invisibleextendedpotion,373,8270 +invextendedpotion,373,8270 +invisibilityexpotion,373,8270 +invisibleexpotion,373,8270 +invexpotion,373,8270 +invisibilityextendedpot,373,8270 +invisibleextendedpot,373,8270 +invextendedpot,373,8270 +invisibilityexpot,373,8270 +invisibleexpot,373,8270 +invexpot,373,8270 +iepot,373,8270 +regenerationdualbitpotion,373,8289 +regeneratedualbitpotion,373,8289 +regendualbitpotion,373,8289 +regenerationdbpotion,373,8289 +regeneratedbpotion,373,8289 +regendbpotion,373,8289 +regenerationdualbitpot,373,8289 +regeneratedualbitpot,373,8289 +regendualbitpot,373,8289 +regenerationdbpot,373,8289 +regeneratedbpot,373,8289 +regendbpot,373,8289 +rdbpot,373,8289 +swiftnessdualbitpotion,373,8290 +swiftdualbitpotion,373,8290 +speeddualbitpotion,373,8290 +swiftnessdualbitpot,373,8290 +swiftdualbitpot,373,8290 +speeddualbitpot,373,8290 +swiftnessdbpotion,373,8290 +swiftdbpotion,373,8290 +speeddbpotion,373,8290 +swiftnessdbpot,373,8290 +swiftdbpot,373,8290 +speeddbpot,373,8290 +swdbpot,373,8290 +poisondualbitpotion,373,8292 +aciddualbitpotion,373,8292 +poisondualbitpot,373,8292 +aciddualbitpot,373,8292 +poisondbpotion,373,8292 +aciddbpotion,373,8292 +poisondbpot,373,8292 +aciddbpot,373,8292 +pdbpot,373,8292 +strengthdualbitpotion,373,8297 +strongdualbitpotion,373,8297 +strdualbitpotion,373,8297 +strengthdualbitpot,373,8297 +strongdualbitpot,373,8297 +strdualbitpot,373,8297 +strengthdbpotion,373,8297 +strongdbpotion,373,8297 +strdbpotion,373,8297 +strengthdbpot,373,8297 +strongdbpot,373,8297 +strdbpot,373,8297 +stdbpot,373,8297 +splashmundanepotion,373,16384 +splmundanepotion,373,16384 +splashregenerationpotion,373,16385 +splashregeneratepotion,373,16385 +splashregenpotion,373,16385 +splashregenerationpot,373,16385 +splashregeneratepot,373,16385 +splashregenpot,373,16385 +regenerationsplashpotion,373,16385 +regeneratesplashpotion,373,16385 +regensplashpotion,373,16385 +splregenerationpotion,373,16385 +splregeneratepotion,373,16385 +splregenpotion,373,16385 +splregenerationpot,373,16385 +splregeneratepot,373,16385 +splregenpot,373,16385 +sprpot,373,16385 +splashswiftnesspotion,373,16386 +splashswiftpotion,373,16386 +splashspeedpotion,373,16386 +splashswiftnesspot,373,16386 +splashswiftpot,373,16386 +splashspeedpot,373,16386 +splswiftnesspotion,373,16386 +splswiftpotion,373,16386 +splspeedpotion,373,16386 +splswiftnesspot,373,16386 +splswiftpot,373,16386 +splspeedpot,373,16386 +spswpot,373,16386 +splashfireresistancepotion,373,16387 +splashfireresistpotion,373,16387 +splashfirerespotion,373,16387 +splashfireresistancepot,373,16387 +splashfireresistpot,373,16387 +splashfirerespot,373,16387 +splfireresistancepotion,373,16387 +splfireresistpotion,373,16387 +splfirerespotion,373,16387 +splfireresistancepot,373,16387 +splfireresistpot,373,16387 +splfirerespot,373,16387 +spfpot,373,16387 +splashpoisonpotion,373,16388 +splashacidpotion,373,16388 +splashpoisonpot,373,16388 +splashacidpot,373,16388 +splpoisonpotion,373,16388 +splacidpotion,373,16388 +splpoisonpot,373,16388 +splacidpot,373,16388 +spppot,373,16388 +splashhealingpotion,373,16389 +splashhealpotion,373,16389 +splashlifepotion,373,16389 +splashhealingpot,373,16389 +splashhealpot,373,16389 +splashlifepot,373,16389 +splhealingpotion,373,16389 +splhealpotion,373,16389 +spllifepotion,373,16389 +splhealingpot,373,16389 +splhealpot,373,16389 +spllifepot,373,16389 +sphpot,373,16389 +splashclearpotion,373,16390 +splashclearpot,373,16390 +splclearpotion,373,16390 +splclearpot,373,16390 +splashnightvisionpotion,373,16390 +splashnvisionpotion,373,16390 +splashnightvpotion,373,16390 +splashdarkvisionpotion,373,16390 +splashdvisionpotion,373,16390 +splashdarkvpotion,373,16390 +splashnightvisionpot,373,16390 +splashnvisionpot,373,16390 +splashnightvpot,373,16390 +splashdarkvisionpot,373,16390 +splashdvisionpot,373,16390 +splashdarkvpot,373,16390 +splnightvisionpotion,373,16390 +splnvisionpotion,373,16390 +splnightvpotion,373,16390 +spldarkvisionpotion,373,16390 +spldvisionpotion,373,16390 +spldarkvpotion,373,16390 +splnightvisionpot,373,16390 +splnvisionpot,373,16390 +splnightvpot,373,16390 +spldarkvisionpot,373,16390 +spldvisionpot,373,16390 +spldarkvpot,373,16390 +spnpot,373,16390 +splashclearextendedpotion,373,16391 +splashclearexpotion,373,16391 +splashclear2potion,373,16391 +splashclearextendedpot,373,16391 +splashclearexpot,373,16391 +splashclear2pot,373,16391 +splclearextendedpotion,373,16391 +splclearexpotion,373,16391 +splclear2potion,373,16391 +splclearextendedpot,373,16391 +splclearexpot,373,16391 +splclear2pot,373,16391 +splashweaknesspotion,373,16392 +splashweakpotion,373,16392 +splashweaknesspot,373,16392 +splashweakpot,373,16392 +splweaknesspotion,373,16392 +splweakpotion,373,16392 +splweaknesspot,373,16392 +splweakpot,373,16392 +spwpot,373,16392 +splashstrengthpotion,373,16393 +splashstrongpotion,373,16393 +splashstrpotion,373,16393 +splashstrengthpot,373,16393 +splashstrongpot,373,16393 +splashstrpot,373,16393 +splstrengthpotion,373,16393 +splstrongpotion,373,16393 +splstrpotion,373,16393 +splstrengthpot,373,16393 +splstrongpot,373,16393 +splstrpot,373,16393 +spstpot,373,16393 +splashslownesspotion,373,16394 +splashslowpotion,373,16394 +splashslownesspot,373,16394 +splashslowpot,373,16394 +splslownesspotion,373,16394 +splslowpotion,373,16394 +splslownesspot,373,16394 +splslowpot,373,16394 +spslpot,373,16394 +splashdiffusepotion,373,16395 +splashdiffusepot,373,16395 +spldiffusepotion,373,16395 +spldiffusepot,373,16395 +splashharmingpotion,373,16396 +splashdamagepotion,373,16396 +splashdmgpotion,373,16396 +splashharmingpot,373,16396 +splashdamagepot,373,16396 +splashdmgpot,373,16396 +splharmingpotion,373,16396 +spldamagepotion,373,16396 +spldmgpotion,373,16396 +splharmingpot,373,16396 +spldamagepot,373,16396 +spldmgpot,373,16396 +spdpot,373,16396 +splashartlesspotion,373,16397 +splashartlesspot,373,16397 +splartlesspotion,373,16397 +splartlesspot,373,16397 +splashthinpotion,373,16398 +splashthinpot,373,16398 +splthinpotion,373,16398 +splthinpot,373,16398 +splashinvisibilitypotion,373,16398 +splashinvisiblepotion,373,16398 +splashinvpotion,373,16398 +splashinvisibilitypot,373,16398 +splashinvisiblepot,373,16398 +splashinvpot,373,16398 +splinvisibilitypotion,373,16398 +splinvisiblepotion,373,16398 +splinvpotion,373,16398 +splinvisibilitypot,373,16398 +splinvisiblepot,373,16398 +splinvpot,373,16398 +spipot,373,16398 +splashthinextendedpotion,373,16399 +splashthinexpotion,373,16399 +splashthin2potion,373,16399 +splashthinextendedpot,373,16399 +splashthinexpot,373,16399 +splashthin2pot,373,16399 +splthinextendedpotion,373,16399 +splthinexpotion,373,16399 +splthin2potion,373,16399 +splthinextendedpot,373,16399 +splthinexpot,373,16399 +splthin2pot,373,16399 +splashawkwardpotion,373,16400 +splashawkwardpot,373,16400 +splawkwardpotion,373,16400 +splawkwardpot,373,16400 +splashbunglingpotion,373,16406 +splashbunglingpot,373,16406 +splbunglingpotion,373,16406 +splbunglingpot,373,16406 +splashbunglingextendedpotion,373,16407 +splashbunglingexpotion,373,16407 +splashbungling2potion,373,16407 +splashbunglingextendedpot,373,16407 +splashbunglingexpot,373,16407 +splashbungling2pot,373,16407 +splbunglingextendedpotion,373,16407 +splbunglingexpotion,373,16407 +splbungling2potion,373,16407 +splbunglingextendedpot,373,16407 +splbunglingexpot,373,16407 +splbungling2pot,373,16407 +splashsmoothpotion,373,16411 +splashsmoothpot,373,16411 +splsmoothpotion,373,16411 +splsmoothpot,373,16411 +splashsuavepotion,373,16413 +splashsuavepot,373,16413 +splsuavepotion,373,16413 +splsuavepot,373,16413 +splashdebonairpotion,373,16414 +splashdebonairpot,373,16414 +spldebonairpotion,373,16414 +spldebonairpot,373,16414 +splashdebonairextendedpotion,373,16415 +splashdebonairexpotion,373,16415 +splashdebonair2potion,373,16415 +splashdebonairextendedpot,373,16415 +splashdebonairexpot,373,16415 +splashdebonair2pot,373,16415 +spldebonairextendedpotion,373,16415 +spldebonairexpotion,373,16415 +spldebonair2potion,373,16415 +spldebonairextendedpot,373,16415 +spldebonairexpot,373,16415 +spldebonair2pot,373,16415 +splashthickpotion,373,16416 +splashthickpot,373,16416 +splthickpotion,373,16416 +splthickpot,373,16416 +splashregenerationleveliipotion,373,16417 +splashregenerateleveliipotion,373,16417 +splashregenleveliipotion,373,16417 +splashregenerationlevel2potion,373,16417 +splashregeneratelevel2potion,373,16417 +splashregenlevel2potion,373,16417 +splashregenerationiipotion,373,16417 +splashregenerateiipotion,373,16417 +splashregeniipotion,373,16417 +splashregenerationleveliipot,373,16417 +splashregenerateleveliipot,373,16417 +splashregenleveliipot,373,16417 +splashregenerationlevel2pot,373,16417 +splashregeneratelevel2pot,373,16417 +splashregenlevel2pot,373,16417 +splashregenerationiipot,373,16417 +splashregenerateiipot,373,16417 +splashregeniipot,373,16417 +splregenerationleveliipotion,373,16417 +splregenerateleveliipotion,373,16417 +splregenleveliipotion,373,16417 +splregenerationlevel2potion,373,16417 +splregeneratelevel2potion,373,16417 +splregenlevel2potion,373,16417 +splregenerationiipotion,373,16417 +splregenerateiipotion,373,16417 +splregeniipotion,373,16417 +splregenerationleveliipot,373,16417 +splregenerateleveliipot,373,16417 +splregenleveliipot,373,16417 +splregenerationlevel2pot,373,16417 +splregeneratelevel2pot,373,16417 +splregenlevel2pot,373,16417 +splregenerationiipot,373,16417 +splregenerateiipot,373,16417 +splregeniipot,373,16417 +spr2pot,373,16417 +splashswiftnessleveliipotion,373,16418 +splashswiftleveliipotion,373,16418 +splashspeedleveliipotion,373,16418 +splashswiftnesslevel2potion,373,16418 +splashswiftlevel2potion,373,16418 +splashspeedlevel2potion,373,16418 +splashswiftnessiipotion,373,16418 +splashswiftiipotion,373,16418 +splashspeediipotion,373,16418 +splashswiftnessleveliipot,373,16418 +splashswiftleveliipot,373,16418 +splashspeedleveliipot,373,16418 +splashswiftnesslevel2pot,373,16418 +splashswiftlevel2pot,373,16418 +splashspeedlevel2pot,373,16418 +splashswiftnessiipot,373,16418 +splashswiftiipot,373,16418 +splashspeediipot,373,16418 +splswiftnessleveliipotion,373,16418 +splswiftleveliipotion,373,16418 +splspeedleveliipotion,373,16418 +splswiftnesslevel2potion,373,16418 +splswiftlevel2potion,373,16418 +splspeedlevel2potion,373,16418 +splswiftnessiipotion,373,16418 +splswiftiipotion,373,16418 +splspeediipotion,373,16418 +splswiftnessleveliipot,373,16418 +splswiftleveliipot,373,16418 +splspeedleveliipot,373,16418 +splswiftnesslevel2pot,373,16418 +splswiftlevel2pot,373,16418 +splspeedlevel2pot,373,16418 +splswiftnessiipot,373,16418 +splswiftiipot,373,16418 +splspeediipot,373,16418 +spsw2pot,373,16418 +splashpoisonleveliipotion,373,16420 +splashacidleveliipotion,373,16420 +splashpoisonlevel2potion,373,16420 +splashacidlevel2potion,373,16420 +splashpoisoniipotion,373,16420 +splashacidiipotion,373,16420 +splashpoisonleveliipot,373,16420 +splashacidleveliipot,373,16420 +splashpoisonlevel2pot,373,16420 +splashacidlevel2pot,373,16420 +splashpoisoniipot,373,16420 +splashacidiipot,373,16420 +splpoisonleveliipotion,373,16420 +splacidleveliipotion,373,16420 +splpoisonlevel2potion,373,16420 +splcidlevel2potion,373,16420 +splpoisoniipotion,373,16420 +splacidiipotion,373,16420 +splpoisonleveliipot,373,16420 +splacidleveliipot,373,16420 +splpoisonlevel2pot,373,16420 +splacidlevel2pot,373,16420 +splpoisoniipot,373,16420 +splacidiipot,373,16420 +spp2pot,373,16420 +splashhealingleveliipotion,373,16421 +splashhealleveliipotion,373,16421 +splashhealinglevel2potion,373,16421 +splashheallevel2potion,373,16421 +splashhealingiipotion,373,16421 +splashhealiipotion,373,16421 +splashhealingleveliipot,373,16421 +splashhealleveliipot,373,16421 +splashhealinglevel2pot,373,16421 +splashheallevel2pot,373,16421 +splashhealingiipot,373,16421 +splashhealiipot,373,16421 +splhealingleveliipotion,373,16421 +splhealleveliipotion,373,16421 +splhealinglevel2potion,373,16421 +splheallevel2potion,373,16421 +splhealingiipotion,373,16421 +splhealiipotion,373,16421 +splhealingleveliipot,373,16421 +splhealleveliipot,373,16421 +splhealinglevel2pot,373,16421 +splheallevel2pot,373,16421 +splhealingiipot,373,16421 +splhealiipot,373,16421 +sph2pot,373,16421 +splashcharmingpotion,373,16422 +splashcharmingpot,373,16422 +splcharmingpotion,373,16422 +splcharmingpot,373,16422 +splashcharmingextendedpotion,373,16423 +splashcharmingexpotion,373,16423 +splashcharming2potion,373,16423 +splashcharmingextendedpot,373,16423 +splashcharmingexpot,373,16423 +splashcharming2pot,373,16423 +splcharmingextendedpotion,373,16423 +splcharmingexpotion,373,16423 +splcharming2potion,373,16423 +splcharmingextendedpot,373,16423 +splcharmingexpot,373,16423 +splcharming2pot,373,16423 +splashstrengthleveliipotion,373,16425 +splashstrongleveliipotion,373,16425 +splashstrleveliipotion,373,16425 +splashstrengthlevel2potion,373,16425 +splashstronglevel2potion,373,16425 +splashstrlevel2potion,373,16425 +splashstrengthiipotion,373,16425 +splashstrongiipotion,373,16425 +splashstriipotion,373,16425 +splashstrengthleveliipot,373,16425 +splashstrongleveliipot,373,16425 +splashstrleveliipot,373,16425 +splashstrengthlevel2pot,373,16425 +splashstronglevel2pot,373,16425 +splashstrlevel2pot,373,16425 +splashstrengthiipot,373,16425 +splashstrongiipot,373,16425 +splashstriipot,373,16425 +splstrengthleveliipotion,373,16425 +splstrongleveliipotion,373,16425 +splstrleveliipotion,373,16425 +splstrengthlevel2potion,373,16425 +splstronglevel2potion,373,16425 +splstrlevel2potion,373,16425 +splstrengthiipotion,373,16425 +splstrongiipotion,373,16425 +splstriipotion,373,16425 +splstrengthleveliipot,373,16425 +splstrongleveliipot,373,16425 +splstrleveliipot,373,16425 +splstrengthlevel2pot,373,16425 +splstronglevel2pot,373,16425 +splstrlevel2pot,373,16425 +splstrengthiipot,373,16425 +splstrongiipot,373,16425 +splstriipot,373,16425 +spst2pot,373,16425 +splashrefinedpotion,373,16427 +splashrefinedpot,373,16427 +splrefinedpotion,373,16427 +splrefinedpot,373,16427 +splashharmingleveliipotion,373,16428 +splashdamageleveliipotion,373,16428 +splashdmgleveliipotion,373,16428 +splashharminglevel2potion,373,16428 +splashdamagelevel2potion,373,16428 +splashdmglevel2potion,373,16428 +splashharmingiipotion,373,16428 +splashdamageiipotion,373,16428 +splashdmgiipotion,373,16428 +splashharmingleveliipot,373,16428 +splashdamageleveliipot,373,16428 +splashdmgleveliipot,373,16428 +splashharminglevel2pot,373,16428 +splashdamagelevel2pot,373,16428 +splashdmglevel2pot,373,16428 +splashharmingiipot,373,16428 +splashdamageiipot,373,16428 +splashdmgiipot,373,16428 +splharmingleveliipotion,373,16428 +spldamageleveliipotion,373,16428 +spldmgleveliipotion,373,16428 +splharminglevel2potion,373,16428 +spldamagelevel2potion,373,16428 +spldmglevel2potion,373,16428 +splharmingiipotion,373,16428 +spldamageiipotion,373,16428 +spldmgiipotion,373,16428 +splharmingleveliipot,373,16428 +spldamageleveliipot,373,16428 +spldmgleveliipot,373,16428 +splharminglevel2pot,373,16428 +spldamagelevel2pot,373,16428 +spldmglevel2pot,373,16428 +splharmingiipot,373,16428 +spldamageiipot,373,16428 +spldmgiipot,373,16428 +spd2pot,373,16428 +splashcordialpotion,373,16429 +splashcordialpot,373,16429 +splcordialpotion,373,16429 +splcordialpot,373,16429 +splashsparklingpotion,373,16430 +splashsparklingpot,373,16430 +splsparklingpotion,373,16430 +splsparklingpot,373,16430 +splashsparklingextendedpotion,373,16431 +splashsparklingexpotion,373,16431 +splashsparkling2potion,373,16431 +splashsparklingextendedpot,373,16431 +splashsparklingexpot,373,16431 +splashsparkling2pot,373,16431 +splsparklingextendedpotion,373,16431 +splsparklingexpotion,373,16431 +splsparkling2potion,373,16431 +splsparklingextendedpot,373,16431 +splsparklingexpot,373,16431 +splsparkling2pot,373,16431 +splashpotentpotion,373,16432 +splashpotentpot,373,16432 +splpotentpotion,373,16432 +splpotentpot,373,16432 +splashrankpotion,373,16438 +splashrankpot,373,16438 +splrankpotion,373,16438 +splrankpot,373,16438 +splashrankextendedpotion,373,16439 +splashrankexpotion,373,16439 +splashrank2potion,373,16439 +splashrankextendedpot,373,16439 +splashrankexpot,373,16439 +splashrank2pot,373,16439 +splrankextendedpotion,373,16439 +splrankexpotion,373,16439 +splrank2potion,373,16439 +splrankextendedpot,373,16439 +splrankexpot,373,16439 +splrank2pot,373,16439 +splashacridpotion,373,16443 +splashacridpot,373,16443 +splacridpotion,373,16443 +splacridpot,373,16443 +splashgrosspotion,373,16445 +splashgrosspot,373,16445 +splgrosspotion,373,16445 +splgrosspot,373,16445 +splashstinkypotion,373,16446 +splashstinkypot,373,16446 +splstinkypotion,373,16446 +splstinkypot,373,16446 +splashstinkyextendedpotion,373,16447 +splashstinkyexpotion,373,16447 +splashstinky2potion,373,16447 +splashstinkyextendedpot,373,16447 +splashstinkyexpot,373,16447 +splashstinky2pot,373,16447 +splstinkyextendedpotion,373,16447 +splstinkyexpotion,373,16447 +splstinky2potion,373,16447 +splstinkyextendedpot,373,16447 +splstinkyexpot,373,16447 +splstinky2pot,373,16447 +splashmundaneextendedpotion,373,16448 +splashmundaneexpotion,373,16448 +splashmundane2potion,373,16448 +splashmundaneextendedpot,373,16448 +splashmundaneexpot,373,16448 +splashmundane2pot,373,16448 +splmundaneextendedpotion,373,16448 +splmundaneexpotion,373,16448 +splmundane2potion,373,16448 +splmundaneextendedpot,373,16448 +splmundaneexpot,373,16448 +splmundane2pot,373,16448 +splashregenerationextendedpotion,373,16449 +splashregenerateextendedpotion,373,16449 +splashregenextendepotion,373,16449 +splashregenerationexpotion,373,16449 +splashregenerateexpotion,373,16449 +splashregenexpotion,373,16449 +splashregenerationextendedpot,373,16449 +splashregenerateextendedpot,373,16449 +splashregenextendepot,373,16449 +splashregenerationexpot,373,16449 +splashregenerateexpot,373,16449 +splashregenexpot,373,16449 +splregenerationextendedpotion,373,16449 +splregenerateextendedpotion,373,16449 +splregenextendepotion,373,16449 +splregenerationexpotion,373,16449 +splregenerateexpotion,373,16449 +splregenexpotion,373,16449 +splregenerationextendedpot,373,16449 +splregenerateextendedpot,373,16449 +splregenextendepot,373,16449 +splregenerationexpot,373,16449 +splregenerateexpot,373,16449 +splregenexpot,373,16449 +sprepot,373,16449 +splashswiftnessextendedpotion,373,16450 +splashswiftextendedpotion,373,16450 +splashspeedextendedpotion,373,16450 +splashswiftnessexpotion,373,16450 +splashswiftexpotion,373,16450 +splashspeedexpotion,373,16450 +splashswiftnessextendedpot,373,16450 +splashswiftextendedpot,373,16450 +splashspeedextendedpot,373,16450 +splashswiftnessexpot,373,16450 +splashswiftexpot,373,16450 +splashspeedexpot,373,16450 +splswiftnessextendedpotion,373,16450 +splswiftextendedpotion,373,16450 +splspeedextendedpotion,373,16450 +splswiftnessexpotion,373,16450 +splswiftexpotion,373,16450 +splspeedexpotion,373,16450 +splswiftnessextendedpot,373,16450 +splswiftextendedpot,373,16450 +splspeedextendedpot,373,16450 +splswiftnessexpot,373,16450 +splswiftexpot,373,16450 +splspeedexpot,373,16450 +spswepot,373,16450 +splashfireresistanceextendedpotion,373,16451 +splashfireresistextendedpotion,373,16451 +splashfireresextendedpotion,373,16451 +splashfireresistanceexpotion,373,16451 +splashfireresistexpotion,373,16451 +splashfireresexpotion,373,16451 +splashfireresistanceextendedpot,373,16451 +splashfireresistextendedpot,373,16451 +splashfireresextendedpot,373,16451 +splashfireresistanceexpot,373,16451 +splashfireresistexpot,373,16451 +splashfireresexpot,373,16451 +splfireresistanceextendedpotion,373,16451 +splfireresistextendedpotion,373,16451 +splfireresextendedpotion,373,16451 +splfireresistanceexpotion,373,16451 +splfireresistexpotion,373,16451 +splfireresexpotion,373,16451 +splfireresistanceextendedpot,373,16451 +splfireresistextendedpot,373,16451 +splfireresextendedpot,373,16451 +splfireresistanceexpot,373,16451 +splfireresistexpot,373,16451 +splfireresexpot,373,16451 +spfepot,373,16451 +splashpoisonextendedpotion,373,16452 +splashacidextendedpotion,373,16452 +splashpoisonexpotion,373,16452 +splashacidexpotion,373,16452 +splashpoisonextendedpot,373,16452 +splashacidextendedpot,373,16452 +splashpoisonexpot,373,16452 +splashacidexpot,373,16452 +splpoisonextendedpotion,373,16452 +splacidextendedpotion,373,16452 +splpoisonexpotion,373,16452 +splacidexpotion,373,16452 +splpoisonextendedpot,373,16452 +splacidextendedpot,373,16452 +splpoisonexpot,373,16452 +splacidexpot,373,16452 +sppepot,373,16452 +splashnightvisionextendedpotion,373,16454 +splashnvisionextendedpotion,373,16454 +splashnightvextendedpotion,373,16454 +splashdarkvisionextendedpotion,373,16454 +splashdvisionextendedpotion,373,16454 +splashdarkvextendedpotion,373,16454 +splashnightvisionextendedpot,373,16454 +splashnvisionextendedpot,373,16454 +splashnightvextendedpot,373,16454 +splashdarkvisionextendedpot,373,16454 +splashdvisionextendedpot,373,16454 +splashdarkvextendedpot,373,16454 +splashnightvisionexpotion,373,16454 +splashnvisionexpotion,373,16454 +splashnightvexpotion,373,16454 +splashdarkvisionexpotion,373,16454 +splashdvisionexpotion,373,16454 +splashdarkvexpotion,373,16454 +splashnightvisionexpot,373,16454 +splashnvisionexpot,373,16454 +splashnightvexpot,373,16454 +splashdarkvisionexpot,373,16454 +splashdvisionexpot,373,16454 +splashdarkvexpot,373,16454 +splnightvisionextendedpotion,373,16454 +splnvisionextendedpotion,373,16454 +splnightvextendedpotion,373,16454 +spldarkvisionextendedpotion,373,16454 +spldvisionextendedpotion,373,16454 +spldarkvextendedpotion,373,16454 +splnightvisionextendedpot,373,16454 +splnvisionextendedpot,373,16454 +splnightvextendedpot,373,16454 +spldarkvisionextendedpot,373,16454 +spldvisionextendedpot,373,16454 +spldarkvextendedpot,373,16454 +splnightvisionexpotion,373,16454 +splnvisionexpotion,373,16454 +splnightvexpotion,373,16454 +spldarkvisionexpotion,373,16454 +spldvisionexpotion,373,16454 +spldarkvexpotion,373,16454 +splnightvisionexpot,373,16454 +splnvisionexpot,373,16454 +splnightvexpot,373,16454 +spldarkvisionexpot,373,16454 +spldvisionexpot,373,16454 +spldarkvexpot,373,16454 +spnepot,373,16454 +splashweaknessextendedpotion,373,16456 +splashweakextendedpotion,373,16456 +splashweaknessexpotion,373,16456 +splashweakexpotion,373,16456 +splashweaknessextendedpot,373,16456 +splashweakextendedpot,373,16456 +splashweaknessexpot,373,16456 +splashweakexpot,373,16456 +splweaknessextendedpotion,373,16456 +sphweakextendedpotion,373,16456 +splweaknessexpotion,373,16456 +splweakexpotion,373,16456 +splweaknessextendedpot,373,16456 +splweakextendedpot,373,16456 +splweaknessexpot,373,16456 +splweakexpot,373,16456 +spwepot,373,16456 +splashstrengthextendedpotion,373,16457 +splashstrongextendedpotion,373,16457 +splashstrextendedpotion,373,16457 +splashstrengthexpotion,373,16457 +splashstrongexpotion,373,16457 +splashstrexpotion,373,16457 +splashstrengthextendedpot,373,16457 +splashstrongextendedpot,373,16457 +splashstrextendedpot,373,16457 +splashstrengthexpot,373,16457 +splashstrongexpot,373,16457 +splashstrexpot,373,16457 +splstrengthextendedpotion,373,16457 +splstrongextendedpotion,373,16457 +splstrextendedpotion,373,16457 +splstrengthexpotion,373,16457 +splstrongexpotion,373,16457 +splstrexpotion,373,16457 +splstrengthextendedpot,373,16457 +splstrongextendedpot,373,16457 +splstrextendedpot,373,16457 +splstrengthexpot,373,16457 +splstrongexpot,373,16457 +splstrexpot,373,16457 +spstepot,373,16457 +splashslownessextendedpotion,373,16458 +splashslowextenedpotion,373,16458 +splashslownessexpotion,373,16458 +splashslowexpotion,373,16458 +splashslownessextendedpot,373,16458 +splashslowextenedpot,373,16458 +splashslownessexpot,373,16458 +splashslowexpot,373,16458 +splslownessextendedpotion,373,16458 +splslowextenedpotion,373,16458 +splslownessexpotion,373,16458 +splslowexpotion,373,16458 +splslownessextendedpot,373,16458 +splslowextenedpot,373,16458 +splslownessexpot,373,16458 +splslowexpot,373,16458 +spslepot,373,16458 +splashinvisibilityextendedpotion,373,16462 +splashinvisibleextendedpotion,373,16462 +splashinvextendedpotion,373,16462 +splashinvisibilityextendedpot,373,16462 +splashinvisibleextendedpot,373,16462 +splashinvextendedpot,373,16462 +splashinvisibilityexpotion,373,16462 +splashinvisibleexpotion,373,16462 +splashinvexpotion,373,16462 +splashinvisibilityexpot,373,16462 +splashinvisibleexpot,373,16462 +splashinvexpot,373,16462 +splinvisibilityextendedpotion,373,16462 +splinvisibleextendedpotion,373,16462 +splinvextendedpotion,373,16462 +splinvisibilityextendedpot,373,16462 +splinvisibleextendedpot,373,16462 +splinvextendedpot,373,16462 +splinvisibilityexpotion,373,16462 +splinvisibleexpotion,373,16462 +splinvexpotion,373,16462 +splinvisibilityexpot,373,16462 +splinvisibleexpot,373,16462 +splinvexpot,373,16462 +spiepot,373,16462 +splashregenerationdualbitpotion,373,16481 +splashregeneratedualbitpotion,373,16481 +splashregendualbitpotion,373,16481 +splashregenerationdualbitpot,373,16481 +splashregeneratedualbitpot,373,16481 +splashregendualbitpot,373,16481 +splregenerationdualbitpotion,373,16481 +splregeneratedualbitpotion,373,16481 +splregendualbitpotion,373,16481 +splregenerationdualbitpot,373,16481 +splregeneratedualbitpot,373,16481 +splregendualbitpot,373,16481 +splashregenerationdbpotion,373,16481 +splashregeneratedbpotion,373,16481 +splashregendbpotion,373,16481 +splashregenerationdbpot,373,16481 +splashregeneratedbpot,373,16481 +splashregendbpot,373,16481 +splregenerationdbpotion,373,16481 +splregeneratedbpotion,373,16481 +splregendbpotion,373,16481 +splregenerationdbpot,373,16481 +splregeneratedbpot,373,16481 +splregendbpot,373,16481 +sprdbpot,373,16481 +splashswiftnessdualbitpotion,373,16482 +splashswiftdualbitpotion,373,16482 +splashspeeddualbitpotion,373,16482 +splashswiftnessdualbitpot,373,16482 +splashswiftdualbitpot,373,16482 +splashspeeddualbitpot,373,16482 +splswiftnessdualbitpotion,373,16482 +splswiftdualbitpotion,373,16482 +splspeeddualbitpotion,373,16482 +splswiftnessdualbitpot,373,16482 +splswiftdualbitpot,373,16482 +splspeeddualbitpot,373,16482 +splashswiftnessdbpotion,373,16482 +splashswiftdbpotion,373,16482 +splashspeeddbpotion,373,16482 +splashswiftnessdbpot,373,16482 +splashswiftdbpot,373,16482 +splashspeeddbpot,373,16482 +splswiftnessdbpotion,373,16482 +splswiftdbpotion,373,16482 +splspeeddbpotion,373,16482 +splswiftnessdbpot,373,16482 +splswiftdbpot,373,16482 +splspeeddbpot,373,16482 +spswdbpot,373,16482 +splashpoisondualbitpotion,373,16484 +splashaciddualbitpotion,373,16484 +splashpoisondualbitpot,373,16484 +splashaciddualbitpot,373,16484 +splpoisondualbitpotion,373,16484 +splaciddualbitpotion,373,16484 +splpoisondualbitpot,373,16484 +splaciddualbitpot,373,16484 +splashpoisondbpotion,373,16484 +splashaciddbpotion,373,16484 +splashpoisondbpot,373,16484 +splashaciddbpot,373,16484 +splpoisondbpotion,373,16484 +splaciddbpotion,373,16484 +splpoisondbpot,373,16484 +splaciddbpot,373,16484 +sppdbpot,373,16484 +splashstrengthdualbitpotion,373,16489 +splashstrongdualbitpotion,373,16489 +splashstrdualbitpotion,373,16489 +splashstrengthdualbitpot,373,16489 +splashstrongdualbitpot,373,16489 +splashstrdualbitpot,373,16489 +splstrengthdualbitpotion,373,16489 +splstrongdualbitpotion,373,16489 +splstrdualbitpotion,373,16489 +splstrengthdualbitpot,373,16489 +splstrongdualbitpot,373,16489 +splstrdualbitpot,373,16489 +splashstrengthdbpotion,373,16489 +splashstrongdbpotion,373,16489 +splashstrdbpotion,373,16489 +splashstrengthdbpot,373,16489 +splashstrongdbpot,373,16489 +splashstrdbpot,373,16489 +splstrengthdbpotion,373,16489 +splstrongdbpotion,373,16489 +splstrdbpotion,373,16489 +splstrengthdbpot,373,16489 +splstrongdbpot,373,16489 +splstrdbpot,373,16489 +spstdbpot,373,16489 +hp1,373,8197 +hp2,373,8229 +rp1,373,8193 +rp1e,373,8257 +rp2,373,8225 +dp1,373,8204 +dp2,373,8236 +swp1,373,8194 +swp1e,373,0 +swp2,373,8226 +slp1,373,8202 +slp1e,373,8266 +strp1,373,8201 +strp1e,373,8265 +strp2,373,8233 +wp1,373,8200 +wp1e,373,8264 +pp1,373,8196 +pp1e,373,8260 +pp2,373,8228 +frp1,373,8195 +frp1e,373,8259 +invp,373,8270 +nvp,373,8262 +dp1s,373,16460 +dp2s,373,16428 +swp1s,373,16386 +swp1es,373,0 +swp2s,373,16418 +slp1s,373,16394 +slp1es,373,16458 +strp1s,373,16393 +strp1es,373,16457 +strp2s,373,16425 +wp1s,373,16456 +wp1es,373,16424 +pp1s,373,16388 +pp1es,373,16452 +pp2s,373,16420 +frp1s,373,16387 +frp1es,373,16451 +invps,373,16430 +nvps,373,16422 +glassbottle,374,0 +bottle,374,0 +gbottle,374,0 +gvase,374,0 +vase,374,0 +glassvase,374,0 +emptyglassbottle,374,0 +emptybottle,374,0 +emptygbottle,374,0 +emptygvase,374,0 +emptyvase,374,0 +emptyglassvase,374,0 +eglassbottle,374,0 +ebottle,374,0 +egbottle,374,0 +egvase,374,0 +evase,374,0 +eglassvase,374,0 +spidereye,375,0 +eyeofspider,375,0 +spiderseye,375,0 +spiderball,375,0 +spidernugget,375,0 +spidersball,375,0 +spidersnugget,375,0 +seye,375,0 +sball,375,0 +snugget,375,0 +fermentedspidereye,376,0 +craftedspidereye,376,0 +fermentedeyeofspider,376,0 +craftedeyeofspider,376,0 +fspidereye,376,0 +feyeofspider,376,0 +ceyeofspider,376,0 +cspidereye,376,0 +blazepowder,377,0 +blazedust,377,0 +goldpowder,377,0 +golddust,377,0 +gdust,377,0 +gpowder,377,0 +bpowder,377,0 +bdust,377,0 +magmacream,378,0 +goldcream,378,0 +blazecream,378,0 +mcream,378,0 +gcream,378,0 +bcream,378,0 +combinedcream,378,0 +ccream,378,0 +bstand,379,0 +pstand,379,0 +brewingstand,379,0 +potionstand,379,0 +cauldronitem,380,0 +ironcauldronitem,380,0 +steelcauldronitem,380,0 +icauldronitem,380,0 +scauldronitem,380,0 +eyeofender,381,0 +endereye,381,0 +endeye,381,0 +evilendereye,381,0 +evileyeofender,381,0 +evilenderpearl,381,0 +eeye,381,0 +eofender,381,0 +speckledmelon,382,0 +goldmelon,382,0 +sparklymelon,382,0 +glisteningmelon,382,0 +glisteringmelon,382,0 +shiningmelon,382,0 +gmelon,382,0 +smelon,382,0 +creeperegg,383,50 +eggcreeper,383,50 +skeletonegg,383,51 +eggskeleton,383,51 +spideregg,383,52 +eggspider,383,52 +giantegg,383,53 +egggiant,383,53 +zombieegg,383,54 +eggzombie,383,54 +slimeegg,383,55 +eggslime,383,55 +ghastegg,383,56 +eggghast,383,56 +zombiepigmanegg,383,57 +zpigmanegg,383,57 +pigmanegg,383,57 +zombiepmanegg,383,57 +zpmanegg,383,57 +zombiepigmegg,383,57 +zpigmegg,383,57 +zombiepigegg,383,57 +zpigegg,383,57 +zombiepmegg,383,57 +zombiepegg,383,57 +eggzombiepigman,383,57 +eggzpigman,383,57 +eggpigman,383,57 +eggzombiepman,383,57 +eggzpman,383,57 +eggzombiepigm,383,57 +eggzpigm,383,57 +eggzombiepig,383,57 +eggzpig,383,57 +eggzombiepm,383,57 +eggzombiep,383,57 +endermanegg,383,58 +eggenderman,383,58 +eggcavespider,383,59 +cavespideregg,383,59 +silverfishegg,383,60 +eggsilverfish,383,60 +blazeegg,383,61 +eggblaze,383,61 +lavaslimeegg,383,62 +lavacubeegg,383,62 +magmacubeegg,383,62 +magmaslimeegg,383,62 +egglavaslime,383,62 +egglavacube,383,62 +eggmagmacube,383,62 +eggmagmaslime,383,62 +bategg,383,65 +eggbat,383,65 +witchegg,383,66 +eggwitch,383,66 +pigegg,383,90 +eggpig,383,90 +sheepegg,383,91 +eggsheep,383,91 +cowegg,383,92 +eggcow,383,92 +chickenegg,383,93 +eggchicken,383,93 +squidegg,383,94 +eggsquid,383,94 +wolfegg,383,95 +eggwolf,383,95 +snowgolemegg,383,97 +sgolemegg,383,97 +eggsnowgolem,383,97 +eggsgolem,383,97 +ocelotegg,383,98 +eggocelot,383,98 +irongolemegg,383,99 +igolemegg,383,99 +eggirongolem,383,99 +eggigolem,383,99 +egghorse,383,100 +horseegg,383,100 +villageregg,383,120 +eggvillager,383,120 +bottleofenchanting,384,0 +enchantingbottle,384,0 +expbottle,384,0 +xpbottle,384,0 +bottleexp,384,0 +bottlexp,384,0 +enchantbottle,384,0 +bottleenchanting,384,0 +bottleenchant,384,0 +bottleoenchanting,384,0 +firecharge,385,0 +fireball,385,0 +grenade,385,0 +bookandquill,386,0 +booknquill,386,0 +bookandfeather,386,0 +booknfeather,386,0 +writeablebook,386,0 +writtenbook,387,0 +readablebook,387,0 +sealedbook,387,0 +diary,387,0 +ownedbook,387,0 +emerald,388,0 +itemframe,389,0 +pictureframe,389,0 +iframe,389,0 +pframe,389,0 +flowerpot,390,0 +pot,390,0 +carrot,391,0 +potato,392,0 +bakedpotato,393,0 +roastedpotato,393,0 +cookedpotato,393,0 +bakepotato,393,0 +roastpotato,393,0 +cookpotato,393,0 +poisonouspotato,394,0 +poisonpotato,394,0 +ppotato,394,0 +emptymap,395,0 +map,395,0 +goldencarrot,396,0 +goldcarrot,396,0 +gcarrot,396,0 +head,397,0 +skeletonhead,397,0 +headskeleton,397,0 +skeletonskull,397,0 +skullskeleton,397,0 +witherhead,397,1 +witherskeletonhead,397,1 +wskeletionhead,397,1 +headwither,397,1 +headwitherskeleton,397,1 +headwskeletion,397,1 +witherskull,397,1 +witherskeletonskull,397,1 +wskeletionskull,397,1 +skullwither,397,1 +skullwitherskeleton,397,1 +skullwskeletion,397,1 +zombiehead,397,2 +headzombie,397,2 +zombieskull,397,2 +skullzombie,397,2 +playerhead,397,3 +humanhead,397,3 +stevehead,397,3 +headplayer,397,3 +headhuman,397,3 +headsteve,397,3 +playerskull,397,3 +humanskull,397,3 +steveskull,397,3 +skullplayer,397,3 +skullhuman,397,3 +skullsteve,397,3 +creeperhead,397,4 +headcreeper,397,4 +creeperskull,397,4 +skullcreeper,397,4 +carrotonastick,398,0 +carrotonstick,398,0 +netherstar,399,0 +hellstar,399,0 +nstar,399,0 +hstar,399,0 +star,399,0 +pumpkinpie,400,0 +pumpkincake,400,0 +ppie,400,0 +pcake,400,0 +pie,400,0 +fireworkrocket,401,0 +fireworkmissle,401,0 +firework,401,0 +fworkrocket,401,0 +fworkmissle,401,0 +fwork,401,0 +fwrocket,401,0 +fwmissle,401,0 +fireworkstar,402,0 +fworkstar,402,0 +fwstar,402,0 +fireworkball,402,0 +fworkball,402,0 +fwball,402,0 +fireworkpowder,402,0 +fworkpowder,402,0 +fwpowder,402,0 +fireworkcharge,402,0 +fworkcharge,402,0 +fwcharge,402,0 +enchantmentbook,403,0 +enchantedbook,403,0 +enchantingbook,403,0 +enchantbook,403,0 +magicalbook,403,0 +magicbook,403,0 +ebook,403,0 +mbook,403,0 +redstonecomparator,404,0 +redstonecomperer,404,0 +redstonecompare,404,0 +rstonecomparator,404,0 +rstonecomperer,404,0 +rstonecompare,404,0 +redscomparator,404,0 +redscomperer,404,0 +redscompare,404,0 +rscomparator,404,0 +rscomperer,404,0 +rscompare,404,0 +comparator,404,0 +comperer,404,0 +compare,404,0 +netherbrick,405,0 +nbrick,405,0 +hellbrick,405,0 +deathbrick,405,0 +dbrick,405,0 +hbrick,405,0 +netherquartz,406,0 +deathquartz,406,0 +hellquartz,406,0 +nquartz,406,0 +dquartz,406,0 +hquartz,406,0 +quartz,406,0 +dynamiteminecart,407,0 +dynamitemcart,407,0 +dynamitecart,407,0 +bombminecart,407,0 +bombmcart,407,0 +bombcart,407,0 +tntminecart,407,0 +tntmcart,407,0 +tntcart,407,0 +dminecart,407,0 +dmcart,407,0 +dcart,407,0 +bminecart,407,0 +bmcart,407,0 +bcart,407,0 +tminecart,407,0 +tmcart,407,0 +tcart,407,0 +hopperminecart,408,0 +hoppermcart,408,0 +hoppercart,408,0 +hopminecart,408,0 +hopmcart,408,0 +hopcart,408,0 +hminecart,408,0 +hmcart,408,0 +hcart,408,0 +ironhorsearmor,417,0 +ironharmor,417,0 +ironarmor,417,0 +ihorsearmor,417,0 +iharmor,417,0 +iarmor,417,0 +steelhorsearmor,417,0 +steelharmor,417,0 +steelarmor,417,0 +shorsearmor,417,0 +sharmor,417,0 +sarmor,417,0 +goldenhorsearmor,418,0 +goldenharmor,418,0 +goldenarmor,418,0 +goldhorsearmor,418,0 +goldharmor,418,0 +goldarmor,418,0 +ghorsearmor,418,0 +gharmor,418,0 +garmor,418,0 +diamondhorsearmor,419,0 +diamondharmor,419,0 +diamondarmor,419,0 +dhorsearmor,419,0 +dharmor,419,0 +darmor,419,0 +crystalhorsearmor,419,0 +crystalharmor,419,0 +crystalarmor,419,0 +chorsearmor,419,0 +charmor,419,0 +carmor,419,0 +lead,420,0 +leash,420,0 +rope,420,0 +nametag,421,0 +tag,421,0 +goldmusicrecord,2256,0 +goldmusicdisk,2256,0 +goldmusicdisc,2256,0 +goldmusiccd,2256,0 +13musicrecord,2256,0 +13musicdisk,2256,0 +13musicdisc,2256,0 +13musiccd,2256,0 +gomusicrecord,2256,0 +gomusicdisk,2256,0 +gomusicdisc,2256,0 +gomusiccd,2256,0 +goldmrecord,2256,0 +goldmdisk,2256,0 +goldmdisc,2256,0 +goldmcd,2256,0 +13mrecord,2256,0 +13mdisk,2256,0 +13mdisc,2256,0 +13mcd,2256,0 +gomrecord,2256,0 +gomdisk,2256,0 +gomdisc,2256,0 +gomcd,2256,0 +goldrecord,2256,0 +golddisk,2256,0 +golddisc,2256,0 +goldcd,2256,0 +13record,2256,0 +13disk,2256,0 +13disc,2256,0 +13cd,2256,0 +gorecord,2256,0 +godisk,2256,0 +godisc,2256,0 +gocd,2256,0 +record1,2256,0 +disk1,2256,0 +disc1,2256,0 +cd1,2256,0 +1record,2256,0 +1disk,2256,0 +1disc,2256,0 +1cd,2256,0 +greenmusicrecord,2257,0 +greenmusicdisk,2257,0 +greenmusicdisc,2257,0 +greenmusiccd,2257,0 +catmusicrecord,2257,0 +catmusicdisk,2257,0 +catmusicdisc,2257,0 +catmusiccd,2257,0 +grmusicrecord,2257,0 +grmusicdisk,2257,0 +grmusicdisc,2257,0 +grmusiccd,2257,0 +greenmrecord,2257,0 +greenmdisk,2257,0 +greenmdisc,2257,0 +greenmcd,2257,0 +catmrecord,2257,0 +catmdisk,2257,0 +catmdisc,2257,0 +catmcd,2257,0 +grmrecord,2257,0 +grmdisk,2257,0 +grmdisc,2257,0 +grmcd,2257,0 +greenrecord,2257,0 +greendisk,2257,0 +greendisc,2257,0 +greencd,2257,0 +catrecord,2257,0 +catdisk,2257,0 +catdisc,2257,0 +catcd,2257,0 +grrecord,2257,0 +grdisk,2257,0 +grdisc,2257,0 +grcd,2257,0 +record2,2257,0 +disk2,2257,0 +disc2,2257,0 +cd2,2257,0 +2record,2257,0 +2disk,2257,0 +2disc,2257,0 +2cd,2257,0 +orangemusicrecord,2258,0 +orangemusicdisk,2258,0 +orangemusicdisc,2258,0 +orangemusiccd,2258,0 +blocksmusicrecord,2258,0 +blocksmusicdisk,2258,0 +blocksmusicdisc,2258,0 +blocksmusiccd,2258,0 +ormusicrecord,2258,0 +ormusicdisk,2258,0 +ormusicdisc,2258,0 +ormusiccd,2258,0 +orangemrecord,2258,0 +orangemdisk,2258,0 +orangemdisc,2258,0 +orangemcd,2258,0 +blocksmrecord,2258,0 +blocksmdisk,2258,0 +blocksmdisc,2258,0 +blocksmcd,2258,0 +ormrecord,2258,0 +ormdisk,2258,0 +ormdisc,2258,0 +ormcd,2258,0 +orangerecord,2258,0 +orangedisk,2258,0 +orangedisc,2258,0 +orangecd,2258,0 +blocksrecord,2258,0 +blocksdisk,2258,0 +blocksdisc,2258,0 +blockscd,2258,0 +orrecord,2258,0 +ordisk,2258,0 +ordisc,2258,0 +orcd,2258,0 +record3,2258,0 +disk3,2258,0 +disc3,2258,0 +cd3,2258,0 +3record,2258,0 +3disk,2258,0 +3disc,2258,0 +3cd,2258,0 +redmusicrecord,2259,0 +redmusicdisk,2259,0 +redmusicdisc,2259,0 +redmusiccd,2259,0 +chirpmusicrecord,2259,0 +chirpmusicdisk,2259,0 +chirpmusicdisc,2259,0 +chirpmusiccd,2259,0 +remusicrecord,2259,0 +remusicdisk,2259,0 +remusicdisc,2259,0 +remusiccd,2259,0 +redmrecord,2259,0 +redmdisk,2259,0 +redmdisc,2259,0 +redmcd,2259,0 +chirpmrecord,2259,0 +chirpmdisk,2259,0 +chirpmdisc,2259,0 +chirpmcd,2259,0 +remrecord,2259,0 +remdisk,2259,0 +remdisc,2259,0 +remcd,2259,0 +redrecord,2259,0 +reddisk,2259,0 +reddisc,2259,0 +redcd,2259,0 +chirprecord,2259,0 +chirpdisk,2259,0 +chirpdisc,2259,0 +chirpcd,2259,0 +rerecord,2259,0 +redisk,2259,0 +redisc,2259,0 +recd,2259,0 +record4,2259,0 +disk4,2259,0 +disc4,2259,0 +cd4,2259,0 +4record,2259,0 +4disk,2259,0 +4disc,2259,0 +4cd,2259,0 +lightgreenmusicrecord,2260,0 +lightgreenmusicdisk,2260,0 +lightgreenmusicdisc,2260,0 +lightgreenmusiccd,2260,0 +lgreenmusicrecord,2260,0 +lgreenmusicdisk,2260,0 +lgreenmusicdisc,2260,0 +lgreenmusiccd,2260,0 +lightgrmusicrecord,2260,0 +lightgrmusicdisk,2260,0 +lightgrmusicdisc,2260,0 +lightgrmusiccd,2260,0 +farmusicrecord,2260,0 +farmusicdisk,2260,0 +farmusicdisc,2260,0 +farmusiccd,2260,0 +lgrmusicrecord,2260,0 +lgrmusicdisk,2260,0 +lgrmusicdisc,2260,0 +lgrmusiccd,2260,0 +lightgreenmrecord,2260,0 +lightgreenmdisk,2260,0 +lightgreenmdisc,2260,0 +lightgreenmcd,2260,0 +lgreenmrecord,2260,0 +lgreenmdisk,2260,0 +lgreenmdisc,2260,0 +lgreenmcd,2260,0 +lightgrmrecord,2260,0 +lightgrmdisk,2260,0 +lightgrmdisc,2260,0 +lightgrmcd,2260,0 +farmrecord,2260,0 +farmdisk,2260,0 +farmdisc,2260,0 +farmcd,2260,0 +lgrmrecord,2260,0 +lgrmdisk,2260,0 +lgrmdisc,2260,0 +lgrmcd,2260,0 +lightgreenrecord,2260,0 +lightgreendisk,2260,0 +lightgreendisc,2260,0 +lightgreencd,2260,0 +lgreenrecord,2260,0 +lgreendisk,2260,0 +lgreendisc,2260,0 +lgreencd,2260,0 +lightgrrecord,2260,0 +lightgrdisk,2260,0 +lightgrdisc,2260,0 +lightgrcd,2260,0 +farrecord,2260,0 +fardisk,2260,0 +fardisc,2260,0 +farcd,2260,0 +lgrrecord,2260,0 +lgrdisk,2260,0 +lgrdisc,2260,0 +lgrcd,2260,0 +record5,2260,0 +disk5,2260,0 +disc5,2260,0 +cd5,2260,0 +5record,2260,0 +5disk,2260,0 +5disc,2260,0 +5cd,2260,0 +purplemusicrecord,2261,0 +purplemusicdisk,2261,0 +purplemusicdisc,2261,0 +purplemusiccd,2261,0 +mallmusicrecord,2261,0 +mallmusicdisk,2261,0 +mallmusicdisc,2261,0 +mallmusiccd,2261,0 +pumusicrecord,2261,0 +pumusicdisk,2261,0 +pumusicdisc,2261,0 +pumusiccd,2261,0 +purplemrecord,2261,0 +purplemdisk,2261,0 +purplemdisc,2261,0 +purplemcd,2261,0 +mallmrecord,2261,0 +mallmdisk,2261,0 +mallmdisc,2261,0 +mallmcd,2261,0 +pumrecord,2261,0 +pumdisk,2261,0 +pumdisc,2261,0 +pumcd,2261,0 +purplerecord,2261,0 +purpledisk,2261,0 +purpledisc,2261,0 +purplecd,2261,0 +mallrecord,2261,0 +malldisk,2261,0 +malldisc,2261,0 +mallcd,2261,0 +purecord,2261,0 +pudisk,2261,0 +pudisc,2261,0 +pucd,2261,0 +record6,2261,0 +disk6,2261,0 +disc6,2261,0 +cd6,2261,0 +6record,2261,0 +6disk,2261,0 +6disc,2261,0 +6cd,2261,0 +pinkmusicrecord,2262,0 +pinkmusicdisk,2262,0 +pinkmusicdisc,2262,0 +pinkmusiccd,2262,0 +mellohimusicrecord,2262,0 +mellohimusicdisk,2262,0 +mellohimusicdisc,2262,0 +mellohimusiccd,2262,0 +pimusicrecord,2262,0 +pimusicdisk,2262,0 +pimusicdisc,2262,0 +pimusiccd,2262,0 +pinkmrecord,2262,0 +pinkmdisk,2262,0 +pinkmdisc,2262,0 +pinkmcd,2262,0 +mellohimrecord,2262,0 +mellohimdisk,2262,0 +mellohimdisc,2262,0 +mellohimcd,2262,0 +pimrecord,2262,0 +pimdisk,2262,0 +pimdisc,2262,0 +pimcd,2262,0 +pinkrecord,2262,0 +pinkdisk,2262,0 +pinkdisc,2262,0 +pinkcd,2262,0 +mellohirecord,2262,0 +mellohidisk,2262,0 +mellohidisc,2262,0 +mellohicd,2262,0 +pirecord,2262,0 +pidisk,2262,0 +pidisc,2262,0 +picd,2262,0 +record7,2262,0 +disk7,2262,0 +disc7,2262,0 +cd7,2262,0 +7record,2262,0 +7disk,2262,0 +7disc,2262,0 +7cd,2262,0 +blackmusicrecord,2263,0 +blackmusicdisk,2263,0 +blackmusicdisc,2263,0 +blackmusiccd,2263,0 +stalmusicrecord,2263,0 +stalmusicdisk,2263,0 +stalmusicdisc,2263,0 +stalmusiccd,2263,0 +blmusicrecord,2263,0 +blmusicdisk,2263,0 +blmusicdisc,2263,0 +blmusiccd,2263,0 +blackmrecord,2263,0 +blackmdisk,2263,0 +blackmdisc,2263,0 +blackmcd,2263,0 +stalmrecord,2263,0 +stalmdisk,2263,0 +stalmdisc,2263,0 +stalmcd,2263,0 +blmrecord,2263,0 +blmdisk,2263,0 +blmdisc,2263,0 +blmcd,2263,0 +blackrecord,2263,0 +blackdisk,2263,0 +blackdisc,2263,0 +blackcd,2263,0 +stalrecord,2263,0 +staldisk,2263,0 +staldisc,2263,0 +stalcd,2263,0 +blrecord,2263,0 +bldisk,2263,0 +bldisc,2263,0 +blcd,2263,0 +record8,2263,0 +disk8,2263,0 +disc8,2263,0 +cd8,2263,0 +8record,2263,0 +8disk,2263,0 +8disc,2263,0 +8cd,2263,0 +whitemusicrecord,2264,0 +whitemusicdisk,2264,0 +whitemusicdisc,2264,0 +whitemusiccd,2264,0 +stradmusicrecord,2264,0 +stradmusicdisk,2264,0 +stradmusicdisc,2264,0 +stradmusiccd,2264,0 +whmusicrecord,2264,0 +whmusicdisk,2264,0 +whmusicdisc,2264,0 +whmusiccd,2264,0 +whitemrecord,2264,0 +whitemdisk,2264,0 +whitemdisc,2264,0 +whitemcd,2264,0 +stradmrecord,2264,0 +stradmdisk,2264,0 +stradmdisc,2264,0 +stradmcd,2264,0 +whmrecord,2264,0 +whmdisk,2264,0 +whmdisc,2264,0 +whmcd,2264,0 +whiterecord,2264,0 +whitedisk,2264,0 +whitedisc,2264,0 +whitecd,2264,0 +stradrecord,2264,0 +straddisk,2264,0 +straddisc,2264,0 +stradcd,2264,0 +whrecord,2264,0 +whdisk,2264,0 +whdisc,2264,0 +whcd,2264,0 +record9,2264,0 +disk9,2264,0 +disc9,2264,0 +cd9,2264,0 +9record,2264,0 +9disk,2264,0 +9disc,2264,0 +9cd,2264,0 +darkgreenmusicrecord,2265,0 +darkgreenmusicdisk,2265,0 +darkgreenmusicdisc,2265,0 +darkgreenmusiccd,2265,0 +dgreenmusicrecord,2265,0 +dgreenmusicdisk,2265,0 +dgreenmusicdisc,2265,0 +dgreenmusiccd,2265,0 +darkgrmusicrecord,2265,0 +darkgrmusicdisk,2265,0 +darkgrmusicdisc,2265,0 +darkgrmusiccd,2265,0 +wardmusicrecord,2265,0 +wardmusicdisk,2265,0 +wardmusicdisc,2265,0 +wardmusiccd,2265,0 +dgrmusicrecord,2265,0 +dgrmusicdisk,2265,0 +dgrmusicdisc,2265,0 +dgrmusiccd,2265,0 +darkgreenmrecord,2265,0 +darkgreenmdisk,2265,0 +darkgreenmdisc,2265,0 +darkgreenmcd,2265,0 +dgreenmrecord,2265,0 +dgreenmdisk,2265,0 +dgreenmdisc,2265,0 +dgreenmcd,2265,0 +darkgrmrecord,2265,0 +darkgrmdisk,2265,0 +darkgrmdisc,2265,0 +darkgrmcd,2265,0 +wardmrecord,2265,0 +wardmdisk,2265,0 +wardmdisc,2265,0 +wardmcd,2265,0 +dgrmrecord,2265,0 +dgrmdisk,2265,0 +dgrmdisc,2265,0 +dgrmcd,2265,0 +darkgreenrecord,2265,0 +darkgreendisk,2265,0 +darkgreendisc,2265,0 +darkgreencd,2265,0 +dgreenrecord,2265,0 +dgreendisk,2265,0 +dgreendisc,2265,0 +dgreencd,2265,0 +darkgrrecord,2265,0 +darkgrdisk,2265,0 +darkgrdisc,2265,0 +darkgrcd,2265,0 +wardrecord,2265,0 +warddisk,2265,0 +warddisc,2265,0 +wardcd,2265,0 +dgrrecord,2265,0 +dgrdisk,2265,0 +dgrdisc,2265,0 +dgrcd,2265,0 +record10,2265,0 +disk10,2265,0 +disc10,2265,0 +cd10,2265,0 +10record,2265,0 +10disk,2265,0 +10disc,2265,0 +10cd,2265,0 +crackedmusicrecord,2266,0 +crackedmusicdisk,2266,0 +crackedmusicdisc,2266,0 +crackedmusiccd,2266,0 +crackmusicrecord,2266,0 +crackmusicdisk,2266,0 +crackmusicdisc,2266,0 +crackmusiccd,2266,0 +11musicrecord,2266,0 +11musicdisk,2266,0 +11musicdisc,2266,0 +11musiccd,2266,0 +cmusicrecord,2266,0 +cmusicdisk,2266,0 +cmusicdisc,2266,0 +cmusiccd,2266,0 +crackedmrecord,2266,0 +crackedmdisk,2266,0 +crackedmdisc,2266,0 +crackedmcd,2266,0 +crackmrecord,2266,0 +crackmdisk,2266,0 +crackmdisc,2266,0 +crackmcd,2266,0 +11mrecord,2266,0 +11mdisk,2266,0 +11mdisc,2266,0 +11mcd,2266,0 +cmrecord,2266,0 +cmdisk,2266,0 +cmdisc,2266,0 +cmcd,2266,0 +crackedrecord,2266,0 +crackeddisk,2266,0 +crackeddisc,2266,0 +crackedcd,2266,0 +crackrecord,2266,0 +crackdisk,2266,0 +crackdisc,2266,0 +crackcd,2266,0 +crecord,2266,0 +cdisk,2266,0 +cdisc,2266,0 +ccd,2266,0 +record11,2266,0 +disk11,2266,0 +disc11,2266,0 +cd11,2266,0 +11record,2266,0 +11disk,2266,0 +11disc,2266,0 +11cd,2266,0 +waitmusicrecord,2267,0 +waitmusicdisk,2267,0 +waitmusicdisc,2267,0 +waitmusiccd,2267,0 +bluemusicrecord,2267,0 +bluemusicdisk,2267,0 +bluemusicdisc,2267,0 +bluemusiccd,2267,0 +12musicrecord,2267,0 +12musicdisk,2267,0 +12musicdisc,2267,0 +12musiccd,2267,0 +cyanmusicrecord,2267,0 +cyanmusicdisk,2267,0 +cyanmusicdisc,2267,0 +cyanmusiccd,2267,0 +waitmrecord,2267,0 +waitmdisk,2267,0 +waitmdisc,2267,0 +waitmcd,2267,0 +bluemrecord,2267,0 +bluemdisk,2267,0 +bluemdisc,2267,0 +bluemcd,2267,0 +12mrecord,2267,0 +12mdisk,2267,0 +12mdisc,2267,0 +12mcd,2267,0 +cyanmrecord,2267,0 +cyanmdisk,2267,0 +cyanmdisc,2267,0 +cyanmcd,2267,0 +waitrecord,2267,0 +waitdisk,2267,0 +waitdisc,2267,0 +waitcd,2267,0 +bluerecord,2267,0 +bluedisk,2267,0 +bluedisc,2267,0 +bluecd,2267,0 +cyanrecord,2267,0 +cyandisk,2267,0 +cyandisc,2267,0 +cyancd,2267,0 +record12,2267,0 +disk12,2267,0 +disc12,2267,0 +cd12,2267,0 +12record,2267,0 +12disk,2267,0 +12disc,2267,0 +12cd,2267,0 diff --git a/stark/bukkit/src/main/resources/lang.json b/stark/bukkit/src/main/resources/lang.json new file mode 100644 index 0000000..c73c067 --- /dev/null +++ b/stark/bukkit/src/main/resources/lang.json @@ -0,0 +1,9 @@ +{ + "reboot": { + "broadcast-lines": [ + "", + "&c&l⚠ &eServer rebooting in {remainingTime}!", + "" + ] + } +} \ No newline at end of file diff --git a/stark/bukkit/src/main/resources/plugin.yml b/stark/bukkit/src/main/resources/plugin.yml new file mode 100644 index 0000000..1c1bc3a --- /dev/null +++ b/stark/bukkit/src/main/resources/plugin.yml @@ -0,0 +1,9 @@ +name: Stark +version: ${bukkit.desc} +author: joeleoli +main: net.evilblock.stark.Stark +depend: + - ProtocolLib +softdepend: + - Vault +load: STARTUP \ No newline at end of file diff --git a/stark/bungee/pom.xml b/stark/bungee/pom.xml new file mode 100644 index 0000000..7068b2b --- /dev/null +++ b/stark/bungee/pom.xml @@ -0,0 +1,130 @@ + + + + net.evilblock.stark + stark-parent + 1.0-SNAPSHOT + + 4.0.0 + + bungee + + + 1.3.41 + + + + + jitpack.io + https://jitpack.io + + + + + + net.evilblock.stark + core + 1.0-SNAPSHOT + compile + + + com.github.HexagonMC.BungeeCord + bungeecord-api + LATEST + system + ${project.basedir}/lib/BungeeCord.jar + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + + + Stark + src/main/kotlin + + + net.md-5 + scriptus + 0.2 + + + initialize + + git-Stark-%s + bungee.desc + + + describe + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + + compile + + + + ${project.basedir}/src/main/kotlin + + + + + + + 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 + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.0.0 + + + package + + shade + + + + + + + + \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/Stark.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/Stark.kt new file mode 100644 index 0000000..80193ed --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/Stark.kt @@ -0,0 +1,197 @@ +package net.evilblock.stark + +import nig.cock.nigger.Pidgin +import net.evilblock.stark.availability.AvailabilityHeartbeatRunnable +import net.evilblock.stark.availability.AvailabilityListeners +import net.evilblock.stark.command.HubCommand +import net.evilblock.stark.command.ReloadCommand +import net.evilblock.stark.command.SetMotdStateCommand +import net.evilblock.stark.core.StarkCore +import net.evilblock.stark.core.mongo.MongoCredentials +import net.evilblock.stark.core.profile.ProfileHandler +import net.evilblock.stark.core.redis.RedisCredentials +import net.evilblock.stark.core.uuid.UUIDCacheLoadRunnable +import net.evilblock.stark.motd.MotdHandler +import net.evilblock.stark.motd.MotdListeners +import net.evilblock.stark.profile.ProfileCheckRunnable +import net.evilblock.stark.profile.ProfileListeners +import net.evilblock.stark.profile.ProfileMessageListeners +import net.evilblock.stark.profile.ProxyProfile +import net.evilblock.stark.staff.StaffListeners +import net.evilblock.stark.staff.StaffMessageListeners +import net.evilblock.stark.sync.SyncHandler +import net.evilblock.stark.sync.SyncListeners +import net.evilblock.stark.sync.SyncMessageListeners +import net.evilblock.stark.sync.runnable.BroadcastCountRunnable +import net.evilblock.stark.sync.runnable.HeartbeatProxyRunnable +import net.evilblock.stark.whitelist.WhitelistListeners +import net.evilblock.stark.whitelist.WhitelistMessageListeners +import com.google.common.io.ByteStreams +import net.md_5.bungee.api.config.ServerInfo +import net.md_5.bungee.api.plugin.Plugin +import net.md_5.bungee.config.Configuration +import net.md_5.bungee.config.ConfigurationProvider +import net.md_5.bungee.config.YamlConfiguration +import java.io.File +import java.io.FileOutputStream +import java.util.* +import java.util.concurrent.TimeUnit + +class Stark : Plugin() { + + lateinit var configuration: Configuration + lateinit var core: StarkCore + + var syncHandler: SyncHandler = SyncHandler() + var motdHandler: MotdHandler = MotdHandler() + lateinit var proxyMessageChannel: Pidgin + + lateinit var mainThread: Thread + + override fun onEnable() { + instance = this + mainThread = Thread.currentThread() + + try { + saveDefaultConfig() + loadConfig() + + core = object : StarkCore(proxy.logger) { + val profileHandler = object : ProfileHandler() { + override fun createProfileInstance(uuid: UUID): ProxyProfile { + return ProxyProfile(uuid) + } + } + + override fun isPrimaryThread(): Boolean { + return Thread.currentThread() == mainThread + } + + override fun getProfileHandler(): ProfileHandler { + return profileHandler + } + } + + core.load(configuration.getString("TimeZone"), + getRedisCredentials("Local"), + getRedisCredentials("Backbone"), + getMongoCredentials()) + + proxy.servers.values.forEach { + core.servers.loadOrCreateServer(it.name, it.address.port) + } + + core.globalMessageChannel.registerListener(ProfileMessageListeners()) + core.globalMessageChannel.registerListener(WhitelistMessageListeners()) + + proxyMessageChannel = Pidgin("Stark:PROXY", core.redis.backboneJedisPool!!) + proxyMessageChannel.registerListener(SyncMessageListeners()) + proxyMessageChannel.registerListener(StaffMessageListeners()) + + motdHandler.loadStates() + + registerListeners() + registerCommands() + + proxy.scheduler.schedule(this, HeartbeatProxyRunnable(), 1, 1, TimeUnit.SECONDS) + proxy.scheduler.schedule(this, BroadcastCountRunnable(), 500, 500, TimeUnit.MILLISECONDS) + proxy.scheduler.schedule(this, AvailabilityHeartbeatRunnable(), 1, 5, TimeUnit.SECONDS) + proxy.scheduler.schedule(this, ProfileCheckRunnable(), 1, 3, TimeUnit.SECONDS) + proxy.scheduler.schedule(this, { proxy.scheduler.runAsync(this, UUIDCacheLoadRunnable()) }, 2, 2, TimeUnit.MINUTES) + } catch (e: Exception) { + logger.severe("An error occurred on startup") + e.printStackTrace() + proxy.stop() + } + } + + override fun onDisable() { + core.redis.close() + core.mongo.close() + } + + fun isHubServer(server: ServerInfo): Boolean { + return server.name.startsWith("Hub-") + } + + fun isRestrictedHubServer(server: ServerInfo): Boolean { + return server.name.contains("Hub", ignoreCase = true) && server.name.contains("Restricted", ignoreCase = true) + } + + private fun saveDefaultConfig() { + if (!dataFolder.exists()) { + dataFolder.mkdir() + } + + val configFile = File(dataFolder, "config.yml") + if (!configFile.exists()) { + try { + configFile.createNewFile() + getResourceAsStream("config.yml").use { `is` -> FileOutputStream(configFile).use { os -> ByteStreams.copy(`is`, os) } } + } catch (e: Throwable) { + throw RuntimeException("Unable to create configuration file", e) + } + } + } + + private fun loadConfig() { + configuration = ConfigurationProvider.getProvider(YamlConfiguration::class.java).load(File(dataFolder, "config.yml")) + } + + fun reloadConfig() { + loadConfig() + motdHandler.loadStates() + } + + private fun registerListeners() { + proxy.pluginManager.registerListener(this, SyncListeners()) + proxy.pluginManager.registerListener(this, WhitelistListeners()) + proxy.pluginManager.registerListener(this, MotdListeners()) + proxy.pluginManager.registerListener(this, AvailabilityListeners()) + proxy.pluginManager.registerListener(this, ProfileListeners()) + proxy.pluginManager.registerListener(this, StaffListeners()) + } + + private fun registerCommands() { + proxy.pluginManager.registerCommand(this, ReloadCommand()) + proxy.pluginManager.registerCommand(this, SetMotdStateCommand()) + proxy.pluginManager.registerCommand(this, HubCommand()) + } + + private fun getRedisCredentials(prefix: String): RedisCredentials { + val builder = RedisCredentials.Builder() + .host(configuration.getString("${prefix}Redis.Host")) + .port(configuration.getInt("${prefix}Redis.Port")) + + if (configuration.contains("${prefix}Redis.Password")) { + builder.password(configuration.getString("${prefix}Redis.Password")) + } + + if (configuration.contains("${prefix}Redis.DbId")) { + builder.dbId(configuration.getInt("${prefix}Redis.DbId")) + } + + return builder.build() + } + + private fun getMongoCredentials(): MongoCredentials { + val builder = MongoCredentials.Builder() + .host(configuration.getString("Mongo.Host")) + .port(configuration.getInt("Mongo.Port")) + + if (configuration.contains("Mongo.Username")) { + builder.username(configuration.getString("Mongo.Username")) + } + + if (configuration.contains("Mongo.Password")) { + builder.password(configuration.getString("Mongo.Password")) + } + + return builder.build() + } + + companion object { + lateinit var instance: Stark + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/availability/AvailabilityHeartbeatRunnable.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/availability/AvailabilityHeartbeatRunnable.kt new file mode 100644 index 0000000..a65b59f --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/availability/AvailabilityHeartbeatRunnable.kt @@ -0,0 +1,15 @@ +package net.evilblock.stark.availability + +import net.evilblock.stark.Stark + +class AvailabilityHeartbeatRunnable : Runnable { + + override fun run() { + Stark.instance.proxy.scheduler.runAsync(Stark.instance) { + Stark.instance.proxy.players.forEach { player -> + Stark.instance.core.availabilityHandler.update(player.uniqueId, true, Stark.instance.proxy.name, null) + } + } + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/availability/AvailabilityListeners.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/availability/AvailabilityListeners.kt new file mode 100644 index 0000000..a00feb9 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/availability/AvailabilityListeners.kt @@ -0,0 +1,25 @@ +package net.evilblock.stark.availability + +import net.evilblock.stark.Stark +import net.md_5.bungee.api.event.PlayerDisconnectEvent +import net.md_5.bungee.api.event.PostLoginEvent +import net.md_5.bungee.api.plugin.Listener +import net.md_5.bungee.event.EventHandler + +class AvailabilityListeners : Listener { + + @EventHandler + fun onPostLoginEvent(event: PostLoginEvent) { + Stark.instance.proxy.scheduler.runAsync(Stark.instance) { + Stark.instance.core.availabilityHandler.update(event.player.uniqueId, true, Stark.instance.proxy.name, null) + } + } + + @EventHandler + fun onPlayerDisconnectEvent(event: PlayerDisconnectEvent) { + Stark.instance.proxy.scheduler.runAsync(Stark.instance) { + Stark.instance.core.availabilityHandler.update(event.player.uniqueId, false, Stark.instance.proxy.name, null) + } + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/command/HubCommand.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/command/HubCommand.kt new file mode 100644 index 0000000..3afa8d2 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/command/HubCommand.kt @@ -0,0 +1,32 @@ +package net.evilblock.stark.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.util.ChatUtil +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.CommandSender +import net.md_5.bungee.api.chat.TextComponent +import net.md_5.bungee.api.connection.ProxiedPlayer +import net.md_5.bungee.api.plugin.Command + +class HubCommand : Command("hub", null, "lobby") { + + override fun execute(sender: CommandSender, args: Array) { + if (sender is ProxiedPlayer) { + if (args.isEmpty()) { + if (Stark.instance.isHubServer(sender.server.info)) { + sender.sendMessage("${ChatColor.RED}You're already connected to a hub server.") + return + } + + val hub = Stark.instance.syncHandler.findNextBestHubServer(sender) + + if (hub != null) { + sender.connect(hub) + } else { + sender.sendMessage("${ChatColor.RED}Couldn't find an open hub server for you to join.") + } + } + } + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/command/ReloadCommand.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/command/ReloadCommand.kt new file mode 100644 index 0000000..10fb7ad --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/command/ReloadCommand.kt @@ -0,0 +1,15 @@ +package net.evilblock.stark.command + +import net.evilblock.stark.Stark +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.CommandSender +import net.md_5.bungee.api.plugin.Command + +class ReloadCommand : Command("starkbungeerl", "stark.bungee.reload") { + + override fun execute(sender: CommandSender, args: Array) { + Stark.instance.reloadConfig() + sender.sendMessage("${ChatColor.GOLD}Reloaded stark bungee config") + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/command/SetMotdStateCommand.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/command/SetMotdStateCommand.kt new file mode 100644 index 0000000..09acf82 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/command/SetMotdStateCommand.kt @@ -0,0 +1,27 @@ +package net.evilblock.stark.command + +import net.evilblock.stark.Stark +import net.evilblock.stark.util.ChatUtil +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.CommandSender +import net.md_5.bungee.api.chat.TextComponent +import net.md_5.bungee.api.plugin.Command + +class SetMotdStateCommand : Command("setmotdstate", "stark.bungee.admin") { + + override fun execute(sender: CommandSender, args: Array) { + if (args.isEmpty()) { + sender.sendMessage(ChatUtil.compile(TextComponent.fromLegacyText("${ChatColor.RED}Usage: /setmotdstate "))) + } else { + val state = Stark.instance.motdHandler.states[args[0]] + + if (state == null) { + sender.sendMessage(ChatUtil.compile(TextComponent.fromLegacyText("${ChatColor.RED}A state by that name doesn't exist."))) + } else { + Stark.instance.motdHandler.active = state + sender.sendMessage(ChatUtil.compile(TextComponent.fromLegacyText("${ChatColor.GREEN}You've updated the MOTD state."))) + } + } + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/event/LoginProfileLoadedEvent.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/event/LoginProfileLoadedEvent.kt new file mode 100644 index 0000000..2901019 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/event/LoginProfileLoadedEvent.kt @@ -0,0 +1,21 @@ +package net.evilblock.stark.event + +import net.evilblock.stark.profile.ProxyProfile +import net.md_5.bungee.api.chat.BaseComponent +import net.md_5.bungee.api.plugin.Cancellable +import net.md_5.bungee.api.plugin.Event + +class LoginProfileLoadedEvent(val profile: ProxyProfile) : Event(), Cancellable { + + private var cancelled: Boolean = false + var cancelReasons: Array? = null + + override fun isCancelled(): Boolean { + return cancelled + } + + override fun setCancelled(cancelled: Boolean) { + this.cancelled = cancelled + } + +} diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/motd/MotdHandler.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/motd/MotdHandler.kt new file mode 100644 index 0000000..eac1348 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/motd/MotdHandler.kt @@ -0,0 +1,39 @@ +package net.evilblock.stark.motd + +import net.evilblock.stark.Stark + +class MotdHandler { + + var active: MotdState? = null + val states: MutableMap = hashMapOf() + + fun loadStates() { + states.clear() + + val config = Stark.instance.configuration + + if (config.contains("ServerList.MOTDStates")) { + for (key in config.getSection("ServerList.MOTDStates").keys) { + val state = MotdState(key, config.getString("ServerList.MOTDStates.$key.MOTD")) + + if (config.contains("ServerList.MOTDStates.$key.Countdown")) { + state.countdown = config.getLong("ServerList.MOTDStates.$key.Countdown") + state.countdownFinishMotd = config.getString("ServerList.MOTDStates.$key.CountdownFinishMOTD") + } + + states[state.name] = state + } + } + + if (config.contains("ServerList.ActiveMOTDState")) { + if (states.containsKey(config.getString("ServerList.ActiveMOTDState"))) { + active = states[config.getString("ServerList.ActiveMOTDState")]!! + } + } + + if (active == null) { + Stark.instance.logger.warning("There's no active MOTD state") + } + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/motd/MotdListeners.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/motd/MotdListeners.kt new file mode 100644 index 0000000..a15defb --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/motd/MotdListeners.kt @@ -0,0 +1,51 @@ +package net.evilblock.stark.motd + +import net.evilblock.stark.Stark +import net.evilblock.stark.core.util.TimeUtils +import net.evilblock.stark.util.ChatUtil +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.chat.ComponentBuilder +import net.md_5.bungee.api.chat.TextComponent +import net.md_5.bungee.api.event.ProxyPingEvent +import net.md_5.bungee.api.plugin.Listener +import net.md_5.bungee.event.EventHandler + +class MotdListeners : Listener { + + @EventHandler + fun onProxyPingEvent(event: ProxyPingEvent) { + val activeMotd = Stark.instance.motdHandler.active + + if (activeMotd != null) { + var textToTranslate = activeMotd.motd + + if (activeMotd.countdown != null) { + val current = System.currentTimeMillis() + val remaining = activeMotd.countdown!! - current + + if (remaining < 0) { + textToTranslate = activeMotd.countdownFinishMotd!! + } else { + textToTranslate = textToTranslate.replace("%countdown%", TimeUtils.formatIntoShortString((remaining / 1000).toInt())) + } + } + + val description = ChatUtil.color(textToTranslate) + val descriptionBuilder = ComponentBuilder("") + + description.split(ChatColor.COLOR_CHAR.toString() + "r").forEach { + descriptionBuilder.append(TextComponent.fromLegacyText(it)) + descriptionBuilder.append(TextComponent("")) + descriptionBuilder.bold(false) + descriptionBuilder.italic(false) + descriptionBuilder.obfuscated(false) + descriptionBuilder.strikethrough(false) + } + + val serverPing = event.response + serverPing.descriptionComponent = ChatUtil.compile(descriptionBuilder.create()) + event.response = serverPing + } + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/motd/MotdState.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/motd/MotdState.kt new file mode 100644 index 0000000..28734e0 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/motd/MotdState.kt @@ -0,0 +1,8 @@ +package net.evilblock.stark.motd + +data class MotdState(val name: String, val motd: String) { + + var countdown: Long? = null + var countdownFinishMotd: String? = null + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/profile/ProfileCheckRunnable.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/profile/ProfileCheckRunnable.kt new file mode 100644 index 0000000..853b37b --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/profile/ProfileCheckRunnable.kt @@ -0,0 +1,26 @@ +package net.evilblock.stark.profile + +import net.evilblock.stark.Stark +import java.util.* + +class ProfileCheckRunnable : Runnable { + + override fun run() { + val toRemove = arrayListOf() + + Stark.instance.core.getProfileHandler().profiles.keys.forEach { + if (Stark.instance.proxy.getPlayer(it) == null) { + toRemove.add(it) + } + } + + toRemove.forEach { + Stark.instance.core.getProfileHandler().profiles.remove(it) + } + + if (toRemove.isNotEmpty()) { + Stark.instance.logger.info("Removed " + toRemove.size + " cached profiles") + } + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/profile/ProfileListeners.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/profile/ProfileListeners.kt new file mode 100644 index 0000000..16388f3 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/profile/ProfileListeners.kt @@ -0,0 +1,39 @@ +package net.evilblock.stark.profile + +import net.evilblock.stark.Stark +import net.evilblock.stark.event.LoginProfileLoadedEvent +import net.md_5.bungee.api.event.LoginEvent +import net.md_5.bungee.api.event.PlayerDisconnectEvent +import net.md_5.bungee.api.event.PostLoginEvent +import net.md_5.bungee.api.plugin.Listener +import net.md_5.bungee.event.EventHandler + +class ProfileListeners : Listener { + + @EventHandler(priority = 99) + fun onLoginEvent(event: LoginEvent) { + if (!event.isCancelled) { + val profile = Stark.instance.core.getProfileHandler().loadProfile(event.connection.uniqueId) + Stark.instance.core.getProfileHandler().profiles[profile.uuid] = profile + + val profileEvent = LoginProfileLoadedEvent(profile) + Stark.instance.proxy.pluginManager.callEvent(profileEvent) + + if (profileEvent.isCancelled) { + event.isCancelled = true + event.setCancelReason(*profileEvent.cancelReasons!!) + } + } + } + + @EventHandler(priority = 0) + fun onPostLoginEvent(event: PostLoginEvent) { + Stark.instance.core.getProfileHandler().profiles[event.player.uniqueId]!!.apply() + } + + @EventHandler(priority = 99) + fun onPlayerDisconnectEvent(event: PlayerDisconnectEvent) { + Stark.instance.core.getProfileHandler().profiles.remove(event.player.uniqueId) + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/profile/ProfileMessageListeners.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/profile/ProfileMessageListeners.kt new file mode 100644 index 0000000..4bb3411 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/profile/ProfileMessageListeners.kt @@ -0,0 +1,25 @@ +package net.evilblock.stark.profile + +import nig.cock.nigger.message.handler.IncomingMessageHandler +import nig.cock.nigger.message.listener.MessageListener +import net.evilblock.stark.Stark +import com.google.gson.JsonObject +import java.util.* + +class ProfileMessageListeners : MessageListener { + + @IncomingMessageHandler("GRANT_UPDATE") + fun onGrantUpdate(data: JsonObject) { + val uuid = UUID.fromString(data.get("uuid").asString) + val profile = Stark.instance.core.getProfileHandler().pullProfileUpdates(uuid) + + profile.apply() + } + + @IncomingMessageHandler("PUNISHMENT_UPDATE") + fun onPunishmentUpdate(data: JsonObject) { + val uuid = UUID.fromString(data.get("uuid").asString) + Stark.instance.core.getProfileHandler().pullProfileUpdates(uuid) + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/profile/ProxyProfile.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/profile/ProxyProfile.kt new file mode 100644 index 0000000..d190ba8 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/profile/ProxyProfile.kt @@ -0,0 +1,23 @@ +package net.evilblock.stark.profile + +import net.evilblock.stark.Stark +import net.evilblock.stark.core.profile.Profile +import java.util.* + +class ProxyProfile(uuid: UUID) : Profile(uuid) { + + override fun apply() { + val player = Stark.instance.proxy.getPlayer(uuid) + + if (player != null) { + getCompoundedPermissions().forEach { + if (it.startsWith("-")) { + player.setPermission(it.substring(1), false) + } else { + player.setPermission(it, true) + } + } + } + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/staff/StaffAction.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/staff/StaffAction.kt new file mode 100644 index 0000000..13641e1 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/staff/StaffAction.kt @@ -0,0 +1,7 @@ +package net.evilblock.stark.staff + +enum class StaffAction { + JOIN_NETWORK, + LEAVE_NETWORK, + SWITCH_SERVER +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/staff/StaffListeners.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/staff/StaffListeners.kt new file mode 100644 index 0000000..679d76b --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/staff/StaffListeners.kt @@ -0,0 +1,75 @@ +package net.evilblock.stark.staff + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.md_5.bungee.api.connection.Server +import net.md_5.bungee.api.event.PlayerDisconnectEvent +import net.md_5.bungee.api.event.ServerConnectEvent +import net.md_5.bungee.api.event.ServerSwitchEvent +import net.md_5.bungee.api.plugin.Listener +import net.md_5.bungee.event.EventHandler +import java.util.* + +class StaffListeners : Listener { + + private val previousServer: MutableMap = hashMapOf() + private val currentServer: MutableMap = hashMapOf() + + @EventHandler(priority = 0) + fun onPlayerDisconnectEvent(event: PlayerDisconnectEvent) { + previousServer.remove(event.player.uniqueId) + + val from = currentServer.remove(event.player.uniqueId) + + if (from != null) { + if (event.player.hasPermission("stark.staff")) { + val profile = Stark.instance.core.getProfileHandler().profiles[event.player.uniqueId]!! + + val data = mapOf( + "playerName" to profile.getPlayerListName(), + "action" to StaffAction.LEAVE_NETWORK.name, + "from" to from.info.name + ) + + Stark.instance.proxyMessageChannel.sendMessage(Message("STAFF_ACTION", data)) + } + } + } + + @EventHandler + fun onServerSwitchEvent(event: ServerSwitchEvent) { + if (event.player.hasPermission("stark.staff")) { + currentServer[event.player.uniqueId] = event.player.server + + val profile = Stark.instance.core.getProfileHandler().profiles[event.player.uniqueId]!! + val previousServer = previousServer[event.player.uniqueId] + + if (previousServer == null) { + val data = hashMapOf( + "playerName" to profile.getPlayerListName(), + "action" to StaffAction.JOIN_NETWORK.name, + "to" to event.player.server.info.name + ) + + Stark.instance.proxyMessageChannel.sendMessage(Message("STAFF_ACTION", data)) + } else { + val data = hashMapOf( + "playerName" to profile.getPlayerListName(), + "action" to StaffAction.SWITCH_SERVER.name, + "from" to previousServer.info.name, + "to" to event.player.server.info.name + ) + + Stark.instance.proxyMessageChannel.sendMessage(Message("STAFF_ACTION", data)) + } + } + } + + @EventHandler + fun onServerConnectEvent(event: ServerConnectEvent) { + if (event.player.hasPermission("stark.staff")) { + previousServer[event.player.uniqueId] = event.player.server + } + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/staff/StaffMessageListeners.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/staff/StaffMessageListeners.kt new file mode 100644 index 0000000..8cab5da --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/staff/StaffMessageListeners.kt @@ -0,0 +1,60 @@ +package net.evilblock.stark.staff + +import nig.cock.nigger.message.handler.IncomingMessageHandler +import nig.cock.nigger.message.listener.MessageListener +import net.evilblock.stark.Stark +import com.google.gson.JsonObject +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.chat.TextComponent + +class StaffMessageListeners : MessageListener { + + private val PREFIX = "${ChatColor.BLUE}${ChatColor.BOLD}[Staff] ${ChatColor.RESET}" + + @IncomingMessageHandler("STAFF_ACTION") + fun onStaffActionMessage(json: JsonObject) { + val playerName = ChatColor.translateAlternateColorCodes('&', json.get("playerName").asString) + + when (StaffAction.valueOf(json.get("action").asString)) { + StaffAction.JOIN_NETWORK -> { + val to = json.get("to").asString + + Stark.instance.proxy.players.forEach { player -> + if (player.hasPermission("stark.staff")) { + player.sendMessage(*TextComponent.fromLegacyText("$PREFIX$playerName ${ChatColor.GREEN}joined ${ChatColor.AQUA}the network ($to)")) + } + } + } + StaffAction.LEAVE_NETWORK -> { + val from = json.get("from").asString + + Stark.instance.proxy.players.forEach { player -> + if (player.hasPermission("stark.staff")) { + if (player.server.info.name == from) { + player.sendMessage(*TextComponent.fromLegacyText("$PREFIX$playerName ${ChatColor.RED}left ${ChatColor.AQUA}the network (from your server)")) + } else { + player.sendMessage(*TextComponent.fromLegacyText("$PREFIX$playerName ${ChatColor.RED}left ${ChatColor.AQUA}the network (from $from)")) + } + } + } + } + StaffAction.SWITCH_SERVER -> { + val from = json.get("from").asString + val to = json.get("to").asString + + Stark.instance.proxy.players.forEach { player -> + if (player.hasPermission("stark.staff")) { + if (player.server.info.name == to && player.name != ChatColor.stripColor(playerName)) { + player.sendMessage(*TextComponent.fromLegacyText("$PREFIX$playerName ${ChatColor.AQUA}joined your server (from $from)")) + } else if (player.server.info.name == from) { + player.sendMessage(*TextComponent.fromLegacyText("$PREFIX$playerName ${ChatColor.AQUA}left your server (to $to)")) + } else { + player.sendMessage(*TextComponent.fromLegacyText("$PREFIX$playerName ${ChatColor.AQUA}joined $to (from $from)")) + } + } + } + } + } + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/SyncHandler.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/SyncHandler.kt new file mode 100644 index 0000000..b131225 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/SyncHandler.kt @@ -0,0 +1,72 @@ +package net.evilblock.stark.sync + +import nig.cock.nigger.message.Message +import net.evilblock.stark.Stark +import net.evilblock.stark.core.server.Server +import net.md_5.bungee.api.config.ServerInfo +import net.md_5.bungee.api.connection.ProxiedPlayer + +class SyncHandler { + + val proxies: MutableMap = hashMapOf() + + fun getGlobalOnlineCount(): Int { + return proxies.values.sumBy { proxy -> + return if (proxy.isOnline()) { + proxy.online + } else { + 0 + } + } + Stark.instance.proxy.onlineCount + } + + fun broadcastGlobalOnlineCount() { + Stark.instance.core.globalMessageChannel.sendMessage(Message("GLOBAL_COUNT", mapOf("globalCount" to getGlobalOnlineCount().toString()))) + } + + fun heartbeatProxy() { + val data = mapOf( + "proxy" to Stark.instance.proxy.name, + "online" to Stark.instance.proxy.onlineCount + ) + + Stark.instance.proxyMessageChannel.sendMessage(Message("PROXY_HEARTBEAT", data)) + } + + fun findNextBestHubServer(proxiedPlayer: ProxiedPlayer): ServerInfo? { + val hubServers = arrayListOf() + + for (server in Stark.instance.proxy.servers.values) { + if (Stark.instance.isRestrictedHubServer(server)) { + if (proxiedPlayer.hasPermission("stark.bungee.restricted-hub")) { + val optionalServer = Stark.instance.core.servers.getServerByName("Restricted-Hub") + + if (optionalServer.isPresent) { + val restrictedHub = optionalServer.get() + + if (restrictedHub.isOnline() && restrictedHub.playerCount < restrictedHub.maxSlots) { + return Stark.instance.proxy.getServerInfo("Restricted-Hub") + } + } + } + } else if (Stark.instance.isHubServer(server)) { + val optionalServer = Stark.instance.core.servers.getServerByName(server.name) + + if (optionalServer.isPresent) { + val hub = optionalServer.get() + + if (hub.isOnline() && hub.playerCount < hub.maxSlots) { + hubServers.add(hub) + } + } + } + } + + if (hubServers.isEmpty()) { + return null + } + + return Stark.instance.proxy.getServerInfo(hubServers.sortedBy { server -> server.playerCount }.reversed().first().serverName) + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/SyncListeners.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/SyncListeners.kt new file mode 100644 index 0000000..aba6987 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/SyncListeners.kt @@ -0,0 +1,64 @@ +package net.evilblock.stark.sync + +import net.evilblock.stark.Stark +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.ServerPing +import net.md_5.bungee.api.chat.TextComponent +import net.md_5.bungee.api.event.* +import net.md_5.bungee.api.plugin.Listener +import net.md_5.bungee.event.EventHandler + +class SyncListeners : Listener { + + @EventHandler + fun onProxyPingEvent(event: ProxyPingEvent) { + val serverPing = event.response + serverPing.players = ServerPing.Players(1000, Stark.instance.syncHandler.getGlobalOnlineCount(), arrayOf()) + } + + @EventHandler + fun onServerConnectEvent(event: ServerConnectEvent) { + val player = event.player + val server = player.server + val target = event.target + + if ((player.server == null || !Stark.instance.isHubServer(server.info)) && Stark.instance.isHubServer(target)) { + val hub = Stark.instance.syncHandler.findNextBestHubServer(player) + + if (hub != null) { + player.sendMessage("${ChatColor.GOLD}Sending you to ${hub.name}...") + event.target = hub + } + } + } + + @EventHandler + fun onServerKickEvent(event: ServerKickEvent) { + val player = event.player + val reason = TextComponent.toPlainText(*event.kickReasonComponent) + val from = event.kickedFrom + + if (player.server == null) { + return + } + + if (from == player.server.info) { + for (regex in kickRegexes) { + if (reason.matches(regex.toRegex())) { + val hub = Stark.instance.syncHandler.findNextBestHubServer(player) + + if (hub != null) { + player.sendMessage("${ChatColor.GOLD}Sending you to ${hub.name}...") + event.isCancelled = true + event.cancelServer = hub + } + } + } + } + } + + companion object { + private val kickRegexes = arrayListOf("You have been kicked", "Server is restarting", "Server closed", "Internal Exception") + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/SyncMessageListeners.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/SyncMessageListeners.kt new file mode 100644 index 0000000..217dd61 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/SyncMessageListeners.kt @@ -0,0 +1,23 @@ +package net.evilblock.stark.sync + +import nig.cock.nigger.message.handler.IncomingMessageHandler +import nig.cock.nigger.message.listener.MessageListener +import net.evilblock.stark.Stark +import com.google.gson.JsonObject + +class SyncMessageListeners : MessageListener { + + @IncomingMessageHandler("PROXY_HEARTBEAT") + fun onProxyHeartbeatMessage(json: JsonObject) { + val proxyId = json["proxy"].asString + + if (Stark.instance.proxy.name !== proxyId) { + val syncedProxy = Stark.instance.syncHandler.proxies[proxyId]?: SyncedProxy(proxyId) + syncedProxy.online = json["online"].asInt + syncedProxy.lastUpdate = System.currentTimeMillis() + + Stark.instance.syncHandler.proxies[syncedProxy.proxyId] = syncedProxy + } + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/SyncedProxy.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/SyncedProxy.kt new file mode 100644 index 0000000..cde03b0 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/SyncedProxy.kt @@ -0,0 +1,9 @@ +package net.evilblock.stark.sync + +data class SyncedProxy(val proxyId: String, var online: Int = 0, var lastUpdate: Long = System.currentTimeMillis()) { + + fun isOnline(): Boolean { + return System.currentTimeMillis() - lastUpdate < 5000 + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/runnable/BroadcastCountRunnable.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/runnable/BroadcastCountRunnable.kt new file mode 100644 index 0000000..4cb607a --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/runnable/BroadcastCountRunnable.kt @@ -0,0 +1,13 @@ +package net.evilblock.stark.sync.runnable + +import net.evilblock.stark.Stark + +class BroadcastCountRunnable : Runnable { + + override fun run() { + Stark.instance.proxy.scheduler.runAsync(Stark.instance) { + Stark.instance.syncHandler.broadcastGlobalOnlineCount() + } + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/runnable/HeartbeatProxyRunnable.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/runnable/HeartbeatProxyRunnable.kt new file mode 100644 index 0000000..76d9e34 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/sync/runnable/HeartbeatProxyRunnable.kt @@ -0,0 +1,13 @@ +package net.evilblock.stark.sync.runnable + +import net.evilblock.stark.Stark + +class HeartbeatProxyRunnable : Runnable { + + override fun run() { + Stark.instance.proxy.scheduler.runAsync(Stark.instance) { + Stark.instance.syncHandler.heartbeatProxy() + } + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/util/ChatUtil.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/util/ChatUtil.kt new file mode 100644 index 0000000..24610ee --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/util/ChatUtil.kt @@ -0,0 +1,24 @@ +package net.evilblock.stark.util + +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.chat.BaseComponent + +object ChatUtil { + + fun color(string: String): String { + return ChatColor.translateAlternateColorCodes('&', string) + } + + fun compile(components: Array): BaseComponent { + val root = components[0] + var current = components[0] + + for (i in 1 until components.size) { + current.addExtra(components[i]) + current = components[i] + } + + return root + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/whitelist/WhitelistListeners.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/whitelist/WhitelistListeners.kt new file mode 100644 index 0000000..ebff540 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/whitelist/WhitelistListeners.kt @@ -0,0 +1,46 @@ +package net.evilblock.stark.whitelist + +import net.evilblock.stark.Stark +import net.evilblock.stark.core.whitelist.WhitelistType +import net.evilblock.stark.event.LoginProfileLoadedEvent +import net.evilblock.stark.util.ChatUtil +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.ServerPing +import net.md_5.bungee.api.chat.TextComponent +import net.md_5.bungee.api.event.PostLoginEvent +import net.md_5.bungee.api.event.ProxyPingEvent +import net.md_5.bungee.api.plugin.Listener +import net.md_5.bungee.event.EventHandler + +class WhitelistListeners : Listener { + + @EventHandler + fun onProxyPingEvent(event: ProxyPingEvent) { + val serverPing = event.response + + if (Stark.instance.core.whitelist.modeType == WhitelistType.MAINTENANCE) { + serverPing.version = ServerPing.Protocol("Whitelisted", 1337) + } + } + + @EventHandler + fun onLoginProfileLoadedEvent(event: LoginProfileLoadedEvent) { + val modeType = Stark.instance.core.whitelist.modeType + + val hasWhitelist = Stark.instance.core.whitelist.getWhitelist(event.profile.uuid).isAboveOrEqual(modeType) + val hasPermission = event.profile.getCompoundedPermissions().contains(modeType.getPermission()) + + if (!hasWhitelist && !hasPermission) { + event.isCancelled = true + event.cancelReasons = arrayOf(ChatUtil.compile(TextComponent.fromLegacyText(ChatColor.GOLD.toString() + modeType.disallowMessage))) + } + } + + @EventHandler + fun onPostLoginEvent(event: PostLoginEvent) { + if (Stark.instance.core.whitelist.modeType == WhitelistType.MAINTENANCE) { + event.player.sendMessage("${ChatColor.GOLD}The network is in maintenance mode, but you were able to join because of your rank.") + } + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/kotlin/net/evilblock/stark/whitelist/WhitelistMessageListeners.kt b/stark/bungee/src/main/kotlin/net/evilblock/stark/whitelist/WhitelistMessageListeners.kt new file mode 100644 index 0000000..9ca9668 --- /dev/null +++ b/stark/bungee/src/main/kotlin/net/evilblock/stark/whitelist/WhitelistMessageListeners.kt @@ -0,0 +1,41 @@ +package net.evilblock.stark.whitelist + +import nig.cock.nigger.message.handler.IncomingMessageHandler +import nig.cock.nigger.message.listener.MessageListener +import net.evilblock.stark.Stark +import net.evilblock.stark.core.whitelist.WhitelistType +import com.google.gson.JsonObject +import java.util.* +import java.util.concurrent.TimeUnit + +class WhitelistMessageListeners : MessageListener { + + @IncomingMessageHandler("WHITELIST_UPDATE") + fun onWhitelistUpdate(data: JsonObject) { + Stark.instance.core.whitelist.setMode(WhitelistType.valueOf(data.get("mode").asString), false) + + for (player in Stark.instance.proxy.players) { + val hasWhitelist = Stark.instance.core.whitelist.getWhitelist(player.uniqueId).isAboveOrEqual(Stark.instance.core.whitelist.modeType) + val hasPermission = player.hasPermission(Stark.instance.core.whitelist.modeType.getPermission()) + + if (!hasWhitelist && !hasPermission) { + Stark.instance.proxy.scheduler.schedule(Stark.instance, { + player.disconnect(Stark.instance.core.whitelist.modeType.disallowMessage) + }, 1L, TimeUnit.MILLISECONDS) + } + } + } + + @IncomingMessageHandler("VERIFIED_UPDATE") + fun onVerifiedUpdate(data: JsonObject) { + val status = data.get("status").asBoolean + val playerUuid = UUID.fromString(data.get("playerUuid").asString) + + if (status) { + Stark.instance.core.whitelist.verified.add(playerUuid) + } else { + Stark.instance.core.whitelist.verified.remove(playerUuid) + } + } + +} \ No newline at end of file diff --git a/stark/bungee/src/main/resources/config.yml b/stark/bungee/src/main/resources/config.yml new file mode 100644 index 0000000..cc898ed --- /dev/null +++ b/stark/bungee/src/main/resources/config.yml @@ -0,0 +1,25 @@ +Mongo: + Host: "127.0.0.1" + Port: 27017 + Username: "admin" + Password: "" +LocalRedis: + Host: "localhost" + Port: 6379 + Password: "" + DbId: 1 +BackboneRedis: + Host: "localhost" + Port: 6379 + Password: "" + DbId: 0 +TimeZone: "EST" +ServerList: + ActiveMOTDState: "Release" + MOTDStates: + Staging: + MOTD: "" + Release: + MOTD: "" + Countdown: 1565463600000 + CountdownFinishMOTD: "" \ No newline at end of file diff --git a/stark/bungee/src/main/resources/plugin.yml b/stark/bungee/src/main/resources/plugin.yml new file mode 100644 index 0000000..448baf3 --- /dev/null +++ b/stark/bungee/src/main/resources/plugin.yml @@ -0,0 +1,4 @@ +name: Stark +version: ${bungee.desc} +author: joeleoli +main: net.evilblock.stark.Stark \ No newline at end of file diff --git a/stark/core/pom.xml b/stark/core/pom.xml new file mode 100644 index 0000000..b7314af --- /dev/null +++ b/stark/core/pom.xml @@ -0,0 +1,158 @@ + + + + net.evilblock.stark + stark-parent + 1.0-SNAPSHOT + + 4.0.0 + + core + + + 1.3.41 + + + + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + provided + + + + net.evilblock + pidgin + 1.0 + compile + + + + org.mongodb + mongo-java-driver + 3.10.2 + + + + com.google.code.gson + gson + 2.8.5 + compile + + + + redis.clients + jedis + 3.1.0-m1 + + + + com.squareup.okhttp + okhttp + 2.7.5 + + + + org.apache.commons + commons-lang3 + 3.9 + compile + + + + commons-io + commons-io + 2.6 + compile + + + + com.google.guava + guava + 28.0-jre + compile + + + + + stark-${project.version} + src/main/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + + ${project.basedir}/src/main/kotlin + src/main/kotlin + + + + + test-compile + test-compile + + + ${project.basedir}/src/test/kotlin + + + + + + + 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 + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.6 + + + make-assembly + package + single + + + jar-with-dependencies + + + + + + + + + \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/StarkCore.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/StarkCore.kt new file mode 100644 index 0000000..6a14d31 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/StarkCore.kt @@ -0,0 +1,68 @@ +package net.evilblock.stark.core + +import nig.cock.nigger.Pidgin +import net.evilblock.stark.core.availability.AvailabilityHandler +import net.evilblock.stark.core.mongo.Mongo +import net.evilblock.stark.core.mongo.MongoCredentials +import net.evilblock.stark.core.profile.Profile +import net.evilblock.stark.core.profile.ProfileHandler +import net.evilblock.stark.core.rank.RankHandler +import net.evilblock.stark.core.rank.RankMessageListeners +import net.evilblock.stark.core.redis.Redis +import net.evilblock.stark.core.redis.RedisCredentials +import net.evilblock.stark.core.server.ServerMessageListeners +import net.evilblock.stark.core.server.Servers +import net.evilblock.stark.core.uuid.UUIDCache +import net.evilblock.stark.core.whitelist.Whitelist +import java.util.* +import java.util.logging.Logger + +abstract class StarkCore(val logger: Logger) { + + lateinit var redis: Redis + lateinit var mongo: Mongo + + val uuidCache: UUIDCache = UUIDCache() + val servers: Servers = Servers() + val rankHandler: RankHandler = RankHandler() + val availabilityHandler: AvailabilityHandler = AvailabilityHandler() + val whitelist: Whitelist = Whitelist() + + lateinit var globalMessageChannel: Pidgin + + init { + instance = this + } + + fun load(timezone: String, localRedisCredentials: RedisCredentials, backboneRedisCredentials: RedisCredentials, mongoCredentials: MongoCredentials) { + TimeZone.setDefault(TimeZone.getTimeZone(timezone)) + + redis = Redis() + redis.load(localRedisCredentials, backboneRedisCredentials) + + mongo = Mongo("stark") + mongo.load(mongoCredentials) + + uuidCache.load() + servers.initialLoad() + rankHandler.load() + whitelist.load() + + getProfileHandler().load() + + globalMessageChannel = Pidgin("Stark:ALL", redis.backboneJedisPool!!) + globalMessageChannel.registerListener(ServerMessageListeners) + globalMessageChannel.registerListener(RankMessageListeners) + } + + abstract fun isPrimaryThread(): Boolean + + abstract fun getProfileHandler(): ProfileHandler + + companion object { + lateinit var instance: StarkCore<*> + + const val COLOR_CODE_CHAR = '§' + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/availability/AvailabilityData.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/availability/AvailabilityData.kt new file mode 100644 index 0000000..c81575c --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/availability/AvailabilityData.kt @@ -0,0 +1,9 @@ +package net.evilblock.stark.core.availability + +data class AvailabilityData(val online: Boolean = false, val proxyId: String = "unknown", val serverId: String = "unknown", val lastTime: Long = -1) { + + fun isOnline(): Boolean { + return online && System.currentTimeMillis() - lastTime <= 3_000L + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/availability/AvailabilityHandler.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/availability/AvailabilityHandler.kt new file mode 100644 index 0000000..59590d2 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/availability/AvailabilityHandler.kt @@ -0,0 +1,39 @@ +package net.evilblock.stark.core.availability + +import net.evilblock.stark.core.StarkCore +import java.util.* + +class AvailabilityHandler { + + fun fetch(uuid: UUID): AvailabilityData { + return StarkCore.instance.redis.runBackboneRedisCommand { redis -> + if (!redis.exists("stark:availability:$uuid")) { + AvailabilityData() + } + + val data = redis.hgetAll("stark:availability:$uuid") + + AvailabilityData(data["online"]!!.toBoolean(), data["proxy"]!!, data["server"]!!, data["lastTime"]!!.toLong()) + } + } + + fun update(uuid: UUID, online: Boolean, proxy: String?, server: String?) { + val data = hashMapOf() + data["online"] = online.toString() + + if (proxy != null) { + data["proxy"] = proxy + } + + if (server != null) { + data["server"] = server + } + + data["lastTime"] = System.currentTimeMillis().toString() + + StarkCore.instance.redis.runBackboneRedisCommand { redis -> + redis.hmset("stark:availability:$uuid", data) + } + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/config/ConfigEntry.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/config/ConfigEntry.kt new file mode 100644 index 0000000..72694b7 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/config/ConfigEntry.kt @@ -0,0 +1,36 @@ +package net.evilblock.stark.core.config + +import org.apache.commons.lang3.StringUtils +import java.lang.UnsupportedOperationException + +abstract class ConfigEntry(private val transformer: ConfigEntryTransformer, private val key: String) { + + private var cached: T? = null + + abstract fun getCachedObject(): Map + + fun get(): T? { + if (cached != null) { + return cached + } + + cached = transformer.transform(getRecursive(getCachedObject(), key) as Any) + + return cached + } + + private fun getRecursive(node: Map, key: String): T? { + val splitPath = key.split(".").toTypedArray() + + return if (node.containsKey(splitPath[0])) { + if (splitPath.size > 1) { + if (node[splitPath[0]] is Map<*, *>) { + getRecursive(node[splitPath[0]] as Map, StringUtils.join(splitPath, '.', 1, splitPath.size)) + } else { + throw UnsupportedOperationException("Recursive node key is not a map") + } + } else node[splitPath[0]] as T? + } else null + } + +} diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/config/ConfigEntryTransformer.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/config/ConfigEntryTransformer.kt new file mode 100644 index 0000000..3115bd3 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/config/ConfigEntryTransformer.kt @@ -0,0 +1,21 @@ +package net.evilblock.stark.core.config + +import net.evilblock.stark.core.config.transform.CastTransformer +import net.evilblock.stark.core.config.transform.StringListTransformer +import net.evilblock.stark.core.config.transform.StringTransformer + +interface ConfigEntryTransformer { + + fun transform(original: Any): R? + + companion object { + + val STRING: ConfigEntryTransformer = StringTransformer() + val STRING_LIST: ConfigEntryTransformer> = StringListTransformer() + val BOOLEAN: ConfigEntryTransformer = CastTransformer(Boolean::class.java) + val INTEGER: ConfigEntryTransformer = CastTransformer(Int::class.java) + val DOUBLE: ConfigEntryTransformer = CastTransformer(Double::class.java) + + } + +} diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/config/ConfigReader.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/config/ConfigReader.kt new file mode 100644 index 0000000..06dd5d4 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/config/ConfigReader.kt @@ -0,0 +1,27 @@ +package net.evilblock.stark.core.config + +import com.google.common.io.Files +import com.google.gson.GsonBuilder +import com.google.gson.reflect.TypeToken +import java.io.File + +object ConfigReader { + + private val gson = GsonBuilder().setPrettyPrinting().create() + + fun readJsonToMap(file: File): Map? { + var map: Map? = null + + try { + Files.newReader(file, Charsets.UTF_8).use { reader -> + val type = object : TypeToken>() {}.type + map = gson.fromJson(reader, type) + } + } catch (e: Exception) { + e.printStackTrace() + } + + return map + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/config/transform/CastTransformer.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/config/transform/CastTransformer.kt new file mode 100644 index 0000000..65a6048 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/config/transform/CastTransformer.kt @@ -0,0 +1,11 @@ +package net.evilblock.stark.core.config.transform + +import net.evilblock.stark.core.config.ConfigEntryTransformer + +class CastTransformer(private val type: Class) : ConfigEntryTransformer { + + override fun transform(original: Any): T { + return type.cast(original) + } + +} diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/config/transform/StringListTransformer.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/config/transform/StringListTransformer.kt new file mode 100644 index 0000000..b53a84a --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/config/transform/StringListTransformer.kt @@ -0,0 +1,21 @@ +package net.evilblock.stark.core.config.transform + +import net.evilblock.stark.core.StarkCore +import java.util.ArrayList +import net.evilblock.stark.core.config.ConfigEntryTransformer + +class StringListTransformer : ConfigEntryTransformer> { + + override fun transform(original: Any): List { + val newList = ArrayList() + + if (original is List<*>) { + for (element in original) { + newList.add(element.toString().replace('&', StarkCore.COLOR_CODE_CHAR)) + } + } + + return newList + } + +} diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/config/transform/StringTransformer.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/config/transform/StringTransformer.kt new file mode 100644 index 0000000..a9da981 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/config/transform/StringTransformer.kt @@ -0,0 +1,16 @@ +package net.evilblock.stark.core.config.transform + +import net.evilblock.stark.core.StarkCore +import net.evilblock.stark.core.config.ConfigEntryTransformer + +class StringTransformer : ConfigEntryTransformer { + + override fun transform(original: Any): String { + return if (original is String) { + original.replace('&', StarkCore.COLOR_CODE_CHAR) + } else { + "" + } + } + +} diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/grant/Grant.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/grant/Grant.kt new file mode 100644 index 0000000..5ede2fa --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/grant/Grant.kt @@ -0,0 +1,15 @@ +package net.evilblock.stark.core.grant + +import java.util.* + +open class Grant(val uuid: UUID = UUID.randomUUID()) { + + lateinit var reason: String + var issuedBy: UUID? = null + var issuedAt: Long = -1 + var expiresAt: Long? = null + var removalReason: String?= null + var removedBy: UUID?= null + var removedAt: Long?= null + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/grant/GrantSerializer.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/grant/GrantSerializer.kt new file mode 100644 index 0000000..9d7f1e3 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/grant/GrantSerializer.kt @@ -0,0 +1,43 @@ +package net.evilblock.stark.core.grant + +import org.bson.Document +import java.util.* + +object GrantSerializer { + + @JvmStatic + fun serialize(document: Document, grant: Grant): Document { + document["id"] = grant.uuid.toString() + document["reason"] = grant.reason + document["issuedBy"] = if (grant.issuedBy != null) grant.issuedBy.toString() else null + document["issuedAt"] = grant.issuedAt + document["expiresAt"] = grant.expiresAt + document["removalReason"] = grant.removalReason + document["removedBy"] = if (grant.removedBy != null) grant.removedBy!!.toString() else null + document["removedAt"] = grant.removedAt + return document + } + + @JvmStatic + fun deserialize(document: Document, grant: T): T { + grant.reason = document.getString("reason") + grant.issuedAt = document.getLong("issuedAt")!! + + val addedBy = document.getString("issuedBy") + if (addedBy != null) { + grant.issuedBy = UUID.fromString(addedBy) + } + + grant.expiresAt = document.getLong("expiresAt") + grant.removalReason = document.getString("removalReason") + grant.removedAt = document.getLong("removedAt") + + val removedBy = document.getString("removedBy") + if (removedBy != null) { + grant.removedBy = UUID.fromString(removedBy) + } + + return grant + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/mongo/Mongo.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/mongo/Mongo.kt new file mode 100644 index 0000000..d456ac0 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/mongo/Mongo.kt @@ -0,0 +1,31 @@ +package net.evilblock.stark.core.mongo + +import com.mongodb.MongoClient +import com.mongodb.MongoClientOptions +import com.mongodb.MongoCredential +import com.mongodb.ServerAddress +import java.io.Closeable + +class Mongo(private val dbName: String) : Closeable { + + lateinit var client: MongoClient + lateinit var database: com.mongodb.client.MongoDatabase + + fun load(credentials: MongoCredentials) { + client = if (credentials.shouldAuthenticate()) { + val serverAddress = ServerAddress(credentials.host, credentials.port) + val credential = MongoCredential.createCredential(credentials.username!!, "admin", credentials.password!!.toCharArray()) + + MongoClient(serverAddress, credential, MongoClientOptions.builder().build()) + } else { + MongoClient(credentials.host, credentials.port) + } + + database = client.getDatabase(dbName) + } + + override fun close() { + client.close() + } + +} diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/mongo/MongoCredentials.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/mongo/MongoCredentials.kt new file mode 100644 index 0000000..7e8c497 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/mongo/MongoCredentials.kt @@ -0,0 +1,41 @@ +package net.evilblock.stark.core.mongo + +data class MongoCredentials( + var host: String = "127.0.0.1", + var port: Int = 27017, + var username: String? = null, + var password: String? = null) { + + fun shouldAuthenticate(): Boolean { + return username != null && (password != null && password!!.isNotEmpty() && password!!.isNotBlank()) + } + + class Builder { + private val credentials: MongoCredentials = MongoCredentials() + + fun host(host: String): Builder { + credentials.host = host + return this + } + + fun port(port: Int): Builder { + credentials.port = port + return this + } + + fun username(username: String): Builder { + credentials.username = username + return this + } + + fun password(password: String): Builder { + credentials.password = password + return this + } + + fun build(): MongoCredentials { + return credentials + } + } + +} diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/Profile.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/Profile.kt new file mode 100644 index 0000000..52d7ab5 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/Profile.kt @@ -0,0 +1,115 @@ +package net.evilblock.stark.core.profile + +import net.evilblock.stark.core.StarkCore +import net.evilblock.stark.core.profile.grant.ProfileGrant +import net.evilblock.stark.core.profile.punishment.ProfilePunishment +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentType +import net.evilblock.stark.core.profile.grant.ProfileGrantSerializer +import net.evilblock.stark.core.profile.lookup.GeoInfo +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentSerializer +import net.evilblock.stark.core.rank.Rank +import org.bson.Document +import java.util.* +import kotlin.collections.ArrayList +import kotlin.collections.HashSet + +abstract class Profile(val uuid: UUID) { + + var rankGrants: MutableList = ArrayList() + var punishments: MutableList = ArrayList() + var dateJoined = Date() + var ipAddresses: MutableSet = HashSet() + var geoInfo: GeoInfo? = null + + fun getCompoundedPermissions(): List { + val permissions = ArrayList() + permissions.addAll(StarkCore.instance.rankHandler.getDefaultRank().getCompoundedPermissions()) + + ArrayList(this.rankGrants) + .sortedBy { grant -> grant.rank.displayOrder } + .forEach { grant -> + if (grant.isActive()) { + permissions.addAll(grant.rank.getCompoundedPermissions()) + } + } + + return permissions + } + + fun getRank(): Rank { + var currentGrant: ProfileGrant? = null + + for (grant in this.rankGrants) { + if (grant.isActive()) { + if (currentGrant == null) { + currentGrant = grant + continue + } + + if (currentGrant.rank.displayOrder > grant.rank.displayOrder) { + currentGrant = grant + } + } + } + + return currentGrant?.rank ?: StarkCore.instance.rankHandler.getDefaultRank() + } + + fun getPlayerListName(): String { + return getRank().playerListPrefix.replace('&', StarkCore.COLOR_CODE_CHAR) + StarkCore.instance.uuidCache.name(this.uuid) + } + + fun getDisplayName(): String { + return getRank().prefix.replace('&', StarkCore.COLOR_CODE_CHAR) + StarkCore.instance.uuidCache.name(this.uuid) + } + + fun update(document: Document) { + val freshGrants = ArrayList() + val freshPunishments = ArrayList() + + for (grantDocument in document.getList("grants", Document::class.java)) { + try { + freshGrants.add(ProfileGrantSerializer.deserialize(grantDocument)) + } catch (ignore: IllegalStateException) { } + } + + for (punishmentDocument in document.getList("punishments", Document::class.java)) { + try { + freshPunishments.add(ProfilePunishmentSerializer.deserialize(punishmentDocument)) + } catch (ignore: IllegalStateException) { } + } + + this.rankGrants = freshGrants + this.punishments = freshPunishments + this.dateJoined = Date(document.getLong("dateJoined")!!) + this.ipAddresses = document.getList("ipAddresses", String::class.java).toHashSet() + + if (document["geoInfo"] != null) { + val geoInfoDocument = document["geoInfo"] as Document + + this.geoInfo = GeoInfo( + geoInfoDocument.getString("ip"), + geoInfoDocument.getString("city"), + geoInfoDocument.getString("region"), + geoInfoDocument.getString("country"), + geoInfoDocument.getString("postal") + ) + } + } + + fun getActivePunishment(type: ProfilePunishmentType): ProfilePunishment? { + for (punishment in this.punishments) { + if (punishment.type == type && punishment.isActive()) { + return punishment + } + } + return null + } + + fun hasPermission(permission: String): Boolean { + return getCompoundedPermissions().contains(permission) + } + + abstract fun apply() + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/ProfileHandler.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/ProfileHandler.kt new file mode 100644 index 0000000..f1d2a41 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/ProfileHandler.kt @@ -0,0 +1,216 @@ +package net.evilblock.stark.core.profile + +import net.evilblock.stark.core.StarkCore +import net.evilblock.stark.core.profile.lookup.GeoLookup +import net.evilblock.stark.core.profile.punishment.ProfilePunishment +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentSerializer +import net.evilblock.stark.core.profile.punishment.ProfilePunishmentType +import net.evilblock.stark.core.util.mojanguser.MojangUser +import com.google.gson.JsonParser +import com.mongodb.client.MongoCollection +import com.mongodb.client.model.ReplaceOptions +import com.squareup.okhttp.OkHttpClient +import com.squareup.okhttp.Request +import net.evilblock.stark.core.profile.grant.ProfileGrantSerializer +import org.bson.Document +import java.util.* +import java.util.concurrent.ForkJoinPool +import java.util.stream.Collectors + +abstract class ProfileHandler { + + lateinit var collection: MongoCollection + val profiles = HashMap() + + fun load() { + collection = StarkCore.instance.mongo.database.getCollection("profiles") + collection.createIndex(Document("uuid", 1)) + collection.createIndex(Document("ipAddress", 1)) + } + + abstract fun createProfileInstance(uuid: UUID): T + + fun getByUUID(uuid: UUID): T? { + return this.profiles[uuid] + } + + fun loadProfile(uuid: UUID): T { + return loadProfile(uuid, null) + } + + fun loadProfile(uuid: UUID, ipAddress: String?): T { + if (StarkCore.instance.isPrimaryThread()) { + throw IllegalStateException("Cannot query mongo on primary thread") + } + + if (this.profiles.containsKey(uuid)) { + return this.profiles[uuid]!! + } + + val profile = createProfileInstance(uuid) + var save = false + + val document = collection.find(Document("uuid", uuid.toString())).first() + + if (document != null) { + profile.update(document) + } else { + save = true + } + + if (ipAddress != null) { + // add ip to ip history + if (!profile.ipAddresses.contains(ipAddress)) { + save = true + profile.ipAddresses.add(ipAddress) + } + + // async geo lookup + if (profile.geoInfo == null) { + ForkJoinPool.commonPool().execute { + val geoInfo = GeoLookup.lookup(ipAddress) + + if (geoInfo != null) { + profile.geoInfo = geoInfo + collection.updateOne(Document("uuid", profile.uuid.toString()), Document("\$set", Document("geoInfo", geoInfo.toDocument()))) + } + } + } + } + + if (save) { + saveProfile(profile) + } + + return profile + } + + fun pullProfileUpdates(uuid: UUID): T { + val profile: Profile + + if (profiles.containsKey(uuid)) { + profile = profiles[uuid]!! + profile.update(collection.find(Document("uuid", uuid.toString())).first()!!) + } else { + profile = loadProfile(uuid) + } + + profile.apply() + + return profile + } + + fun saveProfile(profile: T) { + val grants = profile.rankGrants.stream().map { ProfileGrantSerializer.serialize(it) }.collect(Collectors.toList()) + val punishments = profile.punishments.stream().map { ProfilePunishmentSerializer.serialize(it) }.collect(Collectors.toList()) + + val document = Document() + document["uuid"] = profile.uuid.toString() + document["grants"] = grants + document["punishments"] = punishments + document["dateJoined"] = profile.dateJoined.time + document["ipAddresses"] = profile.ipAddresses + + collection.replaceOne(Document("uuid", profile.uuid.toString()), document, ReplaceOptions().upsert(true)) + } + + fun findActivePunishment(uuid: UUID, ipAddress: String): Pair? { + val orExpressions = Arrays.asList( + Document("uuid", uuid.toString()), + Document("ipAddresses", ipAddress) + ) + + for (profileDocument in collection.find(Document("\$or", orExpressions))) { + val punishments = profileDocument.getList("punishments", Document::class.java) + .stream() + .map { ProfilePunishmentSerializer.deserialize(it) } + .filter { Objects.nonNull(it) } + .collect(Collectors.toList()) + + for (punishment in punishments) { + if (punishment.type == ProfilePunishmentType.BLACKLIST || punishment.type == ProfilePunishmentType.BAN) { + if (punishment.isActive()) { + return Pair(UUID.fromString(profileDocument.getString("uuid")), punishment) + } + } + } + } + + return null + } + + fun findAlts(target: T): List { + if (target.ipAddresses.isEmpty()) { + return emptyList() + } + + val query = Document("ipAddresses", target.ipAddresses) + val results = arrayListOf() + + for (profileDocument in collection.find(query)) { + val uuid = UUID.fromString(profileDocument.getString("uuid")) + + if (uuid != null && !results.contains(uuid) && target.uuid != uuid) { + results.add(uuid) + } + } + + return results + } + + fun fetchProfileByUsername(username: String): T? { + val uuid = StarkCore.instance.uuidCache.uuid(username) + + if (uuid != null && this.profiles.containsKey(uuid)) { + return this.profiles[uuid]!! + } + + val mojangUser = fetchMojangUser(username) + + return if (mojangUser != null) { + loadProfile(mojangUser.uuid) + } else null + } + + fun fetchMojangUser(username: String): MojangUser? { + return fetchMojangUserFromRedis(username) ?: fetchMojangUserFromMojang(username) + } + + private fun fetchMojangUserFromRedis(username: String): MojangUser? { + val uuid = StarkCore.instance.uuidCache.uuid(username) + + return if (uuid != null) { + MojangUser(uuid, username) + } else null + } + + private fun fetchMojangUserFromMojang(username: String): MojangUser? { + StarkCore.instance.logger.info("Fetching username from Mojang API: $username") + + try { + val request = Request.Builder().url("https://api.mojang.com/users/profiles/minecraft/$username").build() + val response = client.newCall(request).execute() + + if (response.isSuccessful) { + val json = parser.parse(response.body().string()).asJsonObject + val uuid = UUID.fromString(json.get("id").asString.replaceFirst("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})".toRegex(), "$1-$2-$3-$4-$5")) + val name = json.get("name").asString + + // update uuid cache + StarkCore.instance.uuidCache.update(uuid, name) + + return MojangUser(uuid, name) + } + } catch (e: Exception) { + StarkCore.instance.logger.info("Failed to retrieve user info from Mojang API: $username") + } + + return null + } + + companion object { + private val client = OkHttpClient() + private val parser = JsonParser() + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/grant/ProfileGrant.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/grant/ProfileGrant.kt new file mode 100644 index 0000000..e1d2238 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/grant/ProfileGrant.kt @@ -0,0 +1,15 @@ +package net.evilblock.stark.core.profile.grant + +import net.evilblock.stark.core.grant.Grant +import net.evilblock.stark.core.rank.Rank +import java.util.* + +class ProfileGrant(uuid: UUID = UUID.randomUUID()) : Grant(uuid) { + + lateinit var rank: Rank + + fun isActive(): Boolean { + return removedAt == null && (expiresAt == null || System.currentTimeMillis() < expiresAt!!) + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/grant/ProfileGrantSerializer.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/grant/ProfileGrantSerializer.kt new file mode 100644 index 0000000..756faab --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/grant/ProfileGrantSerializer.kt @@ -0,0 +1,27 @@ +package net.evilblock.stark.core.profile.grant + +import net.evilblock.stark.core.StarkCore +import net.evilblock.stark.core.grant.GrantSerializer +import org.bson.Document +import java.util.* + +object ProfileGrantSerializer { + + @JvmStatic + fun serialize(grant: ProfileGrant): Document { + val document = GrantSerializer.serialize(Document(), grant) + document["rank"] = grant.rank.id + return document + } + + @JvmStatic + fun deserialize(document: Document): ProfileGrant { + val rank = StarkCore.instance.rankHandler.getById(document.getString("rank")) + ?: throw IllegalStateException("Rank doesn't exist") + + val grant = GrantSerializer.deserialize(document, ProfileGrant(UUID.fromString(document.getString("id")))) + grant.rank = rank + return grant + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/lookup/GeoInfo.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/lookup/GeoInfo.kt new file mode 100644 index 0000000..a68986c --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/lookup/GeoInfo.kt @@ -0,0 +1,16 @@ +package net.evilblock.stark.core.profile.lookup + +import org.bson.Document + +class GeoInfo(val ip: String, val city: String?, val region: String?, val country: String?, val postal: String?) { + + fun toDocument(): Document { + return Document() + .append("ip", ip) + .append("city", city) + .append("region", region) + .append("country", country) + .append("postal", postal) + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/lookup/GeoLookup.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/lookup/GeoLookup.kt new file mode 100644 index 0000000..9b75b6a --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/lookup/GeoLookup.kt @@ -0,0 +1,66 @@ +package net.evilblock.stark.core.profile.lookup + +import com.google.gson.JsonParser +import com.squareup.okhttp.OkHttpClient +import com.squareup.okhttp.Request + +object GeoLookup { + + private val client = OkHttpClient() + private val parser = JsonParser() + + @JvmStatic + fun lookup(ipAddress: String): GeoInfo? { + try { + val request = Request.Builder() + .url("https://ipinfo.io/$ipAddress/json") + .addHeader("Authorization","Bearer da6c7fbdd3a220") + .build() + + val response = client.newCall(request).execute() + + if (response.isSuccessful && response.code() == 200) { + val json = parser.parse(response.body().string()).asJsonObject + + val ip = json.get("ip").asString + + val city = if (json.has("city")) { + json.get("city").asString + } else { + null + } + + val region = if (json.has("region")) { + json.get("region").asString + } else { + null + } + + val country = if (json.has("country")) { + json.get("country").asString + } else { + null + } + + val postal = if (json.has("postal")) { + json.get("postal").asString + } else { + "None" + } + + return GeoInfo( + ip, + city, + region, + country, + postal + ) + } + } catch (e: Exception) { + e.printStackTrace() + } + + return null + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/punishment/ProfilePunishment.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/punishment/ProfilePunishment.kt new file mode 100644 index 0000000..14fa7ce --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/punishment/ProfilePunishment.kt @@ -0,0 +1,14 @@ +package net.evilblock.stark.core.profile.punishment + +import net.evilblock.stark.core.grant.Grant +import java.util.* + +class ProfilePunishment(uuid: UUID = UUID.randomUUID()) : Grant(uuid) { + + lateinit var type: ProfilePunishmentType + + fun isActive(): Boolean { + return removedAt == null && (expiresAt == null || System.currentTimeMillis() < expiresAt!!) + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/punishment/ProfilePunishmentSerializer.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/punishment/ProfilePunishmentSerializer.kt new file mode 100644 index 0000000..93d3989 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/punishment/ProfilePunishmentSerializer.kt @@ -0,0 +1,23 @@ +package net.evilblock.stark.core.profile.punishment + +import net.evilblock.stark.core.grant.GrantSerializer +import org.bson.Document +import java.util.* + +object ProfilePunishmentSerializer { + + @JvmStatic + fun serialize(punishment: ProfilePunishment): Document { + val document = GrantSerializer.serialize(Document(), punishment) + document["type"] = punishment.type.name + return document + } + + @JvmStatic + fun deserialize(document: Document): ProfilePunishment { + val punishment = ProfilePunishment(UUID.fromString(document.getString("id"))) + punishment.type = ProfilePunishmentType.valueOf(document.getString("type")) + return punishment + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/punishment/ProfilePunishmentType.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/punishment/ProfilePunishmentType.kt new file mode 100644 index 0000000..28fba90 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/profile/punishment/ProfilePunishmentType.kt @@ -0,0 +1,14 @@ +package net.evilblock.stark.core.profile.punishment + +import java.util.Arrays + +enum class ProfilePunishmentType constructor(val action: String, val color: String, vararg kickMessages: String) { + + BLACKLIST("blacklisted", "&4", "&cYou've been blacklisted from the Kihar Network."), + BAN("banned", "&c", "&cYou've been banned from the Kihar Network."), + MUTE("muted", "&e"), + WARN("warned", "&a"); + + val kickMessages: List = Arrays.asList(*kickMessages) + +} diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/rank/Rank.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/rank/Rank.kt new file mode 100644 index 0000000..53876cc --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/rank/Rank.kt @@ -0,0 +1,46 @@ +package net.evilblock.stark.core.rank + +import net.evilblock.stark.core.StarkCore + +class Rank(val id: String, + var displayName: String, + var displayOrder: Int, + var permissions: MutableList, + var prefix: String, + var playerListPrefix: String, + var gameColor: String, + var default: Boolean, + var inherits: List, + var hidden: Boolean) { + + constructor(id: String) : this(id, id, 999, arrayListOf(), "", "", "&f", false, arrayListOf(), false) + + /** + * Recursively gets all permissions of this rank and the ranks it inherits. + */ + fun getCompoundedPermissions(): List { + val toReturn = ArrayList() + toReturn.addAll(permissions) + + for (inheritedRank in getInheritedRanks()) { + toReturn.addAll(inheritedRank.getCompoundedPermissions()) + } + + return toReturn + } + + fun getInheritedRanks(): List { + val toReturn = arrayListOf() + for (rank in StarkCore.instance.rankHandler.getRanks()) { + if (inherits.contains(rank.id)) { + toReturn.add(rank) + } + } + return toReturn + } + + fun getColoredName(): String { + return gameColor.replace("&", "§") + displayName + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/rank/RankHandler.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/rank/RankHandler.kt new file mode 100644 index 0000000..5700985 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/rank/RankHandler.kt @@ -0,0 +1,159 @@ +package net.evilblock.stark.core.rank + +import net.evilblock.stark.core.StarkCore +import com.mongodb.client.MongoCollection +import com.mongodb.client.model.ReplaceOptions +import org.bson.Document +import java.lang.RuntimeException +import java.util.ArrayList +import java.util.HashMap + +class RankHandler { + + private lateinit var collection: MongoCollection + private val ranks = HashMap() + private val defaultRank = Rank("default", "Default", Integer.MAX_VALUE, ArrayList(), "", "", "", true, arrayListOf(), false) + + fun load() { + collection = StarkCore.instance.mongo.database.getCollection("ranks") + collection.createIndex(Document("id", 1)) + + loadRanks() + createRanks() //will fix error yes + } + + fun getRanks(): List { + return ArrayList(ranks.values) + } + + fun getMutableRankMap(): MutableMap { + return ranks + } + + fun getById(id: String): Rank? { + return ranks[id] + } + + fun getByName(name: String): Rank? { + for (rank in ranks.values) { + if (rank.displayName.equals(name, ignoreCase = true)) { + return rank + } + } + + return null + } + + fun getDefaultRank(): Rank { + for (rank in ranks.values) { + if (rank.default) { + return rank + } + } + + return defaultRank + } + + fun loadRanks() { + val freshRanks = ArrayList() + + for (document in collection.find()) { + try { + freshRanks.add(RankSerializer.deserialize(document)) + } catch (e: Exception) { + if (document.containsKey("id")) { + throw RuntimeException("Failed to load rank from document: " + document.getString("id"), e) + } else { + throw RuntimeException("Failed to load rank from document: Couldn't identify rank ID", e) + } + } + } + + for (rank in freshRanks) { + var internalRank: Rank? = getById(rank.id) + + if (internalRank == null) { + internalRank = rank + } + + if (rank != internalRank) { + internalRank.displayOrder = rank.displayOrder + internalRank.displayName = rank.displayName + internalRank.prefix = rank.prefix + internalRank.playerListPrefix = rank.playerListPrefix + internalRank.permissions = rank.permissions + } + + ranks[rank.id] = internalRank + } + + } + + fun createRanks() { + if (getByName("Owner") == null) { + var rank = Rank("Owner") + rank.displayName = "Owner" + rank.prefix = "&7[&4Owner&7]&4" + rank.displayOrder = 0 + rank.playerListPrefix = "&4" + rank.permissions = mutableListOf() + + saveRank(rank) + } + if (getByName("Manager") == null) { + var rank = Rank("Manager") + rank.displayName = "Manager" + rank.prefix = "&7[&4Manager&7]&4" + rank.displayOrder = 1 + rank.playerListPrefix = "&4" + rank.permissions = mutableListOf() + rank.permissions.add(0, "stark.permission") + rank.permissions.add(1, "stark.permission1") + //like this i think u can test it + + saveRank(rank) + } + //just keep doing that for all ranks + loadRanks() // put whichever one has this class ye + } + + fun pullRankUpdate(rankId: String) { + val document = collection.find(Document("id", rankId)).first() + + if (document != null) { + val freshRank = RankSerializer.deserialize(document) + val originalRank = ranks.replace(freshRank.id, freshRank) + + if (originalRank != null) { + for (profile in StarkCore.instance.getProfileHandler().profiles.values) { + var referenceSwitched = false + + for (grant in profile.rankGrants) { + if (grant.rank == originalRank) { + grant.rank = freshRank + referenceSwitched = true + } + } + + if (referenceSwitched) { + profile.apply() + } + } + } + } + } + + fun saveRank(rank: Rank) { + collection.replaceOne(Document("id", rank.id), RankSerializer.serialize(rank), ReplaceOptions().upsert(true)) + } + + fun deleteRank(rank: Rank) { + ranks.remove(rank.id) + collection.deleteOne(Document("id", rank.id)) + } + + fun saveAllRanks() { + this.ranks.values.forEach { this.saveRank(it) } + } + +} diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/rank/RankMessageListeners.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/rank/RankMessageListeners.kt new file mode 100644 index 0000000..f016b20 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/rank/RankMessageListeners.kt @@ -0,0 +1,24 @@ +package net.evilblock.stark.core.rank + +import nig.cock.nigger.message.handler.IncomingMessageHandler +import nig.cock.nigger.message.listener.MessageListener +import net.evilblock.stark.core.StarkCore +import com.google.gson.JsonObject + +object RankMessageListeners : MessageListener { + + @IncomingMessageHandler("RANK_UPDATE") + fun onRankUpdate(data: JsonObject) { + val id = data["id"].asString + + when (data["action"].asString) { + "UPDATE" -> { + StarkCore.instance.rankHandler.pullRankUpdate(id) + } + "DELETE" -> { + StarkCore.instance.rankHandler.getMutableRankMap().remove(id) + } + } + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/rank/RankSerializer.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/rank/RankSerializer.kt new file mode 100644 index 0000000..612e61a --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/rank/RankSerializer.kt @@ -0,0 +1,50 @@ +package net.evilblock.stark.core.rank + +import net.evilblock.stark.core.StarkCore +import org.bson.Document +import java.util.ArrayList + +object RankSerializer { + + @JvmStatic + fun deserialize(document: Document): Rank { + val rank = Rank(document.getString("id")) + + rank.permissions = ArrayList() + rank.displayName = document.getString("displayName") + rank.displayOrder = document.getInteger("displayOrder")!! + rank.prefix = document.getString("prefix") + rank.playerListPrefix = document.getString("playerListPrefix") + rank.gameColor = document.getString("gameColor") + rank.default = document.getBoolean("default")!! + rank.inherits = document.getList("inherits", String::class.java) + + if (document.containsKey("hidden")) { + rank.hidden = document.getBoolean("hidden") + } + + if (document.containsKey("permissions")) { + rank.permissions.addAll(document.getList("permissions", String::class.java)) + } + + return rank + } + + @JvmStatic + fun serialize(rank: Rank): Document { + val document = Document("id", rank.id) + + document["displayName"] = rank.displayName + document["displayOrder"] = rank.displayOrder + document["prefix"] = rank.prefix.replace(StarkCore.COLOR_CODE_CHAR, '&') + document["playerListPrefix"] = rank.playerListPrefix.replace(StarkCore.COLOR_CODE_CHAR, '&') + document["gameColor"] = rank.gameColor.replace(StarkCore.COLOR_CODE_CHAR, '&') + document["inherits"] = rank.inherits + document["hidden"] = rank.hidden + document["default"] = rank.default + document["permissions"] = rank.permissions + + return document + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/rank/runnable/RankLoadRunnable.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/rank/runnable/RankLoadRunnable.kt new file mode 100644 index 0000000..3eb5ac2 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/rank/runnable/RankLoadRunnable.kt @@ -0,0 +1,11 @@ +package net.evilblock.stark.core.rank.runnable + +import net.evilblock.stark.core.StarkCore + +class RankLoadRunnable : Runnable { + + override fun run() { + StarkCore.instance.rankHandler.loadRanks() + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/redis/Redis.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/redis/Redis.kt new file mode 100644 index 0000000..235b18c --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/redis/Redis.kt @@ -0,0 +1,96 @@ +package net.evilblock.stark.core.redis + +import net.evilblock.stark.core.StarkCore +import redis.clients.jedis.Jedis +import redis.clients.jedis.JedisPool +import redis.clients.jedis.JedisPoolConfig +import java.io.Closeable + +/** + * A class used for managing Jedis connections and executing Jedis commands. + */ +class Redis : Closeable { + + var localJedisPool: JedisPool? = null + var backboneJedisPool: JedisPool? = null + + fun load(localCredentials: RedisCredentials, backboneCredentials: RedisCredentials) { + try { + val password = if (localCredentials.shouldAuthenticate()) { + localCredentials.password!! + } else { + null + } + + localJedisPool = JedisPool(JedisPoolConfig(), localCredentials.host, localCredentials.port, TIMEOUT, password, localCredentials.dbId) + } catch (e: Exception) { + StarkCore.instance.logger.warning("Couldn't connect to a Redis instance at ${localCredentials.host}:${localCredentials.port}") + e.printStackTrace() + } + + try { + val password = if (backboneCredentials.shouldAuthenticate()) { + backboneCredentials.password!! + } else { + null + } + + backboneJedisPool = JedisPool(JedisPoolConfig(), backboneCredentials.host, backboneCredentials.port, TIMEOUT, password, backboneCredentials.dbId) + } catch (e: Exception) { + StarkCore.instance.logger.warning("Couldn't connect to a Backbone Redis instance at ${backboneCredentials.host}:${backboneCredentials.port}") + e.printStackTrace() + } + } + + /** + * Close any open Jedis connections. + */ + override fun close() { + if (localJedisPool != null && !localJedisPool!!.isClosed) { + localJedisPool!!.close() + } + + if (backboneJedisPool != null && !backboneJedisPool!!.isClosed) { + backboneJedisPool!!.close() + } + } + + /** + * A functional method for using a pooled [Jedis] resource and returning data. + * + * @param lambda the function + */ + fun runRedisCommand(lambda: (Jedis) -> T): T { + if (localJedisPool == null || localJedisPool!!.isClosed) { + throw IllegalStateException("Local jedis pool couldn't connect or is closed") + } + + try { + localJedisPool!!.resource.use { jedis -> return lambda(jedis) } + } catch (e: Exception) { + throw RuntimeException("Could not use resource and return", e) + } + } + + /** + * A functional method for using a pooled [Jedis] resource and returning data. + * + * @param lambda the function + */ + fun runBackboneRedisCommand(lambda: (Jedis) -> T): T { + if (backboneJedisPool == null || backboneJedisPool!!.isClosed) { + throw IllegalStateException("Backbone jedis pool couldn't connect or is closed") + } + + try { + backboneJedisPool!!.resource.use { jedis -> return lambda(jedis) } + } catch (e: Exception) { + throw RuntimeException("Could not use resource and return", e) + } + } + + companion object { + private const val TIMEOUT = 5000 + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/redis/RedisCredentials.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/redis/RedisCredentials.kt new file mode 100644 index 0000000..9c677e1 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/redis/RedisCredentials.kt @@ -0,0 +1,41 @@ +package net.evilblock.stark.core.redis + +data class RedisCredentials( + var host: String = "localhost", + var port: Int = 6379, + var password: String? = null, + var dbId: Int = 0) { + + fun shouldAuthenticate(): Boolean { + return password != null && password!!.isNotEmpty() && password!!.isNotBlank() + } + + class Builder { + val credentials: RedisCredentials = RedisCredentials() + + fun host(host: String): Builder { + credentials.host = host + return this + } + + fun port(port: Int): Builder { + credentials.port = port + return this + } + + fun password(password: String): Builder { + credentials.password = password + return this + } + + fun dbId(dbId: Int): Builder { + credentials.dbId = dbId + return this + } + + fun build(): RedisCredentials { + return credentials + } + } + +} diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/server/Server.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/server/Server.kt new file mode 100644 index 0000000..0778564 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/server/Server.kt @@ -0,0 +1,110 @@ +package net.evilblock.stark.core.server + +import java.util.* +import kotlin.collections.HashMap + +class Server(val serverName: String, val serverGroup: String, val serverPort: Int) { + + /** + * The last heartbeat time in milliseconds + */ + var lastHeartbeat: Long = 0 + + /** + * The current uptime in milliseconds + */ + var currentUptime: Long = 0 + + /** + * The current TPS + */ + var currentTps: Double = 0.toDouble() + + /** + * The current player count + */ + var playerCount: Int = 0 + + /** + * The maximum amount of players the server can hold + */ + var maxSlots: Int = 0 + + /** + * If the server is currently whitelisted + */ + var whitelisted: Boolean = false + + /** + * Loads server data from key-specific populated map + */ + constructor(map: Map) : this(map["serverName"]!!, map["serverGroup"]!!, map["serverPort"]!!.toInt()) { + lastHeartbeat = map["lastHeartbeat"]!!.toLong() + currentUptime = map["currentUptime"]!!.toLong() + currentTps = map["currentTps"]!!.toDouble() + playerCount = map["playerCount"]!!.toInt() + maxSlots = map["maxSlots"]!!.toInt() + whitelisted = map["whitelisted"]!!.toBoolean() + } + + /** + * Gets whether the server is considered online or offline. + * If the last heartbeat time of this server exceeds 5 seconds, the server is considered offline. + * + * @return true if the server is considered online, otherwise false. + */ + fun isOnline(): Boolean { + return (System.currentTimeMillis() - lastHeartbeat) < 5000L + } + + /** + * Gets the current uptime in milliseconds. + * + * @return an [<] object where the value is this server's current + * uptime and is present if [Server.isOnline] returns true + */ + fun getCurrentUptime(): Optional { + return if (isOnline()) Optional.of(currentUptime) else Optional.empty() + } + + /** + * Gets the current TPS. + * + * @return an [<] object where the value is this server's current + * TPS and is present if [Server.isOnline] returns true + */ + fun getCurrentTps(): Optional { + return if (isOnline()) Optional.of(currentTps) else Optional.empty() + } + + /** + * Gets the current player count. + * + * @return an [<] object where the value is this server's current + * player count and is present if [Server.isOnline] returns true + */ + fun getPlayerCount(): Optional { + return if (isOnline()) Optional.of(playerCount) else Optional.empty() + } + + /** + * Gets a map populated with the key-specific data provided by + * this server. + * + * @return a [,][<] object + */ + fun toMap(): Map { + val map = HashMap() + map["serverName"] = serverName + map["serverGroup"] = serverGroup + map["serverPort"] = serverPort.toString() + map["lastHeartbeat"] = lastHeartbeat.toString() + map["currentUptime"] = currentUptime.toString() + map["currentTps"] = currentTps.toString() + map["playerCount"] = playerCount.toString() + map["maxSlots"] = maxSlots.toString() + map["whitelisted"] = whitelisted.toString() + return map + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/server/ServerGroup.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/server/ServerGroup.kt new file mode 100644 index 0000000..fd63414 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/server/ServerGroup.kt @@ -0,0 +1,54 @@ +package net.evilblock.stark.core.server + +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import java.util.ArrayList +import java.util.HashMap + +class ServerGroup(groupName: String) { + + /** + * The group name + */ + val groupName: String = groupName + + /** + * The list of servers + */ + val servers: MutableList = ArrayList() + + /** + * The global configuration + */ + var configuration: JsonObject = JsonObject() + + constructor(map: Map) : this(map["groupName"]!!) { + configuration = PARSER.parse(map["configuration"]!!).asJsonObject + } + + /** + * Gets the total player count + * + * @return the sum of all the servers' player counts + */ + fun getTotalPlayerCount(): Int { + return servers.stream().mapToInt { server -> server.getPlayerCount().orElse(0) }.sum() + } + + /** + * Gets a map of the key-specific data provided by this group. + * + * @return a [,][<] object + */ + fun toMap(): Map { + val map = HashMap() + map["groupName"] = groupName + map["configuration"] = configuration.toString() + return map + } + + companion object { + private val PARSER = JsonParser() + } + +} diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/server/ServerMessageListeners.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/server/ServerMessageListeners.kt new file mode 100644 index 0000000..cb3c106 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/server/ServerMessageListeners.kt @@ -0,0 +1,53 @@ +package net.evilblock.stark.core.server + +import nig.cock.nigger.message.handler.IncomingMessageHandler +import nig.cock.nigger.message.listener.MessageListener +import net.evilblock.stark.core.StarkCore +import com.google.gson.GsonBuilder +import com.google.gson.JsonObject +import com.google.gson.reflect.TypeToken + +object ServerMessageListeners : MessageListener { + + @IncomingMessageHandler("GLOBAL_COUNT") + fun onGlobalCount(json: JsonObject) { + StarkCore.instance.servers.globalCount = json["globalCount"].asInt + } + + @IncomingMessageHandler("SERVER_GROUP_UPDATE") + fun onServerGroupUpdate(json: JsonObject) { + val map = GSON.fromJson>(json, TYPE) as Map + + val optionalGroup = StarkCore.instance.servers.getServerGroupByName(map["groupName"]!!) + + if (optionalGroup.isPresent) { + optionalGroup.get().configuration = GSON.fromJson(map["configuration"]!!, JsonObject::class.java) + } else { + val group = ServerGroup(map) + StarkCore.instance.servers.groups[group.groupName] = group + } + } + + @IncomingMessageHandler("SERVER_UPDATE") + fun onServerUpdate(json: JsonObject) { + val map = GSON.fromJson>(json, TYPE) as Map + + val optionalServer = StarkCore.instance.servers.getServerByName(map["serverName"]!!) + + if (optionalServer.isPresent) { + val server = optionalServer.get() + server.lastHeartbeat = map["lastHeartbeat"]!!.toLong() + server.currentUptime = map["currentUptime"]!!.toLong() + server.currentTps = map["currentTps"]!!.toDouble() + server.playerCount = map["playerCount"]!!.toInt() + server.maxSlots = map["maxSlots"]!!.toInt() + server.whitelisted = map["whitelisted"]!!.toBoolean() + } else { + StarkCore.instance.servers.loadOrCreateServer(map["serverName"]!!, map["serverPort"]!!.toInt()) + } + } + + private val GSON = GsonBuilder().create() + private val TYPE = TypeToken.getParameterized(Map::class.java, String::class.java, String::class.java).rawType + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/server/Servers.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/server/Servers.kt new file mode 100644 index 0000000..34c65a7 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/server/Servers.kt @@ -0,0 +1,264 @@ +package net.evilblock.stark.core.server + +import nig.cock.nigger.message.Message +import net.evilblock.stark.core.StarkCore +import redis.clients.jedis.ScanParams +import java.util.* +import java.util.stream.Collector +import java.util.stream.Collectors +import java.util.stream.Stream +import kotlin.collections.HashMap + +/** + * A class used for managing and synchronizing server information. + * + * Redis tree structure: + * + * stark: + * serverSync: + * server: + * serverMeta: + * Hub-1: + * serverName: Hub-1 + * serverGroup: Hubs + * serverPort: 25588 + * ... + * portLookup: + * 25588: Hub-1 + * group: + * Hubs: + * groupName: Hubs + * configuration: JSON string + * + * The load procedure goes as follows: + * - Load server group types into memory + * - Load servers and pair them with their + * assigned group + * + * If the [Servers] module is being instantiated through a bukkit + * instance, the [ensureServerExists] and [checkNamePortMatch] + * functions should be executed in that order on startup. + * This ensures that the bungee data matches the bukkit data. + * + */ +class Servers { + + var globalCount = 0 + val groups: MutableMap = hashMapOf() + + fun initialLoad() { + loadServerGroups() + loadServers() + } + + /** + * Streams all servers. + * + * @return a [<] of the list of servers + */ + fun streamServers(): Stream { + return groups.values + .stream() + .flatMap { serverGroup -> serverGroup.servers.stream() } + } + + /** + * Streams all servers. + * + * @return a [<] of the list of servers + */ + fun streamServerGroups(): Stream { + return groups.values.stream() + } + + /** + * Gets a [Server] by the given [serverName] if loaded into memory. + * + * @param serverName the server name + * + * @return an [Server] object if the cache contains a server matching the given [serverName] + */ + fun getServerByName(serverName: String): Optional { + return Optional.ofNullable(streamServers() + .filter { server -> server.serverName == serverName } + .collect(collectFirst())) + } + + /** + * Attempts to fetch server data by name and instantiate a new [Server] object if any data was found. + * + * @param serverName the server port + * + * @return an [<] object where the value is present if any data was successfully retrieved from Redis. + */ + fun lookupServerByName(serverName: String): Optional { + return Optional.ofNullable(StarkCore.instance.redis.runBackboneRedisCommand { client -> + val map = client.hgetAll("stark:serverSync:server:serverMeta:$serverName") + if (map.isEmpty()) null else Server(map) + }) + } + + /** + * Gets a [ServerGroup] object by the given [groupName]. + * + * @param groupName the group name + * + * @return an [ServerGroup] object if the cache contains a server group matching the given [groupName] + */ + fun getServerGroupByName(groupName: String): Optional { + return Optional.ofNullable(streamServerGroups() + .filter { serverGroup -> serverGroup.groupName == groupName } + .collect(collectFirst())) + } + + /** + * Saves a server group to Redis. + * + * @param serverGroup the server group + */ + fun saveServerGroup(serverGroup: ServerGroup) { + StarkCore.instance.redis.runBackboneRedisCommand { jedis -> + jedis.hmset("stark:serverSync:group:${serverGroup.groupName}", serverGroup.toMap()) + } + + StarkCore.instance.globalMessageChannel.sendMessage(Message(SERVER_GROUP_UPDATE, serverGroup.toMap())) + } + + /** + * Loads or creates and stores a new [ServerGroup] object. + * + * @param groupName the group name + * + * @return the existing or newly created [ServerGroup] object + */ + fun loadOrCreateServerGroup(groupName: String): ServerGroup { + return StarkCore.instance.redis.runBackboneRedisCommand { jedis -> + val exists = jedis.exists("stark:serverSync:group:$groupName") + + val group = if (exists) { + ServerGroup(jedis.hgetAll("stark:serverSync:group:$groupName")) + } else { + ServerGroup(groupName) + } + + if (!exists) { + saveServerGroup(group) + + StarkCore.instance.globalMessageChannel.sendMessage(Message(SERVER_GROUP_UPDATE, group.toMap())) + } + + groups[groupName] = group + + return@runBackboneRedisCommand group + } + } + + /** + * Saves a server to Redis. + * + * @param server the server + */ + fun saveServer(server: Server) { + StarkCore.instance.redis.runBackboneRedisCommand { jedis -> + jedis.set("stark:serverSync:server:portLookup:${server.serverPort}", server.serverPort.toString()) + jedis.hmset("stark:serverSync:server:serverMeta:${server.serverName}", server.toMap()) + } + + StarkCore.instance.globalMessageChannel.sendMessage(Message(SERVER_UPDATE, server.toMap())) + } + + /** + * Loads or creates and stores a new [Server] object. + * + * @param serverName the server name + * @param serverPort the server port + * + * @return the existing or newly created [Server] object + */ + fun loadOrCreateServer(serverName: String, serverPort: Int): Server { + return StarkCore.instance.redis.runBackboneRedisCommand { jedis -> + val exists = jedis.exists("stark:serverSync:server:serverMeta:$serverName") + + val server = if (exists) { + Server(jedis.hgetAll("stark:serverSync:server:serverMeta:$serverName")) + } else { + Server(serverName, "default", serverPort) + } + + if (!exists) { + saveServer(server) + + StarkCore.instance.globalMessageChannel.sendMessage(Message(SERVER_UPDATE, server.toMap())) + } + + val group = getServerGroupByName(server.serverGroup) + + if (group.isPresent) { + group.get().servers.add(server) + } else { + loadOrCreateServerGroup(server.serverGroup).servers.add(server) + } + + return@runBackboneRedisCommand server + } + } + + /** + * Loads the server groups stored in Redis into memory. + */ + private fun loadServerGroups() { + StarkCore.instance.redis.runBackboneRedisCommand { jedis -> + jedis.scan("0", ScanParams().match("stark:serverSync:group:*")).result.forEach {key -> + groups[key.split(":").last()] = ServerGroup(key.split(":").last()) + } + } + } + + /** + * Loads the servers stored in Redis into memory. + */ + private fun loadServers() { + StarkCore.instance.redis.runBackboneRedisCommand { jedis -> + jedis.scan("0", ScanParams().match("stark:serverSync:server:serverMeta:*")).result.forEach { key -> + lookupServerByName(key.split(":").last()).ifPresent { server -> + val group = getServerGroupByName(server.serverGroup) + + if (group.isPresent) { + group.get().servers.add(server) + } else { + loadOrCreateServerGroup(server.serverGroup).servers.add(server) + } + } + } + } + } + + /** + * Checks if a [Server] by the [serverName] exists and if the + * [serverPort] matches. + * + * If the name port is a mismatch, that means the bukkit + * and bungee configurations are mismatched. + */ + fun checkNamePortMatch(serverName: String, serverPort: Int): Boolean { + val server = lookupServerByName(serverName) + + if (server.isPresent) { + if (server.get().serverPort == serverPort) { + return true + } + } + + return false + } + + private fun collectFirst(): Collector { + return Collectors.collectingAndThen(Collectors.toList()) { list -> if (list.isEmpty()) null else list[0] } + } + + companion object { + private const val SERVER_GROUP_UPDATE = "SERVER_GROUP_UPDATE" + private const val SERVER_UPDATE = "SERVER_UPDATE" + } + +} diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/util/Pair.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/util/Pair.kt new file mode 100644 index 0000000..5819ea5 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/util/Pair.kt @@ -0,0 +1,3 @@ +package net.evilblock.stark.core.util + +data class Pair(var key: K?, var value: V?) \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/util/TimeUtils.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/util/TimeUtils.kt new file mode 100644 index 0000000..0dbd9c0 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/util/TimeUtils.kt @@ -0,0 +1,165 @@ +package net.evilblock.stark.core.util + +import java.text.SimpleDateFormat +import java.util.* +import java.util.regex.Pattern + +object TimeUtils { + + private val mmssBuilder: ThreadLocal = ThreadLocal.withInitial { java.lang.StringBuilder() } + private val dateFormat: SimpleDateFormat = SimpleDateFormat("MM/dd/yyyy HH:mm") + + @JvmStatic + fun formatIntoHHMMSS(secs: Int): String { + return formatIntoMMSS(secs) + } + + @JvmStatic + fun formatLongIntoHHMMSS(secs: Long): String { + val unconvertedSeconds = secs.toInt() + return formatIntoMMSS(unconvertedSeconds) + } + + @JvmStatic + fun formatIntoMMSS(secs: Int): String { + var secs = secs + val seconds = secs % 60 + secs -= seconds + var minutesCount = (secs / 60).toLong() + val minutes = minutesCount % 60L + minutesCount -= minutes + val hours = minutesCount / 60L + val result = mmssBuilder.get() + result.setLength(0) + if (hours > 0L) { + if (hours < 10L) { + result.append("0") + } + result.append(hours) + result.append(":") + } + if (minutes < 10L) { + result.append("0") + } + result.append(minutes) + result.append(":") + if (seconds < 10) { + result.append("0") + } + result.append(seconds) + return result.toString() + } + + @JvmStatic + fun formatLongIntoMMSS(secs: Long): String { + val unconvertedSeconds = secs.toInt() + return formatIntoMMSS(unconvertedSeconds) + } + + @JvmStatic + fun formatIntoDetailedString(secs: Int): String { + if (secs == 0) { + return "0 seconds" + } + val remainder = secs % 86400 + val days = secs / 86400 + val hours = remainder / 3600 + val minutes = remainder / 60 - hours * 60 + val seconds = remainder % 3600 - minutes * 60 + val fDays = if (days > 0) " " + days + " day" + if (days > 1) "s" else "" else "" + val fHours = if (hours > 0) " " + hours + " hour" + if (hours > 1) "s" else "" else "" + val fMinutes = if (minutes > 0) " " + minutes + " minute" + if (minutes > 1) "s" else "" else "" + val fSeconds = if (seconds > 0) " " + seconds + " second" + if (seconds > 1) "s" else "" else "" + return (fDays + fHours + fMinutes + fSeconds).trim { it <= ' ' } + } + + @JvmStatic + fun formatIntoShortString(secs: Int): String { + if (secs == 0) { + return "0 seconds" + } + + val remainder = secs % 86400 + val days = secs / 86400 + val hours = remainder / 3600 + val minutes = remainder / 60 - hours * 60 + val seconds = remainder % 3600 - minutes * 60 + + val builder = StringBuilder() + + if (days > 0) { + if (hours > 0) { + builder.append("$days day${if (days == 1) "" else "s"} $hours hour${if (hours == 1) "" else "s"}") + } else { + builder.append("$days day${if (days == 1) "" else "s"}") + } + } else if (hours > 0) { + if (minutes > 0) { + builder.append("$hours hour${if (hours == 1) "" else "s"} $minutes minute${if (minutes == 1) "" else "s"}") + } else { + builder.append("$hours hour${if (hours == 1) "" else "s"}") + } + } else if (minutes > 0) { + if (seconds > 0) { + builder.append("$minutes minute${if (minutes == 1) "" else "s"} $seconds second${if (seconds == 1) "" else "s"}") + } else { + builder.append("$minutes minute${if (minutes == 1) "" else "s"}") + } + } + + return builder.trim().toString() + } + + @JvmStatic + fun formatLongIntoDetailedString(secs: Long): String { + val unconvertedSeconds = secs.toInt() + return formatIntoDetailedString(unconvertedSeconds) + } + + @JvmStatic + fun formatIntoCalendarString(date: Date): String { + return dateFormat.format(date) + } + + @JvmStatic + fun parseTime(time: String): Int { + if (time == "0" || time == "") { + return 0 + } + val lifeMatch = arrayOf("y", "w", "d", "h", "m", "s") + val lifeInterval = intArrayOf(31_536_000, 604800, 86400, 3600, 60, 1) + var seconds = -1 + for (i in lifeMatch.indices) { + val matcher = Pattern.compile("([0-9]+)" + lifeMatch[i]).matcher(time) + while (matcher.find()) { + if (seconds == -1) { + seconds = 0 + } + seconds += Integer.parseInt(matcher.group(1)) * lifeInterval[i] + } + } + if (seconds == -1) { + throw IllegalArgumentException("Invalid time provided.") + } + return seconds + } + + @JvmStatic + fun parseTimeToLong(time: String): Long { + val unconvertedSeconds = parseTime(time) + return unconvertedSeconds.toLong() + } + + @JvmStatic + fun getSecondsBetween(a: Date, b: Date): Int { + return getSecondsBetweenLong(a, b).toInt() + } + + @JvmStatic + fun getSecondsBetweenLong(a: Date, b: Date): Long { + val diff = a.getTime() - b.getTime() + val absDiff = Math.abs(diff) + return absDiff / 1000L + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/util/mojanguser/MojangUser.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/util/mojanguser/MojangUser.kt new file mode 100644 index 0000000..c09775e --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/util/mojanguser/MojangUser.kt @@ -0,0 +1,5 @@ +package net.evilblock.stark.core.util.mojanguser + +import java.util.UUID + +data class MojangUser(var uuid: UUID, var username: String) diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/uuid/UUIDCache.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/uuid/UUIDCache.kt new file mode 100644 index 0000000..6764b94 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/uuid/UUIDCache.kt @@ -0,0 +1,70 @@ +package net.evilblock.stark.core.uuid + +import net.evilblock.stark.core.StarkCore +import java.util.ArrayList +import java.util.UUID +import java.util.concurrent.ConcurrentHashMap + +class UUIDCache { + + private var uuidToName = ConcurrentHashMap() + private var nameToUuid = ConcurrentHashMap() + + fun load() { + val newUuidToName = ConcurrentHashMap() + val newNameToUuid = ConcurrentHashMap() + + val cache = StarkCore.instance.redis.runBackboneRedisCommand { redis -> + redis.hgetAll("UUIDCache") + } + + cache!!.forEach { (key, name) -> + val uuid = UUID.fromString(key) + newUuidToName[uuid] = name + newNameToUuid[name.toLowerCase()] = uuid + } + + uuidToName = newUuidToName + nameToUuid = newNameToUuid + } + + fun uuid(name: String): UUID? { + return nameToUuid[name.toLowerCase()] + } + + fun name(uuid: UUID): String { + return uuidToName[uuid]?: "Unknown" + } + + fun update(uuid: UUID, name: String) { + val toRemove = ArrayList() + + nameToUuid.forEach { (key, value) -> + if (value === uuid) { + toRemove.add(key) + } + } + + for (remove in toRemove) { + nameToUuid.remove(remove) + } + + uuidToName[uuid] = name + nameToUuid[name.toLowerCase()] = uuid + + StarkCore.instance.redis.runBackboneRedisCommand { redis -> + redis.hset("UUIDCache", uuid.toString(), name) + } + } + + fun cache(uuid: UUID, name: String) { + uuidToName[uuid] = name + nameToUuid[name.toLowerCase()] = uuid + } + + fun reset() { + nameToUuid.clear() + uuidToName.clear() + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/uuid/UUIDCacheLoadRunnable.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/uuid/UUIDCacheLoadRunnable.kt new file mode 100644 index 0000000..44ebfa0 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/uuid/UUIDCacheLoadRunnable.kt @@ -0,0 +1,11 @@ +package net.evilblock.stark.core.uuid + +import net.evilblock.stark.core.StarkCore + +class UUIDCacheLoadRunnable : Runnable { + + override fun run() { + StarkCore.instance.uuidCache.load() + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/whitelist/Whitelist.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/whitelist/Whitelist.kt new file mode 100644 index 0000000..4b4c614 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/whitelist/Whitelist.kt @@ -0,0 +1,98 @@ +package net.evilblock.stark.core.whitelist + +import nig.cock.nigger.message.Message +import net.evilblock.stark.core.StarkCore +import net.evilblock.stark.core.redis.Redis +import java.util.* +import kotlin.collections.HashSet + +class Whitelist { + + var modeType: WhitelistType = WhitelistType.NONE + var verified: HashSet = hashSetOf() + + fun load() { + StarkCore.instance.redis.runBackboneRedisCommand { + if (it.exists("stark:whitelistMode")) { + modeType = WhitelistType.valueOf(it.get("stark:whitelistMode")) + } + + verified.addAll(it.smembers("stark:verifiedStatus").map { member -> UUID.fromString(member) }) + } + } + + fun setMode(type: WhitelistType, update: Boolean) { + modeType = type + + if (update) { + StarkCore.instance.redis.runBackboneRedisCommand { + it.set("stark:whitelistMode", type.toString()) + } + } + } + + fun getWhitelist(uuid: UUID): WhitelistType { + return StarkCore.instance.redis.runBackboneRedisCommand { + val retrieved = it.get("stark:whitelistAccess:player.$uuid") + + if (retrieved == null || retrieved.isEmpty()) { + WhitelistType.values()[0] + } else { + WhitelistType.valueOf(retrieved) + } + } + } + + fun setWhitelist(uuid: UUID, type: WhitelistType) { + StarkCore.instance.redis.runBackboneRedisCommand { + it.set("stark:whitelistAccess:player.$uuid", type.toString()) + } + } + + fun getWhitelistTokens(uuid: UUID): Int { + return StarkCore.instance.redis.runBackboneRedisCommand { + val retrieved = it.get("stark:whitelistTokens:player.$uuid") + + if (retrieved == null) { + 0 + } else { + val toInt = retrieved.toInt() + + if (toInt <= 0) { + 0 + } else { + toInt + } + } + } + } + + fun setWhitelistTokens(uuid: UUID, tokens: Int) { + StarkCore.instance.redis.runBackboneRedisCommand { + it.set("stark:whitelistTokens:player.$uuid", tokens.toString()) + } + } + + fun setVerified(uuid: UUID, status: Boolean): Boolean { + val successful = if (status) { + verified.add(uuid) + } else { + verified.remove(uuid) + } + + if (successful) { + StarkCore.instance.redis.runBackboneRedisCommand { + if (status) { + it.sadd("stark:verifiedStatus", uuid.toString()) + } else { + it.srem("stark:verifiedStatus", uuid.toString()) + } + } + + StarkCore.instance.globalMessageChannel.sendMessage(Message("VERIFIED_UPDATE", mapOf("status" to status, "playerUuid" to uuid.toString()))) + } + + return successful + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/net/evilblock/stark/core/whitelist/WhitelistType.kt b/stark/core/src/main/kotlin/net/evilblock/stark/core/whitelist/WhitelistType.kt new file mode 100644 index 0000000..d54b648 --- /dev/null +++ b/stark/core/src/main/kotlin/net/evilblock/stark/core/whitelist/WhitelistType.kt @@ -0,0 +1,15 @@ +package net.evilblock.stark.core.whitelist + +enum class WhitelistType(val displayName: String, val disallowMessage: String) { + NONE("None", ""), + PURCHASED("Purchased", "&4The network is currently whitelisted.\n&4Additional info may be found at 1v1.club"), + MAINTENANCE("Maintenance", "&4The network is currently in maintenance mode.\n46Visit our website or twitter for more information."); + + fun isAboveOrEqual(type: WhitelistType): Boolean { + return this.ordinal >= type.ordinal + } + + fun getPermission(): String { + return "stark.whitelist.access.${this.name}" + } +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/nig/cock/nigger/Pidgin.kt b/stark/core/src/main/kotlin/nig/cock/nigger/Pidgin.kt new file mode 100644 index 0000000..9c9f933 --- /dev/null +++ b/stark/core/src/main/kotlin/nig/cock/nigger/Pidgin.kt @@ -0,0 +1,94 @@ +package nig.cock.nigger + +import nig.cock.nigger.morph.JsonMorph +import nig.cock.nigger.message.Message +import nig.cock.nigger.message.handler.IncomingMessageHandler +import nig.cock.nigger.message.handler.MessageExceptionHandler +import nig.cock.nigger.message.listener.MessageListener +import nig.cock.nigger.message.listener.MessageListenerData +import com.google.gson.* +import java.util.ArrayList +import java.util.concurrent.ForkJoinPool +import redis.clients.jedis.JedisPool +import redis.clients.jedis.JedisPubSub +import java.lang.IllegalStateException + +/** + * A Jedis Pub/Sub implementation. + */ +class Pidgin(private val channel: String, private val jedisPool: JedisPool, private val options: PidginOptions = PidginOptions()) { + + private var jedisPubSub: JedisPubSub? = null + private val messageListeners: MutableList = ArrayList() + + init { + setupPubSub() + } + + @JvmOverloads + fun sendMessage(message: Message, exceptionHandler: MessageExceptionHandler? = null) { + try { + val jsonObject = JsonObject() + jsonObject.addProperty("messageId", message.id) + jsonObject.add("messageData", morph.fromObject(message.data)) + + jedisPool.resource.use { jedis -> jedis.publish(channel, jsonObject.toString()) } + } catch (e: Exception) { + exceptionHandler?.onException(e) + } + } + + fun registerListener(messageListener: MessageListener) { + for (method in messageListener::class.java.declaredMethods) { + if (method.getDeclaredAnnotation(IncomingMessageHandler::class.java) != null && method.parameters.isNotEmpty()) { + if (!JsonObject::class.java.isAssignableFrom(method.parameters[0].type)) { + throw IllegalStateException("First parameter should be of JsonObject type") + } + + val messageId = method.getDeclaredAnnotation(IncomingMessageHandler::class.java).id + + messageListeners.add(MessageListenerData(messageListener, method, messageId)) + } + } + } + + private fun setupPubSub() { + jedisPubSub = object : JedisPubSub() { + override fun onMessage(channel: String, message: String) { + if (channel.equals(this@Pidgin.channel, ignoreCase = true)) { + try { + val messagePayload = parser.parse(message).asJsonObject + val messageId = messagePayload.get("messageId").asString + val messageData = messagePayload.get("messageData").asJsonObject + + for (data in messageListeners) { + if (data.id == messageId) { + data.method.invoke(data.instance, messageData) + } + } + } catch (e: JsonParseException) { + println("[Pidgin] Expected JSON message but could not parse message") + e.printStackTrace() + } catch (e: Exception) { + println("[Pidgin] Failed to handle message") + e.printStackTrace() + } + } + } + } + + if (options.async) { + ForkJoinPool.commonPool().execute { jedisPool.resource.use { jedis -> jedis.subscribe(jedisPubSub!!, channel) } } + } else { + jedisPool.resource.use { jedis -> jedis.subscribe(jedisPubSub!!, channel) } + } + } + + companion object { + + val parser = JsonParser() + val morph = JsonMorph() + + } + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/nig/cock/nigger/PidginOptions.kt b/stark/core/src/main/kotlin/nig/cock/nigger/PidginOptions.kt new file mode 100644 index 0000000..51ea2de --- /dev/null +++ b/stark/core/src/main/kotlin/nig/cock/nigger/PidginOptions.kt @@ -0,0 +1,7 @@ +package nig.cock.nigger + +data class PidginOptions(val async: Boolean) { + + constructor() : this(true) + +} \ No newline at end of file diff --git a/stark/core/src/main/kotlin/nig/cock/nigger/message/Message.kt b/stark/core/src/main/kotlin/nig/cock/nigger/message/Message.kt new file mode 100644 index 0000000..be41821 --- /dev/null +++ b/stark/core/src/main/kotlin/nig/cock/nigger/message/Message.kt @@ -0,0 +1,7 @@ +package nig.cock.nigger.message + +class Message(var id: String, var data: Map) { + + constructor(id: String) : this(id, hashMapOf()) + +} diff --git a/stark/core/src/main/kotlin/nig/cock/nigger/message/handler/IncomingMessageHandler.kt b/stark/core/src/main/kotlin/nig/cock/nigger/message/handler/IncomingMessageHandler.kt new file mode 100644 index 0000000..6217d9f --- /dev/null +++ b/stark/core/src/main/kotlin/nig/cock/nigger/message/handler/IncomingMessageHandler.kt @@ -0,0 +1,5 @@ +package nig.cock.nigger.message.handler + +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) +@Retention(AnnotationRetention.RUNTIME) +annotation class IncomingMessageHandler(val id: String) diff --git a/stark/core/src/main/kotlin/nig/cock/nigger/message/handler/MessageExceptionHandler.kt b/stark/core/src/main/kotlin/nig/cock/nigger/message/handler/MessageExceptionHandler.kt new file mode 100644 index 0000000..8694a1a --- /dev/null +++ b/stark/core/src/main/kotlin/nig/cock/nigger/message/handler/MessageExceptionHandler.kt @@ -0,0 +1,10 @@ +package nig.cock.nigger.message.handler + +class MessageExceptionHandler { + + fun onException(e: Exception) { + println("Failed to send message") + e.printStackTrace() + } + +} diff --git a/stark/core/src/main/kotlin/nig/cock/nigger/message/listener/MessageListener.kt b/stark/core/src/main/kotlin/nig/cock/nigger/message/listener/MessageListener.kt new file mode 100644 index 0000000..e0480fd --- /dev/null +++ b/stark/core/src/main/kotlin/nig/cock/nigger/message/listener/MessageListener.kt @@ -0,0 +1,3 @@ +package nig.cock.nigger.message.listener + +interface MessageListener diff --git a/stark/core/src/main/kotlin/nig/cock/nigger/message/listener/MessageListenerData.kt b/stark/core/src/main/kotlin/nig/cock/nigger/message/listener/MessageListenerData.kt new file mode 100644 index 0000000..919f6d0 --- /dev/null +++ b/stark/core/src/main/kotlin/nig/cock/nigger/message/listener/MessageListenerData.kt @@ -0,0 +1,10 @@ +package nig.cock.nigger.message.listener + +import java.lang.reflect.Method + +/** + * A wrapper class that holds all the information needed to + * identify and execute a message function. + * + */ +data class MessageListenerData(val instance: Any, val method: Method, val id: String) diff --git a/stark/core/src/main/kotlin/nig/cock/nigger/morph/JsonMorph.kt b/stark/core/src/main/kotlin/nig/cock/nigger/morph/JsonMorph.kt new file mode 100644 index 0000000..005111c --- /dev/null +++ b/stark/core/src/main/kotlin/nig/cock/nigger/morph/JsonMorph.kt @@ -0,0 +1,147 @@ +package nig.cock.nigger.morph + +import nig.cock.nigger.morph.adapter.JsonTypeAdapter +import nig.cock.nigger.morph.adapter.impl.UUIDTypeAdapter +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonNull +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import java.lang.reflect.Array +import java.lang.reflect.Constructor +import java.lang.reflect.Modifier +import java.lang.reflect.Type +import java.util.HashMap +import java.util.UUID + +/** + * JsonMorph allows you to easily serialize Java types into + * JSON by using purely reflection or serialization through + * type adapters. + */ +class JsonMorph { + + private val typeAdapters: MutableMap, JsonTypeAdapter> + + init { + this.typeAdapters = HashMap(DEFAULT_ADAPTERS) + } + + fun registerTypeAdapter(typeAdapter: JsonTypeAdapter, type: Class): JsonMorph { + this.typeAdapters[type] = typeAdapter as JsonTypeAdapter + return this + } + + fun fromJson(json: JsonObject, typeOfSrc: Type): T? { + return null + } + + fun fromObject(src: T): JsonElement { + if (src is JsonElement) { + return src + } else if (typeAdapters.containsKey(src::class.java)) { + return typeAdapters[src::class.java]!!.toJson(src) + } else if (isSupportedType(src::class.java)) { + return serializeType(src) + } else if (src::class.java.isArray) { + val array = JsonArray() + + for (i in 0 until Array.getLength(src)) { + array.add(fromObject(Array.get(src, i))) + } + + return array + } else if (Map::class.java.isAssignableFrom(src.javaClass)) { + val obj = JsonObject() + + for (entry in (src as Map<*, *>).entries) { + if (entry.value != null) { + obj.add(entry.key.toString(), fromObject(entry.value!!)) + } + } + + return obj + } else if (Collection::class.java.isAssignableFrom(src.javaClass)) { + val array = JsonArray() + + for (obj in src as Collection<*>) { + if (obj != null) { + array.add(fromObject(obj)) + } + } + + return array + } else { + val obj = JsonObject() + + for (field in src::class.java.declaredFields) { + if (Modifier.isStatic(field.modifiers)) { + continue + } + + if (!field.isAccessible) { + field.isAccessible = true + } + + try { + if (typeAdapters.containsKey(field.type)) { + // JsonObject#add is null-safe + obj.add(field.name, typeAdapters[field.type]!!.toJson(field.get(src))) + } else if (isSupportedType(field.type)) { + obj.add(field.name, serializeType(field.get(src))) + } else { + obj.add(field.name, fromObject(field.get(src))) + } + } catch (e: Exception) { + e.printStackTrace() + } + + } + + return obj + } + } + + companion object { + + private val JSON_PRIMITIVE_EMPTY_CONSTRUCTOR: Constructor<*> + private val DEFAULT_ADAPTERS: MutableMap, JsonTypeAdapter> + + init { + try { + JSON_PRIMITIVE_EMPTY_CONSTRUCTOR = JsonPrimitive::class.java.getDeclaredConstructor(Any::class.java) + JSON_PRIMITIVE_EMPTY_CONSTRUCTOR.isAccessible = true + + DEFAULT_ADAPTERS = HashMap() + DEFAULT_ADAPTERS[UUID::class.java] = UUIDTypeAdapter() as JsonTypeAdapter + } catch (e: Exception) { + throw RuntimeException("Could not process static block", e) + } + } + + private fun isSupportedType(klass: Class): Boolean { + return klass.isPrimitive || + klass == String::class.java || + klass.name == Boolean::class.javaObjectType.name || + klass == Char::class.java || + klass.name == Char::class.javaObjectType.name || + klass == Boolean::class.java || + klass.name == Boolean::class.javaObjectType.name || + Number::class.java.isAssignableFrom(klass) + } + + private fun serializeType(`object`: Any?): JsonElement { + return if (`object` == null) { + JsonNull.INSTANCE + } else { + try { + JSON_PRIMITIVE_EMPTY_CONSTRUCTOR.newInstance(`object`) as JsonElement + } catch (e: Exception) { + throw RuntimeException("Could not instantiate a new JsonPrimitive instance") + } + + } + } + } + +} diff --git a/stark/core/src/main/kotlin/nig/cock/nigger/morph/adapter/JsonTypeAdapter.kt b/stark/core/src/main/kotlin/nig/cock/nigger/morph/adapter/JsonTypeAdapter.kt new file mode 100644 index 0000000..f12e24c --- /dev/null +++ b/stark/core/src/main/kotlin/nig/cock/nigger/morph/adapter/JsonTypeAdapter.kt @@ -0,0 +1,11 @@ +package nig.cock.nigger.morph.adapter + +import com.google.gson.JsonElement + +interface JsonTypeAdapter { + + fun toJson(src: T): JsonElement + + fun toType(element: JsonElement): T + +} diff --git a/stark/core/src/main/kotlin/nig/cock/nigger/morph/adapter/impl/UUIDTypeAdapter.kt b/stark/core/src/main/kotlin/nig/cock/nigger/morph/adapter/impl/UUIDTypeAdapter.kt new file mode 100644 index 0000000..478d0e2 --- /dev/null +++ b/stark/core/src/main/kotlin/nig/cock/nigger/morph/adapter/impl/UUIDTypeAdapter.kt @@ -0,0 +1,18 @@ +package nig.cock.nigger.morph.adapter.impl + +import nig.cock.nigger.morph.adapter.JsonTypeAdapter +import com.google.gson.JsonElement +import com.google.gson.JsonPrimitive +import java.util.UUID + +class UUIDTypeAdapter : JsonTypeAdapter { + + override fun toJson(src: UUID): JsonElement { + return JsonPrimitive(src.toString()) + } + + override fun toType(element: JsonElement): UUID { + return UUID.fromString(element.asString) + } + +} diff --git a/stark/core/src/main/kotlin/nig/cock/nigger/morph/annotation/MorphExclude.kt b/stark/core/src/main/kotlin/nig/cock/nigger/morph/annotation/MorphExclude.kt new file mode 100644 index 0000000..0efe4c4 --- /dev/null +++ b/stark/core/src/main/kotlin/nig/cock/nigger/morph/annotation/MorphExclude.kt @@ -0,0 +1,5 @@ +package nig.cock.nigger.morph.annotation + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FIELD) +annotation class MorphExclude diff --git a/stark/pom.xml b/stark/pom.xml new file mode 100644 index 0000000..7256cd1 --- /dev/null +++ b/stark/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + net.evilblock.stark + stark-parent + pom + 1.0-SNAPSHOT + + + bukkit + bungee + core + rank-converter + + + \ No newline at end of file diff --git a/stark/rank-converter/pom.xml b/stark/rank-converter/pom.xml new file mode 100644 index 0000000..3b28274 --- /dev/null +++ b/stark/rank-converter/pom.xml @@ -0,0 +1,124 @@ + + + + net.evilblock.stark + stark-parent + 1.0-SNAPSHOT + + 4.0.0 + + rank-converter + + + 1.3.41 + + + + + net.hylist + spigot-server + 1.7.10-R0.1-SNAPSHOT + provided + + + net.hylist + spigot-api + 1.7.10-R0.1-SNAPSHOT + provided + + + net.evilblock.stark + bukkit + 1.0-SNAPSHOT + provided + + + me.lucko.luckperms + luckperms-api + 4.4 + compile + + + + + Stark + src/main/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/main/java + + + + + test-compile + test-compile + + + ${project.basedir}/src/test/kotlin + ${project.basedir}/src/test/java + + + + + + + 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 + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.6 + + + make-assembly + package + single + + + jar-with-dependencies + + false + + + + + + + + \ No newline at end of file diff --git a/stark/rank-converter/src/main/kotlin/net/evilblock/stark/rankconverter/RankConverter.kt b/stark/rank-converter/src/main/kotlin/net/evilblock/stark/rankconverter/RankConverter.kt new file mode 100644 index 0000000..fa656e8 --- /dev/null +++ b/stark/rank-converter/src/main/kotlin/net/evilblock/stark/rankconverter/RankConverter.kt @@ -0,0 +1,13 @@ +package net.evilblock.stark.rankconverter + +import net.evilblock.stark.Stark + +interface RankConverter { + + fun convert() + + fun log(message: String) { + Stark.instance.logger.info("[Rank Conversion] $message") + } + +} \ No newline at end of file diff --git a/stark/rank-converter/src/main/kotlin/net/evilblock/stark/rankconverter/StarkRankConverter.kt b/stark/rank-converter/src/main/kotlin/net/evilblock/stark/rankconverter/StarkRankConverter.kt new file mode 100644 index 0000000..de94603 --- /dev/null +++ b/stark/rank-converter/src/main/kotlin/net/evilblock/stark/rankconverter/StarkRankConverter.kt @@ -0,0 +1,17 @@ +package net.evilblock.stark.rankconverter + +import net.evilblock.stark.Stark +import net.evilblock.stark.rankconverter.command.LuckPermsConvertCommand +import org.bukkit.Bukkit +import org.bukkit.plugin.java.JavaPlugin + +class StarkRankConverter : JavaPlugin() { + + override fun onEnable() { + if (Bukkit.getPluginManager().getPlugin("LuckPerms") != null) { + logger.info("Detected LuckPerms plugin!") + + Stark.instance.commandHandler.registerClass(LuckPermsConvertCommand::class.java) + } + } +} \ No newline at end of file diff --git a/stark/rank-converter/src/main/kotlin/net/evilblock/stark/rankconverter/command/LuckPermsConvertCommand.kt b/stark/rank-converter/src/main/kotlin/net/evilblock/stark/rankconverter/command/LuckPermsConvertCommand.kt new file mode 100644 index 0000000..6480b77 --- /dev/null +++ b/stark/rank-converter/src/main/kotlin/net/evilblock/stark/rankconverter/command/LuckPermsConvertCommand.kt @@ -0,0 +1,18 @@ +package net.evilblock.stark.rankconverter.command + +import net.evilblock.stark.engine.command.Command +import net.evilblock.stark.rankconverter.conversion.LuckPermsRankConverter +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender + +object LuckPermsConvertCommand { + + @Command(["rank convert lp"], permission = "op", async = true) + @JvmStatic + fun execute(sender: CommandSender) { + sender.sendMessage("${ChatColor.GREEN}Converting ranks from LuckPerms...") + LuckPermsRankConverter.convert() + sender.sendMessage("${ChatColor.GREEN}Done converting!") + } + +} \ No newline at end of file diff --git a/stark/rank-converter/src/main/kotlin/net/evilblock/stark/rankconverter/conversion/LuckPermsRankConverter.kt b/stark/rank-converter/src/main/kotlin/net/evilblock/stark/rankconverter/conversion/LuckPermsRankConverter.kt new file mode 100644 index 0000000..adc64ef --- /dev/null +++ b/stark/rank-converter/src/main/kotlin/net/evilblock/stark/rankconverter/conversion/LuckPermsRankConverter.kt @@ -0,0 +1,85 @@ +package net.evilblock.stark.rankconverter.conversion + +import net.evilblock.stark.Stark +import net.evilblock.stark.core.rank.Rank +import net.evilblock.stark.rankconverter.RankConverter +import me.lucko.luckperms.api.Contexts +import me.lucko.luckperms.api.Group +import me.lucko.luckperms.api.LuckPermsApi +import org.bukkit.Bukkit +import org.bukkit.ChatColor + +object LuckPermsRankConverter : RankConverter { + + override fun convert() { + val provider = Bukkit.getServicesManager().getRegistration(LuckPermsApi::class.java) + + if (provider != null) { + log("LuckPerms provider found") + + val weightedGroups = provider.provider.groups.sortedByDescending { group -> group.weight.orElse(0) } + log("Found ${weightedGroups.size} groups") //but from what i can see it cant even find the ranks + + val rankToGroup = hashMapOf() + + // create ranks from groups + for ((orderIndex, group) in weightedGroups.withIndex()) { + val rank = Rank(group.name) + rank.displayName = group.displayName ?: group.name + rank.displayOrder = orderIndex + rank.prefix = group.cachedData.getMetaData(Contexts.allowAll()).prefix ?: "" + rank.playerListPrefix = ChatColor.getLastColors(rank.prefix) + rank.gameColor = ChatColor.getLastColors(rank.prefix) + rank.permissions = group.ownNodes.map { node -> node.permission }.toMutableList() + + rankToGroup[rank] = group + } + + // setup rank inheritance + for ((rank, group) in rankToGroup) { + val inherits = arrayListOf() + + for ((otherRank, otherGroup) in rankToGroup) { + if (rank != otherRank) { + if (group.inheritsGroup(otherGroup)) { + inherits.add(otherRank.id) + } + } + } + + rank.inherits = inherits + } + + // save newly created ranks + for (rank in rankToGroup.keys) { + Stark.instance.core.rankHandler.getMutableRankMap()[rank.id] = rank + + log(rank.displayName) // if it prints in the console then it should saved ir read at least :) + } + + Stark.instance.core.rankHandler.saveAllRanks() + + log("Saved ${rankToGroup.size} ranks") + + // apply ranks to users from their groups + provider.provider.userManager.uniqueUsers.thenAcceptAsync { userSet -> + userSet.forEach { uuid -> + provider.provider.userManager.loadUser(uuid).thenAcceptAsync { user -> + val rank = Stark.instance.core.rankHandler.getById(user.primaryGroup) + + // check if a rank by an id of the user's primary group exists + if (rank != null) { + // populate uuid cache if name is not null, otherwise we'll lookup on cmd execute + if (user.name != null) { + Stark.instance.core.uuidCache.update(user.uuid, user.name!!) + } + + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "ogrant ${user.uuid} ${user.primaryGroup} perm Rank Conversion") + } + } + } + } + } + } + +} \ No newline at end of file diff --git a/stark/rank-converter/src/main/resources/plugin.yml b/stark/rank-converter/src/main/resources/plugin.yml new file mode 100644 index 0000000..0394719 --- /dev/null +++ b/stark/rank-converter/src/main/resources/plugin.yml @@ -0,0 +1,8 @@ +name: StarkRanksConverter +version: 1.0 +author: joeleoli +main: net.evilblock.stark.rankconverter.StarkRankConverter +depend: + - Stark +softdepend: + - LuckPerms \ No newline at end of file diff --git a/starkyboy b/starkyboy deleted file mode 160000 index c3c47d5..0000000 --- a/starkyboy +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c3c47d53396f52e1871677fb14dc49d2c0f0e07d