From 629bc7780382db9954e81f5ac01166f61ae64659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=9D=8F=E9=BB=91?= Date: Mon, 1 Apr 2019 21:45:49 +0800 Subject: [PATCH] + update --- build.gradle | 20 +- src/main/resources/Addons/TabooLibDeprecated | Bin 356981 -> 429457 bytes src/main/resources/plugin.yml | 2 +- .../eagletdl/AlreadyStartException.java | 1 + src/main/scala/com/ilummc/tlib/util/Ref.java | 1 - .../ilummc/tlibscala/runtime/RichPlayer.scala | 10 +- src/main/scala/me/skymc/taboolib/Main.java | 5 - .../me/skymc/taboolib/TabooLibLoader.java | 9 +- .../me/skymc/taboolib/client/LogClient.java | 58 - .../me/skymc/taboolib/cloud/TCloudLoader.java | 2 + .../skymc/taboolib/commands/SubCommand.java | 37 - .../taboolib/commands/SubCommandExecutor.java | 10 - .../commands/TabooLibMainCommand.java | 5 + .../builder/SimpleCommandBuilder.java | 19 +- .../commands/language/Language2Command.java | 78 - .../commands/taboolib/AttributesCommand.java | 20 - .../commands/taboolib/EnchantCommand.java | 22 - .../commands/taboolib/FlagCommand.java | 21 - .../commands/taboolib/ImportCommand.java | 50 - .../commands/taboolib/InfoCommand.java | 31 - .../commands/taboolib/ItemCommand.java | 63 - .../commands/taboolib/ItemListCommand.java | 25 - .../commands/taboolib/PotionCommand.java | 24 - .../commands/taboolib/SaveCommand.java | 89 - .../commands/taboolib/SlotCommand.java | 20 - .../commands/taboolib/SoundsCommand.java | 27 - .../commands/taboolib/TagDeleteCommand.java | 35 - .../commands/taboolib/TagDisplayCommand.java | 37 - .../commands/taboolib/TagPrefixCommand.java | 37 - .../commands/taboolib/TagSuffixCommand.java | 37 - .../commands/taboolib/VariableGetCommand.java | 38 - .../commands/taboolib/VariableSetCommand.java | 35 - .../listener/ListenerItemListCommand.java | 107 - .../listener/ListenerSoundsCommand.java | 119 - .../taboolib/common/inject/TInjectLoader.java | 8 +- .../skymc/taboolib/common/nms/NMSHandler.java | 7 - .../common/serialize/TSerializable.java | 20 + .../common/serialize/TSerializer.java | 149 + .../common/serialize/TSerializerElement.java | 15 + .../serialize/TSerializerElementGeneral.java | 217 ++ .../common/serialize/TSerializerExample.java | 85 + .../me/skymc/taboolib/damage/DamageUtils.java | 44 - .../skymc/taboolib/fileutils/EncodeUtils.java | 114 - .../me/skymc/taboolib/particle/EffLib.java | 3144 +++++++++-------- .../me/skymc/taboolib/sound/SoundPack.java | 82 +- .../me/skymc/taboolib/string/Language.java | 126 - .../skymc/taboolib/string/LanguagePack.java | 104 - .../skymc/taboolib/string/LanguageUtils.java | 33 - .../skymc/taboolib/string/PatternUtils.java | 23 - .../taboolib/string/language2/Language2.java | 102 - .../string/language2/Language2Format.java | 152 - .../string/language2/Language2Line.java | 15 - .../string/language2/Language2Type.java | 43 - .../string/language2/Language2Value.java | 216 -- .../language2/value/Language2Action.java | 100 - .../string/language2/value/Language2Book.java | 242 -- .../string/language2/value/Language2Json.java | 174 - .../language2/value/Language2Json2.java | 214 -- .../language2/value/Language2Sound.java | 46 - .../string/language2/value/Language2Text.java | 44 - .../language2/value/Language2Title.java | 103 - .../taboolib/support/SupportWorldGuard.java | 5 +- .../me/skymc/taboolib/update/UpdateTask.java | 75 +- 63 files changed, 2204 insertions(+), 4592 deletions(-) delete mode 100644 src/main/scala/me/skymc/taboolib/client/LogClient.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/SubCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/SubCommandExecutor.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/language/Language2Command.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/AttributesCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/EnchantCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/FlagCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/ImportCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/InfoCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/ItemCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/ItemListCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/PotionCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/SaveCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/SlotCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/SoundsCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/TagDeleteCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/TagDisplayCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/TagPrefixCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/TagSuffixCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/VariableGetCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/VariableSetCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/listener/ListenerItemListCommand.java delete mode 100644 src/main/scala/me/skymc/taboolib/commands/taboolib/listener/ListenerSoundsCommand.java create mode 100644 src/main/scala/me/skymc/taboolib/common/serialize/TSerializable.java create mode 100644 src/main/scala/me/skymc/taboolib/common/serialize/TSerializer.java create mode 100644 src/main/scala/me/skymc/taboolib/common/serialize/TSerializerElement.java create mode 100644 src/main/scala/me/skymc/taboolib/common/serialize/TSerializerElementGeneral.java create mode 100644 src/main/scala/me/skymc/taboolib/common/serialize/TSerializerExample.java delete mode 100644 src/main/scala/me/skymc/taboolib/damage/DamageUtils.java delete mode 100644 src/main/scala/me/skymc/taboolib/fileutils/EncodeUtils.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/Language.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/LanguagePack.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/LanguageUtils.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/PatternUtils.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/language2/Language2.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/language2/Language2Format.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/language2/Language2Line.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/language2/Language2Type.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/language2/Language2Value.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/language2/value/Language2Action.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/language2/value/Language2Book.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/language2/value/Language2Json.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/language2/value/Language2Json2.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/language2/value/Language2Sound.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/language2/value/Language2Text.java delete mode 100644 src/main/scala/me/skymc/taboolib/string/language2/value/Language2Title.java diff --git a/build.gradle b/build.gradle index 600dfad..fb7afbe 100644 --- a/build.gradle +++ b/build.gradle @@ -4,9 +4,8 @@ plugins { id 'idea' id 'com.github.johnrengelman.shadow' version '4.0.4' } - group = 'me.skymc' -version = '4.73' +version = '4.75' sourceCompatibility = 1.8 targetCompatibility = 1.8 @@ -28,11 +27,11 @@ dependencies { exclude(module: 'slf4j-log4j12') exclude(module: 'log4j') } - compile group: 'com.google.code.gson', name: 'gson', version: '2.7' shadow group: 'com.zaxxer', name: 'HikariCP', version: '3.1.0' shadow group: 'org.javalite', name: 'activejdbc', version: '2.0' + compile group: 'org.ow2.asm', name: 'asm', version: '7.0-beta' + compile group: 'com.google.code.gson', name: 'gson', version: '2.7' shadow group: 'com.h2database', name: 'h2', version: '1.4.197' - compile group: 'org.ow2.asm', name: 'asm', version: '6.1.1' shadow group: 'me.clip', name: 'placeholderapi', version: '2.8.4' shadow group: 'net.objecthunter', name: 'exp4j', version: '0.4.8' shadow group: 'org.scala-lang', name: 'scala-library', version: '2.12.8' @@ -42,4 +41,17 @@ dependencies { shadowJar { // 免得 MANIFEST.MF 里面刷一堆 lib 难看 taskActions.removeIf { it.actionClassName.contains 'configureShadowTask' } +} + +processResources { + inputs.property "version", project.version + + from(sourceSets.main.resources.srcDirs) { + include 'plugin.yml' + expand 'version': project.version + } + + from(sourceSets.main.resources.srcDirs) { + exclude 'plugin.yml' + } } \ No newline at end of file diff --git a/src/main/resources/Addons/TabooLibDeprecated b/src/main/resources/Addons/TabooLibDeprecated index 5d14287b7dd9aa2013a5a9bbc72e636fe4bb988c..bf21513e8116b98c1869cde335000179630c37d8 100644 GIT binary patch delta 85683 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 G0WmeRY3N7 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�PP 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 z TDependencyLoader.addToPath(TabooLib.instance(), listFile)); diff --git a/src/main/scala/me/skymc/taboolib/client/LogClient.java b/src/main/scala/me/skymc/taboolib/client/LogClient.java deleted file mode 100644 index 2daf023..0000000 --- a/src/main/scala/me/skymc/taboolib/client/LogClient.java +++ /dev/null @@ -1,58 +0,0 @@ -package me.skymc.taboolib.client; - -import javax.swing.*; -import javax.swing.border.BevelBorder; -import java.awt.*; -import java.text.SimpleDateFormat; - -@Deprecated -public class LogClient extends JFrame { - - /** - * DEFAULT VERSION UID - */ - private static final long serialVersionUID = 1L; - private JTextArea textArea = new JTextArea(); - private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); - - public LogClient(String title) { - super(title); - - // DEFAULT CLOSE OPERATION - setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - - // SETTINGS - final JScrollPane scrollPane = new JScrollPane(); - scrollPane.setBorder(new BevelBorder(BevelBorder.RAISED)); - scrollPane.setViewportView(textArea); - getContentPane().add(scrollPane, BorderLayout.CENTER); - - setSize(700, 500); - setVisible(true); - - // CON'T EDIT - textArea.setEditable(false); - textArea.setFont(new Font("黑体", 0, 18)); - - textArea.setBackground(Color.black); - textArea.setForeground(Color.LIGHT_GRAY); - - addString(title); - addString(""); - } - - public void addString(String a) { - textArea.append(a + '\n'); - textArea.setSelectionStart(textArea.getText().length()); - } - - public void info(String a) { - textArea.append("[" + sdf.format(System.currentTimeMillis()) + " INFO]: " + a + '\n'); - textArea.setSelectionStart(textArea.getText().length()); - } - - public void warn(String a) { - textArea.append("[" + sdf.format(System.currentTimeMillis()) + " WARN]: " + a + '\n'); - textArea.setSelectionStart(textArea.getText().length()); - } -} diff --git a/src/main/scala/me/skymc/taboolib/cloud/TCloudLoader.java b/src/main/scala/me/skymc/taboolib/cloud/TCloudLoader.java index 4be5c95..c498e7f 100644 --- a/src/main/scala/me/skymc/taboolib/cloud/TCloudLoader.java +++ b/src/main/scala/me/skymc/taboolib/cloud/TCloudLoader.java @@ -9,6 +9,7 @@ import me.skymc.taboolib.TabooLib; import me.skymc.taboolib.cloud.expansion.Expansion; import me.skymc.taboolib.cloud.expansion.ExpansionType; import me.skymc.taboolib.common.function.TFunction; +import me.skymc.taboolib.common.schedule.TSchedule; import me.skymc.taboolib.fileutils.FileUtils; import me.skymc.taboolib.plugin.PluginUtils; import org.bukkit.Bukkit; @@ -41,6 +42,7 @@ public class TCloudLoader { } } + @TSchedule(async = true, period = 20 * 60 * 60) public static void refresh() { Bukkit.getScheduler().runTaskAsynchronously(TabooLib.instance(), () -> { long time = System.currentTimeMillis(); diff --git a/src/main/scala/me/skymc/taboolib/commands/SubCommand.java b/src/main/scala/me/skymc/taboolib/commands/SubCommand.java deleted file mode 100644 index a4397bb..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/SubCommand.java +++ /dev/null @@ -1,37 +0,0 @@ -package me.skymc.taboolib.commands; - -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -@Deprecated -public abstract class SubCommand { - - public CommandSender sender; - public String[] args; - - public boolean returnValue = false; - - public SubCommand(CommandSender sender, String[] args) { - this.sender = sender; - this.args = args; - } - - public boolean setReturn(boolean returnValue) { - return this.returnValue = returnValue; - } - - public boolean isPlayer() { - return sender instanceof Player; - } - - public boolean command() { - return returnValue; - } - - public String getArgs(int size) { - return IntStream.range(size, args.length).mapToObj(i -> args[i] + " ").collect(Collectors.joining()).trim(); - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/SubCommandExecutor.java b/src/main/scala/me/skymc/taboolib/commands/SubCommandExecutor.java deleted file mode 100644 index 2633b0a..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/SubCommandExecutor.java +++ /dev/null @@ -1,10 +0,0 @@ -package me.skymc.taboolib.commands; - -import org.bukkit.command.CommandSender; - -@Deprecated -public interface SubCommandExecutor { - - boolean command(CommandSender sender, String[] args); - -} diff --git a/src/main/scala/me/skymc/taboolib/commands/TabooLibMainCommand.java b/src/main/scala/me/skymc/taboolib/commands/TabooLibMainCommand.java index 6582ea4..c95429c 100644 --- a/src/main/scala/me/skymc/taboolib/commands/TabooLibMainCommand.java +++ b/src/main/scala/me/skymc/taboolib/commands/TabooLibMainCommand.java @@ -116,6 +116,11 @@ public class TabooLibMainCommand extends BaseMainCommand { @CommandRegister(priority = 3) BaseSubCommand itemInfo = new BaseSubCommand() { + @Override + public boolean hideInHelp() { + return true; + } + @Override public String getLabel() { return "itemInfo"; diff --git a/src/main/scala/me/skymc/taboolib/commands/builder/SimpleCommandBuilder.java b/src/main/scala/me/skymc/taboolib/commands/builder/SimpleCommandBuilder.java index caefc18..de03b95 100644 --- a/src/main/scala/me/skymc/taboolib/commands/builder/SimpleCommandBuilder.java +++ b/src/main/scala/me/skymc/taboolib/commands/builder/SimpleCommandBuilder.java @@ -1,6 +1,7 @@ package me.skymc.taboolib.commands.builder; import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; import me.skymc.taboolib.commands.builder.type.CompleterCommand; import me.skymc.taboolib.commands.builder.type.CompleterTab; import me.skymc.taboolib.commands.internal.TCommandHandler; @@ -119,8 +120,22 @@ public class SimpleCommandBuilder { aliases, permission, permissionMessage, - (sender, command, s, args) -> completerCommand.execute(sender, args), - (sender, command, s, args) -> completerTab.execute(sender, args), + (sender, command, s, args) -> { + try { + return completerCommand.execute(sender, args); + } catch (Throwable t) { + t.printStackTrace(); + } + return false; + }, + (sender, command, s, args) -> { + try { + return completerTab.execute(sender, args); + } catch (Throwable t) { + t.printStackTrace(); + } + return Lists.newArrayList(); + }, silence); build = true; return this; diff --git a/src/main/scala/me/skymc/taboolib/commands/language/Language2Command.java b/src/main/scala/me/skymc/taboolib/commands/language/Language2Command.java deleted file mode 100644 index 6cc56cc..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/language/Language2Command.java +++ /dev/null @@ -1,78 +0,0 @@ -package me.skymc.taboolib.commands.language; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.Main; -import me.skymc.taboolib.TabooLib; -import me.skymc.taboolib.commands.builder.SimpleCommandBuilder; -import me.skymc.taboolib.common.loader.Instantiable; -import me.skymc.taboolib.string.language2.Language2Value; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -/** - * @author sky - * @since 2018年2月13日 下午5:11:01 - */ -@Instantiable("Language2Command") -public class Language2Command { - - public Language2Command() { - SimpleCommandBuilder.create("language2", TabooLib.instance()) - .aliases("lang2") - .permission("taboolib.admin") - .execute((sender, args) -> { - if (args.length == 0) { - TLocale.sendTo(sender, "COMMANDS.LANGUAGE2.HELP", "langauge2"); - } else if ("reload".equalsIgnoreCase(args[0])) { - reload(sender); - } else if ("send".equalsIgnoreCase(args[0])) { - send(sender, args); - } - return true; - }).build(); - } - - private void send(CommandSender sender, String[] args) { - if (args.length < 3) { - TLocale.sendTo(sender, "COMMANDS.PARAMETER.UNKNOWN"); - } else { - long time = System.currentTimeMillis(); - Language2Value value = getLanguage2Value(args); - - if ("ALL".equalsIgnoreCase(args[1])) { - value.broadcast(); - } else { - Player player = Bukkit.getPlayerExact(args[1]); - if (player == null) { - TLocale.sendTo(sender, "COMMANDS.LANGUAGE2.INVALID-PLAYER", args[1]); - } else { - value.send(player); - } - } - - if (sender instanceof Player && ((Player) sender).getItemInHand().getType().equals(Material.COMMAND)) { - TLocale.sendTo(sender, "COMMANDS.LANGUAGE2.SUCCESS-SEND", String.valueOf(System.currentTimeMillis() - time)); - } - } - } - - private Language2Value getLanguage2Value(String[] args) { - Language2Value value = Main.getExampleLanguage2().get(args[2]); - if (args.length > 3) { - int i = 0; - for (String variable : args[3].split("\\|")) { - value.addPlaceholder("$" + i++, variable); - } - } - return value; - } - - private void reload(CommandSender sender) { - TLocale.sendTo(sender, "COMMANDS.RELOAD.LOADING"); - long time = System.currentTimeMillis(); - Main.getExampleLanguage2().reload(); - TLocale.sendTo(sender, "COMMANDS.RELOAD.SUCCESS-ELAPSED-TIME", String.valueOf(System.currentTimeMillis() - time)); - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/AttributesCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/AttributesCommand.java deleted file mode 100644 index 1ec3bf4..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/AttributesCommand.java +++ /dev/null @@ -1,20 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.commands.SubCommand; -import org.bukkit.command.CommandSender; - -import java.util.Arrays; - -public class AttributesCommand extends SubCommand { - - public AttributesCommand(CommandSender sender, String[] args) { - super(sender, args); - - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.ATTRIBUTES.HEAD"); - - Arrays.stream(new String[]{"damage", "speed", "attackspeed", "health", "knockback", "armor", "luck"}).forEach(attribute -> TLocale.sendTo(sender, "COMMANDS.TABOOLIB.ATTRIBUTES.BODY", attribute)); - - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.ATTRIBUTES.FOOT"); - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/EnchantCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/EnchantCommand.java deleted file mode 100644 index b0331eb..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/EnchantCommand.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.commands.SubCommand; -import org.bukkit.command.CommandSender; -import org.bukkit.enchantments.Enchantment; - -import java.util.Arrays; - -public class EnchantCommand extends SubCommand { - - @SuppressWarnings("deprecation") - public EnchantCommand(CommandSender sender, String[] args) { - super(sender, args); - - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.ENCHANTS.HEAD"); - - Arrays.stream(Enchantment.values()).forEach(enchant -> TLocale.sendTo(sender, "COMMANDS.TABOOLIB.ENCHANTS.BODY", String.valueOf(enchant.getId()), enchant.getName())); - - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.ENCHANTS.FOOT"); - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/FlagCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/FlagCommand.java deleted file mode 100644 index 05af222..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/FlagCommand.java +++ /dev/null @@ -1,21 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.commands.SubCommand; -import org.bukkit.command.CommandSender; -import org.bukkit.inventory.ItemFlag; - -import java.util.Arrays; - -public class FlagCommand extends SubCommand { - - public FlagCommand(CommandSender sender, String[] args) { - super(sender, args); - - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.FLAGS.HEAD"); - - Arrays.stream(ItemFlag.values()).forEach(itemFlag -> TLocale.sendTo(sender, "COMMANDS.TABOOLIB.ENCHANTS.BODY", itemFlag.name())); - - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.FLAGS.FOOT"); - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/ImportCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/ImportCommand.java deleted file mode 100644 index 077e847..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/ImportCommand.java +++ /dev/null @@ -1,50 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.Main; -import me.skymc.taboolib.Main.StorageType; -import me.skymc.taboolib.commands.SubCommand; -import me.skymc.taboolib.fileutils.ConfigUtils; -import org.bukkit.command.CommandSender; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.util.Objects; - -public class ImportCommand extends SubCommand { - - public ImportCommand(CommandSender sender, String[] args) { - super(sender, args); - - if (isPlayer()) { - TLocale.sendTo(sender, "COMMANDS.GLOBAL.ONLY-PLAYER"); - return; - } - - if (Main.getStorageType() == StorageType.LOCAL) { - TLocale.Logger.warn("COMMANDS.GLOBAL.ONLY-STORAGE-SQL"); - return; - } - - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.IMPORTDATA.CLEARING"); - Main.getConnection().truncateTable(Main.getTablePrefix() + "_playerdata"); - - if (!Main.getPlayerDataFolder().exists()) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.IMPORTDATA.EMPTYDATA"); - return; - } - - int size = Objects.requireNonNull(Main.getPlayerDataFolder().listFiles()).length; - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.IMPORTDATA.IMPORTING-START", String.valueOf(size)); - - int loop = 1; - for (File file : Objects.requireNonNull(Main.getPlayerDataFolder().listFiles())) { - Main.getConnection().intoValue(Main.getTablePrefix() + "_playerdata", file.getName().replace(".yml", ""), ConfigUtils.encodeYAML(YamlConfiguration.loadConfiguration(file))); - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.IMPORTDATA.IMPORTING-PROGRESS", file.getName().replace(".yml", ""), String.valueOf(loop), String.valueOf(size)); - loop++; - } - - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.IMPORTDATA.SUCCESS"); - } - -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/InfoCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/InfoCommand.java deleted file mode 100644 index 1eae955..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/InfoCommand.java +++ /dev/null @@ -1,31 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.commands.SubCommand; -import me.skymc.taboolib.inventory.ItemUtils; -import me.skymc.taboolib.itemnbtapi.NBTItem; -import me.skymc.taboolib.json.JSONReader; -import org.bukkit.Material; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -public class InfoCommand extends SubCommand { - - @SuppressWarnings("deprecation") - public InfoCommand(CommandSender sender, String[] args) { - super(sender, args); - if (isPlayer()) { - Player player = (Player) sender; - if (player.getItemInHand().getType().equals(Material.AIR)) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.INFO.INVALID-ITEM"); - } else { - NBTItem nbt = new NBTItem(player.getItemInHand()); - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.INFO.ITEM-INFO", - player.getItemInHand().getType().name(), - ItemUtils.getCustomName(player.getItemInHand()), - player.getItemInHand().getTypeId() + ":" + player.getItemInHand().getDurability(), - JSONReader.formatJson(nbt.asNBTString())); - } - } - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/ItemCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/ItemCommand.java deleted file mode 100644 index 7d13386..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/ItemCommand.java +++ /dev/null @@ -1,63 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.commands.SubCommand; -import me.skymc.taboolib.inventory.ItemUtils; -import me.skymc.taboolib.other.NumberUtils; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -import java.util.HashMap; - -public class ItemCommand extends SubCommand { - - public ItemCommand(CommandSender sender, String[] args) { - super(sender, args); - - if (args.length < 2) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.ITEM.INVALID-NAME"); - return; - } - - if (ItemUtils.getCacheItem(args[1]) == null) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.ITEM.INVALID-ITEM", args[1]); - return; - } - - Player player; - Integer amount = 1; - ItemStack item = ItemUtils.getCacheItem(args[1]).clone(); - - if (args.length > 2) { - player = Bukkit.getPlayerExact(args[2]); - if (player == null) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.ITEM.INVALID-PLAYER", args[2]); - return; - } - } else if (sender instanceof Player) { - player = (Player) sender; - } else { - TLocale.sendTo(sender, "COMMANDS.GLOBAL.ONLY-PLAYER"); - return; - } - - if (args.length > 3) { - amount = NumberUtils.getInteger(args[3]); - if (amount < 1) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.ITEM.INVALID-NUMBER"); - return; - } - } - item.setAmount(amount); - - HashMap map = player.getInventory().addItem(item); - if (map.size() > 0) { - player.getWorld().dropItem(player.getLocation(), item); - } - - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.ITEM.SUCCESS", player.getName()); - setReturn(true); - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/ItemListCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/ItemListCommand.java deleted file mode 100644 index dcf062d..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/ItemListCommand.java +++ /dev/null @@ -1,25 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import me.skymc.taboolib.commands.SubCommand; -import me.skymc.taboolib.commands.taboolib.listener.ListenerItemListCommand; -import me.skymc.taboolib.other.NumberUtils; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -/** - * @author sky - * @since 2018年2月4日 下午8:08:22 - */ -public class ItemListCommand extends SubCommand { - - public ItemListCommand(CommandSender sender, String[] args) { - super(sender, args); - if (isPlayer()) { - if (args.length == 1) { - ListenerItemListCommand.openInventory((Player) sender, 1); - } else { - ListenerItemListCommand.openInventory((Player) sender, NumberUtils.getInteger(args[1])); - } - } - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/PotionCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/PotionCommand.java deleted file mode 100644 index cdd3e3c..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/PotionCommand.java +++ /dev/null @@ -1,24 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.commands.SubCommand; -import org.bukkit.command.CommandSender; -import org.bukkit.potion.PotionEffectType; - -public class PotionCommand extends SubCommand { - - @SuppressWarnings("deprecation") - public PotionCommand(CommandSender sender, String[] args) { - super(sender, args); - - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.POTIONS.HEAD"); - - for (PotionEffectType potionEffectType : PotionEffectType.values()) { - if (potionEffectType != null) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.POTIONS.BODY", String.valueOf(potionEffectType.getId()), potionEffectType.getName()); - } - } - - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.POTIONS.FOOT"); - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/SaveCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/SaveCommand.java deleted file mode 100644 index 667d84a..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/SaveCommand.java +++ /dev/null @@ -1,89 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.Main; -import me.skymc.taboolib.commands.SubCommand; -import me.skymc.taboolib.fileutils.ConfigUtils; -import me.skymc.taboolib.inventory.ItemUtils; -import me.skymc.taboolib.message.ChatCatcher; -import me.skymc.taboolib.message.ChatCatcher.Catcher; -import me.skymc.taboolib.message.MsgUtils; -import me.skymc.taboolib.playerdata.DataUtils; -import org.bukkit.Material; -import org.bukkit.command.CommandSender; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -/** - * @author sky - */ -public class SaveCommand extends SubCommand { - - public SaveCommand(CommandSender sender, String[] args) { - super(sender, args); - if (!(sender instanceof Player)) { - TLocale.sendTo(sender, "COMMANDS.GLOBAL.ONLY-PLAYER"); - return; - } - - if (args.length < 2) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.SAVE.INVALID-NAME"); - return; - } - - if (((Player) sender).getItemInHand().getType().equals(Material.AIR)) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.SAVE.INVALID-ITEM"); - return; - } - - if (ItemUtils.getItemCachesFinal().containsKey(args[1])) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.SAVE.INVALID-ITEM-FINAL-EXISTS"); - return; - } - - if (ItemUtils.getItemCaches().containsKey(args[1])) { - // 检查聊天引导 - if (ChatCatcher.contains((Player) sender)) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.SAVE.GUIDE-EXISTS"); - return; - } - - ChatCatcher.call((Player) sender, new Catcher() { - - @Override - public void cancel() { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.SAVE.GUIDE-QUIT"); - } - - @Override - public Catcher before() { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.SAVE.GUIDE-BEFORE", args[1]); - return this; - } - - @SuppressWarnings("deprecation") - @Override - public boolean after(String message) { - if ("yes".equalsIgnoreCase(message)) { - saveItem(args[1], ((Player) sender).getItemInHand()); - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.SAVE.SUCCESS", args[1]); - } else { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.SAVE.GUIDE-QUIT"); - } - return false; - } - }); - } else { - saveItem(args[1], ((Player) sender).getItemInHand()); - MsgUtils.send(sender, "物品 &f" + args[1] + " &7已保存"); - } - } - - private void saveItem(String name, ItemStack item) { - FileConfiguration conf = ConfigUtils.load(Main.getInst(), ItemUtils.getItemCacheFile()); - conf.set(name + ".bukkit", item); - DataUtils.saveConfiguration(conf, ItemUtils.getItemCacheFile()); - ItemUtils.reloadItemCache(); - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/SlotCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/SlotCommand.java deleted file mode 100644 index 4897a70..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/SlotCommand.java +++ /dev/null @@ -1,20 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.commands.SubCommand; -import org.bukkit.command.CommandSender; - -import java.util.Arrays; - -public class SlotCommand extends SubCommand { - - public SlotCommand(CommandSender sender, String[] args) { - super(sender, args); - - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.SLOTS.HEAD"); - - Arrays.stream(new String[]{"mainhand", "offhand", "feet", "legs", "chest", "head", "all"}).forEach(slots -> TLocale.sendTo(sender, "COMMANDS.TABOOLIB.SLOTS.BODY", slots)); - - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.SLOTS.FOOT"); - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/SoundsCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/SoundsCommand.java deleted file mode 100644 index 2d2f63f..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/SoundsCommand.java +++ /dev/null @@ -1,27 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import me.skymc.taboolib.commands.SubCommand; -import me.skymc.taboolib.commands.taboolib.listener.ListenerSoundsCommand; -import me.skymc.taboolib.other.NumberUtils; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -/** - * @author sky - * @since 2018-03-18 21:02:26 - */ -public class SoundsCommand extends SubCommand { - - public SoundsCommand(CommandSender sender, String[] args) { - super(sender, args); - if (isPlayer()) { - if (args.length == 1) { - ListenerSoundsCommand.openInventory((Player) sender, 1, null); - } else if (args.length == 2) { - ListenerSoundsCommand.openInventory((Player) sender, NumberUtils.getInteger(args[1]), null); - } else { - ListenerSoundsCommand.openInventory((Player) sender, NumberUtils.getInteger(args[1]), args[2]); - } - } - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/TagDeleteCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/TagDeleteCommand.java deleted file mode 100644 index 1e7d565..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/TagDeleteCommand.java +++ /dev/null @@ -1,35 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.commands.SubCommand; -import me.skymc.taboolib.itagapi.TagDataHandler; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -/** - * @author sky - * @since 2018-03-19 23:13:35 - */ -public class TagDeleteCommand extends SubCommand { - - public TagDeleteCommand(CommandSender sender, String[] args) { - super(sender, args); - if (args.length < 2) { - TLocale.sendTo(sender, "COMMANDS.PARAMETER.UNKNOWN"); - return; - } - - Player player = Bukkit.getPlayerExact(args[1]); - if (player == null) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.PLAYERTAG.INVALID-PLAYER", args[1]); - return; - } - - TagDataHandler.getHandler().reset(player); - - if (sender instanceof Player) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.PLAYERTAG.SUCCESS-DELETE", args[1]); - } - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/TagDisplayCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/TagDisplayCommand.java deleted file mode 100644 index 8808e6d..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/TagDisplayCommand.java +++ /dev/null @@ -1,37 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.commands.SubCommand; -import me.skymc.taboolib.itagapi.TagDataHandler; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -/** - * @author sky - * @since 2018-03-19 23:13:35 - */ -public class TagDisplayCommand extends SubCommand { - - public TagDisplayCommand(CommandSender sender, String[] args) { - super(sender, args); - if (args.length < 3) { - TLocale.sendTo(sender, "COMMANDS.PARAMETER.UNKNOWN"); - return; - } - - Player player = Bukkit.getPlayerExact(args[1]); - if (player == null) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.PLAYERTAG.INVALID-PLAYER", args[1]); - return; - } - - String value = TLocale.Translate.setPlaceholders(player, getArgs(2)); - TagDataHandler.getHandler().setDisplay(player, value); - - if (sender instanceof Player) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.PLAYERTAG.SUCCESS-DISPLAY-SET", args[1], value); - } - } - -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/TagPrefixCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/TagPrefixCommand.java deleted file mode 100644 index 895be34..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/TagPrefixCommand.java +++ /dev/null @@ -1,37 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.commands.SubCommand; -import me.skymc.taboolib.itagapi.TagDataHandler; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -/** - * @author sky - * @since 2018-03-19 23:13:35 - */ -public class TagPrefixCommand extends SubCommand { - - public TagPrefixCommand(CommandSender sender, String[] args) { - super(sender, args); - if (args.length < 3) { - TLocale.sendTo(sender, "COMMANDS.PARAMETER.UNKNOWN"); - return; - } - - Player player = Bukkit.getPlayerExact(args[1]); - if (player == null) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.PLAYERTAG.INVALID-PLAYER", args[1]); - return; - } - - String value = TLocale.Translate.setPlaceholders(player, getArgs(2)); - TagDataHandler.getHandler().setPrefix(player, value); - - if (sender instanceof Player) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.PLAYERTAG.SUCCESS-PREFIX-SET", args[1], value); - } - } - -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/TagSuffixCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/TagSuffixCommand.java deleted file mode 100644 index 08d5b9f..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/TagSuffixCommand.java +++ /dev/null @@ -1,37 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.commands.SubCommand; -import me.skymc.taboolib.itagapi.TagDataHandler; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -/** - * @author sky - * @since 2018-03-19 23:13:35 - */ -public class TagSuffixCommand extends SubCommand { - - public TagSuffixCommand(CommandSender sender, String[] args) { - super(sender, args); - if (args.length < 3) { - TLocale.sendTo(sender, "COMMANDS.PARAMETER.UNKNOWN"); - return; - } - - Player player = Bukkit.getPlayerExact(args[1]); - if (player == null) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.PLAYERTAG.INVALID-PLAYER", args[1]); - return; - } - - String value = TLocale.Translate.setPlaceholders(player, getArgs(2)); - TagDataHandler.getHandler().setSuffix(player, value); - - if (sender instanceof Player) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.PLAYERTAG.SUCCESS-SUFFIX-SET", args[1], value); - } - } - -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/VariableGetCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/VariableGetCommand.java deleted file mode 100644 index 7688b43..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/VariableGetCommand.java +++ /dev/null @@ -1,38 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.commands.SubCommand; -import me.skymc.taboolib.database.GlobalDataManager; -import org.bukkit.command.CommandSender; - -/** - * @author sky - */ -public class VariableGetCommand extends SubCommand { - - public VariableGetCommand(CommandSender sender, String[] args) { - super(sender, args); - - if (args.length < 3) { - TLocale.sendTo(sender, "COMMANDS.PARAMETER.INSUFFICIENT"); - return; - } - - if (!("-a".equals(args[1]) || "-s".equals(args[1]))) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.VARIABLE.READ-ERROR-TYPE"); - return; - } - - Long time = System.currentTimeMillis(); - String value = null; - - if ("-s".equals(args[1])) { - value = GlobalDataManager.getVariable(args[2], null); - } else if ("-a".equals(args[1])) { - value = GlobalDataManager.getVariableAsynchronous(args[2], null); - } - - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.VARIABLE.READ-SUCCESS", String.valueOf(System.currentTimeMillis() - time)); - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.VARIABLE.READ-RESULT", value == null ? "null" : value); - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/VariableSetCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/VariableSetCommand.java deleted file mode 100644 index d1f9634..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/VariableSetCommand.java +++ /dev/null @@ -1,35 +0,0 @@ -package me.skymc.taboolib.commands.taboolib; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.commands.SubCommand; -import me.skymc.taboolib.database.GlobalDataManager; -import org.bukkit.command.CommandSender; - -public class VariableSetCommand extends SubCommand { - - public VariableSetCommand(CommandSender sender, String[] args) { - super(sender, args); - - if (args.length < 4) { - TLocale.sendTo(sender, "COMMANDS.PARAMETER.INSUFFICIENT"); - return; - } - - if (!("-a".equals(args[1]) || "-s".equals(args[1]))) { - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.VARIABLE.WRITE-ERROR-TYPE"); - return; - } - - Long time = System.currentTimeMillis(); - String value = getArgs(3); - - if ("-s".equals(args[1])) { - GlobalDataManager.setVariable(args[2], value); - } else if ("-a".equals(args[1])) { - GlobalDataManager.setVariableAsynchronous(args[2], value); - } - - TLocale.sendTo(sender, "COMMANDS.TABOOLIB.VARIABLE.WRITE-SUCCESS", String.valueOf(System.currentTimeMillis() - time)); - setReturn(true); - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/listener/ListenerItemListCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/listener/ListenerItemListCommand.java deleted file mode 100644 index 0346b2f..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/listener/ListenerItemListCommand.java +++ /dev/null @@ -1,107 +0,0 @@ -package me.skymc.taboolib.commands.taboolib.listener; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.inventory.InventoryUtil; -import me.skymc.taboolib.inventory.ItemUtils; -import me.skymc.taboolib.listener.TListener; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; - -/** - * @author sky - * @since 2018年2月4日 下午4:35:00 - */ -@TListener -public class ListenerItemListCommand implements Listener { - - /** - * 打开物品库界面 - * - * @param player - * @param page - */ - public static void openInventory(Player player, int page) { - ItemLibraryHolder holder = new ItemLibraryHolder(page); - Inventory inventory = Bukkit.createInventory(holder, 54, TLocale.asString("COMMANDS.TABOOLIB.ITEMLIST.MENU.TITLE", String.valueOf(page))); - - LinkedHashMap map = new LinkedHashMap<>(); - map.putAll(ItemUtils.getItemCachesFinal()); - map.putAll(ItemUtils.getItemCaches()); - - int loop = 0; - for (String name : map.keySet()) { - if (loop >= (page - 1) * 28) { - if (loop < page * 28) { - int slot = InventoryUtil.SLOT_OF_CENTENTS.get(loop - ((page - 1) * 28)); - ItemStack item = map.get(name).clone(); - ItemMeta meta = item.getItemMeta(); - List lore = meta.hasLore() ? meta.getLore() : new ArrayList<>(); - lore.addAll(TLocale.asStringList("COMMANDS.TABOOLIB.ITEMLIST.MENU.LORE", name)); - meta.setLore(lore); - item.setItemMeta(meta); - inventory.setItem(slot, item); - holder.ITEMS_DATA.put(slot, name); - } else { - break; - } - } - loop++; - } - - if (page > 1) { - inventory.setItem(47, ItemUtils.setName(new ItemStack(Material.ARROW), TLocale.asString("COMMANDS.TABOOLIB.ITEMLIST.MENU.BACK"))); - } - if (((int) Math.ceil(map.size() / 28D)) > page) { - inventory.setItem(51, ItemUtils.setName(new ItemStack(Material.ARROW), TLocale.asString("COMMANDS.TABOOLIB.ITEMLIST.MENU.NEXT"))); - } - player.openInventory(inventory); - } - - @EventHandler - public void inventoryClick(InventoryClickEvent e) { - if (e.getInventory().getHolder() instanceof ItemLibraryHolder) { - e.setCancelled(true); - - if (e.getCurrentItem() == null || e.getCurrentItem().getType().equals(Material.AIR) || e.getRawSlot() >= e.getInventory().getSize()) { - return; - } - - int i = e.getRawSlot(); - if (i == 47) { - openInventory((Player) e.getWhoClicked(), ((ItemLibraryHolder) e.getInventory().getHolder()).PAGE - 1); - } else if (i == 51) { - openInventory((Player) e.getWhoClicked(), ((ItemLibraryHolder) e.getInventory().getHolder()).PAGE + 1); - } else { - e.getWhoClicked().getInventory().addItem(ItemUtils.getCacheItem(((ItemLibraryHolder) e.getInventory().getHolder()).ITEMS_DATA.get(e.getRawSlot()))); - } - } - } - - public static class ItemLibraryHolder implements InventoryHolder { - - public final int PAGE; - public final HashMap ITEMS_DATA = new HashMap<>(); - - public ItemLibraryHolder(int page) { - this.PAGE = page; - } - - @Override - public Inventory getInventory() { - return null; - } - } -} diff --git a/src/main/scala/me/skymc/taboolib/commands/taboolib/listener/ListenerSoundsCommand.java b/src/main/scala/me/skymc/taboolib/commands/taboolib/listener/ListenerSoundsCommand.java deleted file mode 100644 index 3c42a88..0000000 --- a/src/main/scala/me/skymc/taboolib/commands/taboolib/listener/ListenerSoundsCommand.java +++ /dev/null @@ -1,119 +0,0 @@ -package me.skymc.taboolib.commands.taboolib.listener; - -import com.ilummc.tlib.resources.TLocale; -import me.skymc.taboolib.inventory.InventoryUtil; -import me.skymc.taboolib.inventory.ItemUtils; -import me.skymc.taboolib.listener.TListener; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.Sound; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.stream.Collectors; - -/** - * @author sky - * @since 2018年2月4日 下午4:35:00 - */ -@TListener -public class ListenerSoundsCommand implements Listener { - - public static void openInventory(Player player, int page, String search) { - if (page < 1) { - page = 1; - } - - SoundLibraryHolder holder = new SoundLibraryHolder(page, search); - Inventory inventory = Bukkit.createInventory(holder, 54, TLocale.asString("COMMANDS.TABOOLIB.SOUNDS.MENU.TITLE", String.valueOf(page))); - List soundFilter = Arrays.stream(Sound.values()).filter(sound -> search == null || sound.name().contains(search.toUpperCase())).collect(Collectors.toList()); - List soundLore = TLocale.asStringList("COMMANDS.TABOOLIB.SOUNDS.MENU.LORE"); - - int loop = 0; - for (Sound sound : soundFilter) { - if (loop >= (page - 1) * 28) { - if (loop < page * 28) { - int slot = InventoryUtil.SLOT_OF_CENTENTS.get(loop - ((page - 1) * 28)); - ItemStack item = new ItemStack(Material.MAP); - ItemMeta meta = item.getItemMeta(); - meta.setDisplayName("§f§n" + sound); - meta.setLore(soundLore); - item.setItemMeta(meta); - inventory.setItem(slot, item); - holder.SOUNDS_DATA.put(slot, sound); - } else { - break; - } - } - loop++; - } - - if (page > 1) { - inventory.setItem(47, ItemUtils.setName(new ItemStack(Material.ARROW), TLocale.asString("COMMANDS.TABOOLIB.SOUNDS.MENU.BACK"))); - } - if (((int) Math.ceil(Sound.values().length / 28D)) > page) { - inventory.setItem(51, ItemUtils.setName(new ItemStack(Material.ARROW), TLocale.asString("COMMANDS.TABOOLIB.SOUNDS.MENU.NEXT"))); - } - - if (!(player.getOpenInventory().getTopInventory().getHolder() instanceof SoundLibraryHolder)) { - TLocale.sendTo(player, "COMMANDS.TABOOLIB.SOUNDS.RESULT.SEARCH", (search == null ? "*" : search), String.valueOf(soundFilter.size())); - } - player.openInventory(inventory); - } - - @EventHandler - public void inventoryClick(InventoryClickEvent e) { - if (e.getInventory().getHolder() instanceof SoundLibraryHolder) { - e.setCancelled(true); - - if (e.getCurrentItem() == null || e.getCurrentItem().getType().equals(Material.AIR) || e.getRawSlot() >= e.getInventory().getSize()) { - return; - } - - SoundLibraryHolder soundLibraryHolder = ((SoundLibraryHolder) e.getInventory().getHolder()); - int i = e.getRawSlot(); - if (i == 47) { - openInventory((Player) e.getWhoClicked(), soundLibraryHolder.PAGE - 1, soundLibraryHolder.SEARCH); - } else if (i == 51) { - openInventory((Player) e.getWhoClicked(), soundLibraryHolder.PAGE + 1, soundLibraryHolder.SEARCH); - } else { - Sound sound = soundLibraryHolder.SOUNDS_DATA.get(e.getRawSlot()); - if (e.getClick().isKeyboardClick()) { - ((Player) e.getWhoClicked()).playSound(e.getWhoClicked().getLocation(), sound, 1f, 0f); - } else if (e.getClick().isLeftClick()) { - ((Player) e.getWhoClicked()).playSound(e.getWhoClicked().getLocation(), sound, 1f, 1f); - } else if (e.getClick().isRightClick()) { - ((Player) e.getWhoClicked()).playSound(e.getWhoClicked().getLocation(), sound, 1f, 2f); - } else if (e.getClick().isCreativeAction()) { - TLocale.sendTo(e.getWhoClicked(), "COMMANDS.TABOOLIB.SOUNDS.RESULT.COPY", sound.name()); - } - } - } - } - - public static class SoundLibraryHolder implements InventoryHolder { - - public final int PAGE; - public final String SEARCH; - public final HashMap SOUNDS_DATA = new HashMap<>(); - - public SoundLibraryHolder(int page, String search) { - this.PAGE = page; - this.SEARCH = search; - } - - @Override - public Inventory getInventory() { - return null; - } - } -} diff --git a/src/main/scala/me/skymc/taboolib/common/inject/TInjectLoader.java b/src/main/scala/me/skymc/taboolib/common/inject/TInjectLoader.java index 7151031..c8907bd 100644 --- a/src/main/scala/me/skymc/taboolib/common/inject/TInjectLoader.java +++ b/src/main/scala/me/skymc/taboolib/common/inject/TInjectLoader.java @@ -20,7 +20,7 @@ import java.util.Map; */ public class TInjectLoader implements TabooLibLoader.Loader { - private static Map, TInjectTask> injectTypes = Maps.newHashMap(); + private static Map, TInjectTask> injectTypes = Maps.newLinkedHashMap(); static { // Instance Inject @@ -50,11 +50,7 @@ public class TInjectLoader implements TabooLibLoader.Loader { // TConfiguration Inject injectTypes.put(TConfiguration.class, (plugin, field, args, instance) -> { try { - if (args.length == 0) { - TLogger.getGlobalLogger().error("Invalid inject arguments: " + field.getName() + " (" + field.getType().getName() + ")"); - } else { - field.set(instance, TConfiguration.createInResource(plugin, args[0])); - } + field.set(instance, TConfiguration.createInResource(plugin, args.length == 0 ? "config.yml" : args[0])); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/scala/me/skymc/taboolib/common/nms/NMSHandler.java b/src/main/scala/me/skymc/taboolib/common/nms/NMSHandler.java index 51749e6..008fd74 100644 --- a/src/main/scala/me/skymc/taboolib/common/nms/NMSHandler.java +++ b/src/main/scala/me/skymc/taboolib/common/nms/NMSHandler.java @@ -1,7 +1,5 @@ package me.skymc.taboolib.common.nms; -import me.skymc.taboolib.TabooLib; -import me.skymc.taboolib.commands.builder.SimpleCommandBuilder; import me.skymc.taboolib.common.function.TFunction; import me.skymc.taboolib.common.versioncontrol.SimpleVersionControl; import org.bukkit.entity.Player; @@ -21,11 +19,6 @@ public abstract class NMSHandler { } catch (Exception e) { e.printStackTrace(); } - SimpleCommandBuilder.create("title", TabooLib.instance()) - .execute((sender, args) -> { - handler.sendTitle((Player) sender, "TabooLib", 10, 40, 10, "author Bkm016", 10, 40, 10); - return true; - }).build(); } abstract public void sendTitle(Player player, String title, int titleFadein, int titleStay, int titleFadeout, String subtitle, int subtitleFadein, int subtitleStay, int subtitleFadeout); diff --git a/src/main/scala/me/skymc/taboolib/common/serialize/TSerializable.java b/src/main/scala/me/skymc/taboolib/common/serialize/TSerializable.java new file mode 100644 index 0000000..2529adf --- /dev/null +++ b/src/main/scala/me/skymc/taboolib/common/serialize/TSerializable.java @@ -0,0 +1,20 @@ +package me.skymc.taboolib.common.serialize; + +/** + * @Author 坏黑 + * @Since 2019-03-08 17:28 + */ +public interface TSerializable { + + void read(String fieldName, String value); + + String write(String fieldName, Object value); + + default void read(String value) { + TSerializer.read(this, value); + } + + default String write() { + return TSerializer.write(this); + } +} \ No newline at end of file diff --git a/src/main/scala/me/skymc/taboolib/common/serialize/TSerializer.java b/src/main/scala/me/skymc/taboolib/common/serialize/TSerializer.java new file mode 100644 index 0000000..d89ce4b --- /dev/null +++ b/src/main/scala/me/skymc/taboolib/common/serialize/TSerializer.java @@ -0,0 +1,149 @@ +package me.skymc.taboolib.common.serialize; + +import ch.njol.skript.classes.ConfigurationSerializer; +import com.google.gson.*; +import org.bukkit.configuration.serialization.ConfigurationSerializable; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; + +/** + * @Author 坏黑 + * @Since 2019-03-08 17:31 + */ +public class TSerializer { + + public static T read(String value, Class type) { + return ConfigurationSerializer.deserializeCS(value, type); + } + + public static TSerializable read(TSerializable serializable, String serializedString) { + try { + JsonObject jsonObject = (JsonObject) new JsonParser().parse(serializedString); + if (jsonObject.has("serializeObject")) { + JsonObject serializeObject = jsonObject.getAsJsonObject("serializeObject"); + for (Map.Entry jsonElementEntry : serializeObject.entrySet()) { + try { + Field declaredField = serializable.getClass().getDeclaredField(jsonElementEntry.getKey()); + declaredField.setAccessible(true); + Optional serializer = Arrays.stream(TSerializerElementGeneral.values()).filter(serializerElements -> serializerElements.getSerializer().matches(declaredField.getType())).findFirst(); + if (serializer.isPresent()) { + declaredField.set(serializable, serializer.get().getSerializer().read(jsonElementEntry.getValue().getAsString())); + } else { + serializable.read(jsonElementEntry.getKey(), jsonElementEntry.getValue().getAsString()); + } + } catch (Throwable t) { + t.printStackTrace(); + } + } + } + } catch (Throwable t) { + t.printStackTrace(); + } + return serializable; + } + + public static String write(ConfigurationSerializable serializable) { + return ConfigurationSerializer.serializeCS(serializable); + } + + public static String write(TSerializable serializable) { + JsonObject jsonObject = new JsonObject(); + JsonObject serializeObject = new JsonObject(); + for (Field declaredField : serializable.getClass().getDeclaredFields()) { + try { + if (!Modifier.isStatic(declaredField.getModifiers())) { + declaredField.setAccessible(true); + Optional serializer = Arrays.stream(TSerializerElementGeneral.values()).filter(serializerElements -> serializerElements.getSerializer().matches(declaredField.getType())).findFirst(); + Object o = declaredField.get(serializable); + if (o == null) { + continue; + } + if (serializer.isPresent()) { + serializeObject.addProperty(declaredField.getName(), serializer.get().getSerializer().write(o)); + } else { + Optional.ofNullable(serializable.write(declaredField.getName(), o)).ifPresent(value -> serializeObject.addProperty(declaredField.getName(), value)); + } + } + } catch (Throwable t) { + t.printStackTrace(); + } + } + jsonObject.add("serializeObject", serializeObject); + return jsonObject.toString(); + } + + public static void readMap(Map map, String serializedString, TSerializerElementGeneral elementKey, TSerializerElementGeneral elementValue) { + readMap(map, serializedString, elementKey.getSerializer(), elementValue.getSerializer()); + } + + public static void readMap(Map map, String serializedString, TSerializerElement elementKey, TSerializerElement elementValue) { + try { + JsonObject jsonObject = (JsonObject) new JsonParser().parse(serializedString); + for (Map.Entry jsonElementEntry : jsonObject.entrySet()) { + try { + map.put(elementKey.read(jsonElementEntry.getKey()), elementValue.read(jsonElementEntry.getValue().getAsString())); + } catch (Throwable t) { + t.printStackTrace(); + } + } + } catch (Throwable t) { + t.printStackTrace(); + } + } + + public static String writeMap(Map map, TSerializerElementGeneral elementKey, TSerializerElementGeneral elementValue) { + return writeMap(map, elementKey.getSerializer(), elementValue.getSerializer()); + } + + public static String writeMap(Map map, TSerializerElement elementKey, TSerializerElement elementValue) { + JsonObject jsonObject = new JsonObject(); + for (Map.Entry entry : map.entrySet()) { + try { + jsonObject.addProperty(elementKey.write(entry.getKey()), elementValue.write(entry.getValue())); + } catch (Throwable t) { + t.printStackTrace(); + } + } + return jsonObject.toString(); + } + + public static void readCollection(Collection collection, String serializedString, TSerializerElementGeneral elementValue) { + readCollection(collection, serializedString, elementValue.getSerializer()); + } + + public static void readCollection(Collection collection, String serializedString, TSerializerElement elementValue) { + try { + JsonArray jsonArray = (JsonArray) new JsonParser().parse(serializedString); + for (JsonElement jsonElement : jsonArray) { + try { + collection.add(elementValue.read(jsonElement.getAsString())); + } catch (Throwable t) { + t.printStackTrace(); + } + } + } catch (Throwable t) { + t.printStackTrace(); + } + } + + public static String writeCollection(Collection collection, TSerializerElementGeneral elementValue) { + return writeCollection(collection, elementValue.getSerializer()); + } + + public static String writeCollection(Collection collection, TSerializerElement elementValue) { + JsonArray jsonArray = new JsonArray(); + for (Object object : collection) { + try { + jsonArray.add(new JsonPrimitive(elementValue.write(object))); + } catch (Throwable t) { + t.printStackTrace(); + } + } + return jsonArray.toString(); + } +} diff --git a/src/main/scala/me/skymc/taboolib/common/serialize/TSerializerElement.java b/src/main/scala/me/skymc/taboolib/common/serialize/TSerializerElement.java new file mode 100644 index 0000000..3232eab --- /dev/null +++ b/src/main/scala/me/skymc/taboolib/common/serialize/TSerializerElement.java @@ -0,0 +1,15 @@ +package me.skymc.taboolib.common.serialize; + +/** + * @Author 坏黑 + * @Since 2019-03-08 17:28 + */ +public interface TSerializerElement { + + T read(String value); + + String write(Object value); + + boolean matches(Class objectClass); + +} \ No newline at end of file diff --git a/src/main/scala/me/skymc/taboolib/common/serialize/TSerializerElementGeneral.java b/src/main/scala/me/skymc/taboolib/common/serialize/TSerializerElementGeneral.java new file mode 100644 index 0000000..c6bd743 --- /dev/null +++ b/src/main/scala/me/skymc/taboolib/common/serialize/TSerializerElementGeneral.java @@ -0,0 +1,217 @@ +package me.skymc.taboolib.common.serialize; + +import ch.njol.skript.classes.ConfigurationSerializer; +import me.skymc.taboolib.other.NumberUtils; +import org.bukkit.Location; +import org.bukkit.inventory.ItemStack; + +import java.util.Objects; + +/** + * @Author 坏黑 + * @Since 2019-03-10 18:49 + */ +public enum TSerializerElementGeneral { + + STRING(new TSerializerElement() { + + @Override + public String read(String value) { + return String.valueOf(value); + } + + @Override + public String write(Object value) { + return Objects.toString(value); + } + + @Override + public boolean matches(Class objectClass) { + return String.class.equals(objectClass); + } + }), + + INT(new TSerializerElement() { + + @Override + public Integer read(String value) { + try { + return Integer.parseInt(value); + } catch (Throwable t) { + t.printStackTrace(); + } + return 0; + } + + @Override + public String write(Object value) { + return Objects.toString(value); + } + + @Override + public boolean matches(Class objectClass) { + return Integer.class.equals(objectClass) || Integer.TYPE.equals(objectClass); + } + }), + + LONG(new TSerializerElement() { + + @Override + public Long read(String value) { + try { + return Long.parseLong(value); + } catch (Throwable t) { + t.printStackTrace(); + } + return 0L; + } + + @Override + public String write(Object value) { + return Objects.toString(value); + } + + @Override + public boolean matches(Class objectClass) { + return Long.class.equals(objectClass) || Long.TYPE.equals(objectClass); + } + }), + + SHORT(new TSerializerElement() { + + @Override + public Short read(String value) { + try { + return Short.parseShort(value); + } catch (Throwable t) { + t.printStackTrace(); + } + return (short) 0; + } + + @Override + public String write(Object value) { + return Objects.toString(value); + } + + @Override + public boolean matches(Class objectClass) { + return Short.class.equals(objectClass) || Short.TYPE.equals(objectClass); + } + }), + + DOUBLE(new TSerializerElement() { + + @Override + public Double read(String value) { + try { + return Double.parseDouble(value); + } catch (Throwable t) { + t.printStackTrace(); + } + return 0D; + } + + @Override + public String write(Object value) { + return Objects.toString(value); + } + + @Override + public boolean matches(Class objectClass) { + return Double.class.equals(objectClass) || Boolean.TYPE.equals(objectClass); + } + }), + + FLOAT(new TSerializerElement() { + + @Override + public Float read(String value) { + try { + return Float.parseFloat(value); + } catch (Throwable t) { + t.printStackTrace(); + } + return 0f; + } + + @Override + public String write(Object value) { + return Objects.toString(value); + } + + @Override + public boolean matches(Class objectClass) { + return Float.class.equals(objectClass) || Float.TYPE.equals(objectClass); + } + }), + + BOOLEAN(new TSerializerElement() { + + @Override + public Boolean read(String value) { + try { + return NumberUtils.getBooleanAbbreviation(value); + } catch (Throwable t) { + t.printStackTrace(); + } + return false; + } + + @Override + public String write(Object value) { + return Objects.toString(value); + } + + @Override + public boolean matches(Class objectClass) { + return Boolean.class.equals(objectClass) || Boolean.TYPE.equals(objectClass); + } + }), + + ITEM_STACK(new TSerializerElement() { + + @Override + public ItemStack read(String value) { + return ConfigurationSerializer.deserializeCS(value, ItemStack.class); + } + + @Override + public String write(Object value) { + return ConfigurationSerializer.serializeCS((ItemStack) value); + } + + @Override + public boolean matches(Class objectClass) { + return ItemStack.class.equals(objectClass); + } + }), + + LOCATION(new TSerializerElement() { + + @Override + public Location read(String value) { + return ConfigurationSerializer.deserializeCS(value, Location.class); + } + + @Override + public String write(Object value) { + return ConfigurationSerializer.serializeCS((Location) value); + } + + @Override + public boolean matches(Class objectClass) { + return Location.class.equals(objectClass); + } + }); + + private TSerializerElement serializer; + + TSerializerElementGeneral(TSerializerElement serializer) { + this.serializer = serializer; + } + + public TSerializerElement getSerializer() { + return serializer; + } +} \ No newline at end of file diff --git a/src/main/scala/me/skymc/taboolib/common/serialize/TSerializerExample.java b/src/main/scala/me/skymc/taboolib/common/serialize/TSerializerExample.java new file mode 100644 index 0000000..1d5d8a5 --- /dev/null +++ b/src/main/scala/me/skymc/taboolib/common/serialize/TSerializerExample.java @@ -0,0 +1,85 @@ +package me.skymc.taboolib.common.serialize; + +import com.google.common.collect.Lists; + +import java.util.Collection; +import java.util.List; + +/** + * @Author 坏黑 + * @Since 2019-04-01 17:49 + */ +public class TSerializerExample { + + public static void main(String[] args) { + // 创建对象 + SimpleData date = new SimpleData(); + // 修改参数 + date.number = 100; + // 序列化 + String value = date.write(); + // 打印 + System.out.println(value); + // 创建新的对象 + SimpleData dataCopy = new SimpleData(); + // 反序列化 + dataCopy.read(value); + // 打印 + System.out.println(dataCopy); + } + + /** + * 创建序列化类 + * 实现 TSerializable 接口 + */ + public static class SimpleData implements TSerializable { + + /** + * 基本类型不需要手动进行序列化 + * 包含: String、int、short、long、double、float、boolean、ItemStack、Location + */ + private String text; + private int number; + /** + * 特殊类型需要进行手动序列化 + * 本工具提供了基本容器的序列化方法 + */ + private List list = Lists.newArrayList(); + + /** + * 基本类型不会执行以下两个方法 + * 由 TSerializer 自动进行序列化和反序列化步骤 + */ + @Override + public void read(String fieldName, String value) { + switch (fieldName) { + case "list": { + // List 类型可以直接通过 TSerializer 提供的预设方法进行反序列化 + TSerializer.readCollection(list, value, TSerializerElementGeneral.DOUBLE); + break; + } + default: + } + } + + @Override + public String write(String fieldName, Object value) { + switch (fieldName) { + case "list": { + // 序列化同理 + return TSerializer.writeCollection((Collection) value, TSerializerElementGeneral.DOUBLE); + } + } + return null; + } + + @Override + public String toString() { + return "SimpleData{" + + "text='" + text + '\'' + + ", number=" + number + + ", list=" + list + + '}'; + } + } +} diff --git a/src/main/scala/me/skymc/taboolib/damage/DamageUtils.java b/src/main/scala/me/skymc/taboolib/damage/DamageUtils.java deleted file mode 100644 index 1ffdd99..0000000 --- a/src/main/scala/me/skymc/taboolib/damage/DamageUtils.java +++ /dev/null @@ -1,44 +0,0 @@ -package me.skymc.taboolib.damage; - -import org.bukkit.entity.Entity; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; -import org.bukkit.event.entity.EntityDamageByEntityEvent; - -/** - * @author sky - */ -public class DamageUtils { - - public static Player getAttackerInDamageEvent(EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Player) { - return (Player) e.getDamager(); - } else if (e.getDamager() instanceof Projectile && ((Projectile) e.getDamager()).getShooter() instanceof Player) { - return (Player) ((Projectile) e.getDamager()).getShooter(); - } else { - return null; - } - } - - // ********************************* - // - // Deprecated - // - // ********************************* - - @Deprecated - public static void damage(Player player, LivingEntity victim, double damage) { - dmg(player, victim, damage); - } - - @Deprecated - public static void damage(Player player, Entity victim, double damage) { - dmg(player, (LivingEntity) victim, damage); - } - - @Deprecated - public static void dmg(LivingEntity attacker, LivingEntity victim, double damage) { - attacker.damage(damage, victim); - } -} diff --git a/src/main/scala/me/skymc/taboolib/fileutils/EncodeUtils.java b/src/main/scala/me/skymc/taboolib/fileutils/EncodeUtils.java deleted file mode 100644 index 9897b37..0000000 --- a/src/main/scala/me/skymc/taboolib/fileutils/EncodeUtils.java +++ /dev/null @@ -1,114 +0,0 @@ -package me.skymc.taboolib.fileutils; - -import java.io.*; -import java.nio.charset.Charset; -import java.nio.charset.UnsupportedCharsetException; - -/** - * @author Unknown - */ -public class EncodeUtils { - - /** - * 把指定文件或目录转换成指定的编码 - * - * @param fileName 要转换的文件 - * @param fromCharsetName 源文件的编码 - * @param toCharsetName 要转换的编码 - * @throws Exception - */ - public static void convert(String fileName, String fromCharsetName, String toCharsetName) throws Exception { - convert(new File(fileName), fromCharsetName, toCharsetName, null); - } - - /** - * 把指定文件或目录转换成指定的编码 - * - * @param file 要转换的文件或目录 - * @param fromCharsetName 源文件的编码 - * @param toCharsetName 要转换的编码 - * @throws Exception - */ - public static void convert(File file, String fromCharsetName, String toCharsetName) throws Exception { - convert(file, fromCharsetName, toCharsetName, null); - } - - /** - * 把指定文件或目录转换成指定的编码 - * - * @param fromCharsetName 源文件的编码 - * @param toCharsetName 转换的编码 - * @param filter 文件名过滤器 - * @throws Exception - */ - public static void convert(String fileName, String fromCharsetName, String toCharsetName, FilenameFilter filter) throws Exception { - convert(new File(fileName), fromCharsetName, toCharsetName, filter); - } - - /** - * 把指定文件或目录转换成指定的编码 - * - * @param file 要转换的文件或目录 - * @param fromCharsetName 源文件的编码 - * @param toCharsetName 要转换的编码 - * @param filter 文件名过滤器 - * @throws Exception - */ - public static void convert(File file, String fromCharsetName, String toCharsetName, FilenameFilter filter) throws Exception { - if (file.isDirectory()) { - File[] fileList; - if (filter == null) { - fileList = file.listFiles(); - } else { - fileList = file.listFiles(filter); - } - for (File f : fileList) { - convert(f, fromCharsetName, toCharsetName, filter); - } - } else { - if (filter == null || filter.accept(file.getParentFile(), file.getName())) { - String fileContent = getFileContentFromCharset(file, fromCharsetName); - saveFile2Charset(file, toCharsetName, fileContent); - } - } - } - - /** - * 以指定编码方式读取文件,返回文件内容 - * - * @param file 要转换的文件 - * @param fromCharsetName 源文件的编码 - * @return - * @throws Exception - */ - public static String getFileContentFromCharset(File file, String fromCharsetName) throws Exception { - if (!Charset.isSupported(fromCharsetName)) { - throw new UnsupportedCharsetException(fromCharsetName); - } - InputStream inputStream = new FileInputStream(file); - InputStreamReader reader = new InputStreamReader(inputStream, fromCharsetName); - char[] chs = new char[(int) file.length()]; - reader.read(chs); - String str = new String(chs).trim(); - reader.close(); - return str; - } - - /** - * 以指定编码方式写文本文件,存在会覆盖 - * - * @param file 要写入的文件 - * @param toCharsetName 要转换的编码 - * @param content 文件内容 - * @throws Exception - */ - public static void saveFile2Charset(File file, String toCharsetName, String content) throws Exception { - if (!Charset.isSupported(toCharsetName)) { - throw new UnsupportedCharsetException(toCharsetName); - } - OutputStream outputStream = new FileOutputStream(file); - OutputStreamWriter outWrite = new OutputStreamWriter(outputStream, toCharsetName); - outWrite.write(content); - outWrite.close(); - } -} diff --git a/src/main/scala/me/skymc/taboolib/particle/EffLib.java b/src/main/scala/me/skymc/taboolib/particle/EffLib.java index e9a1cfc..b9e4c95 100644 --- a/src/main/scala/me/skymc/taboolib/particle/EffLib.java +++ b/src/main/scala/me/skymc/taboolib/particle/EffLib.java @@ -2,10 +2,7 @@ package me.skymc.taboolib.particle; import me.skymc.taboolib.methods.ReflectionUtils; import me.skymc.taboolib.methods.ReflectionUtils.PackageType; -import org.bukkit.Bukkit; -import org.bukkit.Color; -import org.bukkit.Location; -import org.bukkit.Material; +import org.bukkit.*; import org.bukkit.entity.Player; import org.bukkit.util.Vector; @@ -39,1589 +36,1594 @@ import java.util.Map.Entry; * *

* It would be nice if you provide credit to me if you use this class in a published project - * + * * @author DarkBlade12 * @version 1.7 */ public enum EffLib { - - /** - * A particle effect which is displayed by exploding tnt and creepers: - *

    - *
  • It looks like a white cloud - *
  • The speed value influences the velocity at which the particle flies off - *
- */ - EXPLOSION_NORMAL("explode", 0, -1, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed by exploding ghast fireballs and wither skulls: - *
    - *
  • It looks like a gray ball which is fading away - *
  • The speed value slightly influences the size of this particle effect - *
- */ - EXPLOSION_LARGE("largeexplode", 1, -1), - /** - * A particle effect which is displayed by exploding tnt and creepers: - *
    - *
  • It looks like a crowd of gray balls which are fading away - *
  • The speed value has no influence on this particle effect - *
- */ - EXPLOSION_HUGE("hugeexplosion", 2, -1), - /** - * A particle effect which is displayed by launching fireworks: - *
    - *
  • It looks like a white star which is sparkling - *
  • The speed value influences the velocity at which the particle flies off - *
- */ - FIREWORKS_SPARK("fireworksSpark", 3, -1, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed by swimming entities and arrows in water: - *
    - *
  • It looks like a bubble - *
  • The speed value influences the velocity at which the particle flies off - *
- */ - WATER_BUBBLE("bubble", 4, -1, ParticleProperty.DIRECTIONAL, ParticleProperty.REQUIRES_WATER), - /** - * A particle effect which is displayed by swimming entities and shaking wolves: - *
    - *
  • It looks like a blue drop - *
  • The speed value has no influence on this particle effect - *
- */ - WATER_SPLASH("splash", 5, -1, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed on water when fishing: - *
    - *
  • It looks like a blue droplet - *
  • The speed value influences the velocity at which the particle flies off - *
- */ - WATER_WAKE("wake", 6, 7, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed by water: - *
    - *
  • It looks like a tiny blue square - *
  • The speed value has no influence on this particle effect - *
- */ - SUSPENDED("suspended", 7, -1, ParticleProperty.REQUIRES_WATER), - /** - * A particle effect which is displayed by air when close to bedrock and the in the void: - *
    - *
  • It looks like a tiny gray square - *
  • The speed value has no influence on this particle effect - *
- */ - SUSPENDED_DEPTH("depthSuspend", 8, -1, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed when landing a critical hit and by arrows: - *
    - *
  • It looks like a light brown cross - *
  • The speed value influences the velocity at which the particle flies off - *
- */ - CRIT("crit", 9, -1, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed when landing a hit with an enchanted weapon: - *
    - *
  • It looks like a cyan star - *
  • The speed value influences the velocity at which the particle flies off - *
- */ - CRIT_MAGIC("magicCrit", 10, -1, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed by primed tnt, torches, droppers, dispensers, end portals, brewing stands and monster spawners: - *
    - *
  • It looks like a little gray cloud - *
  • The speed value influences the velocity at which the particle flies off - *
- */ - SMOKE_NORMAL("smoke", 11, -1, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed by fire, minecarts with furnace and blazes: - *
    - *
  • It looks like a large gray cloud - *
  • The speed value influences the velocity at which the particle flies off - *
- */ - SMOKE_LARGE("largesmoke", 12, -1, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed when splash potions or bottles o' enchanting hit something: - *
    - *
  • It looks like a white swirl - *
  • The speed value causes the particle to only move upwards when set to 0 - *
  • Only the motion on the y-axis can be controlled, the motion on the x- and z-axis are multiplied by 0.1 when setting the values to 0 - *
- */ - SPELL("spell", 13, -1), - /** - * A particle effect which is displayed when instant splash potions hit something: - *
    - *
  • It looks like a white cross - *
  • The speed value causes the particle to only move upwards when set to 0 - *
  • Only the motion on the y-axis can be controlled, the motion on the x- and z-axis are multiplied by 0.1 when setting the values to 0 - *
- */ - SPELL_INSTANT("instantSpell", 14, -1), - /** - * A particle effect which is displayed by entities with active potion effects: - *
    - *
  • It looks like a colored swirl - *
  • The speed value causes the particle to be colored black when set to 0 - *
  • The particle color gets lighter when increasing the speed and darker when decreasing the speed - *
- */ - SPELL_MOB("mobSpell", 15, -1, ParticleProperty.COLORABLE), - /** - * A particle effect which is displayed by entities with active potion effects applied through a beacon: - *
    - *
  • It looks like a transparent colored swirl - *
  • The speed value causes the particle to be always colored black when set to 0 - *
  • The particle color gets lighter when increasing the speed and darker when decreasing the speed - *
- */ - SPELL_MOB_AMBIENT("mobSpellAmbient", 16, -1, ParticleProperty.COLORABLE), - /** - * A particle effect which is displayed by witches: - *
    - *
  • It looks like a purple cross - *
  • The speed value causes the particle to only move upwards when set to 0 - *
  • Only the motion on the y-axis can be controlled, the motion on the x- and z-axis are multiplied by 0.1 when setting the values to 0 - *
- */ - SPELL_WITCH("witchMagic", 17, -1), - /** - * A particle effect which is displayed by blocks beneath a water source: - *
    - *
  • It looks like a blue drip - *
  • The speed value has no influence on this particle effect - *
- */ - DRIP_WATER("dripWater", 18, -1), - /** - * A particle effect which is displayed by blocks beneath a lava source: - *
    - *
  • It looks like an orange drip - *
  • The speed value has no influence on this particle effect - *
- */ - DRIP_LAVA("dripLava", 19, -1), - /** - * A particle effect which is displayed when attacking a villager in a village: - *
    - *
  • It looks like a cracked gray heart - *
  • The speed value has no influence on this particle effect - *
- */ - VILLAGER_ANGRY("angryVillager", 20, -1), - /** - * A particle effect which is displayed when using bone meal and trading with a villager in a village: - *
    - *
  • It looks like a green star - *
  • The speed value has no influence on this particle effect - *
- */ - VILLAGER_HAPPY("happyVillager", 21, -1, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed by mycelium: - *
    - *
  • It looks like a tiny gray square - *
  • The speed value has no influence on this particle effect - *
- */ - TOWN_AURA("townaura", 22, -1, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed by note blocks: - *
    - *
  • It looks like a colored note - *
  • The speed value causes the particle to be colored green when set to 0 - *
- */ - NOTE("note", 23, -1, ParticleProperty.COLORABLE), - /** - * A particle effect which is displayed by nether portals, endermen, ender pearls, eyes of ender, ender chests and dragon eggs: - *
    - *
  • It looks like a purple cloud - *
  • The speed value influences the spread of this particle effect - *
- */ - PORTAL("portal", 24, -1, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed by enchantment tables which are nearby bookshelves: - *
    - *
  • It looks like a cryptic white letter - *
  • The speed value influences the spread of this particle effect - *
- */ - ENCHANTMENT_TABLE("enchantmenttable", 25, -1, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed by torches, active furnaces, magma cubes and monster spawners: - *
    - *
  • It looks like a tiny flame - *
  • The speed value influences the velocity at which the particle flies off - *
- */ - FLAME("flame", 26, -1, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed by lava: - *
    - *
  • It looks like a spark - *
  • The speed value has no influence on this particle effect - *
- */ - LAVA("lava", 27, -1), - /** - * A particle effect which is currently unused: - *
    - *
  • It looks like a transparent gray square - *
  • The speed value has no influence on this particle effect - *
- */ - FOOTSTEP("footstep", 28, -1), - /** - * A particle effect which is displayed when a mob dies: - *
    - *
  • It looks like a large white cloud - *
  • The speed value influences the velocity at which the particle flies off - *
- */ - CLOUD("cloud", 29, -1, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed by redstone ore, powered redstone, redstone torches and redstone repeaters: - *
    - *
  • It looks like a tiny colored cloud - *
  • The speed value causes the particle to be colored red when set to 0 - *
- */ - REDSTONE("reddust", 30, -1, ParticleProperty.COLORABLE), - /** - * A particle effect which is displayed when snowballs hit a block: - *
    - *
  • It looks like a little piece with the snowball texture - *
  • The speed value has no influence on this particle effect - *
- */ - SNOWBALL("snowballpoof", 31, -1), - /** - * A particle effect which is currently unused: - *
    - *
  • It looks like a tiny white cloud - *
  • The speed value influences the velocity at which the particle flies off - *
- */ - SNOW_SHOVEL("snowshovel", 32, -1, ParticleProperty.DIRECTIONAL), - /** - * A particle effect which is displayed by slimes: - *
    - *
  • It looks like a tiny part of the slimeball icon - *
  • The speed value has no influence on this particle effect - *
- */ - SLIME("slime", 33, -1), - /** - * A particle effect which is displayed when breeding and taming animals: - *
    - *
  • It looks like a red heart - *
  • The speed value has no influence on this particle effect - *
- */ - HEART("heart", 34, -1), - /** - * A particle effect which is displayed by barriers: - *
    - *
  • It looks like a red box with a slash through it - *
  • The speed value has no influence on this particle effect - *
- */ - BARRIER("barrier", 35, 8), - /** - * A particle effect which is displayed when breaking a tool or eggs hit a block: - *
    - *
  • It looks like a little piece with an item texture - *
- */ - ITEM_CRACK("iconcrack", 36, -1, ParticleProperty.DIRECTIONAL, ParticleProperty.REQUIRES_DATA), - /** - * A particle effect which is displayed when breaking blocks or sprinting: - *
    - *
  • It looks like a little piece with a block texture - *
  • The speed value has no influence on this particle effect - *
- */ - BLOCK_CRACK("blockcrack", 37, -1, ParticleProperty.REQUIRES_DATA), - /** - * A particle effect which is displayed when falling: - *
    - *
  • It looks like a little piece with a block texture - *
- */ - BLOCK_DUST("blockdust", 38, 7, ParticleProperty.DIRECTIONAL, ParticleProperty.REQUIRES_DATA), - /** - * A particle effect which is displayed when rain hits the ground: - *
    - *
  • It looks like a blue droplet - *
  • The speed value has no influence on this particle effect - *
- */ - WATER_DROP("droplet", 39, 8), - /** - * A particle effect which is currently unused: - *
    - *
  • It has no visual effect - *
- */ - ITEM_TAKE("take", 40, 8), - /** - * A particle effect which is displayed by elder guardians: - *
    - *
  • It looks like the shape of the elder guardian - *
  • The speed value has no influence on this particle effect - *
  • The offset values have no influence on this particle effect - *
- */ - MOB_APPEARANCE("mobappearance", 41, 8), - - /** - * 龙息 - */ + + /** + * A particle effect which is displayed by exploding tnt and creepers: + *
    + *
  • It looks like a white cloud + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + EXPLOSION_NORMAL("explode", 0, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by exploding ghast fireballs and wither skulls: + *
    + *
  • It looks like a gray ball which is fading away + *
  • The speed value slightly influences the size of this particle effect + *
+ */ + EXPLOSION_LARGE("largeexplode", 1, -1), + /** + * A particle effect which is displayed by exploding tnt and creepers: + *
    + *
  • It looks like a crowd of gray balls which are fading away + *
  • The speed value has no influence on this particle effect + *
+ */ + EXPLOSION_HUGE("hugeexplosion", 2, -1), + /** + * A particle effect which is displayed by launching fireworks: + *
    + *
  • It looks like a white star which is sparkling + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + FIREWORKS_SPARK("fireworksSpark", 3, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by swimming entities and arrows in water: + *
    + *
  • It looks like a bubble + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + WATER_BUBBLE("bubble", 4, -1, ParticleProperty.DIRECTIONAL, ParticleProperty.REQUIRES_WATER), + /** + * A particle effect which is displayed by swimming entities and shaking wolves: + *
    + *
  • It looks like a blue drop + *
  • The speed value has no influence on this particle effect + *
+ */ + WATER_SPLASH("splash", 5, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed on water when fishing: + *
    + *
  • It looks like a blue droplet + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + WATER_WAKE("wake", 6, 7, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by water: + *
    + *
  • It looks like a tiny blue square + *
  • The speed value has no influence on this particle effect + *
+ */ + SUSPENDED("suspended", 7, -1, ParticleProperty.REQUIRES_WATER), + /** + * A particle effect which is displayed by air when close to bedrock and the in the void: + *
    + *
  • It looks like a tiny gray square + *
  • The speed value has no influence on this particle effect + *
+ */ + SUSPENDED_DEPTH("depthSuspend", 8, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed when landing a critical hit and by arrows: + *
    + *
  • It looks like a light brown cross + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + CRIT("crit", 9, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed when landing a hit with an enchanted weapon: + *
    + *
  • It looks like a cyan star + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + CRIT_MAGIC("magicCrit", 10, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by primed tnt, torches, droppers, dispensers, end portals, brewing stands and monster spawners: + *
    + *
  • It looks like a little gray cloud + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + SMOKE_NORMAL("smoke", 11, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by fire, minecarts with furnace and blazes: + *
    + *
  • It looks like a large gray cloud + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + SMOKE_LARGE("largesmoke", 12, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed when splash potions or bottles o' enchanting hit something: + *
    + *
  • It looks like a white swirl + *
  • The speed value causes the particle to only move upwards when set to 0 + *
  • Only the motion on the y-axis can be controlled, the motion on the x- and z-axis are multiplied by 0.1 when setting the values to 0 + *
+ */ + SPELL("spell", 13, -1), + /** + * A particle effect which is displayed when instant splash potions hit something: + *
    + *
  • It looks like a white cross + *
  • The speed value causes the particle to only move upwards when set to 0 + *
  • Only the motion on the y-axis can be controlled, the motion on the x- and z-axis are multiplied by 0.1 when setting the values to 0 + *
+ */ + SPELL_INSTANT("instantSpell", 14, -1), + /** + * A particle effect which is displayed by entities with active potion effects: + *
    + *
  • It looks like a colored swirl + *
  • The speed value causes the particle to be colored black when set to 0 + *
  • The particle color gets lighter when increasing the speed and darker when decreasing the speed + *
+ */ + SPELL_MOB("mobSpell", 15, -1, ParticleProperty.COLORABLE), + /** + * A particle effect which is displayed by entities with active potion effects applied through a beacon: + *
    + *
  • It looks like a transparent colored swirl + *
  • The speed value causes the particle to be always colored black when set to 0 + *
  • The particle color gets lighter when increasing the speed and darker when decreasing the speed + *
+ */ + SPELL_MOB_AMBIENT("mobSpellAmbient", 16, -1, ParticleProperty.COLORABLE), + /** + * A particle effect which is displayed by witches: + *
    + *
  • It looks like a purple cross + *
  • The speed value causes the particle to only move upwards when set to 0 + *
  • Only the motion on the y-axis can be controlled, the motion on the x- and z-axis are multiplied by 0.1 when setting the values to 0 + *
+ */ + SPELL_WITCH("witchMagic", 17, -1), + /** + * A particle effect which is displayed by blocks beneath a water source: + *
    + *
  • It looks like a blue drip + *
  • The speed value has no influence on this particle effect + *
+ */ + DRIP_WATER("dripWater", 18, -1), + /** + * A particle effect which is displayed by blocks beneath a lava source: + *
    + *
  • It looks like an orange drip + *
  • The speed value has no influence on this particle effect + *
+ */ + DRIP_LAVA("dripLava", 19, -1), + /** + * A particle effect which is displayed when attacking a villager in a village: + *
    + *
  • It looks like a cracked gray heart + *
  • The speed value has no influence on this particle effect + *
+ */ + VILLAGER_ANGRY("angryVillager", 20, -1), + /** + * A particle effect which is displayed when using bone meal and trading with a villager in a village: + *
    + *
  • It looks like a green star + *
  • The speed value has no influence on this particle effect + *
+ */ + VILLAGER_HAPPY("happyVillager", 21, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by mycelium: + *
    + *
  • It looks like a tiny gray square + *
  • The speed value has no influence on this particle effect + *
+ */ + TOWN_AURA("townaura", 22, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by note blocks: + *
    + *
  • It looks like a colored note + *
  • The speed value causes the particle to be colored green when set to 0 + *
+ */ + NOTE("note", 23, -1, ParticleProperty.COLORABLE), + /** + * A particle effect which is displayed by nether portals, endermen, ender pearls, eyes of ender, ender chests and dragon eggs: + *
    + *
  • It looks like a purple cloud + *
  • The speed value influences the spread of this particle effect + *
+ */ + PORTAL("portal", 24, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by enchantment tables which are nearby bookshelves: + *
    + *
  • It looks like a cryptic white letter + *
  • The speed value influences the spread of this particle effect + *
+ */ + ENCHANTMENT_TABLE("enchantmenttable", 25, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by torches, active furnaces, magma cubes and monster spawners: + *
    + *
  • It looks like a tiny flame + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + FLAME("flame", 26, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by lava: + *
    + *
  • It looks like a spark + *
  • The speed value has no influence on this particle effect + *
+ */ + LAVA("lava", 27, -1), + /** + * A particle effect which is currently unused: + *
    + *
  • It looks like a transparent gray square + *
  • The speed value has no influence on this particle effect + *
+ */ + FOOTSTEP("footstep", 28, -1), + /** + * A particle effect which is displayed when a mob dies: + *
    + *
  • It looks like a large white cloud + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + CLOUD("cloud", 29, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by redstone ore, powered redstone, redstone torches and redstone repeaters: + *
    + *
  • It looks like a tiny colored cloud + *
  • The speed value causes the particle to be colored red when set to 0 + *
+ */ + REDSTONE("reddust", 30, -1, ParticleProperty.COLORABLE), + /** + * A particle effect which is displayed when snowballs hit a block: + *
    + *
  • It looks like a little piece with the snowball texture + *
  • The speed value has no influence on this particle effect + *
+ */ + SNOWBALL("snowballpoof", 31, -1), + /** + * A particle effect which is currently unused: + *
    + *
  • It looks like a tiny white cloud + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + SNOW_SHOVEL("snowshovel", 32, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by slimes: + *
    + *
  • It looks like a tiny part of the slimeball icon + *
  • The speed value has no influence on this particle effect + *
+ */ + SLIME("slime", 33, -1), + /** + * A particle effect which is displayed when breeding and taming animals: + *
    + *
  • It looks like a red heart + *
  • The speed value has no influence on this particle effect + *
+ */ + HEART("heart", 34, -1), + /** + * A particle effect which is displayed by barriers: + *
    + *
  • It looks like a red box with a slash through it + *
  • The speed value has no influence on this particle effect + *
+ */ + BARRIER("barrier", 35, 8), + /** + * A particle effect which is displayed when breaking a tool or eggs hit a block: + *
    + *
  • It looks like a little piece with an item texture + *
+ */ + ITEM_CRACK("iconcrack", 36, -1, ParticleProperty.DIRECTIONAL, ParticleProperty.REQUIRES_DATA), + /** + * A particle effect which is displayed when breaking blocks or sprinting: + *
    + *
  • It looks like a little piece with a block texture + *
  • The speed value has no influence on this particle effect + *
+ */ + BLOCK_CRACK("blockcrack", 37, -1, ParticleProperty.REQUIRES_DATA), + /** + * A particle effect which is displayed when falling: + *
    + *
  • It looks like a little piece with a block texture + *
+ */ + BLOCK_DUST("blockdust", 38, 7, ParticleProperty.DIRECTIONAL, ParticleProperty.REQUIRES_DATA), + /** + * A particle effect which is displayed when rain hits the ground: + *
    + *
  • It looks like a blue droplet + *
  • The speed value has no influence on this particle effect + *
+ */ + WATER_DROP("droplet", 39, 8), + /** + * A particle effect which is currently unused: + *
    + *
  • It has no visual effect + *
+ */ + ITEM_TAKE("take", 40, 8), + /** + * A particle effect which is displayed by elder guardians: + *
    + *
  • It looks like the shape of the elder guardian + *
  • The speed value has no influence on this particle effect + *
  • The offset values have no influence on this particle effect + *
+ */ + MOB_APPEARANCE("mobappearance", 41, 8), + + /** + * 龙息 + */ DRAGON_BREATH("dragonbreath", 42, 9), - + /** * 末地烛 */ END_ROD("endrod", 43, 9), - + /** * 伤害 */ DAMAGE_INDICATOR("damageIndicator", 44, 9), - + /** * 挥砍 */ SWEEP_ATTACK("sweepAttack", 45, 9), - /** - * 不死图腾 - */ - TOTEM("totem", 46, 11); - - private static final Map NAME_MAP = new HashMap<>(); - private static final Map ID_MAP = new HashMap<>(); - private final String name; - private final int id; - private final int requiredVersion; - private final List properties; - - // Initialize map for quick name and id lookup - static { - for (EffLib effect : values()) { - NAME_MAP.put(effect.name, effect); - ID_MAP.put(effect.id, effect); - } - } - - /** - * Construct a new particle effect - * - * @param name Name of this particle effect - * @param id Id of this particle effect - * @param requiredVersion Version which is required (1.x) - * @param properties Properties of this particle effect - */ - EffLib(String name, int id, int requiredVersion, ParticleProperty... properties) { - this.name = name; - this.id = id; - this.requiredVersion = requiredVersion; - this.properties = Arrays.asList(properties); - } - - /** - * Returns the name of this particle effect - * - * @return The name - */ - public String getName() { - return name; - } - - /** - * Returns the id of this particle effect - * - * @return The id - */ - public int getId() { - return id; - } - - /** - * Returns the required version for this particle effect (1.x) - * - * @return The required version - */ - public int getRequiredVersion() { - return requiredVersion; - } - - /** - * Determine if this particle effect has a specific property - * - * @return Whether it has the property or not - */ - public boolean hasProperty(ParticleProperty property) { - return properties.contains(property); - } - - /** - * Determine if this particle effect is supported by your current server version - * - * @return Whether the particle effect is supported or not - */ - public boolean isSupported() { - return requiredVersion == -1 || ParticlePacket.getVersion() >= requiredVersion; - } - - /** - * Returns the particle effect with the given name - * - * @param name Name of the particle effect - * @return The particle effect - */ - public static EffLib fromName(String name) { - for (Entry entry : NAME_MAP.entrySet()) { - if (!entry.getKey().equalsIgnoreCase(name)) { - continue; - } - return entry.getValue(); - } - return null; - } - - /** - * Returns the particle effect with the given id - * - * @param id Id of the particle effect - * @return The particle effect - */ - public static EffLib fromId(int id) { - for (Entry entry : ID_MAP.entrySet()) { - if (entry.getKey() != id) { - continue; - } - return entry.getValue(); - } - return null; - } - - /** - * Determine if water is at a certain location - * - * @param location Location to check - * @return Whether water is at this location or not - */ - private static boolean isWater(Location location) { - Material material = location.getBlock().getType(); - return material == Material.WATER || material == Material.STATIONARY_WATER; - } - - /** - * Determine if the distance between @param location and one of the players exceeds 256 - * - * @param location Location to check - * @return Whether the distance exceeds 256 or not - */ - private static boolean isLongDistance(Location location, List players) { - String world = location.getWorld().getName(); - for (Player player : players) { - Location playerLocation = player.getLocation(); - if (!world.equals(playerLocation.getWorld().getName()) || playerLocation.distanceSquared(location) < 65536) { - continue; - } - return true; - } - return false; - } - - /** - * Determine if the data type for a particle effect is correct - * - * @param effect Particle effect - * @param data Particle data - * @return Whether the data type is correct or not - */ - private static boolean isDataCorrect(EffLib effect, ParticleData data) { - return ((effect == BLOCK_CRACK || effect == BLOCK_DUST) && data instanceof BlockData) || (effect == ITEM_CRACK && data instanceof ItemData); - } - - /** - * Determine if the color type for a particle effect is correct - * - * @param effect Particle effect - * @param color Particle color - * @return Whether the color type is correct or not - */ - private static boolean isColorCorrect(EffLib effect, ParticleColor color) { - return ((effect == SPELL_MOB || effect == SPELL_MOB_AMBIENT || effect == REDSTONE) && color instanceof OrdinaryColor) || (effect == NOTE && color instanceof NoteColor); - } - - /** - * Displays a particle effect which is only visible for all players within a certain range in the world of @param center - * - * @param offsetX Maximum distance particles can fly away from the center on the x-axis - * @param offsetY Maximum distance particles can fly away from the center on the y-axis - * @param offsetZ Maximum distance particles can fly away from the center on the z-axis - * @param speed Display speed of the particles - * @param amount Amount of particles - * @param center Center location of the effect - * @param range Range of the visibility - * @throws ParticleVersionException If the particle effect is not supported by the server version - * @throws ParticleDataException If the particle effect requires additional data - * @throws IllegalArgumentException If the particle effect requires water and none is at the center location - * @see ParticlePacket - * @see ParticlePacket#sendTo(Location, double) - */ - public void display(float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, double range) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { - if (!isSupported()) { - throw new ParticleVersionException("This particle effect is not supported by your server version"); - } - if (hasProperty(ParticleProperty.REQUIRES_DATA)) { - throw new ParticleDataException("This particle effect requires additional data"); - } - if (hasProperty(ParticleProperty.REQUIRES_WATER) && !isWater(center)) { - throw new IllegalArgumentException("There is no water at the center location"); - } - new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, range > 256, null).sendTo(center, range); - } - - /** - * Displays a particle effect which is only visible for the specified players - * - * @param offsetX Maximum distance particles can fly away from the center on the x-axis - * @param offsetY Maximum distance particles can fly away from the center on the y-axis - * @param offsetZ Maximum distance particles can fly away from the center on the z-axis - * @param speed Display speed of the particles - * @param amount Amount of particles - * @param center Center location of the effect - * @param players Receivers of the effect - * @throws ParticleVersionException If the particle effect is not supported by the server version - * @throws ParticleDataException If the particle effect requires additional data - * @throws IllegalArgumentException If the particle effect requires water and none is at the center location - * @see ParticlePacket - * @see ParticlePacket#sendTo(Location, List) - */ - public void display(float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, List players) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { - if (!isSupported()) { - throw new ParticleVersionException("This particle effect is not supported by your server version"); - } - if (hasProperty(ParticleProperty.REQUIRES_DATA)) { - throw new ParticleDataException("This particle effect requires additional data"); - } - if (hasProperty(ParticleProperty.REQUIRES_WATER) && !isWater(center)) { - throw new IllegalArgumentException("There is no water at the center location"); - } - new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, isLongDistance(center, players), null).sendTo(center, players); - } - - /** - * Displays a particle effect which is only visible for the specified players - * - * @param offsetX Maximum distance particles can fly away from the center on the x-axis - * @param offsetY Maximum distance particles can fly away from the center on the y-axis - * @param offsetZ Maximum distance particles can fly away from the center on the z-axis - * @param speed Display speed of the particles - * @param amount Amount of particles - * @param center Center location of the effect - * @param players Receivers of the effect - * @throws ParticleVersionException If the particle effect is not supported by the server version - * @throws ParticleDataException If the particle effect requires additional data - * @throws IllegalArgumentException If the particle effect requires water and none is at the center location - * @see #display(float, float, float, float, int, Location, List) - */ - public void display(float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, Player... players) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { - display(offsetX, offsetY, offsetZ, speed, amount, center, Arrays.asList(players)); - } - - /** - * Displays a single particle which flies into a determined direction and is only visible for all players within a certain range in the world of @param center - * - * @param direction Direction of the particle - * @param speed Display speed of the particle - * @param center Center location of the effect - * @param range Range of the visibility - * @throws ParticleVersionException If the particle effect is not supported by the server version - * @throws ParticleDataException If the particle effect requires additional data - * @throws IllegalArgumentException If the particle effect is not directional or if it requires water and none is at the center location - * @see ParticlePacket#ParticlePacket(EffLib, Vector, float, boolean, ParticleData) - * @see ParticlePacket#sendTo(Location, double) - */ - public void display(Vector direction, float speed, Location center, double range) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { - if (!isSupported()) { - throw new ParticleVersionException("This particle effect is not supported by your server version"); - } - if (hasProperty(ParticleProperty.REQUIRES_DATA)) { - throw new ParticleDataException("This particle effect requires additional data"); - } - if (!hasProperty(ParticleProperty.DIRECTIONAL)) { - throw new IllegalArgumentException("This particle effect is not directional"); - } - if (hasProperty(ParticleProperty.REQUIRES_WATER) && !isWater(center)) { - throw new IllegalArgumentException("There is no water at the center location"); - } - new ParticlePacket(this, direction, speed, range > 256, null).sendTo(center, range); - } - - /** - * Displays a single particle which flies into a determined direction and is only visible for the specified players - * - * @param direction Direction of the particle - * @param speed Display speed of the particle - * @param center Center location of the effect - * @param players Receivers of the effect - * @throws ParticleVersionException If the particle effect is not supported by the server version - * @throws ParticleDataException If the particle effect requires additional data - * @throws IllegalArgumentException If the particle effect is not directional or if it requires water and none is at the center location - * @see ParticlePacket#ParticlePacket(EffLib, Vector, float, boolean, ParticleData) - * @see ParticlePacket#sendTo(Location, List) - */ - public void display(Vector direction, float speed, Location center, List players) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { - if (!isSupported()) { - throw new ParticleVersionException("This particle effect is not supported by your server version"); - } - if (hasProperty(ParticleProperty.REQUIRES_DATA)) { - throw new ParticleDataException("This particle effect requires additional data"); - } - if (!hasProperty(ParticleProperty.DIRECTIONAL)) { - throw new IllegalArgumentException("This particle effect is not directional"); - } - if (hasProperty(ParticleProperty.REQUIRES_WATER) && !isWater(center)) { - throw new IllegalArgumentException("There is no water at the center location"); - } - new ParticlePacket(this, direction, speed, isLongDistance(center, players), null).sendTo(center, players); - } - - /** - * Displays a single particle which flies into a determined direction and is only visible for the specified players - * - * @param direction Direction of the particle - * @param speed Display speed of the particle - * @param center Center location of the effect - * @param players Receivers of the effect - * @throws ParticleVersionException If the particle effect is not supported by the server version - * @throws ParticleDataException If the particle effect requires additional data - * @throws IllegalArgumentException If the particle effect is not directional or if it requires water and none is at the center location - * @see #display(Vector, float, Location, List) - */ - public void display(Vector direction, float speed, Location center, Player... players) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { - display(direction, speed, center, Arrays.asList(players)); - } - - /** - * Displays a single particle which is colored and only visible for all players within a certain range in the world of @param center - * - * @param color Color of the particle - * @param center Center location of the effect - * @param range Range of the visibility - * @throws ParticleVersionException If the particle effect is not supported by the server version - * @throws ParticleColorException If the particle effect is not colorable or the color type is incorrect - * @see ParticlePacket#ParticlePacket(EffLib, ParticleColor, boolean) - * @see ParticlePacket#sendTo(Location, double) - */ - public void display(ParticleColor color, Location center, double range) throws ParticleVersionException, ParticleColorException { - if (!isSupported()) { - throw new ParticleVersionException("This particle effect is not supported by your server version"); - } - if (!hasProperty(ParticleProperty.COLORABLE)) { - throw new ParticleColorException("This particle effect is not colorable"); - } - if (!isColorCorrect(this, color)) { - throw new ParticleColorException("The particle color type is incorrect"); - } - new ParticlePacket(this, color, range > 256).sendTo(center, range); - } - - /** - * Displays a single particle which is colored and only visible for the specified players - * - * @param color Color of the particle - * @param center Center location of the effect - * @param players Receivers of the effect - * @throws ParticleVersionException If the particle effect is not supported by the server version - * @throws ParticleColorException If the particle effect is not colorable or the color type is incorrect - * @see ParticlePacket#ParticlePacket(EffLib, ParticleColor, boolean) - * @see ParticlePacket#sendTo(Location, List) - */ - public void display(ParticleColor color, Location center, List players) throws ParticleVersionException, ParticleColorException { - if (!isSupported()) { - throw new ParticleVersionException("This particle effect is not supported by your server version"); - } - if (!hasProperty(ParticleProperty.COLORABLE)) { - throw new ParticleColorException("This particle effect is not colorable"); - } - if (!isColorCorrect(this, color)) { - throw new ParticleColorException("The particle color type is incorrect"); - } - new ParticlePacket(this, color, isLongDistance(center, players)).sendTo(center, players); - } - - /** - * Displays a single particle which is colored and only visible for the specified players - * - * @param color Color of the particle - * @param center Center location of the effect - * @param players Receivers of the effect - * @throws ParticleVersionException If the particle effect is not supported by the server version - * @throws ParticleColorException If the particle effect is not colorable or the color type is incorrect - * @see #display(ParticleColor, Location, List) - */ - public void display(ParticleColor color, Location center, Player... players) throws ParticleVersionException, ParticleColorException { - display(color, center, Arrays.asList(players)); - } - - /** - * Displays a particle effect which requires additional data and is only visible for all players within a certain range in the world of @param center - * - * @param data Data of the effect - * @param offsetX Maximum distance particles can fly away from the center on the x-axis - * @param offsetY Maximum distance particles can fly away from the center on the y-axis - * @param offsetZ Maximum distance particles can fly away from the center on the z-axis - * @param speed Display speed of the particles - * @param amount Amount of particles - * @param center Center location of the effect - * @param range Range of the visibility - * @throws ParticleVersionException If the particle effect is not supported by the server version - * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect - * @see ParticlePacket - * @see ParticlePacket#sendTo(Location, double) - */ - public void display(ParticleData data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, double range) throws ParticleVersionException, ParticleDataException { - if (!isSupported()) { - throw new ParticleVersionException("This particle effect is not supported by your server version"); - } - if (!hasProperty(ParticleProperty.REQUIRES_DATA)) { - throw new ParticleDataException("This particle effect does not require additional data"); - } - if (!isDataCorrect(this, data)) { - throw new ParticleDataException("The particle data type is incorrect"); - } - new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, range > 256, data).sendTo(center, range); - } - - /** - * Displays a particle effect which requires additional data and is only visible for the specified players - * - * @param data Data of the effect - * @param offsetX Maximum distance particles can fly away from the center on the x-axis - * @param offsetY Maximum distance particles can fly away from the center on the y-axis - * @param offsetZ Maximum distance particles can fly away from the center on the z-axis - * @param speed Display speed of the particles - * @param amount Amount of particles - * @param center Center location of the effect - * @param players Receivers of the effect - * @throws ParticleVersionException If the particle effect is not supported by the server version - * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect - * @see ParticlePacket - * @see ParticlePacket#sendTo(Location, List) - */ - public void display(ParticleData data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, List players) throws ParticleVersionException, ParticleDataException { - if (!isSupported()) { - throw new ParticleVersionException("This particle effect is not supported by your server version"); - } - if (!hasProperty(ParticleProperty.REQUIRES_DATA)) { - throw new ParticleDataException("This particle effect does not require additional data"); - } - if (!isDataCorrect(this, data)) { - throw new ParticleDataException("The particle data type is incorrect"); - } - new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, isLongDistance(center, players), data).sendTo(center, players); - } - - /** - * Displays a particle effect which requires additional data and is only visible for the specified players - * - * @param data Data of the effect - * @param offsetX Maximum distance particles can fly away from the center on the x-axis - * @param offsetY Maximum distance particles can fly away from the center on the y-axis - * @param offsetZ Maximum distance particles can fly away from the center on the z-axis - * @param speed Display speed of the particles - * @param amount Amount of particles - * @param center Center location of the effect - * @param players Receivers of the effect - * @throws ParticleVersionException If the particle effect is not supported by the server version - * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect - * @see #display(ParticleData, float, float, float, float, int, Location, List) - */ - public void display(ParticleData data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, Player... players) throws ParticleVersionException, ParticleDataException { - display(data, offsetX, offsetY, offsetZ, speed, amount, center, Arrays.asList(players)); - } - - /** - * Displays a single particle which requires additional data that flies into a determined direction and is only visible for all players within a certain range in the world of @param center - * - * @param data Data of the effect - * @param direction Direction of the particle - * @param speed Display speed of the particles - * @param center Center location of the effect - * @param range Range of the visibility - * @throws ParticleVersionException If the particle effect is not supported by the server version - * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect - * @see ParticlePacket - * @see ParticlePacket#sendTo(Location, double) - */ - public void display(ParticleData data, Vector direction, float speed, Location center, double range) throws ParticleVersionException, ParticleDataException { - if (!isSupported()) { - throw new ParticleVersionException("This particle effect is not supported by your server version"); - } - if (!hasProperty(ParticleProperty.REQUIRES_DATA)) { - throw new ParticleDataException("This particle effect does not require additional data"); - } - if (!isDataCorrect(this, data)) { - throw new ParticleDataException("The particle data type is incorrect"); - } - new ParticlePacket(this, direction, speed, range > 256, data).sendTo(center, range); - } - - /** - * Displays a single particle which requires additional data that flies into a determined direction and is only visible for the specified players - * - * @param data Data of the effect - * @param direction Direction of the particle - * @param speed Display speed of the particles - * @param center Center location of the effect - * @param players Receivers of the effect - * @throws ParticleVersionException If the particle effect is not supported by the server version - * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect - * @see ParticlePacket - * @see ParticlePacket#sendTo(Location, List) - */ - public void display(ParticleData data, Vector direction, float speed, Location center, List players) throws ParticleVersionException, ParticleDataException { - if (!isSupported()) { - throw new ParticleVersionException("This particle effect is not supported by your server version"); - } - if (!hasProperty(ParticleProperty.REQUIRES_DATA)) { - throw new ParticleDataException("This particle effect does not require additional data"); - } - if (!isDataCorrect(this, data)) { - throw new ParticleDataException("The particle data type is incorrect"); - } - new ParticlePacket(this, direction, speed, isLongDistance(center, players), data).sendTo(center, players); - } - - /** - * Displays a single particle which requires additional data that flies into a determined direction and is only visible for the specified players - * - * @param data Data of the effect - * @param direction Direction of the particle - * @param speed Display speed of the particles - * @param center Center location of the effect - * @param players Receivers of the effect - * @throws ParticleVersionException If the particle effect is not supported by the server version - * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect - * @see #display(ParticleData, Vector, float, Location, List) - */ - public void display(ParticleData data, Vector direction, float speed, Location center, Player... players) throws ParticleVersionException, ParticleDataException { - display(data, direction, speed, center, Arrays.asList(players)); - } - - /** - * Represents the property of a particle effect - *

- * This class is part of the ParticleEffect Library and follows the same usage conditions - * - * @author DarkBlade12 - * @since 1.7 - */ - public enum ParticleProperty { - /** - * The particle effect requires water to be displayed - */ - REQUIRES_WATER, - /** - * The particle effect requires block or item data to be displayed - */ - REQUIRES_DATA, - /** - * The particle effect uses the offsets as direction values - */ - DIRECTIONAL, - /** - * The particle effect uses the offsets as color values - */ - COLORABLE - } - - /** - * Represents the particle data for effects like {@link EffLib#ITEM_CRACK}, {@link EffLib#BLOCK_CRACK} and {@link EffLib#BLOCK_DUST} - *

- * This class is part of the ParticleEffect Library and follows the same usage conditions - * - * @author DarkBlade12 - * @since 1.6 - */ - public static abstract class ParticleData { - private final Material material; - private final byte data; - private final int[] packetData; - - /** - * Construct a new particle data - * - * @param material Material of the item/block - * @param data Data value of the item/block - */ - @SuppressWarnings("deprecation") - public ParticleData(Material material, byte data) { - this.material = material; - this.data = data; - this.packetData = new int[] { material.getId(), data }; - } - - /** - * Returns the material of this data - * - * @return The material - */ - public Material getMaterial() { - return material; - } - - /** - * Returns the data value of this data - * - * @return The data value - */ - public byte getData() { - return data; - } - - /** - * Returns the data as an int array for packet construction - * - * @return The data for the packet - */ - public int[] getPacketData() { - return packetData; - } - - /** - * Returns the data as a string for pre 1.8 versions - * - * @return The data string for the packet - */ - public String getPacketDataString() { - return "_" + packetData[0] + "_" + packetData[1]; - } - } - - /** - * Represents the item data for the {@link EffLib#ITEM_CRACK} effect - *

- * This class is part of the ParticleEffect Library and follows the same usage conditions - * - * @author DarkBlade12 - * @since 1.6 - */ - public static final class ItemData extends ParticleData { - /** - * Construct a new item data - * - * @param material Material of the item - * @param data Data value of the item - * @see ParticleData#ParticleData(Material, byte) - */ - public ItemData(Material material, byte data) { - super(material, data); - } - } - - /** - * Represents the block data for the {@link EffLib#BLOCK_CRACK} and {@link EffLib#BLOCK_DUST} effects - *

- * This class is part of the ParticleEffect Library and follows the same usage conditions - * - * @author DarkBlade12 - * @since 1.6 - */ - public static final class BlockData extends ParticleData { - /** - * Construct a new block data - * - * @param material Material of the block - * @param data Data value of the block - * @throws IllegalArgumentException If the material is not a block - * @see ParticleData#ParticleData(Material, byte) - */ - public BlockData(Material material, byte data) throws IllegalArgumentException { - super(material, data); - if (!material.isBlock()) { - throw new IllegalArgumentException("The material is not a block"); - } - } - } - - /** - * Represents the color for effects like {@link EffLib#SPELL_MOB}, {@link EffLib#SPELL_MOB_AMBIENT}, {@link EffLib#REDSTONE} and {@link EffLib#NOTE} - *

- * This class is part of the ParticleEffect Library and follows the same usage conditions - * - * @author DarkBlade12 - * @since 1.7 - */ - public static abstract class ParticleColor { - /** - * Returns the value for the offsetX field - * - * @return The offsetX value - */ - public abstract float getValueX(); - - /** - * Returns the value for the offsetY field - * - * @return The offsetY value - */ - public abstract float getValueY(); - - /** - * Returns the value for the offsetZ field - * - * @return The offsetZ value - */ - public abstract float getValueZ(); - } - - /** - * Represents the color for effects like {@link EffLib#SPELL_MOB}, {@link EffLib#SPELL_MOB_AMBIENT} and {@link EffLib#NOTE} - *

- * This class is part of the ParticleEffect Library and follows the same usage conditions - * - * @author DarkBlade12 - * @since 1.7 - */ - public static final class OrdinaryColor extends ParticleColor { - private final int red; - private final int green; - private final int blue; - - /** - * Construct a new ordinary color - * - * @param red Red value of the RGB format - * @param green Green value of the RGB format - * @param blue Blue value of the RGB format - * @throws IllegalArgumentException If one of the values is lower than 0 or higher than 255 - */ - public OrdinaryColor(int red, int green, int blue) throws IllegalArgumentException { - if (red < 0) { - throw new IllegalArgumentException("The red value is lower than 0"); - } - if (red > 255) { - throw new IllegalArgumentException("The red value is higher than 255"); - } - this.red = red; - if (green < 0) { - throw new IllegalArgumentException("The green value is lower than 0"); - } - if (green > 255) { - throw new IllegalArgumentException("The green value is higher than 255"); - } - this.green = green; - if (blue < 0) { - throw new IllegalArgumentException("The blue value is lower than 0"); - } - if (blue > 255) { - throw new IllegalArgumentException("The blue value is higher than 255"); - } - this.blue = blue; - } - - /** - * Construct a new ordinary color - * - * @param color Bukkit color - */ - public OrdinaryColor(Color color) { - this(color.getRed(), color.getGreen(), color.getBlue()); - } - - /** - * Returns the red value of the RGB format - * - * @return The red value - */ - public int getRed() { - return red; - } - - /** - * Returns the green value of the RGB format - * - * @return The green value - */ - public int getGreen() { - return green; - } - - /** - * Returns the blue value of the RGB format - * - * @return The blue value - */ - public int getBlue() { - return blue; - } - - /** - * Returns the red value divided by 255 - * - * @return The offsetX value - */ - @Override - public float getValueX() { - return red / 255F; - } - - /** - * Returns the green value divided by 255 - * - * @return The offsetY value - */ - @Override - public float getValueY() { - return green / 255F; - } - - /** - * Returns the blue value divided by 255 - * - * @return The offsetZ value - */ - @Override - public float getValueZ() { - return blue / 255F; - } - } - - /** - * Represents the color for the {@link EffLib#NOTE} effect - *

- * This class is part of the ParticleEffect Library and follows the same usage conditions - * - * @author DarkBlade12 - * @since 1.7 - */ - public static final class NoteColor extends ParticleColor { - private final int note; - - /** - * Construct a new note color - * - * @param note Note id which determines color - * @throws IllegalArgumentException If the note value is lower than 0 or higher than 24 - */ - public NoteColor(int note) throws IllegalArgumentException { - if (note < 0) { - throw new IllegalArgumentException("The note value is lower than 0"); - } - if (note > 24) { - throw new IllegalArgumentException("The note value is higher than 24"); - } - this.note = note; - } - - /** - * Returns the note value divided by 24 - * - * @return The offsetX value - */ - @Override - public float getValueX() { - return note / 24F; - } - - /** - * Returns zero because the offsetY value is unused - * - * @return zero - */ - @Override - public float getValueY() { - return 0; - } - - /** - * Returns zero because the offsetZ value is unused - * - * @return zero - */ - @Override - public float getValueZ() { - return 0; - } - - } - - /** - * Represents a runtime exception that is thrown either if the displayed particle effect requires data and has none or vice-versa or if the data type is incorrect - *

- * This class is part of the ParticleEffect Library and follows the same usage conditions - * - * @author DarkBlade12 - * @since 1.6 - */ - private static final class ParticleDataException extends RuntimeException { - private static final long serialVersionUID = 3203085387160737484L; - - /** - * Construct a new particle data exception - * - * @param message Message that will be logged - */ - public ParticleDataException(String message) { - super(message); - } - } - - /** - * Represents a runtime exception that is thrown either if the displayed particle effect is not colorable or if the particle color type is incorrect - *

- * This class is part of the ParticleEffect Library and follows the same usage conditions - * - * @author DarkBlade12 - * @since 1.7 - */ - private static final class ParticleColorException extends RuntimeException { - private static final long serialVersionUID = 3203085387160737484L; - - /** - * Construct a new particle color exception - * - * @param message Message that will be logged - */ - public ParticleColorException(String message) { - super(message); - } - } - - /** - * Represents a runtime exception that is thrown if the displayed particle effect requires a newer version - *

- * This class is part of the ParticleEffect Library and follows the same usage conditions - * - * @author DarkBlade12 - * @since 1.6 - */ - private static final class ParticleVersionException extends RuntimeException { - private static final long serialVersionUID = 3203085387160737484L; - - /** - * Construct a new particle version exception - * - * @param message Message that will be logged - */ - public ParticleVersionException(String message) { - super(message); - } - } - - /** - * Represents a particle effect packet with all attributes which is used for sending packets to the players - *

- * This class is part of the ParticleEffect Library and follows the same usage conditions - * - * @author DarkBlade12 - * @since 1.5 - */ - public static final class ParticlePacket { - private static int version; - private static Class enumParticle; - private static Constructor packetConstructor; - private static Method getHandle; - private static Field playerConnection; - private static Method sendPacket; - private static boolean initialized; - private final EffLib effect; - private float offsetX; - private final float offsetY; - private final float offsetZ; - private final float speed; - private final int amount; - private final boolean longDistance; - private final ParticleData data; - private Object packet; - - /** - * Construct a new particle packet - * - * @param effect Particle effect - * @param offsetX Maximum distance particles can fly away from the center on the x-axis - * @param offsetY Maximum distance particles can fly away from the center on the y-axis - * @param offsetZ Maximum distance particles can fly away from the center on the z-axis - * @param speed Display speed of the particles - * @param amount Amount of particles - * @param longDistance Indicates whether the maximum distance is increased from 256 to 65536 - * @param data Data of the effect - * @throws IllegalArgumentException If the speed or amount is lower than 0 - * @see #initialize() - */ - public ParticlePacket(EffLib effect, float offsetX, float offsetY, float offsetZ, float speed, int amount, boolean longDistance, ParticleData data) throws IllegalArgumentException { - initialize(); - if (speed < 0) { - throw new IllegalArgumentException("The speed is lower than 0"); - } - if (amount < 0) { - throw new IllegalArgumentException("The amount is lower than 0"); - } - this.effect = effect; - this.offsetX = offsetX; - this.offsetY = offsetY; - this.offsetZ = offsetZ; - this.speed = speed; - this.amount = amount; - this.longDistance = longDistance; - this.data = data; - } - - /** - * Construct a new particle packet of a single particle flying into a determined direction - * - * @param effect Particle effect - * @param direction Direction of the particle - * @param speed Display speed of the particle - * @param longDistance Indicates whether the maximum distance is increased from 256 to 65536 - * @param data Data of the effect - * @throws IllegalArgumentException If the speed is lower than 0 - */ - public ParticlePacket(EffLib effect, Vector direction, float speed, boolean longDistance, ParticleData data) throws IllegalArgumentException { - this(effect, (float) direction.getX(), (float) direction.getY(), (float) direction.getZ(), speed, 0, longDistance, data); - } - - /** - * Construct a new particle packet of a single colored particle - * - * @param effect Particle effect - * @param color Color of the particle - * @param longDistance Indicates whether the maximum distance is increased from 256 to 65536 - */ - public ParticlePacket(EffLib effect, ParticleColor color, boolean longDistance) { - this(effect, color.getValueX(), color.getValueY(), color.getValueZ(), 1, 0, longDistance, null); - if (effect == EffLib.REDSTONE && color instanceof OrdinaryColor && ((OrdinaryColor) color).getRed() == 0) { - offsetX = Float.MIN_NORMAL; - } - } - - /** - * Initializes {@link #packetConstructor}, {@link #getHandle}, {@link #playerConnection} and {@link #sendPacket} and sets {@link #initialized} to true if it succeeds - *

- * Note: These fields only have to be initialized once, so it will return if {@link #initialized} is already set to true - * - * @throws VersionIncompatibleException if your bukkit version is not supported by this library - */ - public static void initialize() throws VersionIncompatibleException { - if (initialized) { - return; - } - try { - version = Integer.valueOf(PackageType.getServerVersion().split("_")[1]); - if (version > 7) { - enumParticle = PackageType.MINECRAFT_SERVER.getClass("EnumParticle"); - } - Class packetClass = PackageType.MINECRAFT_SERVER.getClass(version < 7 ? "Packet63WorldParticles" : "PacketPlayOutWorldParticles"); - packetConstructor = ReflectionUtils.getConstructor(packetClass); - getHandle = ReflectionUtils.getMethod("CraftPlayer", PackageType.CRAFTBUKKIT_ENTITY, "getHandle"); - playerConnection = ReflectionUtils.getField("EntityPlayer", PackageType.MINECRAFT_SERVER, false, "playerConnection"); - sendPacket = ReflectionUtils.getMethod(playerConnection.getType(), "sendPacket", PackageType.MINECRAFT_SERVER.getClass("Packet")); - } catch (Exception exception) { - throw new VersionIncompatibleException("Your current bukkit version seems to be incompatible with this library", exception); - } - initialized = true; - } - - /** - * Returns the version of your server (1.x) - * - * @return The version number - */ - public static int getVersion() { - if (!initialized) { - initialize(); - } - return version; - } - - /** - * Determine if {@link #packetConstructor}, {@link #getHandle}, {@link #playerConnection} and {@link #sendPacket} are initialized - * - * @return Whether these fields are initialized or not - * @see #initialize() - */ - public static boolean isInitialized() { - return initialized; - } - - /** - * Initializes {@link #packet} with all set values - * - * @param center Center location of the effect - * @throws PacketInstantiationException If instantion fails due to an unknown error - */ - private void initializePacket(Location center) throws PacketInstantiationException { - if (packet != null) { - return; - } - try { - packet = packetConstructor.newInstance(); - if (version < 8) { - String name = effect.getName(); - if (data != null) { - name += data.getPacketDataString(); - } - ReflectionUtils.setValue(packet, true, "a", name); - } else { - ReflectionUtils.setValue(packet, true, "a", enumParticle.getEnumConstants()[effect.getId()]); - ReflectionUtils.setValue(packet, true, "j", longDistance); - if (data != null) { - int[] packetData = data.getPacketData(); - ReflectionUtils.setValue(packet, true, "k", effect == EffLib.ITEM_CRACK ? packetData : new int[] { packetData[0] | (packetData[1] << 12) }); - } - } - ReflectionUtils.setValue(packet, true, "b", (float) center.getX()); - ReflectionUtils.setValue(packet, true, "c", (float) center.getY()); - ReflectionUtils.setValue(packet, true, "d", (float) center.getZ()); - ReflectionUtils.setValue(packet, true, "e", offsetX); - ReflectionUtils.setValue(packet, true, "f", offsetY); - ReflectionUtils.setValue(packet, true, "g", offsetZ); - ReflectionUtils.setValue(packet, true, "h", speed); - ReflectionUtils.setValue(packet, true, "i", amount); - } catch (Exception exception) { - throw new PacketInstantiationException("Packet instantiation failed", exception); - } - } - - /** - * Sends the packet to a single player and caches it - * - * @param center Center location of the effect - * @param player Receiver of the packet - * @throws PacketInstantiationException If instantion fails due to an unknown error - * @throws PacketSendingException If sending fails due to an unknown error - * @see #initializePacket(Location) - */ - public void sendTo(Location center, Player player) throws PacketInstantiationException, PacketSendingException { - initializePacket(center); - try { - sendPacket.invoke(playerConnection.get(getHandle.invoke(player)), packet); - } catch (Exception exception) { - throw new PacketSendingException("Failed to send the packet to player '" + player.getName() + "'", exception); - } - } - - /** - * Sends the packet to all players in the list - * - * @param center Center location of the effect - * @param players Receivers of the packet - * @throws IllegalArgumentException If the player list is empty - * @see #sendTo(Location center, Player player) - */ - public void sendTo(Location center, List players) throws IllegalArgumentException { - if (players.isEmpty()) { - throw new IllegalArgumentException("The player list is empty"); - } - for (Player player : players) { - sendTo(center, player); - } - } - - /** - * Sends the packet to all players in a certain range - * - * @param center Center location of the effect - * @param range Range in which players will receive the packet (Maximum range for particles is usually 16, but it can differ for some types) - * @throws IllegalArgumentException If the range is lower than 1 - * @see #sendTo(Location center, Player player) - */ - public void sendTo(Location center, double range) throws IllegalArgumentException { - if (range < 1) { - throw new IllegalArgumentException("The range is lower than 1"); - } - String worldName = center.getWorld().getName(); - double squared = range * range; - for (Player player : Bukkit.getOnlinePlayers()) { - if (!player.getWorld().getName().equals(worldName) || player.getLocation().distanceSquared(center) > squared) { - continue; - } - sendTo(center, player); - } - } - - /** - * Represents a runtime exception that is thrown if a bukkit version is not compatible with this library - *

- * This class is part of the ParticleEffect Library and follows the same usage conditions - * - * @author DarkBlade12 - * @since 1.5 - */ - private static final class VersionIncompatibleException extends RuntimeException { - private static final long serialVersionUID = 3203085387160737484L; - - /** - * Construct a new version incompatible exception - * - * @param message Message that will be logged - * @param cause Cause of the exception - */ - public VersionIncompatibleException(String message, Throwable cause) { - super(message, cause); - } - } - - /** - * Represents a runtime exception that is thrown if packet instantiation fails - *

- * This class is part of the ParticleEffect Library and follows the same usage conditions - * - * @author DarkBlade12 - * @since 1.4 - */ - private static final class PacketInstantiationException extends RuntimeException { - private static final long serialVersionUID = 3203085387160737484L; - - /** - * Construct a new packet instantiation exception - * - * @param message Message that will be logged - * @param cause Cause of the exception - */ - public PacketInstantiationException(String message, Throwable cause) { - super(message, cause); - } - } - - /** - * Represents a runtime exception that is thrown if packet sending fails - *

- * This class is part of the ParticleEffect Library and follows the same usage conditions - * - * @author DarkBlade12 - * @since 1.4 - */ - private static final class PacketSendingException extends RuntimeException { - private static final long serialVersionUID = 3203085387160737484L; - - /** - * Construct a new packet sending exception - * - * @param message Message that will be logged - * @param cause Cause of the exception - */ - public PacketSendingException(String message, Throwable cause) { - super(message, cause); - } - } - } + /** + * 掉落方块 + */ + FALLING_DUST("falling_dust", 46, 11, ParticleProperty.REQUIRES_DATA), + + /** + * 不死图腾 + */ + TOTEM("totem", 47, 11); + + private static final Map NAME_MAP = new HashMap<>(); + private static final Map ID_MAP = new HashMap<>(); + private final String name; + private final int id; + private final int requiredVersion; + private final List properties; + + // Initialize map for quick name and id lookup + static { + for (EffLib effect : values()) { + NAME_MAP.put(effect.name, effect); + ID_MAP.put(effect.id, effect); + } + } + + /** + * Construct a new particle effect + * + * @param name Name of this particle effect + * @param id Id of this particle effect + * @param requiredVersion Version which is required (1.x) + * @param properties Properties of this particle effect + */ + EffLib(String name, int id, int requiredVersion, ParticleProperty... properties) { + this.name = name; + this.id = id; + this.requiredVersion = requiredVersion; + this.properties = Arrays.asList(properties); + } + + /** + * Returns the name of this particle effect + * + * @return The name + */ + public String getName() { + return name; + } + + /** + * Returns the id of this particle effect + * + * @return The id + */ + public int getId() { + return id; + } + + /** + * Returns the required version for this particle effect (1.x) + * + * @return The required version + */ + public int getRequiredVersion() { + return requiredVersion; + } + + /** + * Determine if this particle effect has a specific property + * + * @return Whether it has the property or not + */ + public boolean hasProperty(ParticleProperty property) { + return properties.contains(property); + } + + /** + * Determine if this particle effect is supported by your current server version + * + * @return Whether the particle effect is supported or not + */ + public boolean isSupported() { + return requiredVersion == -1 || ParticlePacket.getVersion() >= requiredVersion; + } + + /** + * Returns the particle effect with the given name + * + * @param name Name of the particle effect + * @return The particle effect + */ + public static EffLib fromName(String name) { + for (Entry entry : NAME_MAP.entrySet()) { + if (!entry.getKey().equalsIgnoreCase(name)) { + continue; + } + return entry.getValue(); + } + return null; + } + + /** + * Returns the particle effect with the given id + * + * @param id Id of the particle effect + * @return The particle effect + */ + public static EffLib fromId(int id) { + for (Entry entry : ID_MAP.entrySet()) { + if (entry.getKey() != id) { + continue; + } + return entry.getValue(); + } + return null; + } + + /** + * Determine if water is at a certain location + * + * @param location Location to check + * @return Whether water is at this location or not + */ + private static boolean isWater(Location location) { + Material material = location.getBlock().getType(); + return material == Material.WATER || material == Material.STATIONARY_WATER; + } + + /** + * Determine if the distance between @param location and one of the players exceeds 256 + * + * @param location Location to check + * @return Whether the distance exceeds 256 or not + */ + private static boolean isLongDistance(Location location, List players) { + String world = location.getWorld().getName(); + for (Player player : players) { + Location playerLocation = player.getLocation(); + if (!world.equals(playerLocation.getWorld().getName()) || playerLocation.distanceSquared(location) < 65536) { + continue; + } + return true; + } + return false; + } + + /** + * Determine if the data type for a particle effect is correct + * + * @param effect Particle effect + * @param data Particle data + * @return Whether the data type is correct or not + */ + private static boolean isDataCorrect(EffLib effect, ParticleData data) { + return ((effect == BLOCK_CRACK || effect == BLOCK_DUST) && data instanceof BlockData) || (effect == ITEM_CRACK && data instanceof ItemData); + } + + /** + * Determine if the color type for a particle effect is correct + * + * @param effect Particle effect + * @param color Particle color + * @return Whether the color type is correct or not + */ + private static boolean isColorCorrect(EffLib effect, ParticleColor color) { + return ((effect == SPELL_MOB || effect == SPELL_MOB_AMBIENT || effect == REDSTONE) && color instanceof OrdinaryColor) || (effect == NOTE && color instanceof NoteColor); + } + + /** + * Displays a particle effect which is only visible for all players within a certain range in the world of @param center + * + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param range Range of the visibility + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data + * @throws IllegalArgumentException If the particle effect requires water and none is at the center location + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, double) + */ + public void display(float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, double range) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect requires additional data"); + } + if (hasProperty(ParticleProperty.REQUIRES_WATER) && !isWater(center)) { + throw new IllegalArgumentException("There is no water at the center location"); + } + new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, range > 256, null).sendTo(center, range); + } + + /** + * Displays a particle effect which is only visible for the specified players + * + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data + * @throws IllegalArgumentException If the particle effect requires water and none is at the center location + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, List) + */ + public void display(float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, List players) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect requires additional data"); + } + if (hasProperty(ParticleProperty.REQUIRES_WATER) && !isWater(center)) { + throw new IllegalArgumentException("There is no water at the center location"); + } + new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, isLongDistance(center, players), null).sendTo(center, players); + } + + /** + * Displays a particle effect which is only visible for the specified players + * + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data + * @throws IllegalArgumentException If the particle effect requires water and none is at the center location + * @see #display(float, float, float, float, int, Location, List) + */ + public void display(float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, Player... players) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + display(offsetX, offsetY, offsetZ, speed, amount, center, Arrays.asList(players)); + } + + /** + * Displays a single particle which flies into a determined direction and is only visible for all players within a certain range in the world of @param center + * + * @param direction Direction of the particle + * @param speed Display speed of the particle + * @param center Center location of the effect + * @param range Range of the visibility + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data + * @throws IllegalArgumentException If the particle effect is not directional or if it requires water and none is at the center location + * @see ParticlePacket#ParticlePacket(EffLib, Vector, float, boolean, ParticleData) + * @see ParticlePacket#sendTo(Location, double) + */ + public void display(Vector direction, float speed, Location center, double range) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect requires additional data"); + } + if (!hasProperty(ParticleProperty.DIRECTIONAL)) { + throw new IllegalArgumentException("This particle effect is not directional"); + } + if (hasProperty(ParticleProperty.REQUIRES_WATER) && !isWater(center)) { + throw new IllegalArgumentException("There is no water at the center location"); + } + new ParticlePacket(this, direction, speed, range > 256, null).sendTo(center, range); + } + + /** + * Displays a single particle which flies into a determined direction and is only visible for the specified players + * + * @param direction Direction of the particle + * @param speed Display speed of the particle + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data + * @throws IllegalArgumentException If the particle effect is not directional or if it requires water and none is at the center location + * @see ParticlePacket#ParticlePacket(EffLib, Vector, float, boolean, ParticleData) + * @see ParticlePacket#sendTo(Location, List) + */ + public void display(Vector direction, float speed, Location center, List players) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect requires additional data"); + } + if (!hasProperty(ParticleProperty.DIRECTIONAL)) { + throw new IllegalArgumentException("This particle effect is not directional"); + } + if (hasProperty(ParticleProperty.REQUIRES_WATER) && !isWater(center)) { + throw new IllegalArgumentException("There is no water at the center location"); + } + new ParticlePacket(this, direction, speed, isLongDistance(center, players), null).sendTo(center, players); + } + + /** + * Displays a single particle which flies into a determined direction and is only visible for the specified players + * + * @param direction Direction of the particle + * @param speed Display speed of the particle + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data + * @throws IllegalArgumentException If the particle effect is not directional or if it requires water and none is at the center location + * @see #display(Vector, float, Location, List) + */ + public void display(Vector direction, float speed, Location center, Player... players) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + display(direction, speed, center, Arrays.asList(players)); + } + + /** + * Displays a single particle which is colored and only visible for all players within a certain range in the world of @param center + * + * @param color Color of the particle + * @param center Center location of the effect + * @param range Range of the visibility + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleColorException If the particle effect is not colorable or the color type is incorrect + * @see ParticlePacket#ParticlePacket(EffLib, ParticleColor, boolean) + * @see ParticlePacket#sendTo(Location, double) + */ + public void display(ParticleColor color, Location center, double range) throws ParticleVersionException, ParticleColorException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (!hasProperty(ParticleProperty.COLORABLE)) { + throw new ParticleColorException("This particle effect is not colorable"); + } + if (!isColorCorrect(this, color)) { + throw new ParticleColorException("The particle color type is incorrect"); + } + new ParticlePacket(this, color, range > 256).sendTo(center, range); + } + + /** + * Displays a single particle which is colored and only visible for the specified players + * + * @param color Color of the particle + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleColorException If the particle effect is not colorable or the color type is incorrect + * @see ParticlePacket#ParticlePacket(EffLib, ParticleColor, boolean) + * @see ParticlePacket#sendTo(Location, List) + */ + public void display(ParticleColor color, Location center, List players) throws ParticleVersionException, ParticleColorException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (!hasProperty(ParticleProperty.COLORABLE)) { + throw new ParticleColorException("This particle effect is not colorable"); + } + if (!isColorCorrect(this, color)) { + throw new ParticleColorException("The particle color type is incorrect"); + } + new ParticlePacket(this, color, isLongDistance(center, players)).sendTo(center, players); + } + + /** + * Displays a single particle which is colored and only visible for the specified players + * + * @param color Color of the particle + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleColorException If the particle effect is not colorable or the color type is incorrect + * @see #display(ParticleColor, Location, List) + */ + public void display(ParticleColor color, Location center, Player... players) throws ParticleVersionException, ParticleColorException { + display(color, center, Arrays.asList(players)); + } + + /** + * Displays a particle effect which requires additional data and is only visible for all players within a certain range in the world of @param center + * + * @param data Data of the effect + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param range Range of the visibility + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, double) + */ + public void display(ParticleData data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, double range) throws ParticleVersionException, ParticleDataException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (!hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect does not require additional data"); + } + if (!isDataCorrect(this, data)) { + throw new ParticleDataException("The particle data type is incorrect"); + } + new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, range > 256, data).sendTo(center, range); + } + + /** + * Displays a particle effect which requires additional data and is only visible for the specified players + * + * @param data Data of the effect + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, List) + */ + public void display(ParticleData data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, List players) throws ParticleVersionException, ParticleDataException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (!hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect does not require additional data"); + } + if (!isDataCorrect(this, data)) { + throw new ParticleDataException("The particle data type is incorrect"); + } + new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, isLongDistance(center, players), data).sendTo(center, players); + } + + /** + * Displays a particle effect which requires additional data and is only visible for the specified players + * + * @param data Data of the effect + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect + * @see #display(ParticleData, float, float, float, float, int, Location, List) + */ + public void display(ParticleData data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, Player... players) throws ParticleVersionException, ParticleDataException { + display(data, offsetX, offsetY, offsetZ, speed, amount, center, Arrays.asList(players)); + } + + /** + * Displays a single particle which requires additional data that flies into a determined direction and is only visible for all players within a certain range in the world of @param center + * + * @param data Data of the effect + * @param direction Direction of the particle + * @param speed Display speed of the particles + * @param center Center location of the effect + * @param range Range of the visibility + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, double) + */ + public void display(ParticleData data, Vector direction, float speed, Location center, double range) throws ParticleVersionException, ParticleDataException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (!hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect does not require additional data"); + } + if (!isDataCorrect(this, data)) { + throw new ParticleDataException("The particle data type is incorrect"); + } + new ParticlePacket(this, direction, speed, range > 256, data).sendTo(center, range); + } + + /** + * Displays a single particle which requires additional data that flies into a determined direction and is only visible for the specified players + * + * @param data Data of the effect + * @param direction Direction of the particle + * @param speed Display speed of the particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, List) + */ + public void display(ParticleData data, Vector direction, float speed, Location center, List players) throws ParticleVersionException, ParticleDataException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (!hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect does not require additional data"); + } + if (!isDataCorrect(this, data)) { + throw new ParticleDataException("The particle data type is incorrect"); + } + new ParticlePacket(this, direction, speed, isLongDistance(center, players), data).sendTo(center, players); + } + + /** + * Displays a single particle which requires additional data that flies into a determined direction and is only visible for the specified players + * + * @param data Data of the effect + * @param direction Direction of the particle + * @param speed Display speed of the particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect + * @see #display(ParticleData, Vector, float, Location, List) + */ + public void display(ParticleData data, Vector direction, float speed, Location center, Player... players) throws ParticleVersionException, ParticleDataException { + display(data, direction, speed, center, Arrays.asList(players)); + } + + /** + * Represents the property of a particle effect + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.7 + */ + public enum ParticleProperty { + /** + * The particle effect requires water to be displayed + */ + REQUIRES_WATER, + /** + * The particle effect requires block or item data to be displayed + */ + REQUIRES_DATA, + /** + * The particle effect uses the offsets as direction values + */ + DIRECTIONAL, + /** + * The particle effect uses the offsets as color values + */ + COLORABLE + } + + /** + * Represents the particle data for effects like {@link EffLib#ITEM_CRACK}, {@link EffLib#BLOCK_CRACK} and {@link EffLib#BLOCK_DUST} + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.6 + */ + public static abstract class ParticleData { + private final Material material; + private final byte data; + private final int[] packetData; + + /** + * Construct a new particle data + * + * @param material Material of the item/block + * @param data Data value of the item/block + */ + @SuppressWarnings("deprecation") + public ParticleData(Material material, byte data) { + this.material = material; + this.data = data; + this.packetData = new int[] {material.getId(), data}; + } + + /** + * Returns the material of this data + * + * @return The material + */ + public Material getMaterial() { + return material; + } + + /** + * Returns the data value of this data + * + * @return The data value + */ + public byte getData() { + return data; + } + + /** + * Returns the data as an int array for packet construction + * + * @return The data for the packet + */ + public int[] getPacketData() { + return packetData; + } + + /** + * Returns the data as a string for pre 1.8 versions + * + * @return The data string for the packet + */ + public String getPacketDataString() { + return "_" + packetData[0] + "_" + packetData[1]; + } + } + + /** + * Represents the item data for the {@link EffLib#ITEM_CRACK} effect + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.6 + */ + public static final class ItemData extends ParticleData { + /** + * Construct a new item data + * + * @param material Material of the item + * @param data Data value of the item + * @see ParticleData#ParticleData(Material, byte) + */ + public ItemData(Material material, byte data) { + super(material, data); + } + } + + /** + * Represents the block data for the {@link EffLib#BLOCK_CRACK} and {@link EffLib#BLOCK_DUST} effects + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.6 + */ + public static final class BlockData extends ParticleData { + /** + * Construct a new block data + * + * @param material Material of the block + * @param data Data value of the block + * @throws IllegalArgumentException If the material is not a block + * @see ParticleData#ParticleData(Material, byte) + */ + public BlockData(Material material, byte data) throws IllegalArgumentException { + super(material, data); + if (!material.isBlock()) { + throw new IllegalArgumentException("The material is not a block"); + } + } + } + + /** + * Represents the color for effects like {@link EffLib#SPELL_MOB}, {@link EffLib#SPELL_MOB_AMBIENT}, {@link EffLib#REDSTONE} and {@link EffLib#NOTE} + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.7 + */ + public static abstract class ParticleColor { + /** + * Returns the value for the offsetX field + * + * @return The offsetX value + */ + public abstract float getValueX(); + + /** + * Returns the value for the offsetY field + * + * @return The offsetY value + */ + public abstract float getValueY(); + + /** + * Returns the value for the offsetZ field + * + * @return The offsetZ value + */ + public abstract float getValueZ(); + } + + /** + * Represents the color for effects like {@link EffLib#SPELL_MOB}, {@link EffLib#SPELL_MOB_AMBIENT} and {@link EffLib#NOTE} + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.7 + */ + public static final class OrdinaryColor extends ParticleColor { + private final int red; + private final int green; + private final int blue; + + /** + * Construct a new ordinary color + * + * @param red Red value of the RGB format + * @param green Green value of the RGB format + * @param blue Blue value of the RGB format + * @throws IllegalArgumentException If one of the values is lower than 0 or higher than 255 + */ + public OrdinaryColor(int red, int green, int blue) throws IllegalArgumentException { + if (red < 0) { + throw new IllegalArgumentException("The red value is lower than 0"); + } + if (red > 255) { + throw new IllegalArgumentException("The red value is higher than 255"); + } + this.red = red; + if (green < 0) { + throw new IllegalArgumentException("The green value is lower than 0"); + } + if (green > 255) { + throw new IllegalArgumentException("The green value is higher than 255"); + } + this.green = green; + if (blue < 0) { + throw new IllegalArgumentException("The blue value is lower than 0"); + } + if (blue > 255) { + throw new IllegalArgumentException("The blue value is higher than 255"); + } + this.blue = blue; + } + + /** + * Construct a new ordinary color + * + * @param color Bukkit color + */ + public OrdinaryColor(Color color) { + this(color.getRed(), color.getGreen(), color.getBlue()); + } + + /** + * Returns the red value of the RGB format + * + * @return The red value + */ + public int getRed() { + return red; + } + + /** + * Returns the green value of the RGB format + * + * @return The green value + */ + public int getGreen() { + return green; + } + + /** + * Returns the blue value of the RGB format + * + * @return The blue value + */ + public int getBlue() { + return blue; + } + + /** + * Returns the red value divided by 255 + * + * @return The offsetX value + */ + @Override + public float getValueX() { + return red / 255F; + } + + /** + * Returns the green value divided by 255 + * + * @return The offsetY value + */ + @Override + public float getValueY() { + return green / 255F; + } + + /** + * Returns the blue value divided by 255 + * + * @return The offsetZ value + */ + @Override + public float getValueZ() { + return blue / 255F; + } + } + + /** + * Represents the color for the {@link EffLib#NOTE} effect + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.7 + */ + public static final class NoteColor extends ParticleColor { + private final int note; + + /** + * Construct a new note color + * + * @param note Note id which determines color + * @throws IllegalArgumentException If the note value is lower than 0 or higher than 24 + */ + public NoteColor(int note) throws IllegalArgumentException { + if (note < 0) { + throw new IllegalArgumentException("The note value is lower than 0"); + } + if (note > 24) { + throw new IllegalArgumentException("The note value is higher than 24"); + } + this.note = note; + } + + /** + * Returns the note value divided by 24 + * + * @return The offsetX value + */ + @Override + public float getValueX() { + return note / 24F; + } + + /** + * Returns zero because the offsetY value is unused + * + * @return zero + */ + @Override + public float getValueY() { + return 0; + } + + /** + * Returns zero because the offsetZ value is unused + * + * @return zero + */ + @Override + public float getValueZ() { + return 0; + } + + } + + /** + * Represents a runtime exception that is thrown either if the displayed particle effect requires data and has none or vice-versa or if the data type is incorrect + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.6 + */ + private static final class ParticleDataException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new particle data exception + * + * @param message Message that will be logged + */ + public ParticleDataException(String message) { + super(message); + } + } + + /** + * Represents a runtime exception that is thrown either if the displayed particle effect is not colorable or if the particle color type is incorrect + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.7 + */ + private static final class ParticleColorException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new particle color exception + * + * @param message Message that will be logged + */ + public ParticleColorException(String message) { + super(message); + } + } + + /** + * Represents a runtime exception that is thrown if the displayed particle effect requires a newer version + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.6 + */ + private static final class ParticleVersionException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new particle version exception + * + * @param message Message that will be logged + */ + public ParticleVersionException(String message) { + super(message); + } + } + + /** + * Represents a particle effect packet with all attributes which is used for sending packets to the players + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.5 + */ + public static final class ParticlePacket { + private static int version; + private static Class enumParticle; + private static Constructor packetConstructor; + private static Method getHandle; + private static Field playerConnection; + private static Method sendPacket; + private static boolean initialized; + private final EffLib effect; + private float offsetX; + private final float offsetY; + private final float offsetZ; + private final float speed; + private final int amount; + private final boolean longDistance; + private final ParticleData data; + private Object packet; + + /** + * Construct a new particle packet + * + * @param effect Particle effect + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param longDistance Indicates whether the maximum distance is increased from 256 to 65536 + * @param data Data of the effect + * @throws IllegalArgumentException If the speed or amount is lower than 0 + * @see #initialize() + */ + public ParticlePacket(EffLib effect, float offsetX, float offsetY, float offsetZ, float speed, int amount, boolean longDistance, ParticleData data) throws IllegalArgumentException { + initialize(); + if (speed < 0) { + throw new IllegalArgumentException("The speed is lower than 0"); + } + if (amount < 0) { + throw new IllegalArgumentException("The amount is lower than 0"); + } + this.effect = effect; + this.offsetX = offsetX; + this.offsetY = offsetY; + this.offsetZ = offsetZ; + this.speed = speed; + this.amount = amount; + this.longDistance = longDistance; + this.data = data; + } + + /** + * Construct a new particle packet of a single particle flying into a determined direction + * + * @param effect Particle effect + * @param direction Direction of the particle + * @param speed Display speed of the particle + * @param longDistance Indicates whether the maximum distance is increased from 256 to 65536 + * @param data Data of the effect + * @throws IllegalArgumentException If the speed is lower than 0 + */ + public ParticlePacket(EffLib effect, Vector direction, float speed, boolean longDistance, ParticleData data) throws IllegalArgumentException { + this(effect, (float) direction.getX(), (float) direction.getY(), (float) direction.getZ(), speed, 0, longDistance, data); + } + + /** + * Construct a new particle packet of a single colored particle + * + * @param effect Particle effect + * @param color Color of the particle + * @param longDistance Indicates whether the maximum distance is increased from 256 to 65536 + */ + public ParticlePacket(EffLib effect, ParticleColor color, boolean longDistance) { + this(effect, color.getValueX(), color.getValueY(), color.getValueZ(), 1, 0, longDistance, null); + if (effect == EffLib.REDSTONE && color instanceof OrdinaryColor && ((OrdinaryColor) color).getRed() == 0) { + offsetX = Float.MIN_NORMAL; + } + } + + /** + * Initializes {@link #packetConstructor}, {@link #getHandle}, {@link #playerConnection} and {@link #sendPacket} and sets {@link #initialized} to true if it succeeds + *

+ * Note: These fields only have to be initialized once, so it will return if {@link #initialized} is already set to true + * + * @throws VersionIncompatibleException if your bukkit version is not supported by this library + */ + public static void initialize() throws VersionIncompatibleException { + if (initialized) { + return; + } + try { + version = Integer.valueOf(PackageType.getServerVersion().split("_")[1]); + if (version > 7) { + enumParticle = PackageType.MINECRAFT_SERVER.getClass("EnumParticle"); + } + Class packetClass = PackageType.MINECRAFT_SERVER.getClass(version < 7 ? "Packet63WorldParticles" : "PacketPlayOutWorldParticles"); + packetConstructor = ReflectionUtils.getConstructor(packetClass); + getHandle = ReflectionUtils.getMethod("CraftPlayer", PackageType.CRAFTBUKKIT_ENTITY, "getHandle"); + playerConnection = ReflectionUtils.getField("EntityPlayer", PackageType.MINECRAFT_SERVER, false, "playerConnection"); + sendPacket = ReflectionUtils.getMethod(playerConnection.getType(), "sendPacket", PackageType.MINECRAFT_SERVER.getClass("Packet")); + } catch (Exception exception) { + throw new VersionIncompatibleException("Your current bukkit version seems to be incompatible with this library", exception); + } + initialized = true; + } + + /** + * Returns the version of your server (1.x) + * + * @return The version number + */ + public static int getVersion() { + if (!initialized) { + initialize(); + } + return version; + } + + /** + * Determine if {@link #packetConstructor}, {@link #getHandle}, {@link #playerConnection} and {@link #sendPacket} are initialized + * + * @return Whether these fields are initialized or not + * @see #initialize() + */ + public static boolean isInitialized() { + return initialized; + } + + /** + * Initializes {@link #packet} with all set values + * + * @param center Center location of the effect + * @throws PacketInstantiationException If instantion fails due to an unknown error + */ + private void initializePacket(Location center) throws PacketInstantiationException { + if (packet != null) { + return; + } + try { + packet = packetConstructor.newInstance(); + if (version < 8) { + String name = effect.getName(); + if (data != null) { + name += data.getPacketDataString(); + } + ReflectionUtils.setValue(packet, true, "a", name); + } else { + ReflectionUtils.setValue(packet, true, "a", enumParticle.getEnumConstants()[effect.getId()]); + ReflectionUtils.setValue(packet, true, "j", longDistance); + if (data != null) { + int[] packetData = data.getPacketData(); + ReflectionUtils.setValue(packet, true, "k", effect == EffLib.ITEM_CRACK ? packetData : new int[] {packetData[0] | (packetData[1] << 12)}); + } + } + ReflectionUtils.setValue(packet, true, "b", (float) center.getX()); + ReflectionUtils.setValue(packet, true, "c", (float) center.getY()); + ReflectionUtils.setValue(packet, true, "d", (float) center.getZ()); + ReflectionUtils.setValue(packet, true, "e", offsetX); + ReflectionUtils.setValue(packet, true, "f", offsetY); + ReflectionUtils.setValue(packet, true, "g", offsetZ); + ReflectionUtils.setValue(packet, true, "h", speed); + ReflectionUtils.setValue(packet, true, "i", amount); + } catch (Exception exception) { + throw new PacketInstantiationException("Packet instantiation failed", exception); + } + } + + /** + * Sends the packet to a single player and caches it + * + * @param center Center location of the effect + * @param player Receiver of the packet + * @throws PacketInstantiationException If instantion fails due to an unknown error + * @throws PacketSendingException If sending fails due to an unknown error + * @see #initializePacket(Location) + */ + public void sendTo(Location center, Player player) throws PacketInstantiationException, PacketSendingException { + initializePacket(center); + try { + sendPacket.invoke(playerConnection.get(getHandle.invoke(player)), packet); + } catch (Exception exception) { + throw new PacketSendingException("Failed to send the packet to player '" + player.getName() + "'", exception); + } + } + + /** + * Sends the packet to all players in the list + * + * @param center Center location of the effect + * @param players Receivers of the packet + * @throws IllegalArgumentException If the player list is empty + * @see #sendTo(Location center, Player player) + */ + public void sendTo(Location center, List players) throws IllegalArgumentException { + if (players.isEmpty()) { + throw new IllegalArgumentException("The player list is empty"); + } + for (Player player : players) { + sendTo(center, player); + } + } + + /** + * Sends the packet to all players in a certain range + * + * @param center Center location of the effect + * @param range Range in which players will receive the packet (Maximum range for particles is usually 16, but it can differ for some types) + * @throws IllegalArgumentException If the range is lower than 1 + * @see #sendTo(Location center, Player player) + */ + public void sendTo(Location center, double range) throws IllegalArgumentException { + if (range < 1) { + throw new IllegalArgumentException("The range is lower than 1"); + } + String worldName = center.getWorld().getName(); + double squared = range * range; + for (Player player : Bukkit.getOnlinePlayers()) { + if (!player.getWorld().getName().equals(worldName) || player.getLocation().distanceSquared(center) > squared) { + continue; + } + sendTo(center, player); + } + } + + /** + * Represents a runtime exception that is thrown if a bukkit version is not compatible with this library + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.5 + */ + private static final class VersionIncompatibleException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new version incompatible exception + * + * @param message Message that will be logged + * @param cause Cause of the exception + */ + public VersionIncompatibleException(String message, Throwable cause) { + super(message, cause); + } + } + + /** + * Represents a runtime exception that is thrown if packet instantiation fails + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.4 + */ + private static final class PacketInstantiationException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new packet instantiation exception + * + * @param message Message that will be logged + * @param cause Cause of the exception + */ + public PacketInstantiationException(String message, Throwable cause) { + super(message, cause); + } + } + + /** + * Represents a runtime exception that is thrown if packet sending fails + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.4 + */ + private static final class PacketSendingException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new packet sending exception + * + * @param message Message that will be logged + * @param cause Cause of the exception + */ + public PacketSendingException(String message, Throwable cause) { + super(message, cause); + } + } + } } \ No newline at end of file diff --git a/src/main/scala/me/skymc/taboolib/sound/SoundPack.java b/src/main/scala/me/skymc/taboolib/sound/SoundPack.java index 1b7490b..aad0c6c 100644 --- a/src/main/scala/me/skymc/taboolib/sound/SoundPack.java +++ b/src/main/scala/me/skymc/taboolib/sound/SoundPack.java @@ -1,68 +1,94 @@ package me.skymc.taboolib.sound; +import me.skymc.taboolib.TabooLib; +import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.Sound; import org.bukkit.entity.Player; public class SoundPack { private Sound sound; - private Float a; - private Float b; + private float a; + private float b; + private int delay; + /** + * ENTITY_VILLAGER_NO-0-0 + */ public SoundPack() { this.sound = Sound.valueOf(SoundUtils.getModifiedSound("ENTITY_VILLAGER_NO")); - this.a = 1f; - this.b = 1f; + this.a = 1.0F; + this.b = 1.0F; } - public SoundPack(Sound sound, Float a, Float b) { + public SoundPack(Sound sound, float a, float b) { + this(sound, a, b, 0); + } + + public SoundPack(Sound sound, float a, float b, int delay) { this.sound = sound; this.a = a; this.b = b; + this.delay = delay; } public SoundPack(String s) { + parse(s); + } + + public void play(Player p) { + Bukkit.getScheduler().runTaskLater(TabooLib.instance(), () -> p.playSound(p.getLocation(), this.sound, this.a, this.b), delay); + } + + public void play(Location l) { + Bukkit.getScheduler().runTaskLater(TabooLib.instance(), () -> l.getWorld().playSound(l, this.sound, this.a, this.b), delay); + } + + public void parse(String s) { try { - sound = Sound.valueOf(SoundUtils.getModifiedSound(s.split("-")[0])); - a = Float.valueOf(s.split("-")[1]); - b = Float.valueOf(s.split("-")[2]); - } catch (Exception e) { + String[] split = s.split("-"); + this.sound = Sound.valueOf(SoundUtils.getModifiedSound(split[0])); + this.a = Float.parseFloat(split[1]); + this.b = Float.parseFloat(split[2]); + this.delay = split.length > 3 ? Integer.parseInt(split[3]) : 0; + } catch (Exception var3) { this.sound = Sound.valueOf(SoundUtils.getModifiedSound("ENTITY_VILLAGER_NO")); - this.a = 1f; - this.b = 1f; + this.a = 1.0F; + this.b = 1.0F; + this.delay = 0; } } + // ********************************* + // + // Getter and Setter + // + // ********************************* + public Sound getSound() { return sound; } - public Float getA() { + public float getA() { return a; } - public Float getB() { + public float getB() { return b; } - public void play(Player p) { - p.playSound(p.getLocation(), sound, a, b); - } - - public void parse(String s) { - try { - sound = Sound.valueOf(SoundUtils.getModifiedSound(s.split("-")[0])); - a = Float.valueOf(s.split("-")[1]); - b = Float.valueOf(s.split("-")[2]); - } catch (Exception e) { - this.sound = Sound.valueOf(SoundUtils.getModifiedSound("ENTITY_VILLAGER_NO")); - this.a = 1f; - this.b = 1f; - } + public int getDelay() { + return delay; } @Override public String toString() { - return sound.name() + "-" + a + "-" + b; + return "SoundPack{" + + "sound=" + sound + + ", a=" + a + + ", b=" + b + + ", delay=" + delay + + '}'; } } diff --git a/src/main/scala/me/skymc/taboolib/string/Language.java b/src/main/scala/me/skymc/taboolib/string/Language.java deleted file mode 100644 index 9310b22..0000000 --- a/src/main/scala/me/skymc/taboolib/string/Language.java +++ /dev/null @@ -1,126 +0,0 @@ -package me.skymc.taboolib.string; - -import com.google.common.base.Charsets; -import me.skymc.taboolib.message.MsgUtils; -import org.bukkit.command.CommandSender; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStreamReader; -import java.util.Collections; -import java.util.List; - -@Deprecated -public class Language { - - private FileConfiguration conf = null; - private String langName; - private Plugin plugin; - - public Language(Plugin plugin) { - this("zh_CN", plugin, false); - } - - public Language(String name, Plugin plugin) { - this(name, plugin, false); - } - - public Language(String name, Plugin plugin, boolean utf8) { - this.plugin = plugin; - this.langName = name; - - File file = new File(getLanguageDir(), name + ".yml"); - if (!file.exists()) { - plugin.saveResource("Language/" + name + ".yml", true); - } - - if (utf8) { - reloadUTF8(this.langName); - } else { - reload(this.langName); - } - } - - public FileConfiguration getConfiguration() { - return conf; - } - - public void send(CommandSender sender, String key) { - sender.sendMessage(get(key)); - } - - public void send(Player player, String key) { - player.sendMessage(get(key)); - } - - public void sendList(CommandSender sender, String key) { - List list = getList(key); - for (String msg : list) { - sender.sendMessage(msg); - } - } - - public void sendList(Player player, String key) { - List list = getList(key); - for (String msg : list) { - player.sendMessage(msg); - } - } - - public String get(String key) { - if (conf == null || conf.getString(key) == null) { - return "§4[Language \"" + key + "\" Not Found]"; - } - return conf.getString(key).replace("&", "§"); - } - - public List getList(String key) { - if (conf == null || conf.getString(key) == null) { - return Collections.singletonList("§4[Language \"" + key + "\" Not Found]"); - } - List list = conf.getStringList(key); - for (int i = 0; i < list.size(); i++) { - list.set(i, list.get(i).replace("&", "§")); - } - return list; - } - - public void reload() { - reload(langName); - } - - public void reload(String name) { - File langFile = new File(getLanguageDir(), name + ".yml"); - if (!langFile.exists()) { - MsgUtils.warn("语言文件 " + langName + " 不存在, 请更改配置文件"); - return; - } - conf = YamlConfiguration.loadConfiguration(langFile); - } - - public void reloadUTF8(String name) { - File langFile = new File(getLanguageDir(), name + ".yml"); - if (!langFile.exists()) { - MsgUtils.warn("语言文件 " + langName + " 不存在, 请更改配置文件"); - return; - } - try { - conf = YamlConfiguration.loadConfiguration(new InputStreamReader(new FileInputStream(langFile), Charsets.UTF_8)); - } catch (FileNotFoundException e) { - conf = new YamlConfiguration(); - } - } - - private File getLanguageDir() { - File dir = new File(plugin.getDataFolder(), "Language"); - if (!dir.exists()) { - dir.mkdirs(); - } - return dir; - } -} \ No newline at end of file diff --git a/src/main/scala/me/skymc/taboolib/string/LanguagePack.java b/src/main/scala/me/skymc/taboolib/string/LanguagePack.java deleted file mode 100644 index 6ea3efc..0000000 --- a/src/main/scala/me/skymc/taboolib/string/LanguagePack.java +++ /dev/null @@ -1,104 +0,0 @@ -package me.skymc.taboolib.string; - -import me.skymc.taboolib.message.MsgUtils; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.plugin.Plugin; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -@Deprecated -public class LanguagePack { - - private File filedir; - private File file; - - private FileConfiguration fileconf; - - private String name; - private Plugin plugin; - - private HashMap> lang = new HashMap<>(); - - public LanguagePack(String name, Plugin plugin) { - this.plugin = plugin; - this.name = name; - - filedir = new File(plugin.getDataFolder(), "Languages"); - if (!filedir.exists()) { - filedir.mkdir(); - } - - file = new File(filedir, name + ".yml"); - if (!file.exists()) { - try { - file.createNewFile(); - } catch (IOException e) { - MsgUtils.Console("&8[" + plugin.getName() + "]&4 载入语言文件出错"); - } - } - - fileconf = YamlConfiguration.loadConfiguration(file); - reloadLanguage(); - - MsgUtils.Console("&8[" + plugin.getName() + "]&7 载入语言文件&f: " + name + ".yml"); - } - - public File getLanguageFile() { - return file; - } - - public File getLanguageDir() { - return filedir; - } - - public FileConfiguration getLanguageConfiguration() { - return fileconf; - } - - public HashMap> getLanguage() { - return lang; - } - - public String getLanguageName() { - return name; - } - - public Plugin getLanguagePlugin() { - return plugin; - } - - public void reloadLanguage(String name) { - - file = new File(filedir, name + ".yml"); - if (!file.exists()) { - try { - file.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - fileconf = YamlConfiguration.loadConfiguration(file); - reloadLanguage(); - } - - public void reloadLanguage() { - lang.clear(); - - for (String key : fileconf.getConfigurationSection("").getKeys(false)) { - - List _lang = new ArrayList<>(); - fileconf.getStringList(key).forEach(x -> _lang.add(x.replace("&", "§") - .replace("$plugin_name", plugin.getDescription().getName()) - .replace("$plugin_authors", plugin.getDescription().getAuthors().toString()) - .replace("$plugin_version", plugin.getDescription().getVersion()))); - - lang.put(key, _lang); - } - } -} diff --git a/src/main/scala/me/skymc/taboolib/string/LanguageUtils.java b/src/main/scala/me/skymc/taboolib/string/LanguageUtils.java deleted file mode 100644 index 2e4d935..0000000 --- a/src/main/scala/me/skymc/taboolib/string/LanguageUtils.java +++ /dev/null @@ -1,33 +0,0 @@ -package me.skymc.taboolib.string; - -import me.skymc.taboolib.message.MsgUtils; -import org.bukkit.plugin.Plugin; - -import java.io.File; -import java.util.Collections; -import java.util.List; - -@Deprecated -public class LanguageUtils { - - public static String a(LanguagePack l, String key) { - if (l.getLanguage().containsKey(key)) { - return l.getLanguage().get(key).get(0); - } - return ""; - } - - public static List b(LanguagePack l, String key) { - if (l.getLanguage().containsKey(key)) { - return l.getLanguage().get(key); - } - return Collections.singletonList(""); - } - - public static void saveLanguageFile(String name, Plugin plugin) { - if (!new File(new File(plugin.getDataFolder(), "Languages"), name + ".yml").exists()) { - plugin.saveResource("Languages/" + name + ".yml", true); - MsgUtils.Console("&8[" + plugin.getName() + "]&7 生成语言文件&f: " + name + ".yml"); - } - } -} diff --git a/src/main/scala/me/skymc/taboolib/string/PatternUtils.java b/src/main/scala/me/skymc/taboolib/string/PatternUtils.java deleted file mode 100644 index 0eeeea3..0000000 --- a/src/main/scala/me/skymc/taboolib/string/PatternUtils.java +++ /dev/null @@ -1,23 +0,0 @@ -package me.skymc.taboolib.string; - -@Deprecated -public class PatternUtils { - - @Deprecated - public static String doubleNumber = "((\\-|\\+)?\\d+(\\.\\d+)?)"; - @Deprecated - public static String doubleNumber2 = "(\\d+(\\.\\d+)?)"; - - public static String doubleNumber3 = "((?:\\-|\\+)?\\d+(?:\\.\\d+)?)"; - - public static String consolidateStrings(final String[] args, final int start) { - StringBuilder ret = new StringBuilder(args[start]); - - if (args.length > start + 1) { - for (int i = start + 1; i < args.length; ++i) { - ret.append(" ").append(args[i]); - } - } - return ret.toString(); - } -} diff --git a/src/main/scala/me/skymc/taboolib/string/language2/Language2.java b/src/main/scala/me/skymc/taboolib/string/language2/Language2.java deleted file mode 100644 index 7516212..0000000 --- a/src/main/scala/me/skymc/taboolib/string/language2/Language2.java +++ /dev/null @@ -1,102 +0,0 @@ -package me.skymc.taboolib.string.language2; - -import com.ilummc.tlib.resources.TLocale; -import me.clip.placeholderapi.PlaceholderAPI; -import me.skymc.taboolib.fileutils.ConfigUtils; -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; - -import java.io.File; - -/** - * @author sky - * @since 2018年2月13日 下午2:37:07 - */ -public class Language2 { - - private FileConfiguration configuration; - private File languageFile; - private File languageFolder; - private Plugin plugin; - private String languageName; - - public Language2(Plugin plugin) { - this("zh_CN", plugin); - } - - public Language2(String languageName, Plugin plugin) { - this.languageName = languageName; - this.plugin = plugin; - reload(languageName); - } - - public FileConfiguration getConfiguration() { - return configuration; - } - - public File getLanguageFile() { - return languageFile; - } - - public File getLanguageFolder() { - return languageFolder; - } - - public Plugin getPlugin() { - return plugin; - } - - public String getLanguageName() { - return languageName; - } - - public Language2Value get(String key) { - return new Language2Value(this, key); - } - - public Language2Value get(String key, String... placeholder) { - Language2Value value = new Language2Value(this, key); - for (int i = 0; i < placeholder.length; i++) { - value.addPlaceholder("$" + i, placeholder[i]); - } - return value; - } - - public void reload() { - reload(this.languageName); - } - - public void reload(String languageName) { - createFolder(plugin); - languageName = formatName(languageName); - languageFile = new File(languageFolder, languageName); - if (!languageFile.exists()) { - if (plugin.getResource("Language2/" + languageName) == null) { - TLocale.Logger.error("LANGUAGE2.FAIL-NOTFOUND-FILE", languageName); - } else { - plugin.saveResource("Language2/" + languageName, true); - } - } - configuration = ConfigUtils.load(plugin, languageFile); - } - - public String setPlaceholderAPI(Player player, String string) { - if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null && player != null) { - return PlaceholderAPI.setPlaceholders(player, string); - } - return string; - } - - private String formatName(String name) { - return name.contains(".yml") ? name : name + ".yml"; - } - - private void createFolder(Plugin plugin) { - languageFolder = new File(plugin.getDataFolder(), "Language2"); - if (!languageFolder.exists()) { - languageFolder.mkdir(); - } - } -} diff --git a/src/main/scala/me/skymc/taboolib/string/language2/Language2Format.java b/src/main/scala/me/skymc/taboolib/string/language2/Language2Format.java deleted file mode 100644 index 6fba600..0000000 --- a/src/main/scala/me/skymc/taboolib/string/language2/Language2Format.java +++ /dev/null @@ -1,152 +0,0 @@ -package me.skymc.taboolib.string.language2; - -import me.skymc.taboolib.string.language2.value.*; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - -/** - * @author sky - * @since 2018-03-08 22:45:56 - */ -public class Language2Format implements Language2Line { - - private Language2Value language2Value; - private List language2Lines = new ArrayList<>(); - - public Language2Format(Player player, Language2Value value) { - language2Value = value; - // 语言类型 - Language2Type type = Language2Type.TEXT; - // 递交数据 - List values = new LinkedList<>(); - - // 遍历内容 - for (String line : value.getLanguageValue()) { - // 文本类型 - if (line.contains("[text]")) { - // 递交数据 - parseValue(player, values, type); - // 更改类型 - type = Language2Type.TEXT; - } - // 大标题 - else if (line.contains("[title]")) { - // 递交数据 - parseValue(player, values, type); - // 更改类型 - type = Language2Type.TITLE; - } - // 小标题 - else if (line.contains("[action]")) { - // 递交数据 - parseValue(player, values, type); - // 更改类型 - type = Language2Type.ACTION; - } - // JSON - else if (line.contains("[json]")) { - // 递交数据 - parseValue(player, values, type); - // 更改类型 - type = Language2Type.JSON; - } - // JSON2 - else if (line.contains("[json2]")) { - // 递交数据 - parseValue(player, values, type); - // 更改类型 - type = Language2Type.JSON2; - } - // 音效 - else if (line.contains("[sound]")) { - // 递交数据 - parseValue(player, values, type); - // 更改类型 - type = Language2Type.SOUND; - } - // 书本 - else if (line.contains("[book]")) { - // 递交数据 - parseValue(player, values, type); - // 更改类型 - type = Language2Type.BOOK; - } else if (line.contains("[return]")) { - // 递交数据 - parseValue(player, values, type); - } - // 默认 - else { - // 追加内容 - values.add(line); - } - } - } - - public Language2Value getLanguage2Value() { - return language2Value; - } - - public List getLanguage2Lines() { - return language2Lines; - } - - /** - * 识别内容 - * - * @param player 玩家 - * @param list 数据 - * @param type 类型 - */ - private void parseValue(Player player, List list, Language2Type type) { - if (list.size() == 0) { - return; - } - // 变量转换 - List listPlaceholder = language2Value.setPlaceholder(list, player); - // 大标题 - switch (type) { - case TITLE: - language2Lines.add(new Language2Title(this, listPlaceholder)); - break; - // 小标题 - case ACTION: - language2Lines.add(new Language2Action(this, listPlaceholder)); - break; - // JSON - case JSON: - language2Lines.add(new Language2Json(this, listPlaceholder, player)); - break; - // JSON2 - case JSON2: - language2Lines.add(new Language2Json2(this, listPlaceholder, player)); - break; - // 音效 - case SOUND: - language2Lines.add(new Language2Sound(this, listPlaceholder)); - break; - // 书本 - case BOOK: - language2Lines.add(new Language2Book(this, listPlaceholder, player)); - break; - default: - language2Lines.add(new Language2Text(this, listPlaceholder)); - break; - } - // 清理数据 - list.clear(); - listPlaceholder.clear(); - } - - @Override - public void send(Player player) { - language2Lines.forEach(line -> line.send(player)); - } - - @Override - public void console() { - language2Lines.forEach(Language2Line::console); - } -} diff --git a/src/main/scala/me/skymc/taboolib/string/language2/Language2Line.java b/src/main/scala/me/skymc/taboolib/string/language2/Language2Line.java deleted file mode 100644 index fb0c640..0000000 --- a/src/main/scala/me/skymc/taboolib/string/language2/Language2Line.java +++ /dev/null @@ -1,15 +0,0 @@ -package me.skymc.taboolib.string.language2; - -import org.bukkit.entity.Player; - -/** - * @author sky - * @since 2018-03-08 23:36:22 - */ -public interface Language2Line { - - void send(Player player); - - void console(); - -} diff --git a/src/main/scala/me/skymc/taboolib/string/language2/Language2Type.java b/src/main/scala/me/skymc/taboolib/string/language2/Language2Type.java deleted file mode 100644 index c7fbda8..0000000 --- a/src/main/scala/me/skymc/taboolib/string/language2/Language2Type.java +++ /dev/null @@ -1,43 +0,0 @@ -package me.skymc.taboolib.string.language2; - -/** - * @author sky - * @since 2018年2月13日 下午3:14:00 - */ -public enum Language2Type { - - /** - * 一般文本 - */ - TEXT, - - /** - * JSON 文本 - */ - JSON, - - /** - * JSON2 文本 - */ - JSON2, - - /** - * 大标题 - */ - TITLE, - - /** - * 小标题 - */ - ACTION, - - /** - * 音效 - */ - SOUND, - - /** - * 书本 - */ - BOOK -} diff --git a/src/main/scala/me/skymc/taboolib/string/language2/Language2Value.java b/src/main/scala/me/skymc/taboolib/string/language2/Language2Value.java deleted file mode 100644 index 98f9982..0000000 --- a/src/main/scala/me/skymc/taboolib/string/language2/Language2Value.java +++ /dev/null @@ -1,216 +0,0 @@ -package me.skymc.taboolib.string.language2; - -import me.skymc.taboolib.string.language2.value.Language2Text; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -import java.util.*; -import java.util.Map.Entry; -import java.util.stream.IntStream; - -/** - * @author sky - * @since 2018年2月13日 下午3:05:15 - */ -public class Language2Value { - - private Language2 language; - - private String languageKey; - - private List languageValue; - - private LinkedHashMap placeholder = new LinkedHashMap<>(); - - private boolean enablePlaceholderAPI = false; - - /** - * 构造方法 - */ - public Language2Value(Language2 language, String languageKey) { - // 如果语言文件不存在 - if (language == null || languageKey == null) { - languageValue = Arrays.asList(ChatColor.DARK_RED + "[]", "[return]"); - return; - } - - // 如果语言文本不存在 - if (!language.getConfiguration().contains(languageKey)) { - languageValue = Arrays.asList(ChatColor.DARK_RED + "[]", "[return]"); - return; - } - - // 如果不是集合类型 - if (language.getConfiguration().get(languageKey) instanceof List) { - // 设置文本 - languageValue = asColored(language.getConfiguration().getStringList(languageKey)); - // 追加结尾 - languageValue.add("[return]"); - // 是否启用PAPI - if (languageValue.get(0).contains("[papi]")) { - enablePlaceholderAPI = true; - } - } else { - // 设置文本 - languageValue = Arrays.asList(ChatColor.translateAlternateColorCodes('&', language.getConfiguration().getString(languageKey)), "[return]"); - } - - // 初始化变量 - this.language = language; - this.languageKey = languageKey; - } - - public Language2 getLanguage() { - return language; - } - - public String getLanguageKey() { - return languageKey; - } - - public List getLanguageValue() { - return languageValue; - } - - public LinkedHashMap getPlaceholder() { - return placeholder; - } - - public boolean isEnablePlaceholderAPI() { - return enablePlaceholderAPI; - } - - /** - * 向玩家发送信息 - * - * @param player - */ - public void send(Player player) { - new Language2Format(player, this).send(player); - } - - /** - * 向玩家发送信息 - * - * @param players 玩家 - */ - public void send(List players) { - players.forEach(this::send); - } - - /** - * 向指令发送者发送信息 - * - * @param sender - */ - public void send(CommandSender sender) { - if (sender instanceof Player) { - send((Player) sender); - } else { - console(); - } - } - - /** - * 全服公告 - */ - public void broadcast() { - send(new ArrayList<>(Bukkit.getOnlinePlayers())); - } - - /** - * 发送到后台 - */ - public void console() { - new Language2Format(null, this).console(); - } - - /** - * 获取文本 - * - * @return - */ - public String asString() { - Language2Format format = new Language2Format(null, this); - if (format.getLanguage2Lines().get(0) instanceof Language2Text) { - Language2Text text = (Language2Text) format.getLanguage2Lines().get(0); - return setPlaceholder(text.getText().get(0), null); - } else { - return languageValue.size() == 0 ? ChatColor.DARK_RED + "[]" : setPlaceholder(languageValue.get(0), null); - } - } - - /** - * 获取文本集合 - * - * @return - */ - public List asStringList() { - Language2Format format = new Language2Format(null, this); - if (format.getLanguage2Lines().get(0) instanceof Language2Text) { - Language2Text text = (Language2Text) format.getLanguage2Lines().get(0); - return setPlaceholder(text.getText(), null); - } else { - return Collections.singletonList(languageValue.size() == 0 ? ChatColor.DARK_RED + "[]" : setPlaceholder(languageValue.get(0), null)); - } - } - - /** - * 变量替换 - * - * @param value 替换文本 - * @param player 检测玩家 - * @return String - */ - public String setPlaceholder(String value, Player player) { - for (Entry entry : placeholder.entrySet()) { - value = value.replace(entry.getKey(), entry.getValue()); - } - return isEnablePlaceholderAPI() ? this.language.setPlaceholderAPI(player, value) : value; - } - - /** - * 变量替换 - * - * @param list 替换集合 - * @param player 检测玩家 - * @return {@link List} - */ - public List setPlaceholder(List list, Player player) { - List _list = new ArrayList<>(list); - for (int i = 0; i < _list.size(); i++) { - _list.set(i, setPlaceholder(_list.get(i), player)); - } - return _list; - } - - /** - * 变量替换构造 - * - * @param key 键 - * @param value 值 - * @return {@link Language2Value} - */ - public Language2Value addPlaceholder(String key, String value) { - this.placeholder.put(key, value); - return this; - } - - /** - * 替换颜色 - * - * @param list - * @return - */ - public List asColored(List list) { - IntStream.range(0, list.size()).forEach(i -> list.set(i, ChatColor.translateAlternateColorCodes('&', list.get(i)))); - return list; - } - - @Override - public String toString() { - return asString(); - } -} diff --git a/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Action.java b/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Action.java deleted file mode 100644 index 0f1816a..0000000 --- a/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Action.java +++ /dev/null @@ -1,100 +0,0 @@ -package me.skymc.taboolib.string.language2.value; - -import me.skymc.taboolib.Main; -import me.skymc.taboolib.TabooLib; -import me.skymc.taboolib.display.ActionUtils; -import me.skymc.taboolib.other.NumberUtils; -import me.skymc.taboolib.string.language2.Language2Format; -import me.skymc.taboolib.string.language2.Language2Line; -import me.skymc.taboolib.string.language2.Language2Value; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitRunnable; - -import java.util.List; - -/** - * @author sky - * @since 2018年2月13日 下午3:58:07 - */ -public class Language2Action implements Language2Line { - - private static final String KEY_TEXT = " text: "; - private static final String KEY_STAY = " repeat: "; - - private String text = ""; - - private int repeat = 1; - - private Language2Value value; - - public Language2Action(Language2Format format, List list) { - // 变量初始化 - this.value = format.getLanguage2Value(); - // 遍历文本 - for (String message : list) { - try { - // 动作栏提示 - if (message.startsWith(KEY_TEXT)) { - text = message.substring(KEY_TEXT.length()); - } - // 持续时间 - if (message.startsWith(KEY_STAY)) { - repeat = NumberUtils.getInteger(message.substring(KEY_STAY.length())); - } - } catch (Exception e) { - // 识别异常 - text = ChatColor.DARK_RED + "[]"; - } - } - - // 检查重复次数 - if (repeat < 0) { - repeat = 1; - text = ChatColor.DARK_RED + "[]"; - } - } - - public String getText() { - return text; - } - - public int getRepeat() { - return repeat; - } - - public Language2Value getValue() { - return value; - } - - /** - * 发送给玩家 - * - * @param player 玩家 - */ - @Override - public void send(Player player) { - // 检查版本 - if (TabooLib.getVerint() < 10800) { - player.sendMessage(ChatColor.DARK_RED + "[]"); - } else { - new BukkitRunnable() { - int times = 0; - - @Override - public void run() { - ActionUtils.send(player, text); - if ((times += 1) >= repeat) { - cancel(); - } - } - }.runTaskTimer(Main.getInst(), 0, 20); - } - } - - @Override - public void console() { - Bukkit.getConsoleSender().sendMessage(ChatColor.DARK_RED + "[]"); - } -} diff --git a/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Book.java b/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Book.java deleted file mode 100644 index f2118a3..0000000 --- a/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Book.java +++ /dev/null @@ -1,242 +0,0 @@ -package me.skymc.taboolib.string.language2.value; - -import com.ilummc.tlib.bungee.api.chat.BaseComponent; -import com.ilummc.tlib.bungee.api.chat.ClickEvent; -import com.ilummc.tlib.bungee.api.chat.HoverEvent; -import com.ilummc.tlib.bungee.api.chat.TextComponent; -import me.skymc.taboolib.bookformatter.BookFormatter; -import me.skymc.taboolib.bookformatter.action.ClickAction; -import me.skymc.taboolib.bookformatter.action.HoverAction; -import me.skymc.taboolib.bookformatter.builder.BookBuilder; -import me.skymc.taboolib.bookformatter.builder.PageBuilder; -import me.skymc.taboolib.bookformatter.builder.TextBuilder; -import me.skymc.taboolib.inventory.ItemUtils; -import me.skymc.taboolib.other.NumberUtils; -import me.skymc.taboolib.string.VariableFormatter; -import me.skymc.taboolib.string.language2.Language2Format; -import me.skymc.taboolib.string.language2.Language2Line; -import me.skymc.taboolib.string.language2.Language2Value; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map.Entry; -import java.util.regex.Pattern; - -/** - * @author sky - * @since 2018-03-10 15:55:28 - */ -public class Language2Book implements Language2Line { - - private static final String KEY_TEXT = " text: "; - private static final String KEY_COMMAND = " command: "; - private static final String KEY_SUGGEST = " suggest: "; - private static final String KEY_URL = " url: "; - private static final String KEY_PAGE = " page: "; - private static final String KEY_SHOWTEXT = " showtext: "; - private static final String KEY_SHOWITEM = " showitem: "; - private static final String KEY_OPTION = "@option:"; - private static final Pattern pattern = Pattern.compile("<@(\\S+)>"); - - private Player player; - - private Language2Value value; - - private HashMap options = new HashMap<>(); - - private BookBuilder book; - - public Language2Book(Language2Format format, List list, Player player) { - // 变量 - this.player = player; - this.value = format.getLanguage2Value(); - this.book = BookFormatter.writtenBook(); - // 设置 - formatOptions(list); - // 内容 - PageBuilder page = new PageBuilder(); - // 遍历内容 - for (String line : list) { - // 翻页 - if (line.equals("[page]")) { - book.addPages(page.build()); - page = new PageBuilder(); - } - // 设置 - else if (line.startsWith("@option")) { - break; - } else { - for (VariableFormatter.Variable variable : new VariableFormatter(line, pattern).find().getVariableList()) { - if (variable.isVariable()) { - String node = variable.getText().substring(1); - if (!options.containsKey(node)) { - page.add("§4[]"); - } else { - TextBuilder builder = options.get(node); - BaseComponent component = new TextComponent(builder.getText()); - if (builder.getHover() != null) { - component.setHoverEvent(new HoverEvent(builder.getHover().action(), builder.getHover().value())); - } - if (builder.getClick() != null) { - component.setClickEvent(new ClickEvent(builder.getClick().action(), builder.getClick().value())); - } - page.add(component); - } - } else { - page.add(variable.getText()); - } - } - page.newLine(); - } - } - // 结尾 - book.addPages(page.build()); - } - - - @Override - public void send(Player player) { - BookFormatter.forceOpen(player, book.build()); - } - - @Override - public void console() { - Bukkit.getConsoleSender().sendMessage(ChatColor.DARK_RED + "[]"); - } - - // ********************************* - // - // Getter and Setter - // - // ********************************* - - public static Pattern getPattern() { - return pattern; - } - - public Player getPlayer() { - return player; - } - - public Language2Value getValue() { - return value; - } - - public HashMap getOptions() { - return options; - } - - public BookBuilder getBook() { - return book; - } - - // ********************************* - // - // Private Methods - // - // ********************************* - - private List> getBookPages(List source) { - List> list = new ArrayList<>(); - for (String line : removeOption(source)) { - if (line.equalsIgnoreCase("[page]")) { - list.add(new ArrayList<>()); - } else { - getLatestList(list).add(line); - } - } - return list; - } - - public List getLatestList(List> list) { - if (list.size() == 0) { - List newList = new ArrayList<>(); - list.add(newList); - return newList; - } else { - return list.get(list.size() - 1); - } - } - - public List removeOption(List source) { - List list = new ArrayList<>(); - for (String line : source) { - if (!line.contains("@option")) { - list.add(line); - } else { - return list; - } - } - return list; - } - - private void formatOptions(List list) { - // 获取书本设置 - HashMap> _options = getOptions(list); - for (Entry> entry : _options.entrySet()) { - TextBuilder builder = new TextBuilder(); - // 遍历内容 - for (String _option : entry.getValue()) { - if (_option.startsWith(KEY_TEXT)) { - builder.text(_option.substring(KEY_TEXT.length())); - } else if (_option.startsWith(KEY_COMMAND)) { - builder.onClick(ClickAction.runCommand(_option.substring(KEY_COMMAND.length()))); - } else if (_option.startsWith(KEY_SUGGEST)) { - builder.onClick(ClickAction.suggestCommand(_option.substring(KEY_SUGGEST.length()))); - } else if (_option.startsWith(KEY_URL)) { - try { - builder.onClick(ClickAction.openUrl(_option.substring(KEY_URL.length()))); - } catch (Exception e) { - builder.text("§4[]"); - } - } else if (_option.startsWith(KEY_PAGE)) { - builder.onClick(ClickAction.changePage(NumberUtils.getInteger(_option.substring(KEY_PAGE.length())))); - } else if (_option.startsWith(KEY_SHOWTEXT)) { - builder.onHover(HoverAction.showText(_option.substring(KEY_SHOWTEXT.length()))); - } else if (_option.startsWith(KEY_SHOWITEM)) { - ItemStack item = ItemUtils.getCacheItem(_option.substring(KEY_SHOWITEM.length())); - if (item == null) { - item = new ItemStack(Material.STONE); - } - builder.onHover(HoverAction.showItem(item)); - } - } - options.put(entry.getKey(), builder); - } - } - - private HashMap> getOptions(List list) { - HashMap> options_source = new HashMap<>(); - List option = new ArrayList<>(); - // 遍历 - String optionName = null; - boolean start = false; - // 遍历所有代码 - for (String line : list) { - if (line.startsWith(KEY_OPTION)) { - // 如果已经开始检测 - if (start) { - // 返回源码 - options_source.put(optionName, new ArrayList<>(option)); - // 清除源码 - option.clear(); - } - // 标签 - start = true; - // 当前设置名称 - optionName = line.substring(KEY_OPTION.length()); - } else if (start) { - option.add(line); - } - } - // 返回最后设置 - options_source.put(optionName, option); - return options_source; - } -} diff --git a/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Json.java b/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Json.java deleted file mode 100644 index 42e6f5b..0000000 --- a/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Json.java +++ /dev/null @@ -1,174 +0,0 @@ -package me.skymc.taboolib.string.language2.value; - -import me.skymc.taboolib.inventory.ItemUtils; -import me.skymc.taboolib.jsonformatter.JSONFormatter; -import me.skymc.taboolib.jsonformatter.click.ClickEvent; -import me.skymc.taboolib.jsonformatter.click.OpenUrlEvent; -import me.skymc.taboolib.jsonformatter.click.RunCommandEvent; -import me.skymc.taboolib.jsonformatter.click.SuggestCommandEvent; -import me.skymc.taboolib.jsonformatter.hover.HoverEvent; -import me.skymc.taboolib.jsonformatter.hover.ShowItemEvent; -import me.skymc.taboolib.jsonformatter.hover.ShowTextEvent; -import me.skymc.taboolib.string.language2.Language2Format; -import me.skymc.taboolib.string.language2.Language2Line; -import me.skymc.taboolib.string.language2.Language2Value; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -import java.util.List; - -/** - * @author sky - * @since 2018年2月13日 下午4:11:33 - */ -public class Language2Json implements Language2Line { - - private static final String KEY_TEXT = " text: "; - private static final String KEY_COMMAND = " command: "; - private static final String KEY_SUGGEST = " suggest: "; - private static final String KEY_URL = " url: "; - private static final String KEY_ITEM = " item: "; - - private Player player; - - private Language2Value value; - - private JSONFormatter json = new JSONFormatter(); - - private StringBuffer text = new StringBuffer(); - - public Language2Json(Language2Format format, List list, Player player) { - // 首次检测 - boolean isFirst = true; - boolean isBreak = false; - - // 变量初始化 - this.value = format.getLanguage2Value(); - this.player = player; - - // 动作初始化 - ClickEvent clickEvent = null; - HoverEvent hoverEvent = null; - - // 文本初始化 - String current = ChatColor.DARK_RED + "[]"; - - // 遍历文本 - for (String message : list) { - try { - // 如果是显示文本 - if (message.startsWith(KEY_TEXT)) { - hoverEvent = new ShowTextEvent(message.replace("||", "\n").substring(KEY_TEXT.length())); - } - // 显示物品 - else if (message.startsWith(KEY_ITEM)) { - ItemStack item = ItemUtils.getCacheItem(message.substring(KEY_ITEM.length())); - if (item == null) { - item = new ItemStack(Material.STONE); - } - hoverEvent = new ShowItemEvent(item); - } - // 执行指令 - else if (message.startsWith(KEY_COMMAND)) { - clickEvent = new RunCommandEvent(message.substring(KEY_COMMAND.length())); - } - // 打印指令 - else if (message.startsWith(KEY_SUGGEST)) { - clickEvent = new SuggestCommandEvent(message.substring(KEY_SUGGEST.length())); - } - // 打开连接 - else if (message.startsWith(KEY_URL)) { - clickEvent = new OpenUrlEvent(message.substring(KEY_URL.length())); - } - // 换行 - else if ("[break]".equals(message)) { - append(current, clickEvent, hoverEvent); - // 删除动作 - clickEvent = null; - hoverEvent = null; - // 换行 - json.newLine(); - // 标记 - isBreak = true; - } - // 新内容 - else { - if (!isFirst && !isBreak) { - append(current, clickEvent, hoverEvent); - // 删除动作 - clickEvent = null; - hoverEvent = null; - } - // 更新 - current = message; - // 标记 - isFirst = false; - isBreak = false; - } - } catch (Exception e) { - // 识别异常 - json.append(ChatColor.DARK_RED + "[]"); - } - } - // 追加 - append(current, clickEvent, hoverEvent); - } - - public Player getPlayer() { - return player; - } - - public Language2Value getValue() { - return value; - } - - public JSONFormatter getJson() { - return json; - } - - public StringBuffer getText() { - return text; - } - - /** - * 发送给玩家 - * - * @param player 玩家 - */ - @Override - public void send(Player player) { - json.send(player); - } - - @Override - public void console() { - Bukkit.getConsoleSender().sendMessage(text.toString()); - } - - /** - * 追加 JSON 内容 - * - * @param current 文本 - * @param hoverEvent 显示动作 - */ - private void append(String current, ClickEvent clickEvent, HoverEvent hoverEvent) { - if (clickEvent == null && hoverEvent == null) { - // 纯文本 - json.append(current); - } else if (clickEvent != null && hoverEvent == null) { - // 纯点击 - json.appendClick(current, clickEvent); - } else if (clickEvent == null && hoverEvent != null) { - // 纯显示 - json.appendHover(current, hoverEvent); - } else { - // 全部 - json.appendHoverClick(current, hoverEvent, clickEvent); - } - // 追加显示文本 - text.append(current); - } -} diff --git a/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Json2.java b/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Json2.java deleted file mode 100644 index 2d08958..0000000 --- a/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Json2.java +++ /dev/null @@ -1,214 +0,0 @@ -package me.skymc.taboolib.string.language2.value; - -import me.skymc.taboolib.inventory.ItemUtils; -import me.skymc.taboolib.jsonformatter.JSONFormatter; -import me.skymc.taboolib.jsonformatter.click.ClickEvent; -import me.skymc.taboolib.jsonformatter.click.OpenUrlEvent; -import me.skymc.taboolib.jsonformatter.click.RunCommandEvent; -import me.skymc.taboolib.jsonformatter.click.SuggestCommandEvent; -import me.skymc.taboolib.jsonformatter.hover.HoverEvent; -import me.skymc.taboolib.jsonformatter.hover.ShowItemEvent; -import me.skymc.taboolib.jsonformatter.hover.ShowTextEvent; -import me.skymc.taboolib.string.language2.Language2Format; -import me.skymc.taboolib.string.language2.Language2Line; -import me.skymc.taboolib.string.language2.Language2Value; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map.Entry; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * @author sky - * @since 2018-03-10 15:55:28 - */ -public class Language2Json2 implements Language2Line { - - private static final String KEY_TEXT = " text: "; - private static final String KEY_COMMAND = " command: "; - private static final String KEY_SUGGEST = " suggest: "; - private static final String KEY_URL = " url: "; - private static final String KEY_SHOWTEXT = " showtext: "; - private static final String KEY_SHOWITEM = " showitem: "; - private static final String KEY_OPTION = "@option:"; - private static final Pattern pattern = Pattern.compile("<@(\\S+)>"); - - private Player player; - - private Language2Value value; - - private HashMap options = new HashMap<>(); - - private JSONFormatter json = new JSONFormatter(); - - public Language2Json2(Language2Format format, List list, Player player) { - // 变量 - this.player = player; - this.value = format.getLanguage2Value(); - - // 获取书本设置 - formatOptions(list); - // 遍历内容 - int lineNumber = 0; - int lineNumberEnd = getLineNumberEnd(list); - for (String line : list) { - if (line.startsWith("@option")) { - break; - } else { - Matcher matcher = pattern.matcher(line); - boolean find = false; - while (matcher.find()) { - find = true; - String optionName = matcher.group(1); - String optionFullName = "<@" + matcher.group(1) + ">"; - // 判断设置是否存在 - if (!options.containsKey(optionName)) { - json.append("§4[]"); - } else { - String[] line_split = line.split(optionFullName); - try { - // 单独一行 - if (line_split.length == 0) { - json.append(options.get(optionName)); - } else { - // 前段 - json.append(line_split[0]); - // 变量 - json.append(options.get(optionName)); - // 后段 - if (line_split.length >= 2) { - // 获取文本 - StringBuilder sb = new StringBuilder(); - for (int i = 1; i < line_split.length; i++) { - sb.append(line_split[i]).append(optionFullName); - } - // 更改文本 - line = sb.substring(0, sb.length() - optionFullName.length()); - // 如果后段还有变量 - if (!pattern.matcher(line).find()) { - json.append(line_split[1]); - } - } - } - } catch (Exception e) { - json.append("§4[]"); - } - } - } - if (!find) { - json.append(line); - } - if (++lineNumber < lineNumberEnd) { - json.newLine(); - } - } - } - } - - public Player getPlayer() { - return player; - } - - public Language2Value getValue() { - return value; - } - - public HashMap getOptions() { - return options; - } - - public JSONFormatter getJson() { - return json; - } - - private int getLineNumberEnd(List list) { - int line = list.size(); - for (int i = 0; i < list.size(); i++) { - if (list.get(i).startsWith("@option")) { - return i; - } - } - return line; - } - - private void formatOptions(List list) { - HashMap> _options = getOptions(list); - for (Entry> entry : _options.entrySet()) { - JSONFormatter jsonFormatter = new JSONFormatter(); - String current = ChatColor.DARK_RED + "[]"; - ClickEvent clickEvent = null; - HoverEvent hoverEvent = null; - for (String _option : entry.getValue()) { - if (_option.startsWith(KEY_TEXT)) { - current = _option.substring(KEY_TEXT.length()); - } else if (_option.startsWith(KEY_COMMAND)) { - clickEvent = new RunCommandEvent(_option.substring(KEY_COMMAND.length())); - } else if (_option.startsWith(KEY_SUGGEST)) { - clickEvent = new SuggestCommandEvent(_option.substring(KEY_SUGGEST.length())); - } else if (_option.startsWith(KEY_URL)) { - clickEvent = new OpenUrlEvent(_option.substring(KEY_URL.length())); - } else if (_option.startsWith(KEY_SHOWTEXT)) { - hoverEvent = new ShowTextEvent(_option.replace("||", "\n").substring(KEY_SHOWTEXT.length())); - } else if (_option.startsWith(KEY_SHOWITEM)) { - ItemStack item = ItemUtils.getCacheItem(_option.substring(KEY_SHOWITEM.length())); - if (item == null) { - item = new ItemStack(Material.STONE); - } - hoverEvent = new ShowItemEvent(item); - } - } - append(jsonFormatter, current, clickEvent, hoverEvent); - options.put(entry.getKey(), jsonFormatter); - } - } - - private void append(JSONFormatter json, String current, ClickEvent clickEvent, HoverEvent hoverEvent) { - if (clickEvent == null && hoverEvent == null) { - json.append(current); - } else if (clickEvent != null && hoverEvent == null) { - json.appendClick(current, clickEvent); - } else if (clickEvent == null) { - json.appendHover(current, hoverEvent); - } else { - json.appendHoverClick(current, hoverEvent, clickEvent); - } - } - - private HashMap> getOptions(List list) { - HashMap> options_source = new HashMap<>(); - List option = new ArrayList<>(); - String optionName = null; - boolean start = false; - for (String line : list) { - if (line.startsWith(KEY_OPTION)) { - if (start) { - options_source.put(optionName, new ArrayList<>(option)); - option.clear(); - } - start = true; - optionName = line.substring(KEY_OPTION.length()); - } else if (start) { - option.add(line); - } - } - options_source.put(optionName, option); - return options_source; - } - - @Override - public void send(Player player) { - json.send(player); - } - - @Override - public void console() { - Bukkit.getConsoleSender().sendMessage(ChatColor.DARK_RED + "[]"); - } -} diff --git a/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Sound.java b/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Sound.java deleted file mode 100644 index 957ca2e..0000000 --- a/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Sound.java +++ /dev/null @@ -1,46 +0,0 @@ -package me.skymc.taboolib.string.language2.value; - -import me.skymc.taboolib.sound.SoundPack; -import me.skymc.taboolib.string.language2.Language2Format; -import me.skymc.taboolib.string.language2.Language2Line; -import me.skymc.taboolib.string.language2.Language2Value; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author sky - * @since 2018-03-08 22:43:27 - */ -public class Language2Sound implements Language2Line { - - private List sounds = new ArrayList<>(); - - private Language2Value value; - - public Language2Sound(Language2Format format, List list) { - this.value = format.getLanguage2Value(); - list.forEach(line -> sounds.add(new SoundPack(line))); - } - - public List getSounds() { - return sounds; - } - - public Language2Value getValue() { - return value; - } - - @Override - public void send(Player player) { - sounds.forEach(sound -> sound.play(player)); - } - - @Override - public void console() { - Bukkit.getConsoleSender().sendMessage(ChatColor.DARK_RED + "[]"); - } -} diff --git a/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Text.java b/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Text.java deleted file mode 100644 index a5db632..0000000 --- a/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Text.java +++ /dev/null @@ -1,44 +0,0 @@ -package me.skymc.taboolib.string.language2.value; - -import me.skymc.taboolib.string.language2.Language2Format; -import me.skymc.taboolib.string.language2.Language2Line; -import me.skymc.taboolib.string.language2.Language2Value; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author sky - * @since 2018-03-08 22:43:27 - */ -public class Language2Text implements Language2Line { - - private List text = new ArrayList<>(); - - private Language2Value value; - - public List getText() { - return text; - } - - public Language2Value getValue() { - return value; - } - - public Language2Text(Language2Format format, List list) { - this.value = format.getLanguage2Value(); - text.addAll(list); - } - - @Override - public void send(Player player) { - text.forEach(player::sendMessage); - } - - @Override - public void console() { - text.forEach(line -> Bukkit.getConsoleSender().sendMessage(line)); - } -} diff --git a/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Title.java b/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Title.java deleted file mode 100644 index 257bf33..0000000 --- a/src/main/scala/me/skymc/taboolib/string/language2/value/Language2Title.java +++ /dev/null @@ -1,103 +0,0 @@ -package me.skymc.taboolib.string.language2.value; - -import me.skymc.taboolib.TabooLib; -import me.skymc.taboolib.display.TitleUtils; -import me.skymc.taboolib.string.language2.Language2Format; -import me.skymc.taboolib.string.language2.Language2Line; -import me.skymc.taboolib.string.language2.Language2Value; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.entity.Player; - -import java.util.List; - -/** - * @author sky - * @since 2018年2月13日 下午3:58:07 - */ -public class Language2Title implements Language2Line { - - private static final String KEY_TITLE = " title: "; - private static final String KEY_SUBTITLE = " subtitle: "; - private static final String KEY_STAYRULE = " stay: "; - - private String title = ""; - - private String subtitle = ""; - - private int fade1 = 0; - - private int fade2 = 0; - - private int stay = 20; - - private Language2Value value; - - public Language2Title(Language2Format format, List list) { - // 变量初始化 - this.value = format.getLanguage2Value(); - // 遍历文本 - for (String message : list) { - try { - // 大标题 - if (message.startsWith(KEY_TITLE)) { - title = message.substring(KEY_TITLE.length()); - } - // 小标题 - else if (message.startsWith(KEY_SUBTITLE)) { - subtitle = message.substring(KEY_SUBTITLE.length()); - } - // 持续时间 - else if (message.startsWith(KEY_STAYRULE)) { - String rule = message.substring(KEY_STAYRULE.length()); - fade1 = Integer.valueOf(rule.split("\\|")[0]); - stay = Integer.valueOf(rule.split("\\|")[1]); - fade2 = Integer.valueOf(rule.split("\\|")[2]); - } - } catch (Exception e) { - // 识别异常 - title = ChatColor.DARK_RED + "[]"; - subtitle = ChatColor.DARK_RED + "[]"; - } - } - } - - public String getTitle() { - return title; - } - - public String getSubtitle() { - return subtitle; - } - - public int getFade1() { - return fade1; - } - - public int getFade2() { - return fade2; - } - - public int getStay() { - return stay; - } - - public Language2Value getValue() { - return value; - } - - @Override - public void send(Player player) { - // 检查版本 - if (TabooLib.getVerint() < 10800) { - player.sendMessage(ChatColor.DARK_RED + "[]"); - } else { - TitleUtils.sendTitle(player, title, subtitle, fade1, stay, fade2); - } - } - - @Override - public void console() { - Bukkit.getConsoleSender().sendMessage(ChatColor.DARK_RED + "[]"); - } -} diff --git a/src/main/scala/me/skymc/taboolib/support/SupportWorldGuard.java b/src/main/scala/me/skymc/taboolib/support/SupportWorldGuard.java index 11141b5..f28fc62 100644 --- a/src/main/scala/me/skymc/taboolib/support/SupportWorldGuard.java +++ b/src/main/scala/me/skymc/taboolib/support/SupportWorldGuard.java @@ -8,12 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; -import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; /** @@ -21,6 +18,8 @@ import java.util.stream.Collectors; */ public class SupportWorldGuard { + public static final SupportWorldGuard INSTANCE = new SupportWorldGuard(); + private WorldGuardPlugin worldGuard; public SupportWorldGuard() { diff --git a/src/main/scala/me/skymc/taboolib/update/UpdateTask.java b/src/main/scala/me/skymc/taboolib/update/UpdateTask.java index ad3c22b..be4bba4 100644 --- a/src/main/scala/me/skymc/taboolib/update/UpdateTask.java +++ b/src/main/scala/me/skymc/taboolib/update/UpdateTask.java @@ -6,11 +6,11 @@ import com.google.gson.JsonParser; import com.ilummc.tlib.resources.TLocale; import me.skymc.taboolib.Main; import me.skymc.taboolib.TabooLib; +import me.skymc.taboolib.common.schedule.TSchedule; import me.skymc.taboolib.fileutils.FileUtils; import me.skymc.taboolib.player.PlayerUtils; import me.skymc.taboolib.plugin.PluginUtils; import org.bukkit.Bukkit; -import org.bukkit.scheduler.BukkitRunnable; import java.io.*; @@ -34,46 +34,41 @@ public class UpdateTask { } }; - public UpdateTask() { - new BukkitRunnable() { - - @Override - public void run() { - if (!Main.getInst().getConfig().getBoolean("UPDATE-CHECK", true)) { - return; - } - for (int i = 0; i < updateLocation.length; i++) { - String[] location = updateLocation[i]; - String value = FileUtils.getStringFromURL(location[0], null); - if (value == null) { - continue; - } - JsonElement json = new JsonParser().parse(value); - if (json.isJsonArray()) { - JsonObject releaseData = json.getAsJsonArray().get(0).getAsJsonObject(); - updateLocationUsing = i; - newVersion = releaseData.get("tag_name").getAsDouble(); - // 获取文件长度 - for (JsonElement assetData : releaseData.getAsJsonArray("assets")) { - if (assetData instanceof JsonObject && ((JsonObject) assetData).get("name").getAsString().equals("TabooLib-" + newVersion + ".jar")) { - length = ((JsonObject) assetData).get("size").getAsInt(); - } - } - if (TabooLib.getPluginVersion() >= newVersion) { - TLocale.Logger.info("UPDATETASK.VERSION-LATEST"); - } else { - TLocale.Logger.info("UPDATETASK.VERSION-OUTDATED", String.valueOf(TabooLib.getPluginVersion()), String.valueOf(newVersion)); - // 是否启用启动下载 - if (Main.getInst().getConfig().getBoolean("UPDATE-DOWNLOAD", false)) { - Bukkit.getScheduler().runTask(TabooLib.instance(), () -> updatePlugin(true, false)); - } - } - return; - } - } - TLocale.Logger.error("UPDATETASK.VERSION-FAIL"); + @TSchedule(async = true, delay = 100, period = 20 * 60 * 60 * 6) + static void update() { + if (!Main.getInst().getConfig().getBoolean("UPDATE-CHECK", true)) { + return; + } + for (int i = 0; i < updateLocation.length; i++) { + String[] location = updateLocation[i]; + String value = FileUtils.getStringFromURL(location[0], null); + if (value == null) { + continue; } - }.runTaskTimerAsynchronously(Main.getInst(), 100, 20 * 60 * 60 * 6); + JsonElement json = new JsonParser().parse(value); + if (json.isJsonArray()) { + JsonObject releaseData = json.getAsJsonArray().get(0).getAsJsonObject(); + updateLocationUsing = i; + newVersion = releaseData.get("tag_name").getAsDouble(); + // 获取文件长度 + for (JsonElement assetData : releaseData.getAsJsonArray("assets")) { + if (assetData instanceof JsonObject && ((JsonObject) assetData).get("name").getAsString().equals("TabooLib-" + newVersion + ".jar")) { + length = ((JsonObject) assetData).get("size").getAsInt(); + } + } + if (TabooLib.getPluginVersion() >= newVersion) { + TLocale.Logger.info("UPDATETASK.VERSION-LATEST"); + } else { + TLocale.Logger.info("UPDATETASK.VERSION-OUTDATED", String.valueOf(TabooLib.getPluginVersion()), String.valueOf(newVersion)); + // 是否启用启动下载 + if (Main.getInst().getConfig().getBoolean("UPDATE-DOWNLOAD", false)) { + Bukkit.getScheduler().runTask(TabooLib.instance(), () -> updatePlugin(true, false)); + } + } + return; + } + } + TLocale.Logger.error("UPDATETASK.VERSION-FAIL"); } public static boolean isHaveUpdate() {