From b6eb7eeff9a8854b62d81f7c5d8308b713d1fad0 Mon Sep 17 00:00:00 2001 From: q17866380 <178666380@qq.com> Date: Sat, 25 Jun 2016 23:26:56 +0800 Subject: [PATCH] =?UTF-8?q?2.2.3=E6=BA=90=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Test.java | 9 + .../java/cc/util/bossshop/ClassHelper.java | 794 ++++++++ src/main/java/cc/util/bossshop/FBukkit.java | 133 ++ src/main/java/cc/util/bossshop/Function.java | 102 ++ src/main/java/cc/util/bossshop/JsonExtra.java | 351 ++++ .../java/cc/util/bossshop/LocalLanguage.java | 131 ++ src/main/java/cc/util/bossshop/NMSHelper.java | 254 +++ .../config/CommentedConfiguration.java | 96 + .../bossshop/config/CommentedOptions.java | 17 + .../bossshop/config/CommentedSection.java | 835 +++++++++ .../util/bossshop/config/CommentedValue.java | 207 +++ .../bossshop/config/CommentedYamlConfig.java | 410 +++++ .../bossshop/config/ICommentedSection.java | 45 + .../cc/util/bossshop/config/LineAnalyse.java | 86 + .../cc/util/bossshop/config/YamlNode.java | 73 + .../cc/util/bossshop/config/YamlStack.java | 21 + .../bossshop/filemanager/AConfigManager.java | 22 + .../bossshop/filemanager/AFileManager.java | 163 ++ .../bossshop/filemanager/ALangManager.java | 28 + .../bossshop/pluginmodel/ABukkitPlugin.java | 278 +++ .../util/bossshop/pluginmodel/IClearAble.java | 17 + .../bossshop/pluginmodel/INeedConfig.java | 23 + .../bossshop/pluginmodel/INeedReload.java | 12 + .../bossshop/pluginmodel/ReloadPriority.java | 10 + .../java/org/black_ixx/bossshop/BossShop.java | 214 +++ .../black_ixx/bossshop/api/BSAddonConfig.java | 62 + .../black_ixx/bossshop/api/BossShopAPI.java | 109 ++ .../black_ixx/bossshop/api/BossShopAddon.java | 93 + .../bossshop/command/ACommandBase.java | 181 ++ .../bossshop/command/CommandClear.java | 32 + .../bossshop/command/CommandClose.java | 57 + .../bossshop/command/CommandExc.java | 181 ++ .../bossshop/command/CommandHelp.java | 55 + .../bossshop/command/CommandMail.java | 147 ++ .../bossshop/command/CommandNBT.java | 98 + .../bossshop/command/CommandName.java | 52 + .../bossshop/command/CommandOpen.java | 110 ++ .../bossshop/command/CommandReload.java | 104 ++ .../bossshop/command/CommandSale.java | 103 ++ .../bossshop/command/CommandUnsale.java | 81 + .../org/black_ixx/bossshop/core/BSBuy.java | 657 +++++++ .../bossshop/core/BSBuyParameter.java | 29 + .../org/black_ixx/bossshop/core/BSEnums.java | 47 + .../black_ixx/bossshop/core/BSMultiplier.java | 81 + .../org/black_ixx/bossshop/core/BSShop.java | 376 ++++ .../black_ixx/bossshop/core/BSShopHolder.java | 82 + .../bossshop/core/BSShopManager.java | 203 +++ .../bossshop/events/BSDisplayItemEvent.java | 52 + .../events/BSPlayerPurchaseEvent.java | 55 + .../events/BSPlayerPurchasedEvent.java | 43 + .../bossshop/events/BSReloadedEvent.java | 26 + .../bossshop/listeners/InventoryListener.java | 108 ++ .../bossshop/listeners/PlayerListener.java | 67 + .../bossshop/listeners/SignListener.java | 97 + .../bossshop/lottery/LotteryHolder.java | 224 +++ .../bossshop/lottery/LotteryManager.java | 83 + .../bossshop/lottery/LotteryStatus.java | 25 + .../lottery/ReflashInventoryTask.java | 31 + .../org/black_ixx/bossshop/mail/MailItem.java | 94 + .../black_ixx/bossshop/mail/MailManager.java | 310 ++++ .../bossshop/managers/BuyItemHandler.java | 355 ++++ .../bossshop/managers/ConfigManager.java | 165 ++ .../bossshop/managers/DefaultCreator.java | 519 ++++++ .../bossshop/managers/ItemNameManager.java | 137 ++ .../bossshop/managers/ItemStackChecker.java | 75 + .../bossshop/managers/ItemStackCreator.java | 304 ++++ .../bossshop/managers/LangManager.java | 212 +++ .../bossshop/managers/MultiplierHandler.java | 71 + .../bossshop/managers/RecordManager.java | 152 ++ .../bossshop/managers/ShopCustomizer.java | 134 ++ .../bossshop/managers/StringManager.java | 29 + .../bossshop/managers/TimeHandler.java | 32 + .../bossshop/managers/TransactionLog.java | 38 + .../bossshop/managers/VaultHandler.java | 59 + .../bossshop/managers/WorthHandler.java | 226 +++ .../org/black_ixx/bossshop/misc/Enchant.java | 20 + .../black_ixx/bossshop/misc/NoEconomy.java | 276 +++ .../java/org/black_ixx/bossshop/nbt/NBT.java | 18 + .../bossshop/nbt/NBTEditManager.java | 538 ++++++ .../bossshop/points/CommandPointsAPI.java | 43 + .../bossshop/points/EnjinPointsAPI.java | 67 + .../bossshop/points/FailedPointsAPI.java | 38 + .../black_ixx/bossshop/points/IPointsAPI.java | 32 + .../bossshop/points/PlayerPointsAPI.java | 48 + .../black_ixx/bossshop/points/PointsAPI.java | 18 + .../bossshop/points/PointsAPIPlugin.java | 43 + .../bossshop/points/PointsManager.java | 142 ++ .../black_ixx/bossshop/sale/SaleListener.java | 243 +++ .../black_ixx/bossshop/sale/SaleManager.java | 656 +++++++ .../bossshop/sale/SaleParameter.java | 41 + .../org/black_ixx/bossshop/sale/SaleStep.java | 33 + .../bossshop/util/AttributeRemover.java | 67 + .../org/black_ixx/bossshop/util/Function.java | 13 + .../black_ixx/bossshop/util/MyYamlConfig.java | 342 ++++ src/main/resource/lang/ItemName_zh_CN.lang | 1609 +++++++++++++++++ src/main/resource/lang/ItemName_zh_TW.lang | 1605 ++++++++++++++++ src/main/resource/plugin.yml | 156 ++ 97 files changed, 17042 insertions(+) create mode 100644 src/main/java/Test.java create mode 100644 src/main/java/cc/util/bossshop/ClassHelper.java create mode 100644 src/main/java/cc/util/bossshop/FBukkit.java create mode 100644 src/main/java/cc/util/bossshop/Function.java create mode 100644 src/main/java/cc/util/bossshop/JsonExtra.java create mode 100644 src/main/java/cc/util/bossshop/LocalLanguage.java create mode 100644 src/main/java/cc/util/bossshop/NMSHelper.java create mode 100644 src/main/java/cc/util/bossshop/config/CommentedConfiguration.java create mode 100644 src/main/java/cc/util/bossshop/config/CommentedOptions.java create mode 100644 src/main/java/cc/util/bossshop/config/CommentedSection.java create mode 100644 src/main/java/cc/util/bossshop/config/CommentedValue.java create mode 100644 src/main/java/cc/util/bossshop/config/CommentedYamlConfig.java create mode 100644 src/main/java/cc/util/bossshop/config/ICommentedSection.java create mode 100644 src/main/java/cc/util/bossshop/config/LineAnalyse.java create mode 100644 src/main/java/cc/util/bossshop/config/YamlNode.java create mode 100644 src/main/java/cc/util/bossshop/config/YamlStack.java create mode 100644 src/main/java/cc/util/bossshop/filemanager/AConfigManager.java create mode 100644 src/main/java/cc/util/bossshop/filemanager/AFileManager.java create mode 100644 src/main/java/cc/util/bossshop/filemanager/ALangManager.java create mode 100644 src/main/java/cc/util/bossshop/pluginmodel/ABukkitPlugin.java create mode 100644 src/main/java/cc/util/bossshop/pluginmodel/IClearAble.java create mode 100644 src/main/java/cc/util/bossshop/pluginmodel/INeedConfig.java create mode 100644 src/main/java/cc/util/bossshop/pluginmodel/INeedReload.java create mode 100644 src/main/java/cc/util/bossshop/pluginmodel/ReloadPriority.java create mode 100644 src/main/java/org/black_ixx/bossshop/BossShop.java create mode 100644 src/main/java/org/black_ixx/bossshop/api/BSAddonConfig.java create mode 100644 src/main/java/org/black_ixx/bossshop/api/BossShopAPI.java create mode 100644 src/main/java/org/black_ixx/bossshop/api/BossShopAddon.java create mode 100644 src/main/java/org/black_ixx/bossshop/command/ACommandBase.java create mode 100644 src/main/java/org/black_ixx/bossshop/command/CommandClear.java create mode 100644 src/main/java/org/black_ixx/bossshop/command/CommandClose.java create mode 100644 src/main/java/org/black_ixx/bossshop/command/CommandExc.java create mode 100644 src/main/java/org/black_ixx/bossshop/command/CommandHelp.java create mode 100644 src/main/java/org/black_ixx/bossshop/command/CommandMail.java create mode 100644 src/main/java/org/black_ixx/bossshop/command/CommandNBT.java create mode 100644 src/main/java/org/black_ixx/bossshop/command/CommandName.java create mode 100644 src/main/java/org/black_ixx/bossshop/command/CommandOpen.java create mode 100644 src/main/java/org/black_ixx/bossshop/command/CommandReload.java create mode 100644 src/main/java/org/black_ixx/bossshop/command/CommandSale.java create mode 100644 src/main/java/org/black_ixx/bossshop/command/CommandUnsale.java create mode 100644 src/main/java/org/black_ixx/bossshop/core/BSBuy.java create mode 100644 src/main/java/org/black_ixx/bossshop/core/BSBuyParameter.java create mode 100644 src/main/java/org/black_ixx/bossshop/core/BSEnums.java create mode 100644 src/main/java/org/black_ixx/bossshop/core/BSMultiplier.java create mode 100644 src/main/java/org/black_ixx/bossshop/core/BSShop.java create mode 100644 src/main/java/org/black_ixx/bossshop/core/BSShopHolder.java create mode 100644 src/main/java/org/black_ixx/bossshop/core/BSShopManager.java create mode 100644 src/main/java/org/black_ixx/bossshop/events/BSDisplayItemEvent.java create mode 100644 src/main/java/org/black_ixx/bossshop/events/BSPlayerPurchaseEvent.java create mode 100644 src/main/java/org/black_ixx/bossshop/events/BSPlayerPurchasedEvent.java create mode 100644 src/main/java/org/black_ixx/bossshop/events/BSReloadedEvent.java create mode 100644 src/main/java/org/black_ixx/bossshop/listeners/InventoryListener.java create mode 100644 src/main/java/org/black_ixx/bossshop/listeners/PlayerListener.java create mode 100644 src/main/java/org/black_ixx/bossshop/listeners/SignListener.java create mode 100644 src/main/java/org/black_ixx/bossshop/lottery/LotteryHolder.java create mode 100644 src/main/java/org/black_ixx/bossshop/lottery/LotteryManager.java create mode 100644 src/main/java/org/black_ixx/bossshop/lottery/LotteryStatus.java create mode 100644 src/main/java/org/black_ixx/bossshop/lottery/ReflashInventoryTask.java create mode 100644 src/main/java/org/black_ixx/bossshop/mail/MailItem.java create mode 100644 src/main/java/org/black_ixx/bossshop/mail/MailManager.java create mode 100644 src/main/java/org/black_ixx/bossshop/managers/BuyItemHandler.java create mode 100644 src/main/java/org/black_ixx/bossshop/managers/ConfigManager.java create mode 100644 src/main/java/org/black_ixx/bossshop/managers/DefaultCreator.java create mode 100644 src/main/java/org/black_ixx/bossshop/managers/ItemNameManager.java create mode 100644 src/main/java/org/black_ixx/bossshop/managers/ItemStackChecker.java create mode 100644 src/main/java/org/black_ixx/bossshop/managers/ItemStackCreator.java create mode 100644 src/main/java/org/black_ixx/bossshop/managers/LangManager.java create mode 100644 src/main/java/org/black_ixx/bossshop/managers/MultiplierHandler.java create mode 100644 src/main/java/org/black_ixx/bossshop/managers/RecordManager.java create mode 100644 src/main/java/org/black_ixx/bossshop/managers/ShopCustomizer.java create mode 100644 src/main/java/org/black_ixx/bossshop/managers/StringManager.java create mode 100644 src/main/java/org/black_ixx/bossshop/managers/TimeHandler.java create mode 100644 src/main/java/org/black_ixx/bossshop/managers/TransactionLog.java create mode 100644 src/main/java/org/black_ixx/bossshop/managers/VaultHandler.java create mode 100644 src/main/java/org/black_ixx/bossshop/managers/WorthHandler.java create mode 100644 src/main/java/org/black_ixx/bossshop/misc/Enchant.java create mode 100644 src/main/java/org/black_ixx/bossshop/misc/NoEconomy.java create mode 100644 src/main/java/org/black_ixx/bossshop/nbt/NBT.java create mode 100644 src/main/java/org/black_ixx/bossshop/nbt/NBTEditManager.java create mode 100644 src/main/java/org/black_ixx/bossshop/points/CommandPointsAPI.java create mode 100644 src/main/java/org/black_ixx/bossshop/points/EnjinPointsAPI.java create mode 100644 src/main/java/org/black_ixx/bossshop/points/FailedPointsAPI.java create mode 100644 src/main/java/org/black_ixx/bossshop/points/IPointsAPI.java create mode 100644 src/main/java/org/black_ixx/bossshop/points/PlayerPointsAPI.java create mode 100644 src/main/java/org/black_ixx/bossshop/points/PointsAPI.java create mode 100644 src/main/java/org/black_ixx/bossshop/points/PointsAPIPlugin.java create mode 100644 src/main/java/org/black_ixx/bossshop/points/PointsManager.java create mode 100644 src/main/java/org/black_ixx/bossshop/sale/SaleListener.java create mode 100644 src/main/java/org/black_ixx/bossshop/sale/SaleManager.java create mode 100644 src/main/java/org/black_ixx/bossshop/sale/SaleParameter.java create mode 100644 src/main/java/org/black_ixx/bossshop/sale/SaleStep.java create mode 100644 src/main/java/org/black_ixx/bossshop/util/AttributeRemover.java create mode 100644 src/main/java/org/black_ixx/bossshop/util/Function.java create mode 100644 src/main/java/org/black_ixx/bossshop/util/MyYamlConfig.java create mode 100644 src/main/resource/lang/ItemName_zh_CN.lang create mode 100644 src/main/resource/lang/ItemName_zh_TW.lang create mode 100644 src/main/resource/plugin.yml diff --git a/src/main/java/Test.java b/src/main/java/Test.java new file mode 100644 index 0000000..ef663bd --- /dev/null +++ b/src/main/java/Test.java @@ -0,0 +1,9 @@ + + +public class Test{ + + public static void main(String[] args){ + System.out.println(System.class.getPackage().getSpecificationVersion()); + } + +} diff --git a/src/main/java/cc/util/bossshop/ClassHelper.java b/src/main/java/cc/util/bossshop/ClassHelper.java new file mode 100644 index 0000000..b3de139 --- /dev/null +++ b/src/main/java/cc/util/bossshop/ClassHelper.java @@ -0,0 +1,794 @@ +package cc.util.bossshop; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; + +public class ClassHelper{ + + public static String mTestVersion="v1_7_R4"; + /** + * MC版本,请勿直接获取,请使用{@link ClassHelper#getServerVersion()}来获取 + */ + @Deprecated + private static String mMCVersion; + + /** + * 获取服务的Bukkit版本 + */ + public static String getServerVersion(){ + if(mMCVersion==null){ + if(Bukkit.getServer()!=null){ + String className=Bukkit.getServer().getClass().getPackage().getName(); + mMCVersion=className.substring(className.lastIndexOf('.')+1); + }else mMCVersion=mTestVersion; + } + return mMCVersion; + } + + /** + * 查看指定类名是否加载,不会报错 + * @param name 类完整名字 + */ + public static boolean isClassLoaded(String name){ + try{ + Class.forName(name); + return true; + }catch(Throwable e){ + return false; + } + } + + /** + * 获取org.bukkit.craftbukkit类的全名 + * @param pName 短名字 + * @return 完整名字 + */ + public static String getCBTName(String pName){ + return "org.bukkit.craftbukkit."+ClassHelper.getServerVersion()+"."+pName; + } + + /** + * 获取NMS类的{@link Class}对象 + * @param pClazz NMS类短名字 + */ + public static Class getNMSClass(String pClazz){ + return ClassHelper.getClass(getNMSName(pClazz)); + } + + /** + * 获取NMS类的全名 + * @param pName 短名字 + * @return 完整名字 + */ + public static String getNMSName(String pName){ + return "net.minecraft.server."+ClassHelper.getServerVersion()+"."+pName; + } + + /** + * 获取craftbukkit类的{@link Class}对象 + * @param pClazz craftbukkit类短名字,(org.bukkit.craftbukkit.version后的名字) + */ + public static Class getCBTClass(String pClazz){ + return ClassHelper.getClass(getCBTName(pClazz)); + } + + /** + * 取出指定Object中的指定名字的值域 + * @param pObj 要取数据的实例 + * @param pField 值域名 + * @return 值域的值 + */ + public static Object getFieldValue(Object pObj,String pField){ + Validate.notNull(pObj,"要获取值域的对象不能为null对象"); + Class tClazz=pObj.getClass(); + Field tField=null; + try{ + tField=tClazz.getDeclaredField(pField); + tField.setAccessible(true); + return tField.get(pObj); + }catch(NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e){ + FBukkit.severe("获取"+tClazz.getName()+"的类的实例对象的值域"+pField+"时发生了错误",e); + } + return null; + } + + /** + * 设置指定对象实例的指定名字的值域的值 + * @param pObj 对象实例 + * @param pField 值域名 + * @param pValue 要设置成的值 + */ + public static void setFieldValue(Object pObj,String pField,Object pValue){ + Validate.notNull(pObj,"要获取值域的对象不能为null对象"); + Class tClazz=pObj.getClass(); + Field tField=null; + try{ + tField=tClazz.getDeclaredField(pField); + tField.setAccessible(true); + tField.set(pObj,pValue); + }catch(NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e){ + FBukkit.severe("设置"+tClazz.getName()+"的类的实例对象的值域"+pField+"时发生了错误",e); + } + } + + /** + * 获取指定对象实例指定值域的值 + * @param pObj 对象 + * @param pField 值域 + */ + public static Object getFieldValue(Object pObj,Field pField){ + try{ + Validate.notNull(pObj,"要获取值域的对象不能为null对象"); + pField.setAccessible(true); + return pField.get(pObj); + }catch(IllegalArgumentException|IllegalAccessException e){ + FBukkit.severe("获取"+pObj.getClass().getName()+"的类的实例对象的值域"+pField+"时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 设置指定对象实例的指定名字的值域的值 + * @param pObj 对象实例 + * @param pField 值域名 + * @param pValue 要设置成的值 + */ + public static void setFieldValue(Object pObj,Field pField,Object pValue){ + try{ + pField.setAccessible(true); + pField.set(pObj,pValue); + }catch(IllegalArgumentException|IllegalAccessException e){ + FBukkit.severe("设置"+pObj.getClass().getName()+"的类的实例对象的值域"+pField+"时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 获取指定对象实例,指定类型,指定前缀修饰符的值域的值 + * @param pObj 对象实例 + * @param pFieldType 值域类型 + * @param modifier 值域修饰符,如果不限制则为-1 + */ + public static T getFieldValue(Object pObj,Class pFieldType,int modifier){ + Field tField=ClassHelper.getField(pObj.getClass(),pFieldType,modifier).get(0); + try{ + tField.setAccessible(true); + return (T)tField.get(pObj); + }catch(IllegalArgumentException|IllegalAccessException e){ + FBukkit.severe("获取"+pObj.getClass().getName()+"的类实例的指定类型"+pFieldType+"值域时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 设置指定对象实例,指定类型,指定前缀修饰符的值域的值 + * @param pObj 对象实例 + * @param pFieldType 值域类型 + * @param pValue 要设置成的值 + * @param modifier 值域修饰符,如果不限制则为-1 + */ + public static void setFiledValue(Object pObj,Class pFieldType,Object pValue,int modifier){ + Field tField=ClassHelper.getField(pObj.getClass(),pFieldType,modifier).get(0); + try{ + if(tField!=null){ + tField.setAccessible(true); + tField.set(pObj,pValue); + } + }catch(IllegalArgumentException|IllegalAccessException e){ + FBukkit.severe("设置"+pObj.getClass().getName()+"的类实例的指定类型"+pFieldType+"值域时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 获取指定对象实例,指定类型,指定前缀修饰符的值域 + * @param pClazz 对象实例 + * @param pFieldClassShorName 值域类型短名字 + * @param modifier 值域修饰符,如果不限制则为-1 + */ + public static Field getFieldByClassName(Class pClazz,String pFieldClassShorName,int modifier){ + try{ + Field[] tFields=pClazz.getDeclaredFields(); + for(Field sField : tFields){ + if(sField.getType().getSimpleName().equals(pFieldClassShorName)&&(sField.getModifiers()==modifier||modifier<0)) return sField; + } + throw new NoSuchFieldException("类"+pClazz.getName()+"不存在值域类型短名字为: "+pFieldClassShorName+",且类型修饰符为: "+modifier+"的值域"); + }catch(SecurityException|IllegalArgumentException|NoSuchFieldException e){ + FBukkit.severe("获取"+pClazz.getName()+"类的值域类型短名字为"+pFieldClassShorName+"的值域时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 获取指定对象实例,指定类型,指定前缀修饰符的值域 + * @param pClazz 对象实例 + * @param pFieldName 值域类型短名字 + * @param modifier 值域修饰符,如果不限制则为-1 + */ + public static Field getFieldByFieldName(Class pClazz,String pFieldName,int modifier){ + try{ + Field[] tFields=pClazz.getDeclaredFields(); + for(Field sField : tFields){ + if(sField.getName().equals(pFieldName)&&(sField.getModifiers()==modifier||modifier<0)) return sField; + } + throw new NoSuchFieldException("类"+pClazz.getName()+"不存在名字为: "+pFieldName+",且类型修饰符为: "+modifier+"的值域"); + }catch(SecurityException|IllegalArgumentException|NoSuchFieldException e){ + FBukkit.severe("获取"+pClazz.getName()+"类的值域名字为"+pFieldName+"的值域时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 获取指定对象实例,指定类型,指定前缀修饰符的值域 + * @param pClazz 对象实例 + * @param pFieldType 值域类型 + * @param modifier 值域修饰符,如果不限制则为-1 + */ + public static ArrayList getField(Class pClazz,Class pFieldClazz,int modifier){ + try{ + ArrayList findFields=new ArrayList<>(); + Field[] tFields=pClazz.getDeclaredFields(); + for(Field sField : tFields) + if(isSameClazz(sField.getType(),pFieldClazz)&&(sField.getModifiers()==modifier||modifier<0)) findFields.add(sField); + if(!findFields.isEmpty()) return findFields; + throw new NoSuchFieldException("类"+pClazz.getName()+"不存在类型为: "+pFieldClazz.getName()+",且类型修饰符为: "+modifier+"的值域"); + }catch(SecurityException|IllegalArgumentException|NoSuchFieldException e){ + FBukkit.severe("获取"+pClazz.getName()+"的类的值域"+pFieldClazz+"时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 执行指定对象实例的无参数方法,并返回结果 + * @param pObj 对象实例 + * @param pMethod 方法名 + */ + public static Object invokeMethod(Object pObj,String pMethod){ + return invokeMethod(pObj,pMethod,new Class[]{},new Object[]{}); + } + + /** + * 执行指定实例的单参数方法,并返回结果 + * @param pObj 实例对象 + * @param pMethod 方法名 + * @param pArgClazz 参数类型 + * @param pArg 参数 + */ + public static Object invokeMethod(Object pObj,String pMethod,Class pArgClazz,Object pArg){ + return invokeMethod(pObj,pMethod,new Class[]{pArgClazz},new Object[]{pArg}); + } + + /** + * 执行指定实例的指定方法,并返回结果 + * @param pObj 实例对象 + * @param pMethod 方法名 + * @param pArgsClazzs 参数类型 + * @param pArgs 参数 + */ + public static Object invokeMethod(Object pObj,String pMethod,Class[] pArgsClazzs,Object[] pArgs){ + if(pObj==null) return null; + if(pMethod==null) return null; + Class tClazz=pObj.getClass(); + Method tMethod=null; + try{ + if(pArgsClazzs==null||pArgsClazzs.length==0) tMethod=tClazz.getDeclaredMethod(pMethod); + else tMethod=tClazz.getDeclaredMethod(pMethod,pArgsClazzs); + tMethod.setAccessible(true); + return tMethod.invoke(pObj,pArgs); + }catch(IllegalAccessException|IllegalArgumentException|InvocationTargetException|NoSuchMethodException|SecurityException e){ + FBukkit.severe("执行"+tClazz.getName()+"的类的方法: "+pMethod+",参数: "+Arrays.asList(pArgsClazzs)+" "+"时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 执行指定对象实例的无参数方法,并返回结果 + * @param pObj 对象实例 + * @param pMethod 方法 + */ + public static Object invokeMethod(Object pObj,Method pMethod){ + return invokeMethod(pObj,pMethod,new Object[]{}); + } + + /** + * 执行指定实例的单参数方法,并返回结果 + * @param pObj 实例对象 + * @param pMethod 方法 + * @param pArgClazz 参数类型 + * @param pArg 参数 + */ + public static Object invokeMethod(Object pObj,Method pMethod,Object pArg){ + return invokeMethod(pObj,pMethod,new Object[]{pArg}); + } + + /** + * 执行指定实例的指定方法,并返回结果 + * @param pObj 实例对象 + * @param pMethod 方法 + * @param pArgsClazzs 参数类型 + * @param pArgs 参数 + */ + public static Object invokeMethod(Object pObj,Method pMethod,Object[] pArgs){ + if(pObj==null) return null; + if(pMethod==null) return null; + Class tClazz=pObj.getClass(); + try{ + pMethod.setAccessible(true); + return pMethod.invoke(pObj,pArgs); + }catch(IllegalAccessException|IllegalArgumentException|InvocationTargetException|SecurityException e){ + FBukkit.severe("执行"+tClazz.getName()+"的类的方法: "+pMethod+"时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 执行指定实例且返回类型特定的的无参数方法,并返回结果 + * @param pObj 实例对象 + * @param pMethod 方法名 + */ + public static T invokeMethod(Object pObj,Class pReturnType){ + return invokeMethod(pObj,pReturnType,new Class[]{},new Object[]{}); + } + + /** + * 执行指定实例且返回类型特定的单参数方法,并返回结果 + * @param pObj 实例对象 + * @param pMethod 方法名 + * @param pArgClazz 参数类型 + * @param pArg 参数 + */ + public static T invokeMethod(Object pObj,Class pReturnType,Class pArgClazz,Object pArg){ + return invokeMethod(pObj,pReturnType,new Class[]{pArgClazz},new Object[]{pArg}); + } + + /** + * 执行指定实例的指定方法,并返回结果 + * @param pObj 实例对象 + * @param pMethod 方法名 + * @param pArgsClazzs 参数类型 + * @param pArgs 参数 + */ + public static T invokeMethod(Object pObj,Class pReturnType,Class[] pArgsClazzs,Object[] pArgs){ + Validate.notNull(pObj,"要执行方法的对象不能为null对象"); + Class tClazz=pObj.getClass(); + try{ + Method tMethod=ClassHelper.getUnknowMethod(tClazz,pReturnType,pArgsClazzs).get(0); + tMethod.setAccessible(true); + return (T)tMethod.invoke(pObj,pArgs); + }catch(IllegalAccessException|IllegalArgumentException|InvocationTargetException|SecurityException e){ + FBukkit.severe("执行"+tClazz.getName()+"的类的未知方法,返回类型: "+pReturnType.getName()+"[参数: "+pArgsClazzs+"] "+"时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 执行指定对象名指定的无参静态方法,并返回结果 + * @param pClazz 对象 + * @param pMethod 方法名 + */ + public static Object invokeStaticMethod(String pClazz,String pMethod){ + return invokeStaticMethod(pClazz,pMethod,new Class[]{},new Object[]{}); + } + + /** + * 执行指定对象名指定的单参数参静态方法,并返回结果 + * @param pClazz 对象 + * @param pMethod 方法名 + * @param pArgClazz 参数类型 + * @param pArg 参数 + */ + public static Object invokeStaticMethod(String pClazz,String pMethod,Class pArgClazz,Object pArg){ + return invokeStaticMethod(pClazz,pMethod,new Class[]{pArgClazz},new Object[]{pArg}); + } + + /** + * 执行指定对象名指定的静态方法,并返回结果 + * @param pClazz 对象 + * @param pMethod 方法名 + * @param pArgsClazzs 参数类型 + * @param pArgs 参数 + */ + public static Object invokeStaticMethod(String pClazz,String pMethod,Class[] pArgsClazzs,Object[] pArgs){ + try{ + Class tClazz=Class.forName(pClazz); + return ClassHelper.invokeStaticMethod(tClazz,pMethod,pArgsClazzs,pArgs); + }catch(IllegalArgumentException|SecurityException|ClassNotFoundException e){ + FBukkit.severe("执行"+pClazz+"的类的方法: "+pMethod+",[参数: "+pArgsClazzs+"] "+"时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 执行指定对象指定的无参静态方法,并返回结果 + * @param pClazz 对象 + * @param pMethod 方法名 + */ + public static Object invokeStaticMethod(Class pClazz,String pMethod){ + return invokeStaticMethod(pClazz,pMethod,new Class[]{},new Object[]{}); + } + + /** + * 执行指定对象名指定的单参数参静态方法,并返回结果 + * @param pClazz 对象 + * @param pMethod 方法名 + * @param pArgClazz 参数类型 + * @param pArg 参数 + */ + public static Object invokeStaticMethod(Class pClazz,String pMethod,Class pArgClazz,Object pArg){ + return invokeStaticMethod(pClazz,pMethod,new Class[]{pArgClazz},new Object[]{pArg}); + } + + /** + * 执行指定对象名指定的静态方法,并返回结果 + * @param pClazz 对象 + * @param pMethod 方法名 + * @param pArgsClazzs 参数类型 + * @param pArgs 参数 + */ + public static Object invokeStaticMethod(Class pClazz,String pMethod,Class[] pArgClazz,Object[] pArgs){ + Method tMethod=null; + try{ + if(pArgClazz==null||pArgClazz.length==0) tMethod=pClazz.getDeclaredMethod(pMethod); + else tMethod=pClazz.getDeclaredMethod(pMethod,pArgClazz); + return invokeStaticMethod(tMethod,pArgs); + }catch(IllegalArgumentException|NoSuchMethodException|SecurityException e){ + FBukkit.severe("执行"+pClazz+"的类的方法: +pMethod,[参数: "+pArgClazz+"] "+"时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 执行指定的无参静态方法 + * @param pMethod 方法 + */ + public static Object invokeStaticMethod(Method pMethod){ + try{ + pMethod.setAccessible(true); + return pMethod.invoke(null); + }catch(IllegalAccessException|IllegalArgumentException|InvocationTargetException e){ + FBukkit.severe("执行方法: "+pMethod+",[参数: 无] "+"时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 执行指定的单参静态方法 + * @param pMethod 方法 + * @param pArg 参数 + */ + public static Object invokeStaticMethod(Method pMethod,Object pArg){ + try{ + pMethod.setAccessible(true); + return invokeStaticMethod(pMethod,new Object[]{pArg}); + }catch(IllegalArgumentException e){ + FBukkit.severe("执行方法: "+pMethod+",[参数: "+pArg+"] "+"时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 执行指定的单参静态方法 + * @param pMethod 方法 + * @param pArgs 参数 + */ + public static Object invokeStaticMethod(Method pMethod,Object[] pArgs){ + try{ + pMethod.setAccessible(true); + return pMethod.invoke(null,pArgs); + }catch(IllegalAccessException|IllegalArgumentException|InvocationTargetException e){ + FBukkit.severe("执行方法: "+pMethod+",[参数: "+pArgs+"] "+"时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 执行类的无参构造函数,并返回结果 + * @param pClazz 类全名 + */ + public static Object getInstance(String pClazz){ + return getInstance(pClazz,new Class[]{},new Object[]{}); + } + + /** + * 执行类的单参构造函数,并返回结果 + * @param pClazz 类全名 + * @param pArgClazz 参数类型 + * @param pArg 参数 + */ + public static Object getInstance(String pClazz,Class pArgClazz,Object pArg){ + return getInstance(pClazz,new Class[]{pArgClazz},new Object[]{pArg}); + } + + /** + * 执行类的指定参数构造函数,并返回结果 + * @param pClazz 类全名 + * @param pArgsClazzs 参数类型 + * @param pArgs 参数 + */ + public static Object getInstance(String pClazz,Class[] pArgsClazzs,Object[] pArgs){ + try{ + Class tClazz=Class.forName(pClazz); + return ClassHelper.getInstance(tClazz,pArgsClazzs,pArgs); + }catch(ClassNotFoundException|SecurityException|IllegalArgumentException e){ + FBukkit.severe("使用参数"+pArgsClazzs+"实例化类"+pClazz+"时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 执行类的无参构造函数,并返回结果 + * @param pClazz 类 + */ + public static Object getInstance(Class pClazz){ + return getInstance(pClazz,new Class[]{},new Object[]{}); + } + + /** + * 执行类的单参构造函数,并返回结果 + * @param pClazz 类 + * @param pArgClazz 参数类型 + * @param pArg 参数 + */ + public static Object getInstance(Class pClazz,Class pArgsClazzs,Object pArgs){ + return getInstance(pClazz,new Class[]{pArgsClazzs},new Object[]{pArgs}); + } + + /** + * 执行类指定参数的构造函数,并返回结果 + * @param pClazz 类全名 + * @param pArgsClazzs 参数类型 + * @param pArgs 参数 + */ + public static Object getInstance(Class pClazz,Class[] pArgsClazzs,Object[] pArgs){ + try{ + Constructor tcons=null; + if(pArgsClazzs==null||pArgsClazzs.length==0) tcons=pClazz.getConstructor(); + else tcons=pClazz.getConstructor(pArgsClazzs); + tcons.setAccessible(true); + return tcons.newInstance(pArgs); + }catch(NoSuchMethodException|SecurityException|InstantiationException|IllegalAccessException|IllegalArgumentException|InvocationTargetException e){ + FBukkit.severe("使用参数"+pArgsClazzs+"实例化类"+pClazz+"时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 获取类 + * @param pClazz 类全名 + */ + public static Class getClass(String pClazz){ + try{ + return Class.forName(pClazz); + }catch(ClassNotFoundException e){ + FBukkit.severe("获取类"+pClazz+"时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 获取类指定名字的无参方法 + * @param pClass 类 + * @param pMethodName 方法名 + */ + public static Method getMethod(Class pClass,String pMethodName){ + return getMethod(pClass,pMethodName,new Class[]{}); + } + + /** + * 获取类指定名字的单参方法 + * @param pClass 类 + * @param pMethodName 方法名 + * @param pArgClass 参数类型 + */ + public static Method getMethod(Class pClass,String pMethodName,Class pArgClass){ + return getMethod(pClass,pMethodName,new Class[]{pArgClass}); + } + + /** + * 获取类指定名字的指定参数的方法 + * @param pClass 类 + * @param pMethodName 方法名 + * @param pArgsClass 参数类型 + */ + public static Method getMethod(Class pClass,String pMethodName,Class[] pArgsClazzs){ + Method tMethod=null; + try{ + if(pArgsClazzs==null||pArgsClazzs.length==0) tMethod=pClass.getDeclaredMethod(pMethodName); + else tMethod=pClass.getDeclaredMethod(pMethodName,pArgsClazzs); + return tMethod; + }catch(IllegalArgumentException|NoSuchMethodException|SecurityException e){ + FBukkit.severe("获取"+pClass.getName()+"的类的方法: "+pMethodName+",[参数: "+pArgsClazzs+"] "+"时发生了错误",e); + throw new IllegalStateException(e); + } + } + + /** + * 根据返回类型的短名字获取指定类的无参数方法 + * @param pClass 类 + * @param pReturnTypeSimpleName 类短名字 + */ + public static ArrayList getUnknowMethod(Class pClass,String pReturnTypeSimpleName){ + return ClassHelper.getUnknowMethod(pClass,pReturnTypeSimpleName,new Class[]{}); + } + + /** + * 根据返回类型的短名字获取指定类的单参数方法 + * @param pClass 类 + * @param pReturnTypeSimpleName 类短名字 + * @param pArgClazz 参数类型 + */ + public static ArrayList getUnknowMethod(Class pClass,String pReturnTypeSimpleName,Class pArgClazz){ + return ClassHelper.getUnknowMethod(pClass,pReturnTypeSimpleName,new Class[]{pArgClazz}); + } + + /** + * 根据返回类型的短名字和参数类型来获取自定类的多参数方法 + * @param pClass 类 + * @param pReturnTypeSimpleName 类短名字 + * @param pArgsClazzs 参数类型 + */ + public static ArrayList getUnknowMethod(Class pClass,String pReturnTypeSimpleName,Class[] pArgsClazzs){ + if(pReturnTypeSimpleName==null||pReturnTypeSimpleName.equals("null")) pReturnTypeSimpleName=void.class.getSimpleName(); + Method[] tMethods=pClass.getDeclaredMethods(); + ArrayList findMethodes=new ArrayList(); + for(Method sMethod : tMethods){ + Class tReturn=sMethod.getReturnType(); + if(tReturn.getSimpleName().equals(pReturnTypeSimpleName)&&isSameClazzs(sMethod.getParameterTypes(),pArgsClazzs)) findMethodes.add(sMethod); + } + if(!findMethodes.isEmpty()) return findMethodes; + String tArgsStr=""; + for(int i=0;i0) tArgsStr=tArgsStr.substring(0,tArgsStr.length()-1); + else tArgsStr="无"; + try{ + throw new NoSuchMethodException("未发现类"+pClass.getName()+"返回类型名为: "+pReturnTypeSimpleName+",且参数为: "+tArgsStr+"的方法"); + }catch(NoSuchMethodException e){ + FBukkit.severe(e.getMessage(),e); + throw new IllegalStateException(e); + } + } + + /** + * 根据返回类型来获取指定类的无参数方法 + * @param pClass 类 + * @param pReturnType 返回类型 + * @param pArgClazz 参数类型 + */ + public static ArrayList getUnknowMethod(Class pClass,Class pReturnType){ + return getUnknowMethod(pClass,pReturnType,new Class[]{}); + } + + /** + * 根据返回类型和单参数来获取指定类的单参数方法 + * @param pClass 类 + * @param pReturnType 返回类型 + * @param pArgClazz 参数类型 + */ + public static ArrayList getUnknowMethod(Class pClass,Class pReturnType,Class pArgClazz){ + return getUnknowMethod(pClass,pReturnType,new Class[]{pArgClazz}); + } + + /** + * 根据返回类型和参数来获取指定类的多参数方法 + * @param pClass 类 + * @param pReturnType 返回类型 + * @param pArgsClazzs 参数类型 + */ + public static ArrayList getUnknowMethod(Class pClass,Class pReturnType,Class[] pArgsClazzs){ + Method[] tMethods=pClass.getDeclaredMethods(); + ArrayList findMethodes=new ArrayList(); + for(Method sMethod : tMethods){ + Class tReturn=sMethod.getReturnType(); + if(isSameClazz(tReturn,pReturnType)&&isSameClazzs(sMethod.getParameterTypes(),pArgsClazzs)) findMethodes.add(sMethod); + } + if(!findMethodes.isEmpty()) return findMethodes; + String tArgsStr=""; + for(int i=0;i0) tArgsStr=tArgsStr.substring(0,tArgsStr.length()-1); + else tArgsStr="无"; + try{ + throw new NoSuchMethodException("未发现类"+pClass.getName()+"返回类型名为: "+pReturnType+",且参数为: "+tArgsStr+"的方法"); + }catch(NoSuchMethodException e){ + FBukkit.severe(e.getMessage(),e); + throw new IllegalStateException(e); + } + } + + /** + * 根据返回类型和单参数来获取指定类的单参数方法 + * @param pClass 类 + * @param pReturnType 返回类型 + * @param pArgClassShortName 参数类型 + */ + public static ArrayList getUnknowMethod(Class pClass,Class pReturnType,String pArgClassShortName){ + return getUnknowMethod(pClass,pReturnType,new String[]{pArgClassShortName}); + } + + /** + * 根据返回类型和参数来获取指定类的多参数方法 + * @param pClass 类 + * @param pReturnType 返回类型 + * @param pArgsClassShortName 参数短类型名 + */ + public static ArrayList getUnknowMethod(Class pClass,Class pReturnType,String[] pArgsClassShortName){ + Method[] tMethods=pClass.getDeclaredMethods(); + ArrayList findMethodes=new ArrayList(); + for(Method sMethod : tMethods){ + Class tReturn=sMethod.getReturnType(); + if(isSameClazz(tReturn,pReturnType)&&isSameClazzs(sMethod.getParameterTypes(),pArgsClassShortName)) findMethodes.add(sMethod); + } + if(!findMethodes.isEmpty()) return findMethodes; + String tArgsStr=""; + for(int i=0;i0) tArgsStr=tArgsStr.substring(0,tArgsStr.length()-1); + else tArgsStr="无"; + try{ + throw new NoSuchMethodException("未发现类"+pClass.getName()+"返回类型名为: "+pReturnType+",且参数为: "+tArgsStr+"的方法"); + }catch(NoSuchMethodException e){ + FBukkit.severe(e.getMessage(),e); + throw new IllegalStateException(e); + } + } + + /** + * 比较两个类数组是不是包含切顺序相同的类 + */ + private static boolean isSameClazzs(Class[] pClazzs1,Class[] pClazzs2){ + if(pClazzs1==pClazzs2) return true; + if((pClazzs1==null&&pClazzs2.length==0)||(pClazzs1.length==0&&pClazzs2==null)) return true; + if(pClazzs1==null||pClazzs2==null) return false; + if(pClazzs1.length!=pClazzs2.length) return false; + for(int i=0;i[] pClazzs1,String[] pClazzsShortName){ + if(pClazzs1==null&&pClazzsShortName==null) return true; + if((pClazzs1==null&&pClazzsShortName.length==0)||(pClazzs1.length==0&&pClazzsShortName==null)) return true; + + if(pClazzs1.length!=pClazzsShortName.length) return false; + for(int i=0;i pClazz1,Class pClazz2){ + if(pClazz1==pClazz2) return true; + if((pClazz1==void.class&&pClazz2==null)||(pClazz1==null&&pClazz2==void.class)) return true; + return false; + } + + /** + * 打印一个类所有的方法 + * @param pClass 类 + */ + public static void printfMethod(Class pClass){ + Method[] tMethods=pClass.getDeclaredMethods(); + for(Method sMethod : tMethods){ + System.out.println(sMethod); + } + } + +} diff --git a/src/main/java/cc/util/bossshop/FBukkit.java b/src/main/java/cc/util/bossshop/FBukkit.java new file mode 100644 index 0000000..57cbfe2 --- /dev/null +++ b/src/main/java/cc/util/bossshop/FBukkit.java @@ -0,0 +1,133 @@ +package cc.util.bossshop; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.meta.ItemMeta; + +public class FBukkit{ + + private static String mPrefix="&c[CCLogger]"; + + public static Player[] getOnlinePlayers(){ + Method tMethod=null; + try{ + tMethod=Bukkit.class.getDeclaredMethod("getOnlinePlayers"); + Object tObject=tMethod.invoke(null); + Player[] tPlayers=null; + if(tObject instanceof Player[]) tPlayers=(Player[])tObject; + else{ + Collection tcPlayers=(Collection)tObject; + tPlayers=new Player[tcPlayers.size()]; + tcPlayers.toArray(tPlayers); + } + return tPlayers; + }catch(Throwable exp){ + FBukkit.severe("获取在线玩家列表时发生了错误",exp); + return new Player[0]; + } + } + + public static ArrayList getOnlinePlayersName(){ + ArrayList playerNames=new ArrayList<>(); + for(Player sPlayer : FBukkit.getOnlinePlayers()) + playerNames.add(sPlayer.getName()); + return playerNames; + } + + public static OfflinePlayer[] getOfflinePlayers(){ + Method tMethod=null; + try{ + tMethod=Bukkit.class.getDeclaredMethod("getOfflinePlayers"); + Object tObject=tMethod.invoke(null); + OfflinePlayer[] tPlayers=null; + if(tObject instanceof OfflinePlayer[]) tPlayers=(OfflinePlayer[])tObject; + else{ + Collection tcPlayers=(Collection)tObject; + tPlayers=new OfflinePlayer[tcPlayers.size()]; + tcPlayers.toArray(tPlayers); + } + return tPlayers; + }catch(Throwable exp){ + FBukkit.severe("获取离线玩家列表时发生了错误",exp); + return new OfflinePlayer[0]; + } + } + + public static ArrayList getOfflinePlayersName(){ + ArrayList playerNames=new ArrayList<>(); + for(OfflinePlayer sPlayer : FBukkit.getOfflinePlayers()) + playerNames.add(sPlayer.getName()); + return playerNames; + } + + public static boolean isItemMetaEmpty(ItemMeta pMeat){ + Boolean result=(Boolean)ClassHelper.invokeMethod(pMeat,"isEmpty"); + if(result==null||result) return true; + return false; + } + + /** + * 设置ClassHelper类日志输出前缀 + */ + private static void setLogPrefix(String pPrefix){ + if(StringUtils.isNotEmpty(pPrefix)) + FBukkit.mPrefix=pPrefix; + } + + /** + * 在发生异常时用于输出调试信息,或输出警告信息到控制台 + * @param pMsg 错误的提示消息 + */ + public static void severe(String pMsg){ + FBukkit.log(mPrefix+"&c[ERROR]",pMsg); + } + + /** + * 输出信息到控制台 + * @param pMsg 提示消息 + */ + public static void log(String pPrefix,String pMsg){ + if(StringUtils.isEmpty(pMsg)) return; + if(StringUtils.isEmpty(pPrefix)) + pPrefix=FBukkit.mPrefix; + String sendMessage=ChatColor.translateAlternateColorCodes('&',pPrefix+" "+pMsg); + if(Bukkit.getServer()!=null){ + Bukkit.getConsoleSender().sendMessage(sendMessage); + }else Logger.getLogger("minecraft").log(Level.SEVERE,sendMessage); + } + + /** + * 在发生异常时用于输出调试信息,或输出警告信息到控制台 + * @param msg 错误的提示消息 + * @param thrown 要抛出的异常 + */ + public static void severe(String msg,Throwable thrown){ + FBukkit.severe(mPrefix+"&c[ERROR]",msg,thrown); + } + + /** + * 输出调试信息 + * @param pPrefix 消息前缀 + * @param msg 错误的提示消息 + * @param thrown 要抛出的异常 + */ + public static void severe(String pPrefix,String msg,Throwable thrown){ + if(Bukkit.getServer()!=null){ + FBukkit.log(pPrefix,msg); + String[] lines= org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(thrown).split("(\r?\n)+"); + for(String sLine : lines) + FBukkit.log(pPrefix,sLine); + }else Logger.getLogger("minecraft").log(Level.SEVERE,pPrefix+msg,thrown); + } + + +} diff --git a/src/main/java/cc/util/bossshop/Function.java b/src/main/java/cc/util/bossshop/Function.java new file mode 100644 index 0000000..86608f4 --- /dev/null +++ b/src/main/java/cc/util/bossshop/Function.java @@ -0,0 +1,102 @@ +package cc.util.bossshop; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class Function{ + + /** + * 获取忽略大小写的指定前缀的字符串 + *

如果指定的前缀为空时将返回全部的目标

+ * @param pStrs 搜索目标,不能为null + * @param pPrefix 指定的前缀,不能为null + * @param pIgnoreSame 是否忽略相同的字符串 + */ + public static Collection getSamePrefixIgnoreCase(Collection pStrs,String pPrefix,boolean pIgnoreSame){ + return Function.getSamePrefix(pStrs,pPrefix,true,pIgnoreSame); + } + + /** + * 获取指定前缀的字符串
+ * 相同的字符串不作为拥有该前缀 + *

如果指定的前缀为空时将返回全部的目标

+ * @param pStrs 搜索目标,不能为null + * @param pPrefix 指定的前缀,不能为null + * @param pIgnoreCase 是否忽略大小写 + * @param pIgnoreSame 是否忽略相同的字符串 + */ + public static Collection getSamePrefix(Collection pStrs,String pPrefix,boolean pIgnoreCase,boolean pIgnoreSame){ + ArrayList same=new ArrayList<>(); + if(pStrs==null||pStrs.isEmpty()) return same; + if(pPrefix==null||pPrefix.isEmpty()) return pStrs; + if(pIgnoreCase){ + pPrefix=pPrefix.toLowerCase(); + for(String sStr : pStrs){ + if(sStr==null) continue; + String lowsStr=sStr.toLowerCase(); + if(lowsStr.startsWith(pPrefix)&&(!pIgnoreSame||lowsStr.length()!=pPrefix.length())) + same.add(sStr); + } + }else{ + for(String sStr : pStrs){ + if(sStr==null) continue; + if(sStr.startsWith(pPrefix)&&(!pIgnoreSame||sStr.length()!=pPrefix.length())) + same.add(sStr); + } + } + return same; + } + + /** + * 指定位置是否存在忽略大小的指定字符串 + * @param pStrs 目标位置,不能为null + * @param pTarget 要查找的字符串,可以为null + * @return 是否存在 + */ + public static boolean containsIgnoreCase(Collection pStrs,String pTarget){ + if(pStrs==null) return false; + if(pTarget==null) return pStrs.contains(pTarget); + return Function.getIgnoreCase(pStrs,pTarget)!=null; + } + + /** + * 获取忽略大小的指定字符串 + * @param pStrs 目标位置,不能为null + * @param pTarget 要查找的字符串,不能为null + * @return 查找到的字符串或者null + */ + public static String getIgnoreCase(Collection pStrs,String pTarget){ + if(pStrs==null||pStrs.isEmpty()) return null; + if(pTarget==null) return null; + for(String sStr : pStrs) + if(sStr!=null&&sStr.equalsIgnoreCase(pTarget)) + return sStr; + return null; + } + + /** + * 打印当前位置的堆栈 + */ + public static void printStackTrace(){ + StackTraceElement[] elements=Thread.currentThread().getStackTrace(); + for(StackTraceElement sElement : elements){ + System.out.println(sElement.toString()); + } + } + + /** + * 将数组转化成List + * @param pArrays 要转换的数组,如果数组为null,将返回空List + * @return 转换后的List,非null + */ + public static List asList(T[] pArrays){ + ArrayList newList=new ArrayList<>(); + if(pArrays==null||pArrays.length==0) return newList; + for(T t : pArrays) + newList.add(t); + return newList; + } + + +} diff --git a/src/main/java/cc/util/bossshop/JsonExtra.java b/src/main/java/cc/util/bossshop/JsonExtra.java new file mode 100644 index 0000000..1afa9d5 --- /dev/null +++ b/src/main/java/cc/util/bossshop/JsonExtra.java @@ -0,0 +1,351 @@ +package cc.util.bossshop; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +public class JsonExtra{ + + public enum HoverAction{ + show_text,show_item,show_achievement + } + + public enum ClickAction{ + run_command,suggest_command,open_url + } + + public enum Color{ + black('0'), + dark_aqua('3'), + dark_blue('1'), + dark_gray('8'), + dark_green('2'), + dark_purple('5'), + dark_red('4'), + aqua('b'), + blue('9'), + gold('6'), + gray('7'), + green('a'), + light_purple('d'), + red('c'), + white('f'), + yellow('e'); + + public static final char COLOR_CHAR='§'; + private final char code; + private final String toString; + + private Color(char pChar){ + this.code=pChar; + this.toString=new String(new char[]{'§',code}); + } + + @Override + public String toString(){ + return this.toString; + } + + public static boolean isColor(char c){ + return Color.getColor(c)!=null; + } + + public static Color getColor(char c){ + c=(char)((c&0xDF)+32); + for(Color sColor : Color.values()) + if(sColor.code==c) return sColor; + return null; + } + + } + + public enum Style{ + magic('k',null), + bold('l',"bold"), + strikethrough('m',"obfuscated"), + underline('n',"underlined"), + italic('o',"italic"), + reset('r',null); + + public static final char FORMAT_CHAR='§'; + private final char code; + private final String mLabel; + private final String toString; + + private Style(char pChar,String pLabel){ + this.code=pChar; + this.mLabel=pLabel; + this.toString=new String(new char[]{'§',code}); + } + + @Override + public String toString(){ + return this.toString; + } + + public static boolean isStyle(char c){ + return Style.getStyle(c)!=null; + } + + public static Style getStyle(char c){ + c=(char)((c&0xDF)+32); + for(Style sStyle : Style.values()) + if(sStyle.code==c) return sStyle; + return null; + } + + /** + * 如果为null代表这个是不是Json使用的节点 + */ + public String getJsonLabel(){ + return this.mLabel; + } + + } + + private String mText=""; + private Color mTextColor=null; + private JSONObject mHoverEvent=null; + private JSONObject mClickEvent=null; + private boolean mInherit=true; + private static Pattern COLOR_FORMAT=Pattern.compile("§([0123456789AaBbCcDdEeFfKkLlMmNnOoRr])"); + private ArrayList mExtras=new ArrayList<>(); + /**未设置的值的格式默认继承,true或false代表是否启用,不允许put null*/ + private HashMap mStyles=new HashMap<>(); + + public JsonExtra(){ + this.mText=""; + } + + public JsonExtra(String pText){ + this.setText(pText); + } + + public JsonExtra(String pText,Color pTextColor){ + this.setText(pText); + this.setTextColor(pTextColor); + } + + /**设置文字*/ + public void setText(String pText){ + if(pText==null) this.mText=""; + pText=ChatColor.translateAlternateColorCodes('&',pText); + Color tcolor=this.mTextColor; + ArrayList tChildExtrs=new ArrayList<>(); + HashMap tStyles=new HashMap<>(); + tStyles.putAll(this.mStyles); + Matcher matcher=COLOR_FORMAT.matcher(pText); + int pos=0; + while(matcher.find()){ + char cf=matcher.group(1).charAt(0); + if(matcher.start()!=pos){ + JsonExtra te=new JsonExtra(); + te.mText=pText.substring(pos,matcher.start()); + te.mTextColor=tcolor; + te.mStyles.putAll(tStyles); + te.mInherit=false; + tChildExtrs.add(te); + } + if(Color.isColor(cf)){ + tcolor=Color.getColor(cf); + tStyles.clear(); + }else if(Style.isStyle(cf)){ + if(cf=='r'){ + tcolor=null; + tStyles.clear(); + }else tStyles.put(Style.getStyle(cf),true); + } + pos=matcher.end(); + } + JsonExtra te=new JsonExtra(); + te.mText=pText.substring(pos,pText.length()); + te.mTextColor=tcolor; + te.mStyles.putAll(tStyles); + te.mInherit=false; + tChildExtrs.add(te); + + JsonExtra headExtra=tChildExtrs.remove(0); + this.mText=headExtra.mText; + this.mTextColor=headExtra.mTextColor; + this.mStyles.clear(); + this.mStyles.putAll(headExtra.mStyles); + this.mExtras.addAll(0,tChildExtrs); + } + + /**设置文字颜色*/ + public void setTextColor(Color pTextColor){ + if(pTextColor==null) return; + this.mTextColor=pTextColor; + } + + /** + * 设置字体样式 + */ + public void setStyle(Style...pStyles){ + if(pStyles==null) return; + for(Style sStyle : pStyles) + this.mStyles.put(sStyle,true); + } + + /** + * 移除字体样式 + */ + public void removeStyle(Style...pStyles){ + if(pStyles==null) return; + for(Style sStyle : pStyles) + this.mStyles.remove(sStyle); + } + + /** + * 清除所有格式 + */ + public void clearStyle(){ + this.mStyles.clear(); + } + + /** + * 将所有样式设置为false + */ + public void resetStyle(){ + for(Style sStyle : Style.values()) + this.mStyles.put(sStyle,false); + this.mStyles.put(Style.reset,true); + } + + /** + * 设置是否继承父级的样式 + */ + public void setInherit(boolean pInherit){ + this.mInherit=pInherit; + } + + /** + * 是否继承父级的样式 + */ + public boolean isInherit(){ + return this.mInherit; + } + + /** + * 设置文字点击事件要执行的动作 + * @param pAction 动作类型 + * @param pValue 值 + */ + public void setClickEvent(ClickAction pAction,String pValue){ + if(pAction==null||StringUtils.isEmpty(pValue)) return; + this.mClickEvent=new JSONObject(); + this.mClickEvent.put("action",pAction.name()); + this.mClickEvent.put("value",pValue); + } + + /** + * 设置文件在鼠标悬浮式要执行的动作 + * @param pAction 动作 + * @param pValue 值 + */ + public void setHoverEvent(HoverAction pAction,String pValue){ + if(pAction==null||StringUtils.isEmpty(pValue)) return; + this.mHoverEvent=new JSONObject(); + this.mHoverEvent.put("action",pAction.name()); + this.mHoverEvent.put("value",pValue); + } + + private JSONObject getJson(){ + JSONObject tJson=new JSONObject(); + tJson.put("text",this.mText); + for(Style sStyle :Style.values()){ + if(sStyle.getJsonLabel()==null) continue; + Boolean hasSet=this.mStyles.get(sStyle); + if(hasSet==null){ + if(!this.mInherit) tJson.put(sStyle.getJsonLabel(),false); + }else{ + tJson.put(sStyle.getJsonLabel(),hasSet.booleanValue()); + } + } + if(this.mTextColor!=null) tJson.put("color",this.mTextColor.name()); + if(this.mClickEvent!=null) tJson.put("clickEvent",this.mClickEvent); + if(this.mHoverEvent!=null) tJson.put("hoverEvent",this.mHoverEvent); + if(!this.mExtras.isEmpty()){ + JSONArray tJsonArray=new JSONArray(); + for(JsonExtra sExtra : this.mExtras){ + tJsonArray.add(sExtra.getJson()); + } + tJson.put("extra",tJsonArray); + } + return tJson; + } + + @Override + public String toString(){ + return this.getJson().toJSONString(); + } + + @Override + public JsonExtra clone(){ + JsonExtra clone=new JsonExtra(this.mText,this.mTextColor); + JSONObject jsonObject=null; + if(this.mClickEvent!=null){ + jsonObject=new JSONObject(); + jsonObject.putAll((Map)this.mClickEvent.clone()); + clone.mClickEvent=jsonObject; + } + if(this.mHoverEvent!=null){ + jsonObject=new JSONObject(); + jsonObject.putAll((Map)this.mHoverEvent.clone()); + clone.mHoverEvent=jsonObject; + } + clone.mInherit=this.mInherit; + clone.mStyles.putAll(this.mStyles); + for(JsonExtra childExtra : this.mExtras){ + clone.mExtras.add(childExtra.clone()); + } + return clone; + } + + public String toSimpleString(){ + StringBuilder tStr=new StringBuilder(); + if(this.mTextColor!=null) tStr.append(this.mTextColor.toString()); + for(Entry entry : this.mStyles.entrySet()) + if(entry.getValue()) tStr.append(entry.getKey().toString()); + tStr.append(this.mText); + for(JsonExtra sExtra : this.mExtras) + tStr.append(sExtra.toSimpleString()); + return tStr.toString(); + } + + public void addExtra(JsonExtra pExtra){ + if(pExtra==null) return; + this.mExtras.add(pExtra); + } + + public void addNormalWords(String pWords,boolean pInherit){ + if(StringUtils.isEmpty(pWords)) return; + JsonExtra tExtra=new JsonExtra(pWords,Color.white); + if(!pInherit) + tExtra.mInherit=false; + this.addExtra(tExtra); + } + + /** + * 将Json消息发送给玩家
+ * 如果CommandSender不是玩家,将只发送非Json的消息串 + */ + public void sendToPlayer(CommandSender pSender){ + if(pSender instanceof Player){ + Bukkit.dispatchCommand(Bukkit.getConsoleSender(),"tellraw "+((Player)pSender).getName()+" "+this.toString()); + }else pSender.sendMessage(this.toSimpleString()); + //System.out.println(this.toString()); + } + +} diff --git a/src/main/java/cc/util/bossshop/LocalLanguage.java b/src/main/java/cc/util/bossshop/LocalLanguage.java new file mode 100644 index 0000000..1b28e65 --- /dev/null +++ b/src/main/java/cc/util/bossshop/LocalLanguage.java @@ -0,0 +1,131 @@ +package cc.util.bossshop; + +import java.lang.reflect.Method; +import java.util.ArrayList; + +import org.apache.commons.lang.StringUtils; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import cc.util.bossshop.pluginmodel.ABukkitPlugin; + +public class LocalLanguage{ + + public static final Method method_NMSItemStack_getName; + public static final Method method_NMSItemStack_getUnlocalizedName; + + protected final static boolean isFML; + private static Object instance_LanguageRegistry; + private static Method method_LanguageRegistry_getStringLocalization; + + protected ABukkitPlugin mPlugin; + + static{ + //NMS ItemStck + ItemStack tItem=new ItemStack(Material.STONE); + Object tNMSItemStack_mcitem=ClassHelper.invokeStaticMethod(NMSHelper.method_CraftItemStack_asNMSCopy,tItem); + //NMS ItemStack getName + Object tObj_NBTTagCompound=ClassHelper.getInstance(NMSHelper.clazz_NBTTagCompound); + Object tNBTTagCompound_displaye=ClassHelper.getInstance(NMSHelper.clazz_NBTTagCompound); + String displayName="1234567890",unlocalStoneName="tile.stone"; + Object tNBTTagString_name=ClassHelper.getInstance(NMSHelper.clazz_NBTTagString,String.class,displayName); + ClassHelper.invokeMethod(tNBTTagCompound_displaye,NMSHelper.method_NBTTagCompound_set,new Object[]{"Name",tNBTTagString_name}); + ClassHelper.invokeMethod(tObj_NBTTagCompound,NMSHelper.method_NBTTagCompound_set,new Object[]{"display",tNBTTagCompound_displaye}); + ClassHelper.setFieldValue(tNMSItemStack_mcitem,NMSHelper.field_NMSItemStack_tag,tObj_NBTTagCompound); + ArrayList mayGetNameMethods=ClassHelper.getUnknowMethod(NMSHelper.clazz_NMSItemStack,String.class); + int getNamePos=-1,getUnlocalNamePos=-1; + for(int i=0;i cpwlang=ClassHelper.getClass("cpw.mods.fml.common.registry.LanguageRegistry"); + instance_LanguageRegistry=ClassHelper.invokeStaticMethod(cpwlang,"instace"); + method_LanguageRegistry_getStringLocalization=ClassHelper.getMethod(cpwlang,"getStringLocalization"); + } + } + + protected LocalLanguage(ABukkitPlugin pPlugin){ + this.mPlugin=pPlugin; + } + + /** + * 物品在Lore中是否设置过名字 + */ + public boolean hasSetName(ItemStack pItem){ + if(pItem==null) return false; + if(pItem.getItemMeta()==null) return false; + return pItem.getItemMeta().hasDisplayName(); + } + + /** + * 获取由物品ID格式化后的名字 + *

+ * 例如"MINECRAFT_DIRT"将会格式化未"Minecratf Dirt" + *

+ */ + public String getIDFormatName(ItemStack pItem){ + if(pItem==null) return ""; + char[] tID=pItem.getType().name().toLowerCase().replace("_"," ").toCharArray(); + tID[0]=(char)(tID[0]&(0xDF)); + for(int i=0;i'z') continue; + tID[i+1]=(char)(tID[i+1]&(0xDF)); + i++; + } + } + return new String(tID); + } + + /** + * 获取指定物品指定语言的翻译 + *

+ * 如果物品本身存在显示名字,将返回该名字 + * 如果指定的语言不存在,将返回英文 + * 如果指定的物品不在翻译列表,将返回物品材料名字 + *

+ * @param pItem 指定的物品 + * @param pLang 指定的语言 + * @return 物品显示名字 + */ + public String getDisplayName(ItemStack pItem,String pLang){ + if(pItem==null) return ""; + if(this.hasSetName(pItem)) return pItem.getItemMeta().getDisplayName(); + String nowName=null; + if(isFML){ + String unlocalName=this.getUnlocalizedName(pItem); + nowName=this.getStringLocalization(this.getUnlocalizedName(pItem),pLang); + if(StringUtils.isNotEmpty(nowName)) return nowName; + nowName=this.getStringLocalization(unlocalName+".name",pLang); + if(StringUtils.isNotEmpty(nowName)) return nowName; + } + Object NMSItemStack=ClassHelper.invokeStaticMethod(NMSHelper.method_CraftItemStack_asNMSCopy,pItem); + if(NMSItemStack==null) return this.getIDFormatName(pItem); + return (String)ClassHelper.invokeMethod(NMSItemStack,method_NMSItemStack_getName); + } + + /** + * 获取NMS物品未本地化时的语言key + * @param pItemStack 物品 + * @return 物品语言key + */ + public String getUnlocalizedName(ItemStack pItemStack){ + if(pItemStack==null) return ""; + Object NMSItemStack=ClassHelper.invokeStaticMethod(NMSHelper.method_CraftItemStack_asNMSCopy,pItemStack); + if(NMSItemStack==null) return ""; + return (String)ClassHelper.invokeMethod(NMSItemStack,method_NMSItemStack_getUnlocalizedName); + } + + private String getStringLocalization(String pKey,String pLang){ + return (String)ClassHelper.invokeMethod(instance_LanguageRegistry,method_LanguageRegistry_getStringLocalization,new Object[]{pKey,pLang}); + } + +} diff --git a/src/main/java/cc/util/bossshop/NMSHelper.java b/src/main/java/cc/util/bossshop/NMSHelper.java new file mode 100644 index 0000000..633dd9e --- /dev/null +++ b/src/main/java/cc/util/bossshop/NMSHelper.java @@ -0,0 +1,254 @@ +package cc.util.bossshop; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +/** + * 一个用于获取NMS类的类 + * @author 聪聪 + * + */ +public class NMSHelper{ + + /** + * NMS类 + */ + public static final Class clazz_EntityPlayerMP; + public static final Class clazz_EntityPlayer; + public static final Class clazz_NMSItemStack; + public static final Class clazz_NMSWorld; + public static final Class clazz_IInventory; + public static final Class clazz_NBTBase; + public static final Class clazz_NBTTagByte; + public static final Class clazz_NBTTagShort; + public static final Class clazz_NBTTagInt; + public static final Class clazz_NBTTagLong; + public static final Class clazz_NBTTagFloat; + public static final Class clazz_NBTTagDouble; + public static final Class clazz_NBTTagByteArray; + public static final Class clazz_NBTTagString; + public static final Class clazz_NBTTagIntArray; + public static final Class clazz_NBTTagList; + public static final Class clazz_NBTTagCompound; + public static final Method method_CraftItemStack_asNMSCopy; + public static final Method method_CraftItemStack_asCraftMirror; + public static final Method method_CraftPlayer_getHandle; + public static final Method method_NMSItemStack_getTag; + //public static final Method method_NMSItemStack_setTag;//此方法歧义较大不建议启用 + public static final Method method_NBTTagCompound_isEmpty; + public static final Method method_NBTTagCompound_clone; + public static final Method method_NBTTagCompound_set; + public static final Method method_NBTTagList_add; + public static final Field field_NBTTagString_value; + public static final Field field_NBTTagByte_value; + public static final Field field_NBTTagShort_value; + public static final Field field_NBTTagInt_value; + public static final Field field_NBTTagFloat_value; + public static final Field field_NBTTagDouble_value; + public static final Field field_NBTTagLong_value; + public static final Field field_NBTTagList_value; + public static final Field field_NBTTagCompound_map; + public static final Field field_NMSItemStack_tag; + /** + * CraftBukkit类 + */ + public static final Class clazz_CraftItemStack; + public static final Class clazz_CraftInventory; + public static final Class clazz_CraftPlayer; + + static{ + String packetPath=""; + + clazz_CraftItemStack=ClassHelper.getCBTClass("inventory.CraftItemStack"); + clazz_CraftInventory=ClassHelper.getCBTClass("inventory.CraftInventory"); + Method method_CraftInventory_getInventory=ClassHelper.getMethod(clazz_CraftInventory,"getInventory"); + clazz_IInventory=method_CraftInventory_getInventory.getReturnType(); + clazz_CraftPlayer=ClassHelper.getCBTClass("entity.CraftPlayer"); + method_CraftPlayer_getHandle=ClassHelper.getMethod(clazz_CraftPlayer,"getHandle"); + clazz_EntityPlayerMP=method_CraftPlayer_getHandle.getReturnType(); + clazz_EntityPlayer=clazz_EntityPlayerMP.getSuperclass(); + clazz_NMSWorld=clazz_EntityPlayer.getDeclaredConstructors()[0].getParameterTypes()[0]; + + //NMS ItemStck + ItemStack tItem=new ItemStack(Material.STONE); + method_CraftItemStack_asNMSCopy=ClassHelper.getMethod(clazz_CraftItemStack,"asNMSCopy",ItemStack.class); + Object tNMSItemStack_mcitem=ClassHelper.invokeStaticMethod(method_CraftItemStack_asNMSCopy,tItem); + clazz_NMSItemStack=tNMSItemStack_mcitem.getClass(); + method_NMSItemStack_getTag=ClassHelper.getUnknowMethod(clazz_NMSItemStack,"NBTTagCompound").get(0); + + //NBT + clazz_NBTTagCompound=method_NMSItemStack_getTag.getReturnType(); + packetPath=NMSHelper.getClassPrefix(clazz_NBTTagCompound.getName()); + clazz_NBTBase=ClassHelper.getClass(packetPath+"NBTBase"); + clazz_NBTTagByte=ClassHelper.getClass(packetPath+"NBTTagByte"); + clazz_NBTTagShort=ClassHelper.getClass(packetPath+"NBTTagShort"); + clazz_NBTTagInt=ClassHelper.getClass(packetPath+"NBTTagInt"); + clazz_NBTTagLong=ClassHelper.getClass(packetPath+"NBTTagLong"); + clazz_NBTTagFloat=ClassHelper.getClass(packetPath+"NBTTagFloat"); + clazz_NBTTagDouble=ClassHelper.getClass(packetPath+"NBTTagDouble"); + clazz_NBTTagByteArray=ClassHelper.getClass(packetPath+"NBTTagByteArray"); + clazz_NBTTagString=ClassHelper.getClass(packetPath+"NBTTagString"); + clazz_NBTTagIntArray=ClassHelper.getClass(packetPath+"NBTTagIntArray"); + clazz_NBTTagList=ClassHelper.getClass(packetPath+"NBTTagList"); + + method_NBTTagCompound_isEmpty=ClassHelper.getUnknowMethod(clazz_NBTTagCompound,boolean.class).get(0); + method_NBTTagCompound_clone=ClassHelper.getUnknowMethod(clazz_NBTTagCompound,clazz_NBTBase).get(0); + method_NBTTagCompound_set=ClassHelper.getUnknowMethod(clazz_NBTTagCompound,void.class,new Class[]{String.class,clazz_NBTBase}).get(0); + method_CraftItemStack_asCraftMirror=ClassHelper.getMethod(clazz_CraftItemStack,"asCraftMirror",clazz_NMSItemStack); + method_NBTTagList_add=ClassHelper.getUnknowMethod(clazz_NBTTagList,void.class,clazz_NBTBase).get(0); + + field_NBTTagByte_value=ClassHelper.getField(clazz_NBTTagByte,byte.class,-1).get(0); + field_NBTTagShort_value=ClassHelper.getField(clazz_NBTTagShort,short.class,-1).get(0); + field_NBTTagInt_value=ClassHelper.getField(clazz_NBTTagInt,int.class,-1).get(0); + field_NBTTagFloat_value=ClassHelper.getField(clazz_NBTTagFloat,float.class,-1).get(0); + field_NBTTagDouble_value=ClassHelper.getField(clazz_NBTTagDouble,double.class,-1).get(0); + field_NBTTagLong_value=ClassHelper.getField(clazz_NBTTagLong,long.class,-1).get(0); + field_NBTTagString_value=ClassHelper.getField(clazz_NBTTagString,String.class,Modifier.PRIVATE).get(0); + field_NBTTagList_value=ClassHelper.getField(clazz_NBTTagList,List.class,-1).get(0); + field_NBTTagCompound_map=ClassHelper.getField(clazz_NBTTagCompound,Map.class,-1).get(0); + field_NMSItemStack_tag=ClassHelper.getField(clazz_NMSItemStack,clazz_NBTTagCompound,-1).get(0); + + } + + /** + * 获取类全名的前段字符串 + * @param classFullName + * @return + */ + public static String getClassPrefix(String classFullName){ + String packetPath=""; + int pos=-1; + if((pos=classFullName.lastIndexOf("."))!=-1) + packetPath=classFullName.substring(0,pos+1); + return packetPath; + } + + public static Set getNBTFirstDeepNodeList(ItemStack pItem){ + Object nbtTag=getNBTTagCompound(pItem); + if(nbtTag==null) return null; + Map tMap=getNBTTagMapFromTag(nbtTag); + Set rSet=new HashSet<>(); + for(String skey : tMap.keySet()) + rSet.add(skey); + return rSet; + } + + /** + * 获取Bukkit物品的NBT标签 + * @param pItem Bukkit物品实例 + * @return NBTTagCompound实例或者null + */ + public static Object getNBTTagCompound(ItemStack pItem){ + Object nmsItem=NMSHelper.getNMSItem(pItem); + if(nmsItem==null) return null; + return ClassHelper.invokeMethod(nmsItem,method_NMSItemStack_getTag); + } + + /** + * 获取物品的NBT的的值域Map + * @param pItem 物品,可以为null + * @return 可能为null + */ + public static Map getNBTTagMapFromItem(ItemStack pItem){ + return getNBTTagMapFromTag(getNBTTagCompound(pItem)); + } + + /** + * 获取Bukkit物品对应的MC物品 + * @param pItem Bukkit物品实例 + * @return NBTTagCompound实例或者null + */ + public static Object getNMSItem(ItemStack pItem){ + if(pItem==null||pItem.getType()==Material.AIR) return null; + return ClassHelper.invokeStaticMethod(method_CraftItemStack_asNMSCopy,pItem); + } + + /** + * 获取类类型为NBTTagCompound的值域Map + * @param pNBTTagCompound 实例,类型必须为NBTTagCompound,可以为null + * @return 可能为非null + */ + public static Map getNBTTagMapFromTag(Object pNBTTagCompound){ + if(pNBTTagCompound==null) return new HashMap<>(); + return (Map)ClassHelper.getFieldValue(pNBTTagCompound,field_NBTTagCompound_map); + } + + /** + * 从NBTTagCompound实例中移除指定的标签 + * @param pNBTTagCompound NBTTagCompound实例 + * @param pNBTLabel NBT标签 + * @return 移除的值或null + */ + public static Object removeFromNBTTagCompound(Object pNBTTagCompound,String pNBTLabel){ + Map tMap=NMSHelper.getNBTTagMapFromTag(pNBTTagCompound); + if(tMap==null) return null; + return tMap.remove(pNBTLabel); + } + + /** + * 获取获取物品的Json字符串 + */ + public static String getItemJson(ItemStack pItem){ + if(pItem==null||pItem.getType()==Material.AIR) return "{}"; + String itemJson="{"; + itemJson+="id:"+pItem.getTypeId(); + itemJson+=",Damage:"+pItem.getDurability(); + Object tagNBTTagCompound=getNBTTagCompound(pItem); + if(tagNBTTagCompound!=null) itemJson+=",tag:"+getNBTBaseValue(tagNBTTagCompound); + itemJson+="}"; + return itemJson; + } + + /** + * 获取NBTBase的子类的Json值 + *

+ * 只对NBTTagString的toString方法重写
+ * 其他方法不变 + *

+ * @param pNBTBase + * @return json字符串 + */ + public static String getNBTBaseValue(Object pNBTBase){ + if(pNBTBase==null) return ""; + if(NMSHelper.clazz_NBTTagCompound.isInstance(pNBTBase)){ + Map tNBTContents=(Map)ClassHelper.getFieldValue(pNBTBase,NMSHelper.field_NBTTagCompound_map); + if(tNBTContents==null||tNBTContents.isEmpty()) return "{}"; + String contentJson = "{"; + for (String sKey : tNBTContents.keySet()) { + Object tContentNode=tNBTContents.get(sKey); + if(tContentNode==null) continue; + contentJson+=sKey+':'+getNBTBaseValue(tContentNode)+','; + } + if(contentJson.lastIndexOf(",")!=-1) + contentJson=contentJson.substring(0,contentJson.length()-1); + return contentJson + "}"; + }else if(NMSHelper.clazz_NBTTagList.isInstance(pNBTBase)){ + List tNBTContents=(List)ClassHelper.getFieldValue(pNBTBase,NMSHelper.field_NBTTagList_value); + if(tNBTContents==null||tNBTContents.isEmpty()) return "[]"; + String contentJson = "["; + int i=0; + for(Object tContentNode : tNBTContents){ + if(tContentNode==null) continue; + contentJson+=i+":"+getNBTBaseValue(tContentNode).toString()+','; + i++; + } + if(contentJson.lastIndexOf(",")!=-1) + contentJson=contentJson.substring(0,contentJson.length()-1); + return contentJson+"]"; + }else if(NMSHelper.clazz_NBTTagString.isInstance(pNBTBase)){ + String value=(String)ClassHelper.getFieldValue(pNBTBase,NMSHelper.field_NBTTagString_value); + value=value.replace("\"","\\\""); + return "\""+value+"\""; + }else return pNBTBase.toString(); + } + +} diff --git a/src/main/java/cc/util/bossshop/config/CommentedConfiguration.java b/src/main/java/cc/util/bossshop/config/CommentedConfiguration.java new file mode 100644 index 0000000..2d3ab38 --- /dev/null +++ b/src/main/java/cc/util/bossshop/config/CommentedConfiguration.java @@ -0,0 +1,96 @@ +package cc.util.bossshop.config; + +import java.util.Map; + +import org.apache.commons.lang.Validate; +import org.bukkit.configuration.Configuration; + +/** + * + * 支持注释的配置管理器 + * + */ +public class CommentedConfiguration extends CommentedSection implements Configuration{ + + protected CommentedConfiguration mDefaults; + protected CommentedOptions mOptions; + + + public CommentedConfiguration(){} + + /** + * 为节点添加一个默认值,如果值不存在,将会直接设置到配置其中 + */ + @Override + public void addDefault(String pPath,Object pValue){ + Validate.notNull(pPath,"Path may not be null"); + if(mDefaults==null){ + mDefaults=new CommentedConfiguration(); + } + if(!this.contains(pPath)) this.set(pPath,pValue); + else mDefaults.set(pPath,pValue); + } + + public void addDefault(String pPath,Object pValue,String...pComments){ + this.addDefault(pPath,pValue); + this.addDefaultComments(pPath,pComments); + } + + @Override + public void addDefaults(Map pDefaults){ + Validate.notNull(pDefaults,"默认配置不能为空"); + + for(Map.Entry entry : pDefaults.entrySet()){ + this.addDefault(entry.getKey(),entry.getValue()); + } + } + + @Override + public void addDefaults(Configuration pDefaults){ + Validate.notNull(pDefaults,"默认配置不能为空"); + this.addDefaults(pDefaults.getValues(true)); + } + + @Override + public void setDefaults(Configuration pDefaults){ + if(pDefaults instanceof CommentedConfiguration){ + this.mDefaults=(CommentedConfiguration)pDefaults; + }else{ + this.addDefaults(pDefaults.getValues(true)); + } + } + + @Override + public CommentedConfiguration getDefaults(){ + return this.mDefaults; + } + + @Override + public CommentedSection getParent(){ + return null; + } + + @Override + public CommentedOptions options(){ + if(mOptions==null){ + mOptions=new CommentedOptions(this); + } + return mOptions; + } + + /** + * 设置输入路径的分割用字符,默认为"." + * @param value 作为分割路径用的字符 + */ + public void pathSeparator(char value){ + this.options().pathSeparator(value); + } + + /** + * 获取路径分割用字符 + * @return 分割路径用的字符 + */ + public char pathSeparator(){ + return this.options().pathSeparator(); + } +} diff --git a/src/main/java/cc/util/bossshop/config/CommentedOptions.java b/src/main/java/cc/util/bossshop/config/CommentedOptions.java new file mode 100644 index 0000000..172ec53 --- /dev/null +++ b/src/main/java/cc/util/bossshop/config/CommentedOptions.java @@ -0,0 +1,17 @@ +package cc.util.bossshop.config; + +import org.bukkit.configuration.ConfigurationOptions; + + +public class CommentedOptions extends ConfigurationOptions{ + + protected CommentedOptions(CommentedConfiguration configuration){ + super(configuration); + } + + public String getPathSeparatorRegx(){ + char split=this.pathSeparator(); + return "(? map=new LinkedHashMap<>(); + private final CommentedConfiguration root; + private final CommentedSection parent; + private final String path; + private final String fullPath; + + protected CommentedSection(){ + if(!(this instanceof CommentedConfiguration)) throw new IllegalStateException("Cannot construct a root MemorySection when not a Configuration"); + this.path=""; + this.fullPath=""; + this.parent=null; + this.root=(CommentedConfiguration)this; + } + + protected CommentedSection(CommentedSection parent,String path){ + Validate.notNull(parent,"父节点不能为null"); + Validate.notNull(path,"路径不能为null"); + + this.path=path; + this.parent=parent; + this.root=parent.getRoot(); + Validate.notNull(path,"父节点的根节点不能为空"); + this.fullPath=createPath(parent,path); + } + + @Override + public Set getKeys(boolean deep){ + Set result=new LinkedHashSet(); + + Configuration root=getRoot(); + if(root!=null&&root.options().copyDefaults()){ + ConfigurationSection defaults=getDefaultSection(); + if(defaults!=null){ + result.addAll(defaults.getKeys(deep)); + } + } + mapChildrenKeys(result,this,deep); + + return result; + } + + @Override + public Map getValues(boolean deep){ + Map result=new LinkedHashMap(); + + CommentedConfiguration root=getRoot(); + if(root!=null&&root.options().copyDefaults()){ + ConfigurationSection defaults=getDefaultSection(); + if(defaults!=null){ + result.putAll(defaults.getValues(deep)); + } + } + this.mapChildrenValues(result,this,deep); + return result; + } + + @Override + public boolean contains(String path){ + return this.get(path)!=null; + } + + @Override + public boolean isSet(String path){ + return true; + } + + public String getCurrentPath(){ + return this.fullPath; + } + + public String getName(){ + return this.path; + } + + @Override + public CommentedConfiguration getRoot(){ + return this.root; + } + + @Override + public CommentedSection getParent(){ + return this.parent; + } + + @Override + public Object get(String pPath){ + Validate.notNull(pPath,"获取值的路径不能为null"); + String separator=root.options().getPathSeparatorRegx(); + String[] childPathes=pPath.split(separator); + if(childPathes.length==0) return this; + + CommentedSection section=this; + for(int i=0;i pMapContents){ + CommentedSection section=this.createSection(pPath); + + for(Map.Entry entry : pMapContents.entrySet()){ + if(entry.getValue() instanceof Map){ + section.createSection(entry.getKey().toString(),(Map)entry.getValue()); + }else{ + section.set(entry.getKey().toString(),entry.getValue()); + } + } + + return section; + } + + @Override + public String getString(String path){ + Object def=this.getDefault(path); + return getString(path,def!=null?def.toString():null); + } + + @Override + public String getString(String path,String def){ + Object val=this.get(path,def); + return (val!=null)?val.toString():def; + } + + @Override + public boolean isString(String path){ + Object val=this.get(path); + return val instanceof String; + } + + public int getInt(String path){ + Object def=getDefault(path); + return getInt(path,(def instanceof Number)?NumberConversions.toInt(def):0); + } + + public int getInt(String path,int def){ + Object val=get(path,def); + return (val instanceof Number)?NumberConversions.toInt(val):def; + } + + public boolean isInt(String path){ + Object val=get(path); + return val instanceof Integer; + } + + public boolean getBoolean(String path){ + Object def=getDefault(path); + return getBoolean(path,(def instanceof Boolean)?(Boolean)def:false); + } + + public boolean getBoolean(String path,boolean def){ + Object val=get(path,def); + return (val instanceof Boolean)?(Boolean)val:def; + } + + public boolean isBoolean(String path){ + Object val=get(path); + return val instanceof Boolean; + } + + public double getDouble(String path){ + Object def=getDefault(path); + return getDouble(path,(def instanceof Number)?NumberConversions.toDouble(def):0); + } + + public double getDouble(String path,double def){ + Object val=get(path,def); + return (val instanceof Number)?NumberConversions.toDouble(val):def; + } + + public boolean isDouble(String path){ + Object val=get(path); + return val instanceof Double; + } + + public long getLong(String path){ + Object def=getDefault(path); + return getLong(path,(def instanceof Number)?NumberConversions.toLong(def):0); + } + + public long getLong(String path,long def){ + Object val=get(path,def); + return (val instanceof Number)?NumberConversions.toLong(val):def; + } + + public boolean isLong(String path){ + Object val=get(path); + return val instanceof Long; + } + + // Java + public List getList(String path){ + Object def=getDefault(path); + return getList(path,(def instanceof List)?(List)def:null); + } + + public List getList(String path,List def){ + Object val=get(path,def); + return (List)((val instanceof List)?val:def); + } + + public List> getMapList(String path){ + List list=getList(path); + List> result=new ArrayList>(); + + if(list==null){ + return result; + } + + for(Object object : list){ + if(object instanceof Map){ + result.add((Map)object); + } + } + + return result; + } + + public boolean isList(String path){ + Object val=get(path); + return val instanceof List; + } + + public List getStringList(String path){ + List list=getList(path); + + if(list==null){ + return new ArrayList(0); + } + + List result=new ArrayList(); + + for(Object object : list){ + if((object instanceof String)||(isPrimitiveWrapper(object))){ + result.add(String.valueOf(object)); + } + } + + return result; + } + + public List getIntegerList(String path){ + List list=getList(path); + + if(list==null){ + return new ArrayList(0); + } + + List result=new ArrayList(); + + for(Object object : list){ + if(object instanceof Integer){ + result.add((Integer)object); + }else if(object instanceof String){ + try{ + result.add(Integer.valueOf((String)object)); + }catch(Exception ex){} + }else if(object instanceof Character){ + result.add((int)((Character)object).charValue()); + }else if(object instanceof Number){ + result.add(((Number)object).intValue()); + } + } + + return result; + } + + public List getBooleanList(String path){ + List list=getList(path); + + if(list==null){ + return new ArrayList(0); + } + + List result=new ArrayList(); + + for(Object object : list){ + if(object instanceof Boolean){ + result.add((Boolean)object); + }else if(object instanceof String){ + if(Boolean.TRUE.toString().equals(object)){ + result.add(true); + }else if(Boolean.FALSE.toString().equals(object)){ + result.add(false); + } + } + } + + return result; + } + + public List getDoubleList(String path){ + List list=getList(path); + + if(list==null){ + return new ArrayList(0); + } + + List result=new ArrayList(); + + for(Object object : list){ + if(object instanceof Double){ + result.add((Double)object); + }else if(object instanceof String){ + try{ + result.add(Double.valueOf((String)object)); + }catch(Exception ex){} + }else if(object instanceof Character){ + result.add((double)((Character)object).charValue()); + }else if(object instanceof Number){ + result.add(((Number)object).doubleValue()); + } + } + + return result; + } + + public List getFloatList(String path){ + List list=getList(path); + + if(list==null){ + return new ArrayList(0); + } + + List result=new ArrayList(); + + for(Object object : list){ + if(object instanceof Float){ + result.add((Float)object); + }else if(object instanceof String){ + try{ + result.add(Float.valueOf((String)object)); + }catch(Exception ex){} + }else if(object instanceof Character){ + result.add((float)((Character)object).charValue()); + }else if(object instanceof Number){ + result.add(((Number)object).floatValue()); + } + } + + return result; + } + + public List getLongList(String path){ + List list=getList(path); + + if(list==null){ + return new ArrayList(0); + } + + List result=new ArrayList(); + + for(Object object : list){ + if(object instanceof Long){ + result.add((Long)object); + }else if(object instanceof String){ + try{ + result.add(Long.valueOf((String)object)); + }catch(Exception ex){} + }else if(object instanceof Character){ + result.add((long)((Character)object).charValue()); + }else if(object instanceof Number){ + result.add(((Number)object).longValue()); + } + } + + return result; + } + + public List getByteList(String path){ + List list=getList(path); + + if(list==null){ + return new ArrayList(0); + } + + List result=new ArrayList(); + + for(Object object : list){ + if(object instanceof Byte){ + result.add((Byte)object); + }else if(object instanceof String){ + try{ + result.add(Byte.valueOf((String)object)); + }catch(Exception ex){} + }else if(object instanceof Character){ + result.add((byte)((Character)object).charValue()); + }else if(object instanceof Number){ + result.add(((Number)object).byteValue()); + } + } + + return result; + } + + public List getCharacterList(String path){ + List list=getList(path); + + if(list==null){ + return new ArrayList(0); + } + + List result=new ArrayList(); + + for(Object object : list){ + if(object instanceof Character){ + result.add((Character)object); + }else if(object instanceof String){ + String str=(String)object; + + if(str.length()==1){ + result.add(str.charAt(0)); + } + }else if(object instanceof Number){ + result.add((char)((Number)object).intValue()); + } + } + + return result; + } + + public List getShortList(String path){ + List list=getList(path); + + if(list==null){ + return new ArrayList(0); + } + + List result=new ArrayList(); + + for(Object object : list){ + if(object instanceof Short){ + result.add((Short)object); + }else if(object instanceof String){ + try{ + result.add(Short.valueOf((String)object)); + }catch(Exception ex){} + }else if(object instanceof Character){ + result.add((short)((Character)object).charValue()); + }else if(object instanceof Number){ + result.add(((Number)object).shortValue()); + } + } + + return result; + } + + public Vector getVector(String path){ + Object def=getDefault(path); + return getVector(path,(def instanceof Vector)?(Vector)def:null); + } + + public Vector getVector(String path,Vector def){ + Object val=get(path,def); + return (val instanceof Vector)?(Vector)val:def; + } + + public boolean isVector(String path){ + Object val=get(path); + return val instanceof Vector; + } + + public OfflinePlayer getOfflinePlayer(String path){ + Object def=getDefault(path); + return getOfflinePlayer(path,(def instanceof OfflinePlayer)?(OfflinePlayer)def:null); + } + + public OfflinePlayer getOfflinePlayer(String path,OfflinePlayer def){ + Object val=get(path,def); + return (val instanceof OfflinePlayer)?(OfflinePlayer)val:def; + } + + public boolean isOfflinePlayer(String path){ + Object val=get(path); + return val instanceof OfflinePlayer; + } + + public ItemStack getItemStack(String path){ + Object def=getDefault(path); + return getItemStack(path,(def instanceof ItemStack)?(ItemStack)def:null); + } + + public ItemStack getItemStack(String path,ItemStack def){ + Object val=get(path,def); + return (val instanceof ItemStack)?(ItemStack)val:def; + } + + public boolean isItemStack(String path){ + Object val=get(path); + return val instanceof ItemStack; + } + + public Color getColor(String path){ + Object def=getDefault(path); + return getColor(path,(def instanceof Color)?(Color)def:null); + } + + public Color getColor(String path,Color def){ + Object val=get(path,def); + return (val instanceof Color)?(Color)val:def; + } + + public boolean isColor(String path){ + Object val=get(path); + return val instanceof Color; + } + + @Override + public CommentedSection getConfigurationSection(String path){ + Object val=get(path,null); + if(val!=null){ + return (val instanceof CommentedSection)?(CommentedSection)val:null; + } + + val=get(path,getDefault(path)); + return (val instanceof ConfigurationSection)?createSection(path):null; + } + + @Override + public boolean isConfigurationSection(String path){ + Object val=get(path); + return val instanceof CommentedSection; + } + + protected boolean isPrimitiveWrapper(Object input){ + return input instanceof Integer||input instanceof Boolean||input instanceof Character||input instanceof Byte||input instanceof Short||input instanceof Double||input instanceof Long||input instanceof Float; + } + + @Override + public void addDefault(String path,Object value){ + Validate.notNull(path,"要添加默认值的路径不能为null"); + if(root==this){ + throw new UnsupportedOperationException("不支持的方法addDefault(String, Object)"); + } + root.addDefault(createPath(this,path),value); + } + + public boolean contains(String arg0,boolean arg1){ + return this.get(path)!=null; + } + + @Override + public CommentedSection getDefaultSection(){ + CommentedConfiguration root=getRoot(); + CommentedConfiguration defaults=root==null?null:root.getDefaults(); + + if(defaults!=null){ + if(defaults.isConfigurationSection(getCurrentPath())){ + return defaults.getConfigurationSection(getCurrentPath()); + } + } + return null; + } + + protected Object getDefault(String path){ + Validate.notNull(path,"获取默认值的路径不能为null"); + + CommentedConfiguration root=this.getRoot(); + CommentedConfiguration defaults=root==null?null:root.getDefaults(); + return (defaults==null)?null:defaults.get(createPath(this,path)); + } + + protected void mapChildrenKeys(Set output,CommentedSection section,boolean deep){ + if(section instanceof CommentedSection){ + CommentedSection sec=(CommentedSection)section; + + for(Map.Entry entry : sec.map.entrySet()){ + output.add(createPath(section,entry.getKey(),this)); + if((deep)&&(entry.getValue().getValue() instanceof CommentedSection)){ + CommentedSection subsection=(CommentedSection)entry.getValue().getValue(); + mapChildrenKeys(output,subsection,deep); + } + } + }else{ + Set keys=section.getKeys(deep); + for(String key : keys){ + output.add(createPath(section,key,this)); + } + } + } + + protected void mapChildrenValues(Map output,CommentedSection section,boolean deep){ + if(section instanceof CommentedSection){ + CommentedSection sec=(CommentedSection)section; + + for(Map.Entry entry : sec.map.entrySet()){ + output.put(createPath(section,entry.getKey(),this),entry.getValue().getValue()); + if(entry.getValue().getValue() instanceof CommentedSection){ + if(deep){ + mapChildrenValues(output,(CommentedSection)entry.getValue().getValue(),deep); + } + } + } + }else{ + Map values=section.getValues(deep); + + for(Map.Entry entry : values.entrySet()){ + output.put(createPath(section,entry.getKey(),this),entry.getValue()); + } + } + } + + public static String createPath(CommentedSection section,String key){ + return createPath(section,key,(section==null)?null:section.getRoot()); + } + + public static String createPath(CommentedSection section,String key,CommentedSection relativeTo){ + Validate.notNull(section,"不能从null节点创建路径"); + + CommentedConfiguration root=section.getRoot(); + char separator=root.options().pathSeparator(); + + StringBuilder builder=new StringBuilder(); + if(section!=null){ + for(ConfigurationSection parent=section;(parent!=null)&&(parent!=relativeTo);parent=parent.getParent()){ + if(builder.length()>0){ + builder.insert(0,separator); + } + + builder.insert(0,parent.getName()); + } + } + + if((key!=null)&&(key.length()>0)){ + if(builder.length()>0){ + builder.append(separator); + } + + builder.append(key); + } + + return builder.toString(); + } + + @Override + public String toString(){ + CommentedConfiguration root=this.getRoot(); + StringBuilder builder=new StringBuilder("{path='"+getCurrentPath()+"'"); + builder.append(", root='").append(root==null?null:root.getClass().getSimpleName()); + builder.append(", MapValue=").append(this.map).append("}"); + return builder.toString(); + } + + @Override + public void addComments(String pPath,String...pComments){ + CommentedValue tValue=this.getCommentedValue(pPath); + if(tValue!=null) tValue.addComments(pComments); + else this.setComments(pPath,pComments); + } + + @Override + public void addDefaultComments(String pPath,String...pComments){ + CommentedValue tValue=this.getCommentedValue(pPath); + if(tValue!=null) tValue.addDefaultComments(pComments); + else this.setComments(pPath,pComments); + } + + + @Override + public void setComments(String pPath,String...pComments){ + CommentedValue tValue=this.getCommentedValue(pPath); + if(tValue!=null) tValue.setComments(pComments); + else if(pComments!=null) this.set(pPath,Function.asList(pComments)); + } + + public void setComments(String pPath,Collection pComments){ + CommentedValue tValue=this.getCommentedValue(pPath); + if(tValue!=null) tValue.setComments(pComments); + else if(pComments!=null){ + CommentedSection newSec=this.createSection(pPath); + tValue=newSec.getParent().map.get(newSec.path); + tValue.setComments(pComments); + } + } + + @Override + public ArrayList getComments(String pPath){ + CommentedValue tValue=this.getCommentedValue(pPath); + if(tValue!=null) return tValue.getComments(); + else return new ArrayList<>(); + } + + @Override + public boolean hasComments(String pPath){ + CommentedValue tValue=this.getCommentedValue(pPath); + if(tValue!=null) return tValue.hasComments(); + else return false; + } + + @Override + public ArrayList clearComments(String pPath){ + CommentedValue tValue=this.getCommentedValue(pPath); + if(tValue!=null) return tValue.clearComments(); + else return new ArrayList<>(); + } + +} diff --git a/src/main/java/cc/util/bossshop/config/CommentedValue.java b/src/main/java/cc/util/bossshop/config/CommentedValue.java new file mode 100644 index 0000000..24755e7 --- /dev/null +++ b/src/main/java/cc/util/bossshop/config/CommentedValue.java @@ -0,0 +1,207 @@ +package cc.util.bossshop.config; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.regex.Pattern; + +import org.bukkit.configuration.ConfigurationSection; + +import cc.util.bossshop.Function; + +/** + * 一个值包装类,用来保存注释 + * List值会循环嵌套包装 + * @author 聪聪 + * + */ +public class CommentedValue{ + + /**注释行匹配器*/ + protected final static Pattern COMMENT=Pattern.compile("^([ ]*)# ?(.*?)$"); + /**值包装类的真实值,或是List的嵌套*/ + private Object mValue; + /**注释,请不要直接操作注释*/ + private ArrayList mComments=null; + + private CommentedValue(){} + + /** + * 构造一个包装类并用指定的值初始化 + * @param pParent 该值的父节点,用于值转换时使用 + * @param pValue 值,非null + */ + private CommentedValue(CommentedSection pParent,Object pValue){ + this.setValue(pParent,pValue); + } + + /** + * 设置包装类的值 + * @param pParent 该值的父节点,用于值转换时使用 + * @param pValue 值,非null + */ + public void setValue(CommentedSection pParent,Object pValue){ + if(pValue instanceof CommentedValue){ + CommentedValue cvValue=(CommentedValue)pValue; + this.mValue=CommentedValue.convertValue(pParent,cvValue.mValue); + this.setComments(cvValue.getComments()); + } + this.mValue=CommentedValue.convertValue(pParent,pValue); + } + + /** + * 获取包装的值 + */ + public Object getValue(){ + return this.mValue; + } + + /** + * 添加注释 + * @param pComments 注释 + */ + public void addComments(String...pComments){ + if(pComments==null||pComments.length==0) return; + List addComments=Function.asList(pComments); + addComments.addAll(this.getComments()); + this.setComments(addComments); + } + + /** + * 添加默认注释 + *

如果注释已经存在,将忽略

+ * @param pComments 评论 + */ + public void addDefaultComments(String...pComments){ + if(pComments==null||pComments.length==0) return; + if(!this.hasComments()) + this.setComments(Function.asList(pComments)); + } + + /** + * 设置注释 + *

+ * 请勿设置null来清空注释
+ * 如果要清空注释,请使用{@link CommentedValue#clearComments()} + *

/ + * @param pComments 注释 + */ + public void setComments(String...pComments){ + this.setComments(Function.asList(pComments)); + } + + /** + * 添加集合中的注释到指定的路径 + *

+ * 此函数中会对每行注释进行过滤与格式化 + *

+ * @param pComments 注释 + */ + protected void setComments(Collection pComments){ + if(this.mComments==null) this.mComments=new ArrayList<>(); + else this.mComments.clear(); + + ArrayList addComments=new ArrayList<>(); + for(String sLine : pComments){ //过滤null行,格式化注释 + if(sLine==null) continue; + java.util.regex.Matcher tMatcher=COMMENT.matcher(sLine); + if(!tMatcher.find()) addComments.add(sLine); + else addComments.add(tMatcher.group(2)); + } + this.mComments.addAll(pComments); + } + + /** + * 获取指定路径下的注释的拷贝 + * @return 非null + */ + public ArrayList getComments(){ + ArrayList copyComments=new ArrayList<>(); + if(this.mComments!=null){ + copyComments.addAll(this.mComments); + } + return copyComments; + } + + /** + * 节点是否有注释 + */ + public boolean hasComments(){ + return !this.getComments().isEmpty(); + } + + /** + * 清空指定路径下的注释 + * @return 清理掉的注释,非null + */ + public ArrayList clearComments(){ + ArrayList removedComments=new ArrayList<>(); + if(this.mComments!=null){ + removedComments.addAll(this.getComments()); + this.mComments.clear(); + } + return removedComments; + } + + /** + * 包装一个值 + * @param pParent 该值的父节点 + * @param pValue 值内容,非null + * @return 包装后的值 + */ + protected static CommentedValue wrapperValue(CommentedSection pParent,Object pValue){ + CommentedValue cvValue=null; + if(pValue instanceof CommentedValue){ + cvValue=(CommentedValue)pValue; + cvValue.mValue=CommentedValue.convertValue(pParent,cvValue.mValue); + }else{ + cvValue=new CommentedValue(); + cvValue.mValue=CommentedValue.convertValue(pParent,pValue); + } + return cvValue; + } + + /** + * 转换值,同时会调整父子节点结构 + * @param pParent 父节点 + * @param pValue 值,非null,非CommentedValue + * @return 转换后的值 + */ + private static Object convertValue(CommentedSection pParent,Object pValue){ + if(pValue==null) + throw new IllegalArgumentException("值包装类不能包装null值"); + if(pValue instanceof CommentedValue){ + throw new IllegalArgumentException("值包装类不能包装值包装类实例"); + }else if(pValue instanceof CommentedSection){ + // 此处需要对节点的从属关系进行调整 + CommentedSection cmtedSec=(CommentedSection)pValue; + if(cmtedSec.getParent()==pParent) return cmtedSec; + CommentedSection newCmtedSec=pParent.createSection(cmtedSec.getName()); + for(String childKey : cmtedSec.map.keySet()){ + Object cfgSecValue=cmtedSec.map.get(childKey); + if(cfgSecValue==null) continue; + newCmtedSec.map.put(childKey,CommentedValue.wrapperValue(newCmtedSec,cfgSecValue)); + } + return newCmtedSec; + }else if(pValue instanceof ConfigurationSection){ + // 转换成CommentedSection + ConfigurationSection cfgSec=(ConfigurationSection)pValue; + CommentedSection newSec=new CommentedSection(); + for(String childKey : cfgSec.getKeys(false)){ + Object cfgSecValue=cfgSec.get(childKey); + if(cfgSecValue==null) continue; + newSec.map.put(childKey,CommentedValue.wrapperValue(newSec,cfgSecValue)); + } + } + return pValue; + } + + @Override + public String toString(){ + String str="{Comments:"; + if(this.mComments!=null) str+=this.mComments; + else str+="[]"; + return str+",Value:"+this.mValue+"}"; + } + +} diff --git a/src/main/java/cc/util/bossshop/config/CommentedYamlConfig.java b/src/main/java/cc/util/bossshop/config/CommentedYamlConfig.java new file mode 100644 index 0000000..97ce047 --- /dev/null +++ b/src/main/java/cc/util/bossshop/config/CommentedYamlConfig.java @@ -0,0 +1,410 @@ +package cc.util.bossshop.config; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConstructor; +import org.bukkit.configuration.file.YamlRepresenter; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.error.YAMLException; +import org.yaml.snakeyaml.representer.Representer; + +import cc.util.bossshop.FBukkit; +import cc.util.bossshop.config.YamlNode.ChildType; + +/** + * 支持注释的Yaml文件配置管理器 + * @author 聪聪 + * + */ +public class CommentedYamlConfig extends CommentedConfiguration{ + + protected static final String BLANK_CONFIG="{}\n"; + protected final static Pattern CFG_NODE=Pattern.compile("^([ ]*)(?!- | |#)(.*?)(?=:):( .*)?$"); + protected final static Pattern CFG_VALUE=Pattern.compile("^([ ]*)- (.*?)$"); + protected final static Pattern COMMENT=Pattern.compile("^([ ]*)# ?(.*?)$"); + protected final Yaml yaml; + protected final DumperOptions dumpOptions; + + /** + * 使用指定的消息前缀实例化配置管理器 + * @param pLogPrefix 错误消息前缀 + */ + public CommentedYamlConfig(){ + this.dumpOptions=new DumperOptions(); + this.dumpOptions.setIndent(2); + this.dumpOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + this.dumpOptions.setAllowUnicode(true); + if(!this.dumpOptions.isAllowUnicode()){ + Class clazz=DumperOptions.class; + try{ + Field field=clazz.getDeclaredField("allowUnicode"); + field.setAccessible(true); + field.setBoolean(dumpOptions,true); + }catch(Exception exp){ + this.log("错误,无法设置文件存储为unicode编码",exp); + } + } + Representer yamlRepresenter=new YamlRepresenter(); + yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + this.yaml=new Yaml(new YamlConstructor(),yamlRepresenter,dumpOptions); + } + + /** + * 在发生异常时用于输出调试信息,或输出警告信息到控制台 + * @param level 错误等级 + * @param msg 错误的提示消息 + * @param thrown 要抛出的异常 + */ + private void log(String msg,Throwable thrown){ + FBukkit.severe(msg,thrown); + } + + @Override + public void addDefault(String path,Object value){ + if(this.get(path)==null) this.set(path,value); + super.addDefault(path,value); + } + + /** + * 将从字符串生成的配置节点数据格式化复制到本实例中 + * @param input 格式化后的数据 + * @param section 本实例,用于递归 + */ + protected void convertMapsToSections(Map input,CommentedSection section){ + if(input==null) return; + for(Map.Entry entry : input.entrySet()){ + String key=entry.getKey().toString(); + Object value=entry.getValue(); + if((value instanceof Map)) + convertMapsToSections((Map)value,section.createSection(key)); + else section.set(key,value); + } + } + + /** + * 保存当前配置内存中的数据为字符串 + */ + public String saveToString(){ + String dump=this.yaml.dump(this.getValues(false)); + if(dump.equals(BLANK_CONFIG)) return ""; + else return this.injectComments(dump); + } + + /** + * 保存指定的数据到指定的文件,如果文件不存在将会自动创建 + *

保存数据过程中的任何错误都会被记录到控制台然后忽视

+ * @param pFile 指定的文件 + * @return 是否保存成功 + */ + public boolean saveToFile(File pFile){ + Validate.notNull(pFile,"配置文件不能为null"); + FileOutputStream output=null; + try{ + this.tryCreateFile(pFile); + output=new FileOutputStream(pFile,false); + output.write(this.saveToString().getBytes("UTF-8")); + }catch(FileNotFoundException ex){ + this.log("未找到文件["+pFile+"]",ex); + return false; + }catch(IOException ex){ + this.log("无法保存文件["+pFile+"]",ex); + return false; + }finally{ + if(output!=null) try{ + output.close(); + }catch(IOException exp){} + } + return true; + } + + /** + * 从字符串中载入配置 + */ + public void loadFromString(String pContents) throws InvalidConfigurationException{ + Validate.notNull(pContents,"字符串不能为null"); + Map input=null; + try{ + input=(Map)this.yaml.load(pContents); + }catch(YAMLException e){ + throw new InvalidConfigurationException(e); + }catch(ClassCastException e){ + throw new InvalidConfigurationException("配置文件顶级节点不是Map"); + } + this.clear(); + this.convertMapsToSections(input,this); + this.extractComments(pContents); + } + + /** + * 从给定的文件路径中载入数据 + *

+ * 如果文件不存在将会自动创建
+ * 载入配置文件过程中的任何错误都会被记录到控制台然后忽视
+ * 如果载入失败,配置管理器内容将不变
+ * 编码默认使用 UTF-8 + *

+ * + *@return 是否载入成功 + * @param pFilename 输入的文件路径 + * @throws NullPointerException 如果文件名为空 + */ + public boolean loadFromFile(String pFilename){ + Validate.notNull(pFilename,"文件名不能为null"); + return this.loadFromFile(new File(pFilename)); + } + + /** + * 从给定的文件中载入数据 + *

+ * 如果文件不存在将会自动创建
+ * 载入配置文件过程中的任何错误都会被记录到控制台然后忽视
+ * 如果载入失败,配置管理器内容将不变
+ * 编码默认使用 UTF-8 + *

+ * + * @param pFile 输入文件 + * @return 是否成功加载 + * @throws IllegalArgumentException 如果文件为空 + */ + public boolean loadFromFile(File pFile){ + Validate.notNull(pFile,"文件不能为null"); + FileInputStream input=null; + try{ + byte[] contents=new byte[(int)pFile.length()]; + if(!pFile.isFile()) this.tryCreateFile(pFile); + input=new FileInputStream(pFile); + input.read(contents); + this.loadFromString(new String(contents,"UTF-8")); + }catch(FileNotFoundException ex){ + this.log("无法找到文件["+pFile+"]",ex); + return false; + }catch(IOException ex){ + this.log("无法加载文件["+pFile+"]",ex); + return false; + }catch(InvalidConfigurationException ex){ + this.log("无法加载文件["+pFile+"],配置文件格式错误",ex); + return false; + }finally{ + if(input!=null) try{ + input.close(); + }catch(IOException exp){} + } + return true; + } + + /** + * 从给定的流中载入数据,流不会自动关闭 + *

+ * 如果文件不存在将会自动创建
+ * 载入配置文件过程中的任何错误都会被记录到控制台然后忽视
+ * 如果载入失败,配置管理器内容将不变
+ * 编码默认使用 UTF-8 + *

+ * + * @param stream 输入的数据流 + * @return 是否载入成功 + * @throws IllegalArgumentException 如果输入流为空 + */ + public boolean loadFromStream(InputStream stream){ + Validate.notNull(stream,"输入流不能为空"); + try{ + byte[] contents=new byte[stream.available()]; + this.loadFromString(new String(contents,"UTF-8")); + }catch(IOException ex){ + this.log("无法从输入流加载配置",ex); + return false; + }catch(InvalidConfigurationException ex){ + this.log("无法从输入流加载配置",ex); + return false; + } + return true; + } + + /** + * 创建一个新文件
+ * 如果文件已经存在,将什么都不干 + * @param pFile 文件 + * @throws IOException 创建文件时发生错误 + */ + protected void tryCreateFile(File pFile) throws IOException{ + Validate.notNull(pFile,"配置文件不能为null"); + if(pFile.exists()&&pFile.isFile()) return; + if(!pFile.getParentFile().isDirectory()) pFile.getParentFile().mkdirs(); + pFile.createNewFile(); + } + + /** + * 设置Yaml文件单极的空格缩进个数 + * @param pIndent 缩进 + */ + public void setIndent(int pIndent){ + this.dumpOptions.setIndent(pIndent); + } + + /** + * 获取Yaml文件单极的空格缩进个数 + * @param pIndent 缩进 + */ + public int getIndent(){ + return this.dumpOptions.getIndent(); + } + + /** + * 清空配置管理器 + */ + public void clear(){ + this.map.clear(); + if(this.mDefaults!=null) + this.mDefaults.map.clear(); + } + + /**从字符串中导出注释*/ + protected void extractComments(String pContent){ + if(pContent==null) return; + pContent=pContent.replace("\\t"," "); + ArrayList comments=new ArrayList<>(); + YamlNode parent=new YamlNode("",-1,ChildType.NODE); + YamlNode now=new YamlNode("root",0,ChildType.STRING); + now.setParent(parent); + String[] allLines=pContent.split("[\\r]?\n"); + Matcher matcher=null; + char tWrapper='\''; + int tIndent=this.getIndent(); + for(int i=0;i(); + } + while(!tNode.mClosed){ + if(i>=allLines.length-1) return; //读取完毕 + if(LineAnalyse.hasCloseMark(allLines[++i],tWrapper)) break; + } + continue; + } + if((matcher=CommentedYamlConfig.CFG_VALUE.matcher(sLine)).find()){//值节点 + if(!now.setChildType(ChildType.ARRAY)) return ; + int blackCount=matcher.group(1).length(); + if(blackCount!=now.getDeep()*tIndent) return ; //错误,不在同一级 + boolean tClose=LineAnalyse.isCloseLine(matcher.group(2),tWrapper); + while(!tClose){ + if(i>=allLines.length-1) return; //读取完毕 + if(LineAnalyse.hasCloseMark(allLines[++i],tWrapper)) break; + } + if(!comments.isEmpty()) comments.clear(); //防止值注释混合到下个节点 + continue; + } + if((matcher=CommentedYamlConfig.COMMENT.matcher(sLine)).find()){//注释节点 + comments.add(matcher.group(2)); + continue; + } + return; //不认识的节点,或者处理的时候出错了 + } + } + + /**将注释导入到字符串中*/ + protected String injectComments(String pContent){ + if(pContent==null) return ""; + ArrayList contents=new ArrayList<>(); + YamlNode parent=new YamlNode("",-1,ChildType.NODE); + YamlNode now=new YamlNode("root",0,ChildType.STRING); + now.setParent(parent); + String[] allLines=pContent.split("[\\r]?\n"); + Matcher matcher=null; + char tWrapper='\''; + int tIndent=this.getIndent(); + for(int i=0;i=allLines.length-1) return pContent; //读取完毕 + contents.add(allLines[++i]); + if(LineAnalyse.hasCloseMark(allLines[i],tWrapper)) break; + } + continue; + } + if((matcher=CommentedYamlConfig.CFG_VALUE.matcher(sLine)).find()){//值节点 + if(!now.setChildType(ChildType.ARRAY)) return pContent; + int blackCount=matcher.group(1).length(); + if(blackCount!=now.getDeep()*tIndent) return pContent; //错误,不在同一级 + boolean tClose=LineAnalyse.isCloseLine(matcher.group(2),tWrapper); + contents.add(sLine); + while(!tClose){ + if(i>=allLines.length-1) return pContent; //读取完毕,仍然没有关闭 + contents.add(allLines[++i]); + if(LineAnalyse.hasCloseMark(allLines[i],tWrapper)) break; + } + continue; + } + return pContent; //不认识的节点,或者处理的时候出错了 + } + StringBuilder builder=new StringBuilder(); + for(String sStr : contents){ + builder.append(sStr); + builder.append(System.getProperty("line.separator", "\n")); + } + return builder.toString(); + } + + private void addCommentToContent(ArrayList contents,int blackCount,ArrayList comments){ + if(comments==null||comments.size()==0) return; + String blackPrefix=""; + for(int i=0;i如果注释已经存在,将忽略

+ * @param pComments 评论 + */ + public void addDefaultComments(String pPath,String...pComments); + + /** + * 设置注释 + *

+ * 请勿设置null来清空注释
+ * 如果要清空注释,请使用{@link CommentedValue#clearComments()} + *

/ + * @param pComments 注释 + */ + public void setComments(String pPath,String...pComments); + + /** + * 获取指定路径下的注释的拷贝 + * @return 非null + */ + public ArrayList getComments(String pPath); + + /** + * 节点是否有注释 + */ + public boolean hasComments(String pPath); + + /** + * 清空指定路径下的注释 + * @return 清理掉的注释,非null + */ + public ArrayList clearComments(String pPath); + + +} diff --git a/src/main/java/cc/util/bossshop/config/LineAnalyse.java b/src/main/java/cc/util/bossshop/config/LineAnalyse.java new file mode 100644 index 0000000..a4c5733 --- /dev/null +++ b/src/main/java/cc/util/bossshop/config/LineAnalyse.java @@ -0,0 +1,86 @@ +package cc.util.bossshop.config; + +import org.apache.commons.lang.StringUtils; + +import cc.util.bossshop.config.YamlNode.ChildType; + +public class LineAnalyse{ + + /** + * 分析一个行文本是不是一个节点 + * @param pLine 文本 + * @param pWrapper Yaml字符串包裹字符 + * @return 是返回节点,不是返回null + */ + public static YamlNode analyse(String pLine,char pWrapper,int pIndent){ + if(pLine==null||pLine.trim().length()<=2) return null; + int blackCount=0; + char[] arrs=pLine.toCharArray(); + int i=0; + for(i=0;i=arrs.length) break; //没字符了 + if(arrs[i]!=pWrapper){ + warp=false; //'后面不是单引号,warp到尾部 + if(arrs[i]!=':') return null; //单引号闭合后最后必须是冒号 + } + else i++; //如果是单引号,跳过这个字符的检查 + } + }else{ + if(c==':'){ + if(i>=arrs.length){ //不带值的节点 + int start=0,end=arrs.length-1; + if(arrs[0]==pWrapper){ + start++; + end--; + } + if(start==end) return null;//空标签节点 + return new YamlNode(new String(arrs,start,end),blackCount/pIndent,null); + } + if(arrs[i]==' '){ //带值的节点,后面肯定有值,因为已经trim过了 + i++; + break; + } + } + } + } + if(i>=arrs.length) return null; + int start=0,end=i-2; //去掉冒号加空格 + if(arrs[0]==pWrapper){ + start++; + end--; + } + if(start==end) return null; //空标签节点 + String name=new String(arrs,start,end); + boolean close=true; + if(arrs[i]==pWrapper){ + close=LineAnalyse.isCloseLine(new String(arrs,i,arrs.length-i),pWrapper); + } + YamlNode node=new YamlNode(name,blackCount/pIndent,ChildType.STRING); + node.mClosed=close; + return node; + } + + public static boolean isCloseLine(String pLine,char pWrapper){ + if(StringUtils.isBlank(pLine)) return true; + pLine=pLine.trim(); + if(pLine.charAt(0)!=pWrapper) return true; + if(pLine.length()==1) return false; + return pLine.charAt(pLine.length()-1)==pWrapper; + } + + public static boolean hasCloseMark(String pLine,char pWrapper){ + if(StringUtils.isEmpty(pLine)) return false; + return pLine.charAt(pLine.length()-1)==pWrapper; + } +} diff --git a/src/main/java/cc/util/bossshop/config/YamlNode.java b/src/main/java/cc/util/bossshop/config/YamlNode.java new file mode 100644 index 0000000..8be9528 --- /dev/null +++ b/src/main/java/cc/util/bossshop/config/YamlNode.java @@ -0,0 +1,73 @@ +package cc.util.bossshop.config; + +import org.apache.commons.lang.Validate; + +public class YamlNode{ + + + enum ChildType{ + NODE,STRING,ARRAY + } + + /**该行是否已经关闭*/ + public boolean mClosed=true; + /**节点深度(空格数+1/2)*/ + public final int mDeep; + /**节点名字*/ + public final String mName; + /**子节点类型,默认为null,单值为String.class,多值为List.class,节点为Object.class*/ + private ChildType mChildType=null; + /**父节点*/ + private YamlNode mParent=null; + + + public YamlNode(String pName,int pDeep,ChildType pType){ + if(pName==null) pName=""; + this.mName=pName; + this.mDeep=pDeep; + this.mChildType=pType; + } + + /** + * 设置节点类型,如果节点类型已经存在并且与设置的节点类型不同,将返回false + * @param pType 节点类型不能为null + * @return 是否设置成功 + */ + public boolean setChildType(ChildType pType){ + Validate.notNull(pType,"子节点类型不能为null"); + if(this.mChildType==null||this.mChildType==pType){ + this.mChildType=pType; + return true; + }else return false; + } + + /** + * 获取层级 + */ + public int getDeep(){ + return this.mDeep; + } + + public String getPath(){ + if(this.mParent==null) return this.mName; + String parentPath=this.mParent.getPath(); + if(parentPath.isEmpty()) return this.mName; + else return parentPath+"."+this.mName; + } + + public YamlNode setParent(YamlNode pParent){ + this.mParent=pParent; + return this; + } + + public YamlNode getParent(){ + return this.mParent; + } + + @Override + public String toString(){ + return this.mDeep+"|"+this.getPath(); + } + + +} diff --git a/src/main/java/cc/util/bossshop/config/YamlStack.java b/src/main/java/cc/util/bossshop/config/YamlStack.java new file mode 100644 index 0000000..6755fee --- /dev/null +++ b/src/main/java/cc/util/bossshop/config/YamlStack.java @@ -0,0 +1,21 @@ +package cc.util.bossshop.config; + +import java.util.ArrayList; + +public class YamlStack{ + + private int mDeep=0; + + private final ArrayList mStack=new ArrayList<>(); + + + public int push(String pStr){ + return this.mDeep++; + } + + public String pop(){ + return null; + } + + +} diff --git a/src/main/java/cc/util/bossshop/filemanager/AConfigManager.java b/src/main/java/cc/util/bossshop/filemanager/AConfigManager.java new file mode 100644 index 0000000..b4e3d2c --- /dev/null +++ b/src/main/java/cc/util/bossshop/filemanager/AConfigManager.java @@ -0,0 +1,22 @@ +package cc.util.bossshop.filemanager; + +import cc.util.bossshop.pluginmodel.ABukkitPlugin; + +public abstract class AConfigManager extends AFileManager{ + + public AConfigManager(ABukkitPlugin pPlugin,String pVersion){ + super(pPlugin,"config.yml",pVersion); + } + + @Override + public void addDefaults(){ + super.addDefaults(); + this.mConfig.addDefault("debug",true); + this.mConfig.addDefaultComments("debug","是否开启调试模式","如果开启调试模式,插件报错的时候会输出完整的堆栈错误","还可能会输出一些个人设置的调试信息"); + } + + public boolean isDebug(){ + return this.mConfig.getBoolean("debug"); + } + +} diff --git a/src/main/java/cc/util/bossshop/filemanager/AFileManager.java b/src/main/java/cc/util/bossshop/filemanager/AFileManager.java new file mode 100644 index 0000000..1023636 --- /dev/null +++ b/src/main/java/cc/util/bossshop/filemanager/AFileManager.java @@ -0,0 +1,163 @@ +package cc.util.bossshop.filemanager; + +import java.io.File; + +import org.apache.commons.lang.Validate; + +import cc.util.bossshop.config.CommentedYamlConfig; +import cc.util.bossshop.pluginmodel.ABukkitPlugin; + +public abstract class AFileManager{ + + // 静态全局变量 + /** + * 配置文件版本节点 + */ + public static final String SEC_CFG_VERSION="version"; + // 普通变量 + /** + * 主插件 + */ + protected final ABukkitPlugin mPlugin; + /** + * 管理器依赖的文件名 + */ + protected final String mFileName; + /** + * 管理器依赖的文件 + */ + protected File mFile; + protected CommentedYamlConfig mConfig=new CommentedYamlConfig(); + /** + * 配置文件版本 + */ + protected String mVersion="1.0"; + + /** + * 构造一个基于文件的管理器 + * @param pPlugin 主插件 + * @param pFileName 数据文件的相对路径 + */ + public AFileManager(ABukkitPlugin pPlugin,String pFileName,String pVersion){ + Validate.notNull(pFileName,"配置文件不能为空"); + this.mPlugin=pPlugin; + this.mFileName=pFileName; + this.mVersion=pVersion; + this.mFile=new File(mPlugin.getDataFolder().getAbsolutePath(),mFileName); + } + + /** + * 重载配置文件 + *

+ * 如果文件不存在,自动创建文件
+ * 所有可能发生的错误都会被捕捉并显示在控制台
+ *

+ * @return 是否载入成功 + */ + public boolean reloadConfig(){ + if(!this.mConfig.loadFromFile(this.mFile)){ + this.severe("从文件"+this.mFileName+"载入配置失败,内存中配置未变化,目标文件未改动"); + return false; + } + return true; + } + + /** + * 保存config中的数据到到文件 + *

如果存在注释头部,将会同时保存注释头部

+ */ + public boolean saveConfig(){ + if(!this.mConfig.saveToFile(mFile)){ + this.severe("保存配置"+this.mFileName+"到文件失败,目标文件未改动"); + return false; + } + return true; + } + + /** + * 添加配置文件的默认值 + *

+ * 默认添加了版本
+ * 不应该在该函数中保存配置 + *

+ */ + protected void addDefaults(){ + this.mConfig.addDefault(AFileManager.SEC_CFG_VERSION,this.mVersion); + this.mConfig.addDefaultComments(AFileManager.SEC_CFG_VERSION,"配置文件版本,重要,请勿修改","插件升级配置的时候需要用到"); + } + + /** + * 检查配置文件版本是否需要更新,如果是则返回true + *

请在实现类中的该方法中对文件进行更新操作

+ */ + protected boolean checkUpdate(){ + String tVersion=this.mConfig.getString(SEC_CFG_VERSION,"1.0"); + if(this.mVersion.compareToIgnoreCase(tVersion)<=0) return false; + return true; + } + + /** + * 获取语言节点 + * @param pNode 节点名 + */ + protected String C(String pNode){ + return this.mPlugin.getLangManager().getNode(pNode); + } + + /** + * 获取该文件管理器的配置器 + */ + public CommentedYamlConfig getConfig(){ + return this.mConfig; + } + + /** + * 获取该文件管理器的文件名 + * @return + */ + public String getConfigFilename(){ + return this.mFileName; + + } + + /** + * 发送普通消息到控制台 + */ + public void info(String pMsg){ + this.mPlugin.info(pMsg); + } + + /** + * 发送消息到控制题啊 + * 添加消息到bug日志 + */ + public void warn(String pMsg){ + this.mPlugin.warn(pMsg); + } + + /** + * 输出错误消息到控制台 + * @param pMsg + */ + public void severe(String pMsg){ + this.mPlugin.severe(pMsg); + } + + /** + * 输出调试信息 + *

如果配置文件中未启用调试,将不会输出信息

+ */ + public void debug(String message){ + this.mPlugin.debug(message); + } + + /** + * 输出错误消息到控制台,如果配置文件中启用调试模式,将会同时输出错误堆栈信息 + * @param pMsg 错误的消息 + * @param pExp 异常 + */ + public void severe(String pMsg,Exception pExp){ + this.mPlugin.severe(pMsg,pExp); + } + +} diff --git a/src/main/java/cc/util/bossshop/filemanager/ALangManager.java b/src/main/java/cc/util/bossshop/filemanager/ALangManager.java new file mode 100644 index 0000000..9bc1603 --- /dev/null +++ b/src/main/java/cc/util/bossshop/filemanager/ALangManager.java @@ -0,0 +1,28 @@ +package cc.util.bossshop.filemanager; + +import org.bukkit.ChatColor; + +import cc.util.bossshop.pluginmodel.ABukkitPlugin; + +public abstract class ALangManager extends AFileManager{ + + public ALangManager(ABukkitPlugin pPlugin,String pFileName,String pVersion){ + super(pPlugin,pFileName,pVersion); + } + + /** + * 获取指定消息节点的翻译,大小写敏感 + * @param pNode 消息节点名 + * @return 翻译后的消息 + */ + public String getNode(String pNode){ + String tMsg=this.mConfig.getString(pNode); + if(tMsg!=null) return ChatColor.translateAlternateColorCodes('&',tMsg); + else{ + this.mPlugin.warn("missing language node ["+pNode+"]"); + this.mPlugin.warn("丢失语言节点 ["+pNode+"]"); + return pNode; + } + } + +} diff --git a/src/main/java/cc/util/bossshop/pluginmodel/ABukkitPlugin.java b/src/main/java/cc/util/bossshop/pluginmodel/ABukkitPlugin.java new file mode 100644 index 0000000..979a24e --- /dev/null +++ b/src/main/java/cc/util/bossshop/pluginmodel/ABukkitPlugin.java @@ -0,0 +1,278 @@ +package cc.util.bossshop.pluginmodel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +import cc.util.bossshop.filemanager.AConfigManager; +import cc.util.bossshop.filemanager.ALangManager; +import me.clip.placeholderapi.PlaceholderAPI; + +public abstract class ABukkitPlugin extends JavaPlugin{ + + /** + * 插件消息前缀 + */ + protected String prefix="§c[§7Templet§c]§b"; + /** + * 插件日志前缀 + */ + protected String logpre="[Templet]"; + /** + * 插件实例 + */ + protected static ABukkitPlugin mInstance; + + protected String mPluginName; + + /** + * 可重置状态模块实例列表 + */ + protected ArrayList mClearModel=new ArrayList<>(); + /** + * 需要重新读取配置的模块列表,此列表模块将在配置文件重载后调用重载方法 + */ + protected ArrayList mConfigModels=new ArrayList<>(); + + /** + * 需要重载的模块列表,此列表模块将在配置文件重载后调用重载方法 + */ + protected ArrayList mReloadModels=new ArrayList<>(); + /** + * 配置管理器,需要初始化 + */ + protected AConfigManager mConfigManager; + /** + * 语言管理器,需要初始化 + */ + protected ALangManager mLangManager; + + /** + * 新建一个插件实例 + *

+ * 同时会设置静态mInstance变量的值
+ * 实例化该对象后,请注意同时实例化语言文件和配置文件 + *

+ */ + public ABukkitPlugin(String pPluginName){ + ABukkitPlugin.mInstance=this; + this.mPluginName=pPluginName; + this.logpre="["+pPluginName+"]"; + this.prefix="§c[§7"+pPluginName+"§c]§b"; + } + + /** + * 获取插件实例
+ * 不建议在static模块中初始化的时候调用此方法 + */ + private static ABukkitPlugin getInstance(){ + return ABukkitPlugin.mInstance; + } + + /** + * 获取语言翻译 + */ + public String C(String pNode){ + return this.mLangManager.getNode(pNode); + } + + /** + * 获取插件名字,用于构造命令,权限等 + */ + public String getPluginName(){ + return this.mPluginName; + } + + /** + * 聊天前缀 + */ + public String getChatPrefix(){ + return this.prefix; + } + + /** + * log日志前缀 + */ + public String getLogPrefix(){ + return this.logpre; + } + + /** + * 请使用{@link ABukkitPlugin#getConfigManager()#getConfig()}
+ * 调用此方法将抛出异常 + * + */ + @Deprecated + public FileConfiguration getConfig(){ + throw new IllegalAccessError("请不不要调用此方法"); + } + + /** + * 获取插件配置管理器 + */ + public AConfigManager getConfigManager(){ + return this.mConfigManager; + } + + /** + * 获取插件语言管理器 + */ + public ALangManager getLangManager(){ + return this.mLangManager; + } + + /** + * 重载插件 + */ + public abstract void reloadPlugin(); + + /** + * 清理插件可清理模块列表 + * @return 清理的模块数量 + */ + public int clearModelStatus(){ + int tCount=0; + for(IClearAble sClearModel : this.mClearModel){ + if(sClearModel.clearStatus()) tCount++; + } + return tCount; + } + + /** + * 注册可清理状态模块列表 + */ + public void registerClearModel(IClearAble pClearModel){ + if(this.mClearModel.contains(pClearModel)) return; + this.mClearModel.add(pClearModel); + } + + /** + * 注册需要重载配置的模块列表,此列表只用于非重要实例使用 + */ + public void registerConfigModel(INeedConfig pNeedConfig){ + if(this.mConfigModels.contains(pNeedConfig)) return; + this.mConfigModels.add(pNeedConfig); + } + + /** + * 注册需要重载配置的模块列表,此列表只用于非重要实例使用 + */ + public void registerReloadModel(INeedReload pNeedReload){ + if(this.mReloadModels.contains(pNeedReload)) return; + this.mReloadModels.add(pNeedReload); + } + + /** + * 移除可清理状态模块列表 + * @return 如果存在并移除返回true,不存在返回false + */ + public boolean unregisterClearModel(IClearAble pClearModel){ + return this.mClearModel.remove(pClearModel); + } + + /** + * 移除可重载模块列表 + * @return 如果存在并移除返回true,不存在返回false + */ + public boolean unregisterReloadModel(INeedConfig pReloadModel){ + return this.mConfigModels.remove(pReloadModel); + } + + /** + * 禁用此方法,请使用{@link ABukkitPlugin #reloadPlugin()}方法重载插件 + */ + @Deprecated + @Override + public void reloadConfig(){} + + /** + * 发送消息到指定玩家 + * @param pSender 发送给谁 + * @param pMsg 消息,不需要加前缀 + * @return true + */ + public boolean send(CommandSender pSender,String pMsg){ + return this.send(pSender,pMsg,prefix); + } + + /** + * 发送普通的Console消息 + * @param pMsg 消息,不需要加前缀 + */ + public void info(String pMsg){ + this.send(Bukkit.getConsoleSender(),pMsg,prefix); + } + + /** + * 发送警告的Console消息,暗红色消息 + * @param pMsg 消息,不需要加前缀 + */ + public void warn(String pMsg){ + this.send(Bukkit.getConsoleSender(),pMsg,prefix+ChatColor.DARK_RED+"[WARN]"); + } + + /** + * 发送错误的Console消息,暗红色消息 + * @param pMsg 消息,不需要加前缀 + */ + public void severe(String pMsg){ + this.send(Bukkit.getConsoleSender(),pMsg,prefix+ChatColor.RED+"[ERROR]"); + } + + /** + * 输出错误信息到Console + *

如果配置文件启用调试模式,会同时发送错误堆栈

+ * @param pMsg 消息 + * @param pExp 异常 + */ + public void severe(String pMsg,Throwable pExp){ + this.send(Bukkit.getConsoleSender(),pMsg,prefix+ChatColor.RED+"[ERROR]"); + if(!this.getConfigManager().isDebug()){ + this.severe("配置文件启用调试模式以看到更多错误消息"); + return; + } + String[] lines= org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(pExp).split("(\r?\n)+"); + for(String sLine : lines) + this.debug(sLine); + } + + /** + * 输出调试信息到,红色 + * @param pMsg 消息 + */ + public void debug(String pMsg){ + if(!this.getConfigManager().isDebug()) return; + send(Bukkit.getConsoleSender(),pMsg,prefix+ChatColor.RED+"[DEBUG]"); + } + + /** + * 向用户发送消息 + * @param pSender 目标 + * @param pMsg 消息 + * @param pPrefix 消息前缀 + * @return true + */ + public boolean send(CommandSender pSender,String pMsg,String pPrefix){ + if(StringUtils.isBlank(pMsg)) return true; + if(pSender==null) pSender=Bukkit.getConsoleSender(); + pMsg=ChatColor.translateAlternateColorCodes('&',pMsg); + pPrefix=ChatColor.translateAlternateColorCodes('&',pPrefix); + if(pSender instanceof Player&&Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")){ + pMsg=PlaceholderAPI.setPlaceholders((Player)pSender,pMsg); + pPrefix=PlaceholderAPI.setPlaceholders((Player)pSender,pPrefix); + } + List tMsgs=Arrays.asList(pMsg.split("\n+")); + for(String sMsg : tMsgs) + pSender.sendMessage(pPrefix+" "+sMsg); + return true; + } + +} diff --git a/src/main/java/cc/util/bossshop/pluginmodel/IClearAble.java b/src/main/java/cc/util/bossshop/pluginmodel/IClearAble.java new file mode 100644 index 0000000..46bb30f --- /dev/null +++ b/src/main/java/cc/util/bossshop/pluginmodel/IClearAble.java @@ -0,0 +1,17 @@ +package cc.util.bossshop.pluginmodel; + + +/** + * 可清除内存临时数据 + * @author 聪聪 + * + */ +public interface IClearAble{ + + /** + * 清理内存数据 + * @return 是否成功 + */ + public boolean clearStatus(); + +} diff --git a/src/main/java/cc/util/bossshop/pluginmodel/INeedConfig.java b/src/main/java/cc/util/bossshop/pluginmodel/INeedConfig.java new file mode 100644 index 0000000..7f5b3c3 --- /dev/null +++ b/src/main/java/cc/util/bossshop/pluginmodel/INeedConfig.java @@ -0,0 +1,23 @@ +package cc.util.bossshop.pluginmodel; + + +/** + * 一个用于非重要实例对象在插件主配置重载后调用的接口 + *

+ * 如果实例没有变量存储,但又需要读取配置,可以集成该接口
+ * 该接口的对象的重载操作将在最后调用
+ *

+ * + * @author 聪聪 + * + */ +public interface INeedConfig{ + + /** + * 重载一个实例 + * 确保在重载了配置文件之后重载
+ * 最好只在此函数中做和配置变动相关的操作 + */ + public void setConfig(); + +} diff --git a/src/main/java/cc/util/bossshop/pluginmodel/INeedReload.java b/src/main/java/cc/util/bossshop/pluginmodel/INeedReload.java new file mode 100644 index 0000000..00fd18a --- /dev/null +++ b/src/main/java/cc/util/bossshop/pluginmodel/INeedReload.java @@ -0,0 +1,12 @@ +package cc.util.bossshop.pluginmodel; + +/** + * 如果实例需要在插件重载的时候重新加载除配置以外的内容可以实现该接口 + * @author 聪聪 + * + */ +public interface INeedReload{ + + public boolean reloadConfig(); + +} diff --git a/src/main/java/cc/util/bossshop/pluginmodel/ReloadPriority.java b/src/main/java/cc/util/bossshop/pluginmodel/ReloadPriority.java new file mode 100644 index 0000000..5575bdc --- /dev/null +++ b/src/main/java/cc/util/bossshop/pluginmodel/ReloadPriority.java @@ -0,0 +1,10 @@ +package cc.util.bossshop.pluginmodel; + + +public enum ReloadPriority{ + + LOW, + NORMAL, + HIGH + +} diff --git a/src/main/java/org/black_ixx/bossshop/BossShop.java b/src/main/java/org/black_ixx/bossshop/BossShop.java new file mode 100644 index 0000000..2d1ebe5 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/BossShop.java @@ -0,0 +1,214 @@ +package org.black_ixx.bossshop; + +import java.util.HashMap; + +import org.black_ixx.bossshop.api.BossShopAPI; +import org.black_ixx.bossshop.command.CommandExc; +import org.black_ixx.bossshop.core.BSShopHolder; +import org.black_ixx.bossshop.core.BSShopManager; +import org.black_ixx.bossshop.events.BSReloadedEvent; +import org.black_ixx.bossshop.listeners.InventoryListener; +import org.black_ixx.bossshop.listeners.PlayerListener; +import org.black_ixx.bossshop.listeners.SignListener; +import org.black_ixx.bossshop.lottery.LotteryManager; +import org.black_ixx.bossshop.mail.MailManager; +import org.black_ixx.bossshop.managers.BuyItemHandler; +import org.black_ixx.bossshop.managers.ConfigManager; +import org.black_ixx.bossshop.managers.ItemNameManager; +import org.black_ixx.bossshop.managers.ItemStackCreator; +import org.black_ixx.bossshop.managers.LangManager; +import org.black_ixx.bossshop.managers.MultiplierHandler; +import org.black_ixx.bossshop.managers.RecordManager; +import org.black_ixx.bossshop.managers.ShopCustomizer; +import org.black_ixx.bossshop.managers.TimeHandler; +import org.black_ixx.bossshop.managers.TransactionLog; +import org.black_ixx.bossshop.managers.VaultHandler; +import org.black_ixx.bossshop.managers.WorthHandler; +import org.black_ixx.bossshop.nbt.NBTEditManager; +import org.black_ixx.bossshop.points.PointsManager; +import org.black_ixx.bossshop.sale.SaleListener; +import org.black_ixx.bossshop.sale.SaleManager; +import org.black_ixx.bossshop.util.AttributeRemover; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; + +import cc.util.bossshop.FBukkit; +import cc.util.bossshop.config.CommentedYamlConfig; +import cc.util.bossshop.pluginmodel.ABukkitPlugin; +import cc.util.bossshop.pluginmodel.IClearAble; +import cc.util.bossshop.pluginmodel.INeedConfig; +import cc.util.bossshop.pluginmodel.INeedReload; +import me.clip.placeholderapi.PlaceholderAPI; + +public class BossShop extends ABukkitPlugin{ + + private static BossShop instance; + public static boolean RE=false; + private CommandExc mCommander; + private SaleListener mSaleLis; + private static long mLastNoticeTime=0; + private BossShopAPI mAPI; + @Deprecated + private final HashMap,Object> mManager=new HashMap<>(); + + public BossShop(){ + super("BossShop"); + } + + @Override + public void onEnable(){ + AttributeRemover.init(); + BossShop.instance=this; + this.info("开始初始化"); + this.mCommander=new CommandExc(this); + // 监听器 + new InventoryListener(this); + new SignListener(this); + new PlayerListener(this); + this.mSaleLis=new SaleListener(this); + new LotteryManager(this); + this.mAPI=new BossShopAPI(this); + + // 实例化管理器 + this.mConfigManager=new ConfigManager(this); + this.mManager.put(ConfigManager.class,this.mConfigManager); + this.mLangManager=new LangManager(this); + //此处注册管理器模块,注意注册的顺序 + this.registerManager(LangManager.class,this.mLangManager); + //邮件和商店需要用到NBT,在他们之前注册NBT管理器 + this.registerManager(NBTEditManager.class,new NBTEditManager(this)); + this.registerManager(BSShopManager.class,new BSShopManager(this)); + this.registerManager(MailManager.class,new MailManager(this)); + + this.registerManager(BuyItemHandler.class,new BuyItemHandler(this)); + this.registerManager(ItemStackCreator.class,new ItemStackCreator(this)); + this.registerManager(MultiplierHandler.class,new MultiplierHandler(this)); + this.registerManager(PointsManager.class,new PointsManager(this)); + this.registerManager(RecordManager.class,new RecordManager(this)); + this.registerManager(SaleManager.class,new SaleManager(this)); + this.registerManager(ShopCustomizer.class,new ShopCustomizer(this)); + this.registerManager(TimeHandler.class,new TimeHandler(this)); + this.registerManager(TransactionLog.class,new TransactionLog(this)); + this.registerManager(VaultHandler.class,new VaultHandler(this)); + this.registerManager(WorthHandler.class,new WorthHandler(this)); + this.registerManager(ItemNameManager.class,new ItemNameManager(this)); + // 载入配置 + this.reloadPlugin(); + } + + @Override + public void onDisable(){ + for(Player sPlayer : FBukkit.getOnlinePlayers()){ + Inventory inv=sPlayer.getOpenInventory().getTopInventory(); + if(inv.getHolder() instanceof BSShopHolder){ + inv.clear(); + sPlayer.closeInventory(); + } + } + } + + protected void registerManager(Class clazz,T pManager){ + this.mManager.put(clazz,pManager); + } + + public T getManager(Class clazz){ + if(clazz==null) return null; + T tt=null; + try{ + tt=(T)this.mManager.get(clazz); + }catch(Exception exp){ + this.severe("获取模块并进行强制转换时发生了错误:"+exp.getMessage(),exp); + return null; + } + if(tt==null) throw new IllegalStateException("管理器类"+clazz.getName()+"没有实例化对象"); + return tt; + } + + public static BossShop getInstance(){ + return (BossShop)mInstance; + } + + public BossShopAPI getAPI(){ + return this.mAPI; + } + + public CommandExc getCommandExc(){ + return this.mCommander; + } + + public ConfigManager getConfigManager(){ + return (ConfigManager)this.mConfigManager; + } + + public LangManager getLangManager(){ + return (LangManager)this.mLangManager; + } + + public SaleListener getSaleListener(){ + return this.mSaleLis; + } + + @Override + public void reloadConfig(){ + this.mConfigManager.reloadConfig(); + CommentedYamlConfig tConfig=this.mConfigManager.getConfig(); + this.logpre=ChatColor.translateAlternateColorCodes('&',tConfig.getString("LogPrefix","[BossShop]")); + this.prefix=ChatColor.translateAlternateColorCodes('&',tConfig.getString("MsgPrefix","§c[§7BossShop§c]§b")); + for(INeedConfig sConfig : this.mConfigModels) + sConfig.setConfig(); + } + + public void reloadPlugin(){ + BossShop.isEnablePlaceholderAPI(); + //首先将语言和配置读入,并且模块也读入配置 + this.mLangManager.reloadConfig(); + //主配置的重载 + this.reloadConfig(); + //调用模块的reloadConfig() + for(INeedReload sReload : this.mReloadModels) + sReload.reloadConfig(); + + BSReloadedEvent event=new BSReloadedEvent(this); + Bukkit.getPluginManager().callEvent(event); + } + + public int clearMemoryRecord(){ + int count=0; + for(IClearAble sClearAble : this.mClearModel) + if(sClearAble.clearStatus()) count++; + mLastNoticeTime=0; + return count++; + } + + /** + * 替换占位符为实际字符串 + * @param str + * @return + */ + public static String replaceParam(Player pPlayer,String pStr){ + if(!BossShop.isEnablePlaceholderAPI()) return ChatColor.translateAlternateColorCodes('&',pStr); + try{ + pStr=PlaceholderAPI.setPlaceholders(pPlayer,pStr); + }catch(Throwable exp){ + if(mInstance!=null) mInstance.severe("在调用PlaceHolderAPI是发生了异常,请确保你的插件和PlaceHolderAPI模块版本匹配: "+exp.getMessage(),exp); + } + return pStr; + } + + private static boolean isEnablePlaceholderAPI(){ + if(BossShop.RE) return true; + if(!Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")){ + if(mLastNoticeTime+600000 enabled_addons; + + public BossShopAPI(BossShop plugin){ + this.mPlugin=plugin; + } + + // For Single Shop + public boolean isValidShop(Inventory i){ + return (i.getHolder() instanceof BSShopHolder&&this.mPlugin.getManager(BSShopManager.class).getShop(i.getName())!=null); + } + + public BSShop getShop(String name){ + return this.mPlugin.getManager(BSShopManager.class).getShop(name.toLowerCase()); + } + + public void openShop(Player p,String name){ + BSShop shop=getShop(name); + if(shop==null){ + this.mPlugin.warn("尝试打开不存在的商店["+name+"]"); + return; + } + openShop(p,shop); + } + + public void openShop(Player p,BSShop shop){ + this.mPlugin.getManager(BSShopManager.class).openShop(p,shop); + } + + // Get Managers + public BSShopManager getShopHandler(){ + return this.mPlugin.getManager(BSShopManager.class); + } + + // Modify Shop/Shops + public void addItemToShop(ItemStack menu_item,BSBuy shop_item,BSShop shop){ + shop.addShopItem(shop_item,menu_item); + } + + public void finishedAddingItemsToShop(BSShop shop){ + shop.finishedAddingItems(); + } + + // Get Shop Items + public HashMap> getAllShopItems(){ + HashMap> all=new HashMap>(); + for(BSShop shop : this.mPlugin.getManager(BSShopManager.class).getShops().values()){ + List items=new ArrayList(); + for(Integer pos : shop.getItems().keySet()){ + BSBuy buy=shop.getItems().get(pos); + if(pos==null||buy==null) continue; + items.add(buy); + } + all.put(shop,items); + } + return all; + } + + public HashMap> getAllShopItems(String config_option){ + HashMap> all=new HashMap>(); + for(BSShop shop : this.mPlugin.getManager(BSShopManager.class).getShops().values()){ + List items=new ArrayList(); + for(Integer pos : shop.getItems().keySet()){ + BSBuy buy=shop.getItems().get(pos); + if(pos==null||buy==null) continue; + if(buy.getConfigurationSection().getBoolean(config_option)==false&&buy.getConfigurationSection().getInt(config_option)==0){ + continue; + } + items.add(buy); + } + all.put(shop,items); + } + return all; + } + + // Addon API + protected void addEnabledAddon(BossShopAddon addon){ + Plugin addonplugin=Bukkit.getPluginManager().getPlugin(addon.getAddonName()); + if(addonplugin==null){ return; } + if(enabled_addons==null){ + enabled_addons=new ArrayList(); + } + if(enabled_addons.contains(addon)){ return; } + enabled_addons.add(addon); + } + + public List getEnabledAddons(){ + return enabled_addons; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/api/BossShopAddon.java b/src/main/java/org/black_ixx/bossshop/api/BossShopAddon.java new file mode 100644 index 0000000..333a22c --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/api/BossShopAddon.java @@ -0,0 +1,93 @@ +package org.black_ixx.bossshop.api; + +import org.black_ixx.bossshop.BossShop; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + + +public abstract class BossShopAddon extends JavaPlugin{ + + private BossShop bs; + private boolean b=false; + + @Override + public void onEnable(){ + b=false; + Plugin plugin=Bukkit.getPluginManager().getPlugin("BossShop"); + if(plugin==null){ + printSevere("BossShop 未找到... 你需要它来运行 "+getAddonName()+"! Get it here: http://dev.bukkit.org/bukkit-plugins/bossshop/. Version v"+getRequiredBossShopVersion()+" or newer is required!"); + printInfo("关闭附属..."); + b=true; + Bukkit.getPluginManager().disablePlugin(this); + return; + } + bs=(BossShop)plugin; + double bs_worth=getWorth(bs.getDescription().getVersion()); + double ao_worth=getWorth(getRequiredBossShopVersion()); + if(bs_worth pMsgs){ + for(String sMsg : pMsgs) + this.mPlugin.send(pSender,sMsg); + return true; + } + + /** + * 翻译一个语言节点 + */ + public String C(String node){ + return this.mPlugin.getLangManager().getNode(node); + } + + /** + * 发送该命令的帮助到指定的用户 + * @param pSender 需要获取帮助的用户 + * @return 永远为true + */ + public boolean help(CommandSender pSender){ + ArrayList chelp=getHelp(pSender,false); + if(chelp==null||chelp.isEmpty()){ + send(pSender,this.C("Cmd.UnknowCommand")); + }else for(String ccc : chelp){ + send(pSender,ccc); + } + return true; + } + + /** + * 如果命令无法处理其子命令时调用,命令类名字的格式必须是Command*** + *

+ * 调用此函数的命令需要支持命令的子命令help
+ * 最好在子命令条数大于2条或者帮助行数>=4行数调用此函数 + *

+ * @param pSender 命令发送者 + * @param pLabel 未知的子命令标签,可以为null + * @return true + */ + public boolean unKnowChildCommand(CommandSender pSender,String pLabel){ + ArrayList helpList=this.getHelp(pSender,false); + if(helpList==null||helpList.isEmpty()) + return send(pSender,this.C("Cmd.UnknowCommand")); + String cmdLabel=this.getClass().getSimpleName().substring(7).toLowerCase(); + if(pLabel==null) pLabel=""; + else pLabel="["+pLabel+"]"; + return send(pSender,C("Cmd.unknowChildCommand")+pLabel+","+C("Word.Input")+"/BS "+cmdLabel+" help "+C("Cmd.GetHelp")); + } + + public boolean noPermit(CommandSender sender){ + return this.send(sender,this.C("Cmd.NoPermission")); + } + + public boolean consoleNotAllow(CommandSender pSender){ + return this.send(pSender,this.C("Cmd.ConsoleNotAllow")); + } + + /** + * 获取该命令的标签 + *

+ * 默认使用类名第7位开始的小写字符串
+ * 如果类名不足七位,将截取全部的小写字符串 + *

+ */ + public String getCommandLabel(){ + String className=this.getClass().getSimpleName().toLowerCase(); + if(className.length()>7) return className.substring(7); + else return className; + } + + /** + * 默认为空,请确保不为null + * @param pArgs 玩家Tab的文字参数 + */ + public ArrayList getSubCommand(CommandSender pSender,String[] pArgs){ + return new ArrayList<>(); + } + + /** + * 在按下Tab时,完成子命令提示 + *

+ * 此函数默认只处理子命令参数为1位的情况,如需处理多位,请重写方法 + *

+ * @param pSender Tab请求用户 + * @param pArgs 已有的参数 + */ + public ArrayList onTabComplete(CommandSender pSender,String[] pArgs){ + if(pArgs==null) return new ArrayList<>(); + + ArrayList subLabels=this.getSubCommand(pSender, pArgs); + String lastParam=pArgs[pArgs.length-1]; + if(StringUtils.isEmpty(lastParam)) return subLabels; + ArrayList findTabs=new ArrayList<>(); + Collection findLabels=Function.getSamePrefixIgnoreCase(subLabels,lastParam,true); + if(findLabels.size()==0){ + String findLabel=Function.getIgnoreCase(subLabels,lastParam); + if(findLabel!=null){ + if(this.getMaxArgumentLength()>pArgs.length) findTabs.add(findLabel+" "); + else findTabs.add(findLabel); + } + }else if(findLabels.size()==1){ + String findLabel=findLabels.toArray()[0].toString(); + if(this.getMaxArgumentLength()>pArgs.length) findTabs.add(findLabel+" "); + else findTabs.add(findLabel); + }else findTabs.addAll(findLabels); + return findTabs; + } + + /** + * 获取除去子命令自身的参数最大长度 + * @return >=0 + */ + public int getMaxArgumentLength(){ + return 0; + } + + /** + * 执行该命令 + * @param pSender 命令的执行者 + * @param pArgs 参数 + * @return 执行成功与否(默认为true) + */ + public abstract boolean execute(CommandSender pSender,String[] pArgs); + + /** + * 获取该命令的帮助的文本 + * @param pAll true帮扩help帮助命令,false则排除 + * @return 帮助文本的合集,如果没有帮助指令,集合为Empty + */ + public abstract ArrayList getHelp(CommandSender pSender,boolean pAll); + +} diff --git a/src/main/java/org/black_ixx/bossshop/command/CommandClear.java b/src/main/java/org/black_ixx/bossshop/command/CommandClear.java new file mode 100644 index 0000000..65ca642 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/command/CommandClear.java @@ -0,0 +1,32 @@ +package org.black_ixx.bossshop.command; + +import java.util.ArrayList; + +import org.bukkit.command.CommandSender; + + +public class CommandClear extends ACommandBase{ + + public CommandClear(CommandExc exector){ + super(exector); + } + + @Override + public boolean execute(CommandSender pSender,String[] pArgs){ + if(pArgs.length!=1) return false; + if(!pSender.hasPermission(this.mPluginName+".status.clear")) return false; + int count=this.mExector.getPlugin().clearMemoryRecord(); + return send(pSender,C("Cmd.ClearedModelStatus").replace("%numb%",count+"")); + } + + @Override + public ArrayList getHelp(CommandSender pSender,boolean pAll){ + ArrayList tList=new ArrayList<>(); + if(pSender.hasPermission(this.mPluginName+".status.clear")){ + tList.add(this.mMainCmdLabel+" clear"); + tList.add(C("Help.ClearModelStatus")); + } + return tList; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/command/CommandClose.java b/src/main/java/org/black_ixx/bossshop/command/CommandClose.java new file mode 100644 index 0000000..038c0e7 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/command/CommandClose.java @@ -0,0 +1,57 @@ +package org.black_ixx.bossshop.command; + +import java.util.ArrayList; + +import org.black_ixx.bossshop.core.BSShopHolder; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; + +import cc.util.bossshop.FBukkit; + + +public class CommandClose extends ACommandBase{ + + public CommandClose(CommandExc exector){ + super(exector); + } + + @Override + public boolean execute(CommandSender pSender,String[] pArgs){ + //bs close + if(!pSender.hasPermission(this.mPluginName+".close.other")) return noPermit(pSender); + if(pArgs.length!=1) return help(pSender); + Player tPlayer=Bukkit.getPlayer(pArgs[0]); + if(tPlayer==null) + return send(pSender,C("Main.NoPlayerFound")); + if(!tPlayer.isOnline()) + return send(pSender,C("Main.PlayerOffline")); + Inventory inv=tPlayer.getOpenInventory().getTopInventory(); + if(!(inv.getHolder() instanceof BSShopHolder)) + return send(pSender,C("Main.PlayerNotOpenBSShop")); + tPlayer.closeInventory(); + return send(pSender,C("Main.AlreadyClosePlayerShop")); + } + + @Override + public ArrayList getHelp(CommandSender pSender,boolean pAll){ + ArrayList list=new ArrayList<>(); + if(pSender.hasPermission(this.mPluginName+".close.other")){ + list.add(this.mMainCmdLabel+" close <"+C("Word.PlayerName")+">"); + list.add(C("Help.ClosePlayerShop")); + } + return list; + } + + @Override + public ArrayList getSubCommand(CommandSender pSender,String[] pArgs){ + return FBukkit.getOnlinePlayersName(); + } + + @Override + public int getMaxArgumentLength(){ + return 1; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/command/CommandExc.java b/src/main/java/org/black_ixx/bossshop/command/CommandExc.java new file mode 100644 index 0000000..5fb77c8 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/command/CommandExc.java @@ -0,0 +1,181 @@ +package org.black_ixx.bossshop.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import org.apache.commons.lang.StringUtils; +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSShopManager; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.PluginCommand; +import org.bukkit.command.TabCompleter; + +import cc.util.bossshop.Function; + +public class CommandExc implements CommandExecutor,TabCompleter{ + + private final Map mCommands=new LinkedHashMap<>(); + private final String mPluginCmdLabel="/shop"; + private BossShop mPlugin; + + /** + * 必须在配置文件启用后才能调用此方法 + * @param pPlugin + */ + public CommandExc(BossShop pPlugin){ + this.mPlugin=pPlugin; + //绑定执行器 + PluginCommand tCmd=null; + for(String sMainCmd : this.mPlugin.getDescription().getCommands().keySet()){ + if((tCmd=this.mPlugin.getCommand(sMainCmd))!=null){ + tCmd.setExecutor(this); + tCmd.setTabCompleter(this); + } + } + //注册子命令 + this.register("clear",new CommandClear(this)); + this.register("close",new CommandClose(this)); + this.register("help",new CommandHelp(this)); + this.register("name",new CommandName(this)); + this.register("mail",new CommandMail(this)); + this.register("nbt",new CommandNBT(this)); + this.register("open",new CommandOpen(this)); + this.register("reload",new CommandReload(this)); + this.register("sale",new CommandSale(this)); + this.register("unsale",new CommandUnsale(this)); + } + + /**获取主插件实例*/ + public BossShop getPlugin(){ + return this.mPlugin; + } + + /**获取主命令标签,包括斜杠*/ + public String getLabel(){ + return this.mPluginCmdLabel; + } + + /**获取命令标签Set集合的拷贝*/ + public Set keySet(){ + HashSet cmdLabels=new HashSet<>(); + cmdLabels.addAll(this.mCommands.keySet()); + return cmdLabels; + } + + /** + * 根据标签名获取子命令,忽略标签大小写 + */ + public ACommandBase getCommand(String pLabel){ + for(Entry sEntry : this.mCommands.entrySet()){ + if(sEntry.getKey().equalsIgnoreCase(pLabel)) return sEntry.getValue(); + } + return null; + } + + /** + * 根据标类获取子命令 + */ + public ACommandBase getCommand(Class pClazz){ + for(ACommandBase scmd : this.mCommands.values()){ + if(scmd.getClass()==pClazz) return scmd; + } + return null; + } + + /** + * 注册指令到指令列表中 + * @param pLabel 指令标签 + * @param pCmd 指令实例 + */ + public ACommandBase register(String pLabel,ACommandBase pCmd){ + return this.mCommands.put(pLabel==null?null:pLabel.toLowerCase(),pCmd); + } + + /** + * 更改命令模块的标签名 + * @param pCmdClazz 命令模块类 + * @param pNewLabel 新的名字 + * @return 是否成功 + */ + @Deprecated + public boolean replaceLabel(Class pCmdClazz,String pNewLabel){ + if(StringUtils.isBlank(pNewLabel)) return false; + pNewLabel=pNewLabel.toLowerCase(); + if(this.mCommands.get(pNewLabel)!=null) return false; + for(Map.Entry tCmdEntry : this.mCommands.entrySet()){ + if(pCmdClazz.isInstance(tCmdEntry.getValue())){ + this.mCommands.put(pNewLabel,this.mCommands.remove(tCmdEntry.getKey())); + return true; + } + } + return false; + } + + @Override + public boolean onCommand(CommandSender pSender,Command command,String pLabel,String[] pArgs){ + try{ + String cmdLabel=this.mPlugin.getConfigManager().getMainShop(); + if(pArgs.length>0) cmdLabel=pArgs[0]; + ACommandBase cmd=this.getCommand(cmdLabel); + + if(cmd!=null){ + if(pArgs.length>0) pArgs=Arrays.copyOfRange(pArgs,1,pArgs.length); + return cmd.execute(pSender,pArgs); + }else{ + if(this.mPlugin.getManager(BSShopManager.class).getShop(cmdLabel)!=null) + this.getCommand(CommandOpen.class).execute(pSender,new String[]{cmdLabel}); + else return this.mPlugin.send(pSender,this.mPlugin.C("Cmd.UnknowCommand")); + } + }catch(Throwable exp){ + this.mPlugin.severe(this.mPlugin.C("Cmd.ErrorHappedWhenHandlerCmd")+exp.getMessage(),exp); + this.mPlugin.send(pSender,this.mPlugin.C("Main.ErrorHappend")); + } + return true; + } + + /** + * Tab命令自动补全 + *

+ * 会首先检查 插件名.cmdcomplete权限,如果不存在直接返回
+ *

+ * @return + */ + public List onTabComplete(CommandSender pSender,Command pCmd,String pStr,String[] pArgs){ + if(!pSender.hasPermission(this.mPlugin.getPluginName()+".cmdcomplete")) return null; + ArrayList list=new ArrayList<>(); + if(pArgs.length==0){ + list.addAll(this.mCommands.keySet()); + }else if(pArgs.length==1){ + Collection samePrefixTabs=Function.getSamePrefixIgnoreCase(this.mCommands.keySet(),pArgs[0],true); + if(samePrefixTabs.size()==0){ + String findTab=Function.getIgnoreCase(this.mCommands.keySet(),pArgs[0]); + if(findTab!=null){ + if(this.getCommand(findTab).getMaxArgumentLength()>0) list.add(findTab+" "); + else list.add(findTab); + } + }else if(samePrefixTabs.size()==1){ + String findTab=samePrefixTabs.toArray()[0].toString(); + if(this.getCommand(findTab).getMaxArgumentLength()>0) list.add(findTab+" "); + else list.add(findTab); + }else list.addAll(samePrefixTabs); + }else{ + ACommandBase tCmd=this.getCommand(pArgs[0]); + if(tCmd!=null&&tCmd.getMaxArgumentLength()>0) list.addAll(tCmd.onTabComplete(pSender,Arrays.copyOfRange(pArgs,1,pArgs.length))); + } +// this.mPlugin.send(pSender,"Tab输入:"+Function.asList(pArgs)); +// this.mPlugin.send(pSender,"Tab返回:"+list); + return list; + } + + public void reload(){} + +} diff --git a/src/main/java/org/black_ixx/bossshop/command/CommandHelp.java b/src/main/java/org/black_ixx/bossshop/command/CommandHelp.java new file mode 100644 index 0000000..112ba3c --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/command/CommandHelp.java @@ -0,0 +1,55 @@ +package org.black_ixx.bossshop.command; + +import java.util.ArrayList; + +import org.bukkit.command.CommandSender; + + +public class CommandHelp extends ACommandBase{ + + public CommandHelp(CommandExc exector){ + super(exector); + } + + @Override + public boolean execute(CommandSender sender,String[] args){ + if(!sender.hasPermission(this.mPluginName+".help")) return noPermit(sender); + int page=1; + if(args.length==1){ + try{ + page=Integer.parseInt(args[0]); + }catch(NumberFormatException nfexp){ + page=1; + } + } + ArrayList tt=this.getHelp(sender,true); + int page_numb=tt.size()%10==0?tt.size()/10:tt.size()/10+1; + if(page<1||page>page_numb) page=1; + send(sender,"================("+page+"/"+page_numb+")==============="); + for(int i=(page-1)*10;(i<(page*10))&&(i getHelp(CommandSender sender,boolean pAll){ + ArrayList list=new ArrayList<>(); + if(sender.hasPermission(this.mPluginName+".help")){ + for(String label : this.mExector.keySet()){ + ACommandBase cmd=this.mExector.getCommand(label); + if(cmd==this) continue; + ArrayList helplist=cmd.getHelp(sender,true); + if(helplist==null) throw new IllegalArgumentException("命令模块的帮助结果为null,请联系作者完善代码"); + list.addAll(helplist); + } + } + return list; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/command/CommandMail.java b/src/main/java/org/black_ixx/bossshop/command/CommandMail.java new file mode 100644 index 0000000..7d757fb --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/command/CommandMail.java @@ -0,0 +1,147 @@ +package org.black_ixx.bossshop.command; + +import java.util.ArrayList; + +import org.black_ixx.bossshop.mail.MailManager; +import org.black_ixx.bossshop.managers.WorthHandler; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + + +public class CommandMail extends ACommandBase{ + + private MailManager mMailMan; + private final ArrayList mSubCommand=new ArrayList<>(); + + public CommandMail(CommandExc exector){ + super(exector); + this.mSubCommand.add("check"); + this.mSubCommand.add("help"); + this.mSubCommand.add("recive"); + this.mSubCommand.add("send"); + } + + @Override + public boolean execute(CommandSender pSender,String[] pArgs){ + if(!(pSender instanceof Player)) + return consoleNotAllow(pSender); + Player tPlayer=(Player)pSender; + String cmdLabel="help"; + if(pArgs.length>0) cmdLabel=pArgs[0]; + if(cmdLabel.equalsIgnoreCase("check")){ + if(!tPlayer.hasPermission(this.mPluginName+".mail.check")) + return noPermit(pSender); + return this.checkMail(tPlayer,pArgs); + }else if(cmdLabel.equalsIgnoreCase("recive")){ + if(!tPlayer.hasPermission(this.mPluginName+".mail.recive")) + return noPermit(pSender); + return this.reciveMial(tPlayer,pArgs); + }else if(cmdLabel.equalsIgnoreCase("send")){ + if(!tPlayer.hasPermission(this.mPluginName+".mail.send")) + return noPermit(pSender); + return this.sendMail(tPlayer,pArgs); + } +// else if(cmdLabel.equalsIgnoreCase("undo")){ +// if(!tPlayer.hasPermission("BossShop.mail.undo")) +// return noPermit(pSender); +// return this.undoMail(tPlayer,pArgs); +// } + else if(cmdLabel.equalsIgnoreCase("help")){ + if(!tPlayer.hasPermission("BossShop.mail.help")) + return noPermit(pSender); + return help(pSender); + }else return unKnowChildCommand(pSender,cmdLabel); + } + + private MailManager getMailManager(){ + if(this.mMailMan==null) + this.mMailMan=this.mPlugin.getManager(MailManager.class); + return this.mMailMan; + } + + public boolean checkMail(Player tPlayer,String[] pArgs){ + ArrayList list=this.getMailManager().checkMail(tPlayer.getUniqueId()); + for(String smsg : list) send(tPlayer,smsg); + return true; + } + + public boolean reciveMial(Player tPlayer,String[] pArgs){ + if(this.getMailManager().getMailCount(tPlayer.getUniqueId())<=0) + return send(tPlayer,C("Mail.YouHaveNoMail")); + this.getMailManager().reciveMail(tPlayer); + return true; + } + + public boolean undoMail(Player tPlayer,String[] pArgs){ + return false; + } + + public boolean sendMail(Player tPlayer,String[] pArgs){ +// if(pArgs.length!=2) return help(tPlayer); +// ArrayList tBeSernders=new ArrayList<>(); +// if(pArgs[1].equals("*")){ +// if(!tPlayer.hasPermission("BossShop.mail.send.all")) return noPermit(tPlayer); +// for(Player sPlayer : FBukkit.getOnlinePlayers()){ +// +// } +// } +// String[] tTargets=pArgs[1].split(","); + Player tTarget=Bukkit.getPlayer(pArgs[1]); + if(tTarget==null) + return send(tPlayer,C("Main.NoPlayerFound")+"["+pArgs[1]+"]"); + if(pArgs[1].equalsIgnoreCase(tPlayer.getName())&&!tPlayer.isOp()) + return send(tPlayer,C("Mail.YouCannotSendMailToYouSelf")); + MailManager tMailM=this.getMailManager(); + if(tMailM.isMailFull(tTarget.getUniqueId())) + return send(tPlayer,C("Mail.PlayerMailIsFull").replace("%player%",tTarget.getName())); + ItemStack tItem=tPlayer.getItemInHand(); + if(tItem==null||tItem.getType()==Material.AIR) + return send(tPlayer,C("Cmd.YouShouldTakeItemInHand")); + int tCost=tMailM.getMailSendCost(); + WorthHandler wHandler=this.mPlugin.getManager(WorthHandler.class); + if(tCost>0){ + if(!wHandler.hasMoney(tPlayer,tCost)) + return send(tPlayer,C("YouNeed%%MoneyToSendMail").replace("%numb%",tCost+"")); + else wHandler.takeMoney(tPlayer,tCost); + } + tMailM.addMail(tPlayer,tTarget,tItem); + tPlayer.setItemInHand(null); + return send(tPlayer,C("Mail.MailHasSend")); + } + + @Override + public ArrayList getHelp(CommandSender pSender,boolean pAll){ + ArrayList list=new ArrayList<>(); + if(pAll&&pSender.hasPermission("BossShop.mail.help")){ + list.add(this.mMainCmdLabel+" mail [help]"); + list.add(C("Help.MailHelp")); + } + if(pSender.hasPermission("BossShop.mail.check")){ + list.add(this.mMainCmdLabel+" mail check"); + list.add(C("Help.MailCheck")); + } + if(pSender.hasPermission("BossShop.mail.recive")){ + list.add(this.mMainCmdLabel+" mail recive"); + list.add(C("Help.MailRevice")); + } + if(pSender.hasPermission("BossShop.mail.send")){ + list.add(this.mMainCmdLabel+" mail send <"+C("Word.PlayerName")+">"); + list.add(C("Help.MailSend")); + } + return list; + } + + @Override + public ArrayList getSubCommand(CommandSender pSender,String[] pArgs){ + return this.mSubCommand; + } + + @Override + public int getMaxArgumentLength(){ + return 1; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/command/CommandNBT.java b/src/main/java/org/black_ixx/bossshop/command/CommandNBT.java new file mode 100644 index 0000000..7ac2857 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/command/CommandNBT.java @@ -0,0 +1,98 @@ +package org.black_ixx.bossshop.command; + +import java.util.ArrayList; + +import org.black_ixx.bossshop.nbt.NBT; +import org.black_ixx.bossshop.nbt.NBTEditManager; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public class CommandNBT extends ACommandBase{ + + private final ArrayList mSubCommand=new ArrayList<>(); + + public CommandNBT(CommandExc exector){ + super(exector); + this.mSubCommand.add("add"); + this.mSubCommand.add("clear"); + this.mSubCommand.add("help"); + } + + @Override + public boolean execute(CommandSender pSender,String[] pArgs){ + String label="help"; + if(pArgs.length>0) label=pArgs[0]; + if(label.equalsIgnoreCase("add")){ + if(!pSender.hasPermission(this.mPluginName+".nbt.add")) + return noPermit(pSender); + if(!(pSender instanceof Player)) + return this.consoleNotAllow(pSender); + return this.add((Player)pSender,pArgs); + }else if(label.equalsIgnoreCase("clear")){ + if(!pSender.hasPermission(this.mPluginName+".nbt.clear")) + return noPermit(pSender); + return this.clear(pSender,pArgs); + }else if(label.equalsIgnoreCase("help")){ + if(!pSender.hasPermission(this.mPluginName+".nbt.help")) + return noPermit(pSender); + return help(pSender); + }else return unKnowChildCommand(pSender,label); + } + + @Override + public ArrayList getHelp(CommandSender sender,boolean pAll){ + ArrayList list=new ArrayList<>(); + if(pAll&&sender.hasPermission(this.mPluginName+".nbt.help")){ + list.add(this.mMainCmdLabel+" nbt [help]"); + list.add(this.C("Help.NBTHelp")); + } + if(sender.hasPermission(this.mPluginName+".nbt.add")){ + list.add(this.mMainCmdLabel+" nbt add"); + list.add(this.C("Help.NBTAdd")); + } + if(sender.hasPermission(this.mPluginName+".nbt.clear")){ + list.add(this.mMainCmdLabel+" nbt clear"); + list.add(this.C("Help.NBTClear")); + } + return list; + } + + private boolean add(Player pPlayer,String args[]){ + if(args.length!=1) return this.help(pPlayer); + ItemStack item=pPlayer.getItemInHand(); + if(item==null||item.getType()==Material.AIR){ + this.send(pPlayer,this.C("Cmd.YouShouldTakeItemInHand")); + return true; + } + NBTEditManager NBTMan=this.mPlugin.getManager(NBTEditManager.class); + NBT tNBT=NBTMan.getNBTByItem(item); + if(tNBT!=null){ + this.send(pPlayer,this.C("Cmd.NBTHasExistInStockAndItsID")+tNBT.mLabel); + return true; + } + String label=NBTMan.addItemNBTToStock(item,false); + if(label==null) this.send(pPlayer,this.C("Cmd.ItemMayDonotHaveNBT")); + else this.send(pPlayer,this.C("Cmd.NBTHaveAddAndItsID")+label); + return true; + } + + private boolean clear(CommandSender sender,String args[]){ + if(args.length!=1) return this.help(sender); + int count=this.mPlugin.getManager(NBTEditManager.class).clearNBT(); + return this.send(sender,this.C("Cmd.ClearedNoUsefulNBT").replace("%numb%",count+"")); + } + + @Override + public ArrayList getSubCommand(CommandSender pSender,String[] pArgs){ + return this.mSubCommand; + } + + @Override + public int getMaxArgumentLength(){ + return 1; + } + + +} diff --git a/src/main/java/org/black_ixx/bossshop/command/CommandName.java b/src/main/java/org/black_ixx/bossshop/command/CommandName.java new file mode 100644 index 0000000..cadae8c --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/command/CommandName.java @@ -0,0 +1,52 @@ +package org.black_ixx.bossshop.command; + +import java.util.ArrayList; + +import org.black_ixx.bossshop.managers.ItemNameManager; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + + +public class CommandName extends ACommandBase{ + + private final ArrayList mSubCommand=new ArrayList<>(); + + public CommandName(CommandExc exector){ + super(exector); + this.mSubCommand.add("key"); + } + + @Override + public boolean execute(CommandSender pSender,String[] pArgs){ + if(!pSender.isOp()) return true; + if(!(pSender instanceof Player)) + return consoleNotAllow(pSender); + Player tPlayer=(Player)pSender; + ItemStack inHandItem=tPlayer.getItemInHand(); + if(inHandItem==null||inHandItem.getType()==Material.AIR) + return send(pSender,this.C("Cmd.YouShouldTakeItemInHand")); + if(pArgs.length==0) + return send(pSender,">"+this.mPlugin.getManager(ItemNameManager.class).getDisPlayName(inHandItem)); + else if(pArgs[0].equalsIgnoreCase("key")) + return send(pSender,">"+this.mPlugin.getManager(ItemNameManager.class).getUnlocalizedName(inHandItem)); + else return true; + } + + @Override + public ArrayList getHelp(CommandSender pSender,boolean pAll){ + return new ArrayList<>(); + } + + @Override + public ArrayList getSubCommand(CommandSender pSender,String[] pArgs){ + return this.mSubCommand; + } + + @Override + public int getMaxArgumentLength(){ + return 1; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/command/CommandOpen.java b/src/main/java/org/black_ixx/bossshop/command/CommandOpen.java new file mode 100644 index 0000000..ca3d95e --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/command/CommandOpen.java @@ -0,0 +1,110 @@ +package org.black_ixx.bossshop.command; + +import java.util.ArrayList; + +import org.black_ixx.bossshop.core.BSShop; +import org.black_ixx.bossshop.core.BSShopManager; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import cc.util.bossshop.FBukkit; + + +public class CommandOpen extends ACommandBase{ + + public CommandOpen(CommandExc exector){ + super(exector); + } + + @Override + public boolean execute(CommandSender pSender,String[] args){ + BSShopManager shopMan=this.mPlugin.getManager(BSShopManager.class); + if(args.length==1){ + if(args[0].equalsIgnoreCase("help")){ + if(!pSender.hasPermission(this.mPluginName+".open.help")) + return noPermit(pSender); + return help(pSender); + } + if(!pSender.hasPermission(this.mPluginName+".open.command")) + return this.noPermit(pSender); + if(!(pSender instanceof Player)) + return this.consoleNotAllow(pSender); + Player tPlayer=(Player)pSender; + BSShop tshop=shopMan.getShop(args[0]); + if(tshop==null) return this.send(pSender,this.C("Cmd.NoShopFound")+" ["+args[0]+"]"); + this.openShop(tPlayer,tshop); + }else if(args.length==2){ + Player tPlayer=Bukkit.getPlayer(args[0]); + if(tPlayer==null) + return this.send(pSender,this.C("Cmd.NoPlayerFound")+" ["+args[0]+"]"); + boolean self=pSender.getName().equalsIgnoreCase(args[0]); + if(!self&&!pSender.hasPermission("BossShop.open.other")) + return this.noPermit(pSender); + if(!pSender.hasPermission(this.mPluginName+".open.command")) + return this.noPermit(pSender); + BSShop tshop=shopMan.getShop(args[1]); + if(tshop==null) + return this.send(pSender,this.C("Cmd.NoShopFound")+" ["+args[1]+"]"); + this.openShop(tPlayer,tshop); + if(!self) this.send(pSender,this.C("Cmd.OpenShopForPlayer").replace("%player%",tPlayer.getName().replace("%shop%",tshop.getShopName()))); + } + else return unKnowChildCommand(pSender,null); + return true; + } + + @Override + public ArrayList getHelp(CommandSender sender,boolean pAll){ + ArrayList list=new ArrayList<>(); + if(pAll&&sender.hasPermission(this.mPluginName+".open.help")){ + list.add(this.mMainCmdLabel+" open [help]"); + list.add(this.C("Help.HelpOpen")); + } + if(sender.hasPermission(this.mPluginName+".open.command")){ + list.add(this.mMainCmdLabel+" [open] ["+C("Word.ShopName")+"]"); + list.add(this.C("Help.OpenShop")); + } + if(sender.hasPermission(this.mPluginName+".open.other")){ + list.add(this.mMainCmdLabel+" open <"+C("Word.PlayerName")+"> <"+C("Word.ShopName")+">"); + list.add(this.C("Help.OpenOtherShop")); + } + return list; + } + + private void openShop(final Player pPlayer,final BSShop pShop){ + int tDelay=this.mPlugin.getConfigManager().getShopOpenDelay(); + final BSShopManager shopMan=this.mPlugin.getManager(BSShopManager.class); + if(tDelay<=0){ + shopMan.openShop(pPlayer,pShop); + } + else Bukkit.getScheduler().scheduleSyncDelayedTask(this.mPlugin,new Runnable(){ + + @Override + public void run(){ + shopMan.openShop(pPlayer,pShop); + } + },tDelay); + + } + + @Override + public ArrayList getSubCommand(CommandSender pSender,String[] pArgs){ + ArrayList mayCmd=new ArrayList<>(); + switch(pArgs.length){ + case 1: + mayCmd.addAll(this.mPlugin.getManager(BSShopManager.class).getShops().keySet()); + mayCmd.addAll(FBukkit.getOnlinePlayersName()); + return mayCmd; + case 2: + mayCmd.addAll(this.mPlugin.getManager(BSShopManager.class).getShops().keySet()); + return mayCmd; + } + return super.getSubCommand(pSender, pArgs); + } + + @Override + public int getMaxArgumentLength(){ + return 2; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/command/CommandReload.java b/src/main/java/org/black_ixx/bossshop/command/CommandReload.java new file mode 100644 index 0000000..604332e --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/command/CommandReload.java @@ -0,0 +1,104 @@ +package org.black_ixx.bossshop.command; + +import java.util.ArrayList; + +import org.black_ixx.bossshop.core.BSShop; +import org.black_ixx.bossshop.core.BSShopManager; +import org.bukkit.command.CommandSender; + +public class CommandReload extends ACommandBase{ + + private final ArrayList mSubCommand=new ArrayList<>(); + + public CommandReload(CommandExc exector){ + super(exector); + this.mSubCommand.add("all"); + this.mSubCommand.add("config"); + this.mSubCommand.add("help"); + this.mSubCommand.add("lang"); + this.mSubCommand.add("shop"); + } + + @Override + public boolean execute(CommandSender pSender,String[] pArgs){ + String cmdLabel="all"; + if(pArgs.length>0) cmdLabel=pArgs[0]; + if(cmdLabel.equalsIgnoreCase("all")){ + if(!pSender.hasPermission(this.mPluginName+".reload.all")) + return noPermit(pSender); + this.mPlugin.reloadPlugin(); + return send(pSender,this.C("Cmd.PluginReloaded")); + }else if(cmdLabel.equalsIgnoreCase("config")){ + if(!pSender.hasPermission(this.mPluginName+".reload.config")) + return noPermit(pSender); + this.mPlugin.reloadConfig(); + return send(pSender,this.C("Cmd.ConfigReloacded")); + }else if(cmdLabel.equalsIgnoreCase("help")){ + if(!pSender.hasPermission(this.mPluginName+".reload.help")) + return noPermit(pSender); + return help(pSender); + }else if(cmdLabel.equalsIgnoreCase("lang")){ + if(!pSender.hasPermission(this.mPluginName+".reload.lang")) + return noPermit(pSender); + this.mPlugin.getLangManager().reloadConfig(); + return send(pSender,this.C("Cmd.LangReloacded")); + }if(cmdLabel.equalsIgnoreCase("shop")){ + if(!pSender.hasPermission(this.mPluginName+".reload.shop")) + return noPermit(pSender); + return this.reloadShop(pSender,pArgs); + }else return unKnowChildCommand(pSender,cmdLabel); + } + + @Override + public ArrayList getHelp(CommandSender sender,boolean pAll){ + ArrayList list=new ArrayList<>(); + if(pAll&&sender.hasPermission(this.mPluginName+".reload.help")){ + list.add(this.mMainCmdLabel+" reload help"); + list.add(this.C("Help.ReloadHelp")); + } + if(sender.hasPermission(this.mPluginName+".reload.all")){ + list.add(this.mMainCmdLabel+" reload [all]"); + list.add(this.C("Help.Reload")); + } + if(sender.hasPermission(this.mPluginName+".reload.config")){ + list.add(this.mMainCmdLabel+" reload config"); + list.add(this.C("Help.ReloadConfig")); + } + if(sender.hasPermission(this.mPluginName+".reload.lang")){ + list.add(this.mMainCmdLabel+" reload lang"); + list.add(this.C("Help.ReloadLang")); + } + if(sender.hasPermission(this.mPluginName+".reload.shop")){ + list.add(this.mMainCmdLabel+" reload shop ["+C("Word.ShopName")+"]"); + list.add(this.C("Help.ReloadShop")); + } + return list; + } + + private boolean reloadShop(CommandSender pSender,String[] pArgs){ + BSShopManager shopMan=this.mPlugin.getManager(BSShopManager.class); + if(pArgs.length==1){ + shopMan.reloadConfig(); + return send(pSender,C("Cmd.AlreadyReloadShop").replace("%shop%",C("Word.All"))); + }else if(pArgs.length==2){ + BSShop tShop=shopMan.getShop(pArgs[1]); + if(tShop==null) + return send(pSender,C("Cmd.NoShopFound")+"["+pArgs[1]+"]"); + if(!tShop.reloadShop()) + shopMan.removeShop(tShop); + tShop.updateAllInventory(); + return send(pSender,C("Cmd.AlreadyReloadShop").replace("%shop%",tShop.getShopName())); + }else return help(pSender); + } + + @Override + public ArrayList getSubCommand(CommandSender pSender,String[] pArgs){ + return this.mSubCommand; + } + + @Override + public int getMaxArgumentLength(){ + return 1; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/command/CommandSale.java b/src/main/java/org/black_ixx/bossshop/command/CommandSale.java new file mode 100644 index 0000000..b7e9da8 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/command/CommandSale.java @@ -0,0 +1,103 @@ +package org.black_ixx.bossshop.command; + +import java.util.ArrayList; + +import org.black_ixx.bossshop.sale.SaleManager; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class CommandSale extends ACommandBase{ + + private final ArrayList mSubCommand=new ArrayList<>(); + + public CommandSale(CommandExc exector){ + super(exector); + this.mSubCommand.add("start"); + this.mSubCommand.add("stop"); + this.mSubCommand.add("item"); + this.mSubCommand.add("money"); + this.mSubCommand.add("points"); + } + + @Override + public boolean execute(CommandSender pSender,String[] pArgs){ + if(!pSender.hasPermission(this.mPluginName+".sale.forsale")) + return noPermit(pSender); + if(!(pSender instanceof Player)) + return this.consoleNotAllow(pSender); + Player tPlayer=(Player)pSender; + if(pArgs.length<=1){ + String label="help"; + if(pArgs.length==1) label=pArgs[0]; + if(label.equalsIgnoreCase("help")) + return help(pSender); + else if(label.equalsIgnoreCase("start")){ + this.mPlugin.getSaleListener().startSaleListenrer(tPlayer); + return true; + }else if(label.equalsIgnoreCase("stop")){ + this.mPlugin.getSaleListener().stopSaleListenrer(tPlayer); + return true; + }else return unKnowChildCommand(pSender,label); + } + else if(pArgs.length==5){ + this.mPlugin.getManager(SaleManager.class).sale((Player)pSender,pArgs); + return true; + } + return this.help(pSender); + } + + @Override + public ArrayList getHelp(CommandSender sender,boolean pAll){ + ArrayList list=new ArrayList<>(); + if(sender.hasPermission(this.mPluginName+".sale.forsale")){ + if(pAll){ + list.add("/BossShop sale [help]"); + list.add(this.C("Help.SaleHelp")); + } + // /BossShop sale start + list.add(this.mMainCmdLabel+" sale start"); + list.add(this.C("Help.StartSaleWithChat")); + // /BossShop sale start + list.add(this.mMainCmdLabel+" sale stop"); + list.add(this.C("Help.StopSaleWithChat")); + // /BossShop sale <售卖类型> <单次数量> <份数> <价格类型> <价格> + list.add(this.mMainCmdLabel+" sale <"+C("Word.SellType")+"> <"+C("Word.SigleNumb")+"> <"+C("Word.PartNumb")+"> <"+C("Word.PriceType")+"> <"+C("Word.Price")+">"); + list.add(this.C("Help.SaleForsale")); + list.add(this.C("Help.SaleSellType")); + list.add(this.C("Help.SalePriceType")); + list.add(this.C("Help.SaleOther")); + } + return list; + } + + @Override + public ArrayList getSubCommand(CommandSender pSender,String[] pArgs){ + ArrayList mayCmd=new ArrayList<>(); + switch(pArgs.length){ + case 1: + return this.mSubCommand; + case 2: + mayCmd.add("1"); + mayCmd.add("10"); + mayCmd.add("64"); + return mayCmd; + case 3: + mayCmd.add("1"); + mayCmd.add("10"); + return mayCmd; + case 4: + mayCmd.add("money"); + mayCmd.add("points"); + return mayCmd; + case 5: + return mayCmd; + } + return this.mSubCommand; + } + + @Override + public int getMaxArgumentLength(){ + return 5; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/command/CommandUnsale.java b/src/main/java/org/black_ixx/bossshop/command/CommandUnsale.java new file mode 100644 index 0000000..6488a56 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/command/CommandUnsale.java @@ -0,0 +1,81 @@ +package org.black_ixx.bossshop.command; + +import java.util.ArrayList; + +import org.black_ixx.bossshop.core.BSBuy; +import org.black_ixx.bossshop.mail.MailManager; +import org.black_ixx.bossshop.managers.RecordManager; +import org.black_ixx.bossshop.managers.WorthHandler; +import org.black_ixx.bossshop.sale.SaleManager; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + + +public class CommandUnsale extends ACommandBase{ + + public CommandUnsale(CommandExc exector){ + super(exector); + } + + @Override + public boolean execute(CommandSender pSender,String[] pArgs){ + if(!pSender.hasPermission(this.mPluginName+".unsale.user")) + return noPermit(pSender); + if(pArgs.length!=1) return help(pSender); + SaleManager tSaleM=this.mPlugin.getManager(SaleManager.class); + BSBuy tBuy=tSaleM.getSaleItem(pArgs[0]); + if(tBuy==null) + return send(pSender,C("Main.NoSaleItemFound")+"["+pArgs[0]+"]"); + boolean op=pSender.hasPermission(this.mPluginName+".unsale.admin"); + if(pSender instanceof Player){ + Player tPlayer=(Player)pSender; + if(!tBuy.getOwner().equals(tPlayer.getUniqueId())&&!op) + return send(pSender,C("Main.YouCannotUnsaleOther")); + if(!op){ + RecordManager recordMan=this.mPlugin.getManager(RecordManager.class); + int tCount=recordMan.getUnsaleRecord(tPlayer.getUniqueId()); + int tMaxCount=tSaleM.getUnsaleCount(); + if(tCount>=tMaxCount) + return send(pSender,C("Main.YouTodayCannotUnsaleMore")); + int tCost=tSaleM.getUnSaleCost(); + WorthHandler wHandler=this.mPlugin.getManager(WorthHandler.class); + if(tCost>0){ + if(!wHandler.hasMoney(tPlayer,tCost)) + return send(pSender,C("Main.YouNeed%%MoneyToUnsale").replace("%numb%",tCost+"")); + wHandler.takeMoney(tPlayer,tCost); + } + recordMan.addUnsaleRecord(tPlayer.getUniqueId()); + } + } + tBuy.getShop().removeItem(tBuy.getName(),true); + this.mPlugin.getManager(MailManager.class).addMail(tBuy,C("Main.FromHandUnsale")); + return send(pSender,C("Main.SuccessUnsaleItem")+"["+tBuy.getName()+"]"); + } + + @Override + public ArrayList getHelp(CommandSender pSender,boolean pAll){ + ArrayList list=new ArrayList<>(); + if(pSender.hasPermission(this.mPluginName+".unsale.user")||pSender.hasPermission(this.mPluginName+".unsale.admin")){ + list.add(this.mMainCmdLabel+" unsale <"+C("Word.SaleID")+">"); + list.add(C("Help.Unsale")); + } + return list; + } + + @Override + public ArrayList getSubCommand(CommandSender pSender,String[] pArgs){ + ArrayList allSale=new ArrayList<>(); + if(pSender.hasPermission(this.mPluginName+".unsale.admin")){ + allSale.addAll(this.mPlugin.getManager(SaleManager.class).getSaleItemsName()); + }else if(pSender.hasPermission(this.mPluginName+".unsale.user")&&pSender instanceof Player){ + allSale.addAll(this.mPlugin.getManager(SaleManager.class).getSaleItemsName((Player)pSender)); + } + return allSale; + } + + @Override + public int getMaxArgumentLength(){ + return 1; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/core/BSBuy.java b/src/main/java/org/black_ixx/bossshop/core/BSBuy.java new file mode 100644 index 0000000..b1fdeb8 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/core/BSBuy.java @@ -0,0 +1,657 @@ +package org.black_ixx.bossshop.core; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +import org.apache.commons.lang.StringUtils; +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSEnums.BSBuyType; +import org.black_ixx.bossshop.core.BSEnums.BSPriceType; +import org.black_ixx.bossshop.events.BSPlayerPurchaseEvent; +import org.black_ixx.bossshop.events.BSPlayerPurchasedEvent; +import org.black_ixx.bossshop.lottery.LotteryHolder; +import org.black_ixx.bossshop.lottery.LotteryStatus; +import org.black_ixx.bossshop.mail.MailManager; +import org.black_ixx.bossshop.managers.ItemStackChecker; +import org.black_ixx.bossshop.managers.MultiplierHandler; +import org.black_ixx.bossshop.managers.RecordManager; +import org.black_ixx.bossshop.managers.ShopCustomizer; +import org.black_ixx.bossshop.managers.StringManager; +import org.black_ixx.bossshop.managers.TransactionLog; +import org.black_ixx.bossshop.managers.VaultHandler; +import org.black_ixx.bossshop.managers.WorthHandler; +import org.black_ixx.bossshop.misc.Enchant; +import org.black_ixx.bossshop.nbt.NBTEditManager; +import org.black_ixx.bossshop.sale.SaleManager; +import org.bukkit.Bukkit; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import cc.util.bossshop.JsonExtra; +import cc.util.bossshop.NMSHelper; +import cc.util.bossshop.JsonExtra.ClickAction; +import cc.util.bossshop.JsonExtra.Color; +import cc.util.bossshop.JsonExtra.HoverAction; +import cc.util.bossshop.JsonExtra.Style; + +public class BSBuy{ + + private BossShop mPlugin; + private int numberLimit=-1; + private int personalLimit=-1; + private int mLocation; + private int mWeight=0; + private int mHideItemFlag=0; + private boolean perm_is_group=false; + private boolean mHideNoStock=false; + private boolean mHideNotTime=false; + private String permission; + private String mName; + private String mMsg="undefine"; + private UUID owner=null; + private Object mReward=null; + private Object mPrice=null; + private Date stopTime=null; + private Date startTime=null; + private BSShop mShop=null; + private BSPriceType priceT=null; + private BSBuyType buyT=null; + private boolean mNeedEdit=true; + + public BSBuy(BossShop pPlugin,BSBuyParameter parameter){ + this.mPlugin=pPlugin; + + this.mShop=parameter.shop; + this.priceT=parameter.priceT; + this.buyT=parameter.buyT; + this.mReward=parameter.reward; + this.mPrice=parameter.price; + this.mName=parameter.name; + this.mMsg=transformMessage(parameter.msg); + this.owner=parameter.owner; + this.mLocation=parameter.location; + this.numberLimit=parameter.numberLimit; + this.personalLimit=parameter.personalLimit; + this.startTime=parameter.startTime; + this.stopTime=parameter.stopTime; + this.mHideNoStock=parameter.hideNoStock; + this.mHideNotTime=parameter.hideNotTime; + this.mHideItemFlag=parameter.hideItemFlag & 0xFFFFFFFF; + this.mWeight=parameter.weight; + if(StringUtils.isNotEmpty(parameter.permission)){ + permission=parameter.permission; + if(permission.startsWith("[")&&permission.endsWith("]")&&permission.length()>2){ + String group=permission.substring(1,permission.length()-1); + if(group!=null){ + this.permission=group; + perm_is_group=true; + } + } + } + } + + public int getWeight(){ + return this.mWeight; + } + + /** + * 如果为null,则使用默认 + * @return + */ + public int getHideItemFlag(){ + return this.mHideItemFlag; + } + + /** + * 获取该商品的所有者,如果不存在则为null + * @return + */ + public UUID getOwner(){ + return this.owner; + } + + public boolean hasOwner(){ + return this.owner!=null; + } + + public BSBuyType getBuyType(){ + return buyT; + } + + public BSPriceType getPriceType(){ + return priceT; + } + + public boolean isHideNoStock(){ + return this.mHideNoStock; + } + + public boolean isHideNotTime(){ + return this.mHideNotTime; + } + + public String getShopName(){ + return this.mShop.getShopName(); + } + + public BSShop getShop(){ + return this.mShop; + } + + public Object getReward(){ + return this.mReward; + } + + public Object getPrice(){ + return this.mPrice; + } + + public String getMessage(){ + return this.mMsg; + } + + public int getInventoryLocation(){ + return this.mLocation; + } + + public String getName(){ + return this.mName; + } + + public boolean isBuyTime(){ + Date now=new Date(); + if(this.startTime!=null&&this.startTime.after(now)) return false; + if(this.stopTime!=null&&this.stopTime.before(now)) return false; + return true; + } + + public boolean hasBuyTime(){ + return this.startTime!=null||this.stopTime!=null; + } + + public Date getStartTime(){ + return this.startTime; + } + + public Date getStopTime(){ + return this.stopTime; + } + + public int getPersonalLimit(){ + return this.personalLimit; + } + + public boolean hasPersonalLimit(){ + return this.personalLimit>-1; + } + + public String C(String pNode){ + return this.mPlugin.getLangManager().getNode(pNode); + } + + /** + * 获取物品的配置节点 + */ + public ConfigurationSection getConfigurationSection(){ + return ((BSShop)mShop).getConfig().getConfigurationSection("shop."+mName); + } + + public boolean hasPermission(Player p,boolean msg){ + if(StringUtils.isEmpty(permission)) return true; + if(perm_is_group){ + boolean no_group=true; + for(String group : this.mPlugin.getManager(VaultHandler.class).getPermission().getPlayerGroups(p)){ + no_group=false; + if(group.equalsIgnoreCase(permission)){ return true; } + } + if(no_group&&permission.equalsIgnoreCase("default")){ return true; } + if(msg){ + this.mPlugin.send(p,C("Main.NoPermitToBuyThisItem")); + } + return false; + } + if(p.hasPermission(permission)) return true; + if(msg){ + this.mPlugin.send(p,C("Main.NoPermitToBuyThisItem")); + } + return false; + } + + public boolean isExtraPermissionExisting(){ + if(StringUtils.isEmpty(permission)) return false; + return true; + } + + @SuppressWarnings("unchecked") + public boolean alreadyBought(Player p){ + if(buyT==BSBuyType.Permission){ + for(String s : (List)mReward){ // Loop durch Perms + if(!p.hasPermission(s)){ // Sobald ein Spieler eine Perm nicht + return false; + } + } + return true; // Hat ein Spieler alle Perms wird true returned + } + return false; + } + + public JsonExtra getPersonalChatMenuItem(Player pPlayer,Player pOwner){ + String saleChat=BossShop.replaceParam(pOwner,C("Main.PlayerSaleItem")); + String[] saleCharPart=saleChat.split("%item%"); + //生成Json消息 + JsonExtra itemSaleMsg=new JsonExtra(saleCharPart.length==0?"玩家"+pOwner.getName()+"上架了 ":saleCharPart[0]+" ",Color.green); + //设置消息物品 + JsonExtra itemMsg=new JsonExtra("",Color.white); + ShopCustomizer shopCusMan=this.mPlugin.getManager(ShopCustomizer.class); + ItemStack perItem=shopCusMan.createPersonalMenuItem(pPlayer,this,this.mShop.getMenuItem(this.mLocation)); + String displayeItemName=perItem.getType().name(); + if(perItem.hasItemMeta()&&perItem.getItemMeta().hasDisplayName()){ + displayeItemName=perItem.getItemMeta().getDisplayName(); + }else{ + displayeItemName=displayeItemName.replace("_"," "); + displayeItemName=displayeItemName.charAt(0)+displayeItemName.substring(1).toLowerCase(); + itemMsg.setInherit(false); + itemMsg.setStyle(Style.bold,Style.underline); + } + itemMsg.setText(displayeItemName); + itemMsg.setHoverEvent(HoverAction.show_item,NMSHelper.getItemJson(perItem)); + itemMsg.setClickEvent(ClickAction.run_command,"/BS open "+this.mShop.getShopName()); + itemSaleMsg.addExtra(itemMsg); + + if(saleCharPart.length>1) + itemSaleMsg.addNormalWords(saleCharPart[1],true);//继承父节点样式 + return itemSaleMsg; + } + + /** + * 检查该用户是否能够购买该物品
+ * 如果不能购买,失败原因会立刻在本函数中显示给玩家 + *

所有检查项目包括:
+ * 购买事件是否通过
+ * 是否是购买自己的物品(只有寄售物品存在这条)
+ * 是否有足够的库存
+ * 是否已经达到个人购买上限
+ * 是否在购买时间
+ * 是否有足够的钱
+ * 是否已经购买过了(例如权限)
+ * 是否购买的物品合法(例如不适合的附魔)
+ *

+ * @return 是否能购买 + */ + public boolean canBuy(Player pPlayer,BSShopHolder pHolder){ + //抽奖检查 + if(pHolder instanceof LotteryHolder){ + LotteryHolder lHolder=(LotteryHolder)pHolder; + LotteryStatus tStatus=lHolder.getStatus(); + if(tStatus==LotteryStatus.WAIT){ + // 此两句多余,因为getShopItem里面已经做过判断 + if(this.getBuyType()!=BSBuyType.Lottery) return false; + if(this.getWeight()>0) return false; + }else if(tStatus==LotteryStatus.RESULT){ +// if(!this.canBuy(pPlayer,pHolder)) return false; +// this.stockOut(pPlayer,pHolder); +// lHolder.resetLottery(); + }else return false;//其他情况不允许点击抽奖背包 + } + //权限检查 + if(!this.hasPermission(pPlayer,true)) return false; + //产生购买事件 + BSPlayerPurchaseEvent e1=new BSPlayerPurchaseEvent(pPlayer,this.mShop,this);// Custom + Bukkit.getPluginManager().callEvent(e1); + if(e1.isCancelled()) return false; + + //是不是购买自己的物品,op排除在外 + if(this.hasOwner()&&!pPlayer.isOp()){ + if(this.owner.equals(pPlayer.getUniqueId())){ + this.mPlugin.send(pPlayer,C("Main.CannotBuyYourselfGoods")); + return false; + } + } + //库存数量是否够用 + if(this.numberLimit==0){ + this.mPlugin.send(pPlayer,C("Main.AlreadySoldOut")); + return false; + } + //是否已经达到个人购买上限 + if(this.personalLimit>-1){ + int nLimit=this.mPlugin.getManager(RecordManager.class).getBuyRecord(this.mShop.getShopName(),this.mName,pPlayer.getUniqueId()); + if(nLimit>=this.personalLimit){ + this.mPlugin.send(pPlayer,C("Main.OutOfPersonalLimit")); + return false; + } + } + //是否在购买时间 + Date date=new Date(); + if(this.startTime!=null&&date.before(this.startTime)){ + this.mPlugin.send(pPlayer,C("Main.GoodsNotArriveTime")); + return false; + } + if(this.stopTime !=null&&date.after (this.stopTime )){ + this.mPlugin.send(pPlayer,C("Main.GoodsOutOfDate")); + return false; + } + //是否有足够的钱 + try{ + if(!this.hasPrice(pPlayer)) return false; + }catch(Exception exp){ + this.mPlugin.severe(exp.getMessage(),exp); + this.mPlugin.send(pPlayer,C("Main.ErrorHappend")); + return false; + } + //是否已经购买过了 + if(this.alreadyBought(pPlayer)){ + this.mPlugin.send(pPlayer,C("Main.AlreadyBought")); + return false; + } + //购买的物品是否合法 + if(this.buyT==BSBuyType.Enchantment){ + Enchant e=(Enchant)this.mReward; + ItemStack tItem=pPlayer.getItemInHand(); + if(tItem==null|!ItemStackChecker.isValidEnchantment(tItem,e.getType(),e.getLevel())&!this.mPlugin.getConfigManager().allowUnsafeEnchantments()){ + this.mPlugin.send(pPlayer,C("Enchantment.Invalid")); + return false; + } + } + return true; + } + + /** + * 商品出货一件,此处进行相关库存管理工作,寄售的工作 + *

+ * 所有的工作项目包括:
+ * 库存的调整
+ * 个人购买数量的记录
+ * 价格的收取
+ * 商品的发放
+ * 购买消息的发送
+ * 寄售物品报酬的给予
+ *

+ * @param pPlayer 出货的对象 + */ + public void stockOut(Player pPlayer,BSShopHolder pHolder){ + //收取价格 + String tLeft=this.takePrice(pPlayer); + //给予商品 + if(pHolder instanceof LotteryHolder){ + LotteryHolder tHoler=(LotteryHolder)pHolder; + if(this.buyT==BSBuyType.Lottery){ + //如果是乐透商品,则开始抽奖 + tHoler.startLottery(); + }else{ + //如果是普通商品,在获取商品后,关闭结果界面并回到WAIT状态 + tHoler.resetLottery(); + } + } + this.giveReward(pPlayer); + //给予寄售者的寄售的利润 + if(this.hasOwner()) this.mPlugin.getManager(SaleManager.class).giveSaleReward(this); + //如果存在数量限制,库存减1 + if(this.hasLimit()){ + if(this.numberLimit==0) throw new IllegalStateException("库存为0时,不能进行出货函数的调用,这可能是程序bug,请通知作者"); + this.numberLimit--; + if(this.hasOwner()&&this.numberLimit==0){ + mShop.removeItem(this.mName,true); + if(this.buyT==BSEnums.BSBuyType.Item&&this.mPlugin.getConfigManager().isAutoClearNBT()) + this.mPlugin.getManager(NBTEditManager.class).clearNBT(); + } + else{ + ConfigurationSection sec=getConfigurationSection(); + if(sec!=null){ + sec.set("NumberLimit",this.getLimit()); + this.mShop.saveConfig(); + } + } + } + //如果存在个人购买数量限制,增加购买记录到文件 + if(this.hasPersonalLimit()){ + this.mPlugin.getManager(RecordManager.class).addBuyRecord(this.mShop.getShopName(),this.mName,pPlayer.getUniqueId()); + } + //购买后消息的生成 + if(StringUtils.isNotEmpty(this.mMsg)){ + String tMsg=BossShop.replaceParam(pPlayer,this.mMsg); + if(StringUtils.isNotEmpty(tLeft)&&tMsg.contains("%left%")) + tMsg=tMsg.replace("%left%",tLeft); + this.mPlugin.send(pPlayer,tMsg); + } + //购买记录的记录(非正式) + if(this.mPlugin.getConfigManager().transactionLogEnabled()) + this.mPlugin.getManager(TransactionLog.class).addTransaction(pPlayer,this); + //更新所有依据该商店生成的背包 + if(this.hasLimit()) mShop.updateAllInventory(); + else mShop.updateInventory(pHolder.getInventory(),pPlayer); + //事件产生 + BSPlayerPurchasedEvent e2=new BSPlayerPurchasedEvent(pPlayer,mShop,this);// Custom + Bukkit.getPluginManager().callEvent(e2);// Custom Event end + } + + /** + * 玩家是否满足显示该商品的条件 + *

如果商品是寄售商品,且已经卖完或或者不在售卖时间,将会调用{@link BSShop #removeItem(String, boolean)}

+ * @param pPlayer 玩家 + * @return 是否显示 + */ + public boolean disaplyToPlayer(Player pPlayer){ + if(!this.hasPermission(pPlayer,false)&&this.mPlugin.getConfigManager().hideNoPermitItem()) return false; + if(this.numberLimit==0){ + if(this.hasOwner()){ + this.mShop.removeItem(this.getName(),true); + if(this.getBuyType()==BSEnums.BSBuyType.Item&&this.mPlugin.getConfigManager().isAutoClearNBT()) + this.mPlugin.getManager(NBTEditManager.class).clearNBT(); + return false; + } + if(this.isHideNoStock()) return false; + } + if(!this.isBuyTime()){ + if(this.hasOwner()){ + this.mShop.removeItem(this.getName(),true); + String tMsg=BossShop.replaceParam(pPlayer,C("Mail.FromOutDateSale")); + this.mPlugin.getManager(MailManager.class).addMail(this,tMsg); + return false; + } + if(this.isHideNotTime()) return false; + } + return true; + } + + /** + * 获取BSBuy库存 + * @return + */ + public int getLimit(){ + return this.numberLimit; + } + + public boolean hasLimit(){ + return this.numberLimit>-1; + } + + @SuppressWarnings("unchecked") + public boolean hasPrice(Player p){ + MultiplierHandler multipMan=this.mPlugin.getManager(MultiplierHandler.class); + WorthHandler wHandler=this.mPlugin.getManager(WorthHandler.class); + switch(priceT){ + // EXP + case Exp: + // return hasExp(p, (Integer)price); old + return wHandler.hasExp(p,multipMan.calculateWithMultiplier(p,priceT,(Integer)mPrice)); + // Item + case Item: + return wHandler.hasItems(p,(List)mPrice); + // Money + case Money: + if(mPrice instanceof Integer){ + // return hasMoney(p, (Integer)price); old + return wHandler.hasMoney(p,multipMan.calculateWithMultiplier(p,priceT,(Integer)mPrice)); + } + // return hasMoney(p, (Integer)price); old + return wHandler.hasMoney(p,multipMan.calculateWithMultiplier(p,priceT,(Double)mPrice)); + // Points + case Points: + // return hasPoints(p, (Integer)price); old + return wHandler.hasPoints(p,multipMan.calculateWithMultiplier(p,priceT,(Integer)mPrice)); + // Free + case Free: + return true; + // Nothing + case Nothing: + return true; + } + return false; + } + + + + @SuppressWarnings("unchecked") + public String takePrice(Player p){ + MultiplierHandler multipMan=this.mPlugin.getManager(MultiplierHandler.class); + WorthHandler wHandler=this.mPlugin.getManager(WorthHandler.class); + switch(priceT){ + // EXP + case Exp: + return wHandler.takeExp(p,multipMan.calculateWithMultiplier(p,priceT,(Integer)mPrice)); + // Item + case Item: + return wHandler.takeItems(p,(List)mPrice); + // Money + case Money: + if(mPrice instanceof Integer){ return wHandler.takeMoney(p,multipMan.calculateWithMultiplier(p,priceT,(Integer)mPrice)); } + return wHandler.takeMoney(p,multipMan.calculateWithMultiplier(p,priceT,(Double)mPrice)); + // Points + case Points: + return wHandler.takePoints(p,multipMan.calculateWithMultiplier(p,priceT,(Integer)mPrice)); + // Free + case Free: + return null; + // Nothing + case Nothing: + return null; + } + return null; + } + + @SuppressWarnings("unchecked") + public void giveReward(Player p){ + WorthHandler wHandler=this.mPlugin.getManager(WorthHandler.class); + switch(buyT){ + case Command: + wHandler.giveRewardCommand(p,(List)mReward); + return; + case PlayerCommand: + wHandler.giveRewardPlayerCommand(p,(List)mReward); + return; + case TimeCommand: + wHandler.giveRewardTimeCommand(p,(HashMap)mReward); + return; + case Item: + wHandler.giveRewardItem(p,(List)mReward); + return; + case Permission: + wHandler.giveRewardPermission(p,(List)mReward); + return; + case Money: + if(mReward instanceof Integer){ + wHandler.giveRewardMoney(p,(Integer)mReward); + return; + } + wHandler.giveRewardMoney(p,(Double)mReward); + return; + case Points: + wHandler.giveRewardPoints(p,(Integer)mReward); + return; + case Shop: + wHandler.giveRewardShop(p,(String)mReward); + return; + case Enchantment: + wHandler.giveRewardEnchantment(p,(Enchant)mReward); + return; + case Lottery: + break; + case Nothing: + break; + } + } + + /** + * 翻译BSBuy物品的属性 + */ + public String transformMessage(String msg){ + if(mReward==null){ + mReward=1; + } + if(mPrice==null){ + mPrice=1; + } + if(msg==null||msg.length()==0) return ""; + msg=StringManager.transform(msg); + String priceM=""+mPrice; + if(mPrice instanceof List){ + List list=(List)mPrice; + if((!list.isEmpty())&&list.get(0) instanceof ItemStack){ + String m=""; + int x=0; + for(Object i : list){ + if(i instanceof ItemStack){ + x++; + String material=((ItemStack)i).getType().name().toLowerCase(); + material=material.replaceFirst(material.substring(0,1),material.substring(0,1).toUpperCase()); + m+=""+((ItemStack)i).getAmount()+" "+material+(x){ + List list=(List)mReward; + if((!list.isEmpty())&&list.get(0) instanceof ItemStack){ + String m=""; + int x=0; + for(Object i : list){ + if(i instanceof ItemStack){ + x++; + String material=((ItemStack)i).getType().name().toLowerCase(); + material=material.replaceFirst(material.substring(0,1),material.substring(0,1).toUpperCase()); + m+=""+((ItemStack)i).getAmount()+" "+material+(x0){ + msg=msg.replace("%itemname%",mName); + msg=msg.replace("%shopitemname%",mName); + } + if(priceT!=null&&priceT.name()!=""&&priceT.name().length()>0){ + msg=msg.replace("%pricetype%",priceT.name()); + } + if(priceM!=null&&priceM!=""&&priceM.length()>0){ + msg=msg.replace("%price%",priceM); + } + if(buyT!=null&&buyT.name()!=""&&buyT.name().length()>0){ + msg=msg.replace("%rewardtype%",buyT.name()); + } + if(rewardM!=null&&rewardM!=""&&rewardM.length()>0){ + msg=msg.replace("%reward%",rewardM); + } + return msg; + } + + @Deprecated + public void setInventoryLocation(int i){ + mLocation=i; + } + + public void setNeedEdit(boolean enable){ + this.mNeedEdit=enable; + } + + public boolean isNeedEdit(){ + return this.mNeedEdit; + } + +} + + diff --git a/src/main/java/org/black_ixx/bossshop/core/BSBuyParameter.java b/src/main/java/org/black_ixx/bossshop/core/BSBuyParameter.java new file mode 100644 index 0000000..b55dc76 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/core/BSBuyParameter.java @@ -0,0 +1,29 @@ +package org.black_ixx.bossshop.core; + +import java.util.Date; +import java.util.UUID; + +import org.black_ixx.bossshop.core.BSEnums.BSBuyType; +import org.black_ixx.bossshop.core.BSEnums.BSPriceType; + +public class BSBuyParameter{ + public int numberLimit=-1; + public int personalLimit=-1; + public int location; + public int weight=0; + public int hideItemFlag=0; + public boolean perm_is_group=false; + public boolean hideNoStock=false; + public boolean hideNotTime=false; + public String permission; + public String name; + public String msg="undefine"; + public UUID owner=null; + public Object reward=null; + public Object price=null; + public Date stopTime=null; + public Date startTime=null; + public BSShop shop=null; + public BSPriceType priceT=null; + public BSBuyType buyT=null; +} diff --git a/src/main/java/org/black_ixx/bossshop/core/BSEnums.java b/src/main/java/org/black_ixx/bossshop/core/BSEnums.java new file mode 100644 index 0000000..e311aa7 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/core/BSEnums.java @@ -0,0 +1,47 @@ +package org.black_ixx.bossshop.core; + +public class BSEnums { + public enum BSBuyType{ + Item, + Enchantment, + Command, + TimeCommand, + Permission, + Money, + Points, + Shop, + PlayerCommand, + Lottery, + Nothing + } + + public enum BSPriceType{ + Money, + Item, + Points, + Exp, + Nothing, + Free + } + + public enum STOCK{ + in, + out, + check + } + + public static BSBuyType getBSBuyType(String name){ + for(BSBuyType t : BSEnums.BSBuyType.values()){ + if(name.equalsIgnoreCase(t.toString())) return t; + } + return null; + } + + public static BSPriceType getBSPriceType(String name){ + for(BSPriceType t : BSEnums.BSPriceType.values()){ + if(name.equalsIgnoreCase(t.toString())) return t; + } + return null; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/core/BSMultiplier.java b/src/main/java/org/black_ixx/bossshop/core/BSMultiplier.java new file mode 100644 index 0000000..491358a --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/core/BSMultiplier.java @@ -0,0 +1,81 @@ +package org.black_ixx.bossshop.core; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSEnums.BSPriceType; +import org.bukkit.entity.Player; + +public class BSMultiplier{ + + private String permission="Permission.Node"; + private BSPriceType type=BSPriceType.Nothing; + private double multiplier=1.0; + + public BSMultiplier(BossShop pPlugin,String config_line){ + String[] parts=config_line.split(":",3); + if(parts.length!=3){ + pPlugin.warn("Invalid Multiplier Group Line... \""+config_line+"\"! It should look like this: \"Permission.Node::\""); + return; + } + String permission=parts[0].trim(); + if(parts[1].trim().equalsIgnoreCase("")){ + return; + } + BSPriceType type=null; + for(BSPriceType t : BSPriceType.values()){ + if(t.name().equalsIgnoreCase(parts[1].trim())){ + type=t; + } + } + if(type==null||type==BSPriceType.Item||type==BSPriceType.Nothing||type==BSPriceType.Free){ + pPlugin.warn("Invalid Multiplier Group Line... \""+config_line+"\"! It should look like this: \"Permission.Node::\". '"+parts[1].trim()+"' is no valid PriceType... you can use: 'Money', 'Points' and 'EXP'!"); + return; + } + double multiplier=1.0; + try{ + multiplier=Double.parseDouble(parts[2].trim()); + }catch(Exception e){ + pPlugin.warn("Invalid Multiplier Group Line... \""+config_line+"\"! It should look like this: \"Permission.Node::\". '"+parts[2].trim()+"' is no valid multiplier... What you can use instead (examples): 0.25, 0.3, 0.75, 1.0, 1.5, 2.0 etc.!"); + return; + } + setup(permission,type,multiplier); + } + + public BSMultiplier(String permission,BSPriceType type,double multiplier){ + setup(permission,type,multiplier); + } + + public void setup(String permission,BSPriceType type,double multiplier){ + this.permission=permission; + this.type=type; + this.multiplier=multiplier; + } + + public boolean isValid(){ + return type!=BSPriceType.Nothing; + } + + public BSPriceType getType(){ + return type; + } + + public double getMultiplier(){ + return multiplier; + } + + public String getPermission(){ + return permission; + } + + public boolean hasPermission(Player p){ + return p.hasPermission(permission); + } + + public double calculateWithMultiplier(double d){ + return d*multiplier; + } + + public int calculateWithMultiplier(int d){ + return (int)(d*multiplier); + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/core/BSShop.java b/src/main/java/org/black_ixx/bossshop/core/BSShop.java new file mode 100644 index 0000000..c62f087 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/core/BSShop.java @@ -0,0 +1,376 @@ +package org.black_ixx.bossshop.core; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.lottery.LotteryHolder; +import org.black_ixx.bossshop.mail.MailManager; +import org.black_ixx.bossshop.managers.BuyItemHandler; +import org.black_ixx.bossshop.managers.ItemStackCreator; +import org.black_ixx.bossshop.managers.StringManager; +import org.black_ixx.bossshop.util.AttributeRemover; +import org.bukkit.Bukkit; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +import cc.util.bossshop.FBukkit; +import cc.util.bossshop.filemanager.AFileManager; + +public class BSShop extends AFileManager{ + + private static final Pattern PATTERN=Pattern.compile("[%]([^%]+)[%]"); + public final String mShopName; + private String sign_text="[BossShop]"; + private String mDisplayname="[BossShop]"; + private boolean needPermToCreateSign=true; + private boolean mValid=true; + private boolean mSpecial=false; + private int mInvSize=9; + private int mTotalWeight=0; + private final HashMap mShopItems=new HashMap(); + private final HashMap mMenuItems=new HashMap(); + private String mEnterMsg=""; + private String mLeaveMsg=""; + + /** + * 新建一个商店 + * @param fileName /shops/下的商店配置文件名字 + */ + public BSShop(BossShop pPlugin,String fileName,String shopName){ + super(pPlugin,"shops/"+fileName,"1.0"); + this.mShopName=shopName; + } + + /** + * 重载商店配置 + * @return 是否载入 + */ + @Override + public boolean reloadConfig(){ + this.info(C("Console.StartReloadShop")+this.mShopName); + if(!super.reloadConfig()) return false; + this.mMenuItems.clear(); + this.mShopItems.clear(); + this.addDefaults(); + this.sign_text=this.mConfig.getString("signs.text"); + this.needPermToCreateSign=this.mConfig.getBoolean("signs.NeedPermissionToCreateSign"); + this.mSpecial=this.mConfig.getBoolean("ShopTypeSpecial",false); + this.mValid=true; + this.mDisplayname=StringManager.transform(this.mConfig.getString("DisplayName","")); + this.mEnterMsg=StringManager.transform(this.mConfig.getString("EnterMessage","进入"+this.mDisplayname+"商店")); + this.mLeaveMsg=StringManager.transform(this.mConfig.getString("LeaveMessage","离开"+this.mDisplayname+"商店")); + this.loadItems(); + this.finishedAddingItems(); + this.saveConfig(); + return true; + } + + private void addDefaultItem(String name,String rewardType,String priceType,Object reward,Object price,List menuitem,String message,int loc,String permission){ + ConfigurationSection c=this.mConfig.getConfigurationSection("shop").createSection(name); + c.set("RewardType",rewardType); + c.set("PriceType",priceType); + c.set("Price",price); + c.set("Reward",reward); + c.set("MenuItem",menuitem); + c.set("Message",message); + c.set("InventoryLocation",loc); + c.set("ExtraPermission",permission); + } + + @Override + protected void addDefaults(){ + super.addDefaults(); + if(this.mConfig.get("ShopName")!=null) this.mConfig.set("ShopName",null); + this.mConfig.addDefault("signs.text","[ExtraShop]"); + this.mConfig.addDefault("signs.NeedPermissionToCreateSign",false); + if(this.mConfig.getConfigurationSection("shop")==null){ + this.mConfig.createSection("shop"); + } + } + + public String getShopName(){ + return mShopName; + } + + public String getDisplayName(){ + return mDisplayname; + } + + public String getEnterMessage(){ + return this.mEnterMsg; + } + + public String getLeaveMessage(){ + return this.mLeaveMsg; + } + + public String getSignText(){ + return sign_text; + } + + public boolean needPermToCreateSign(){ + return needPermToCreateSign; + } + + public int getInventorySize(){ + return mInvSize; + } + + public HashMap getItems(){ + return mShopItems; + } + + /** + * 获取商店下一个可用的上架位置,从1开始,<=0为无位置 + */ + public int getNextAvailableLoc(){ + Set tPos=this.getMenuItemSet(); + for(int i=0;i<81;i++){ + if(!tPos.contains(i)) return i+1; + } + return 0; + } + + /** + * 根据物品名(此为配置文件节点名,非显示的名字),获取商店物品 + * @param name 物品名 + * @return 如果不存在返回null + */ + public BSBuy getShopItem(String name){ + for(BSBuy sbuy : this.mShopItems.values()){ + if(sbuy.getName().equalsIgnoreCase(name)) return sbuy; + } + return null; + } + + public BSBuy getShopItem(int pos){ + return this.mShopItems.get(pos); + } + + /** + * 获取菜单物品的拷贝 + * @param pos + * @return + */ + public ItemStack getMenuItem(int pos){ + ItemStack menuItem=this.mMenuItems.get(pos); + if(menuItem==null) return null; + else return menuItem.clone(); + } + + /** + * 获取商店物品位置Set集合的拷贝 + */ + public Set getMenuItemSet(){ + Set pos=new HashSet(); + pos.addAll(this.mMenuItems.keySet()); + return pos; + } + + /** + * 添加物品到商店 + * @param buy + * @param menu_item + * @param manager + */ + public void addShopItem(BSBuy buy,ItemStack menu_item){ + boolean need_reedit=false; + if(menu_item.hasItemMeta()){ + ItemMeta meta=menu_item.getItemMeta(); + if(!need_reedit&&meta instanceof SkullMeta){ + SkullMeta skullMeta=(SkullMeta)meta; + if(skullMeta.hasOwner()) need_reedit=PATTERN.matcher(skullMeta.getOwner()).find(); + } + if(meta.hasDisplayName()){ + String st=buy.transformMessage(meta.getDisplayName()); + if(!need_reedit) need_reedit=PATTERN.matcher(st).find(); + meta.setDisplayName(st); + } + if(meta.hasLore()){ + List tList=meta.getLore(); + List l=new ArrayList(); + for(String s : tList){ + l.add(buy.transformMessage(s)); + if(!need_reedit) need_reedit=PATTERN.matcher(s).find(); + } + if(!FBukkit.isItemMetaEmpty(meta)) meta.setLore(l); + } + if(!FBukkit.isItemMetaEmpty(meta)) menu_item.setItemMeta(meta); + } + if(buy.hasLimit()||buy.hasBuyTime()||buy.hasPersonalLimit()) need_reedit=true; + buy.setNeedEdit(need_reedit); + menu_item=AttributeRemover.hideAttributes(menu_item,buy.getHideItemFlag()); + mShopItems.put(buy.getInventoryLocation(),buy); + mMenuItems.put(buy.getInventoryLocation(),menu_item); + this.mTotalWeight+=buy.getWeight()>0?buy.getWeight():0; + } + + /** + * 重新设置商店背包大小,在每次重载商店后调用 + */ + private void resetInventorySize(){ + int highest=0; + for(Integer pos : mShopItems.keySet()){ + BSBuy b=mShopItems.get(pos); + if(b!=null&&pos>highest){ + highest=pos; + } + } + highest++; + this.mInvSize=BSShop.getSuitableSize(highest); + } + + public void finishedAddingItems(){ + this.resetInventorySize(); + } + + /** + * 获取合适的背包大小 + */ + public static int getSuitableSize(int pSize){ + if(pSize>72) return 81; + int m=pSize/9; + if(m*9 + * 外部勿直接调用该函数,请通过{@link BSShopManager#openShop(Player, BSShop)来打开商店}
+ * 所有的信息均存储在holder中 + *

+ * @param pPlayer 玩家 + */ + public void openInventory(Player pPlayer){ + if(!this.mValid) return; + + BSShopHolder holder=null; + if(this.mSpecial) holder=new LotteryHolder((BossShop)this.mPlugin,pPlayer,this); + else holder=new BSShopHolder((BossShop)this.mPlugin,pPlayer,this); + Inventory inventory=Bukkit.createInventory(holder,this.mInvSize,this.mDisplayname); + holder.setInventory(inventory); + holder.updateInventory(); + pPlayer.openInventory(inventory); + } + + /** + * 为指定玩家更新指定的背包,如果商店已经无效,将会清空玩家背包并关闭 + * @param inv 指定的背包 + * @param player 指定的玩家 + */ + public void updateInventory(Inventory inv,Player player){ + if(!(inv.getHolder() instanceof BSShopHolder)) return; + BSShopHolder tHolder=(BSShopHolder)inv.getHolder(); + tHolder.updateInventory(); + } + + /** + * 为所有玩家更新打开此商店的背包,如果商店已经无效,将会清空玩家背包并关闭 + */ + public void updateAllInventory(){ + for(Player player : FBukkit.getOnlinePlayers()){ + Inventory inv=player.getOpenInventory().getTopInventory(); + if(inv.getHolder() instanceof BSShopHolder){ + BSShop shop=((BSShopHolder)inv.getHolder()).getShop(); + if(shop==this) this.updateInventory(inv,player); + } + } + } + + /** + * 检查商店是否特殊类型的商店 + */ + public boolean isShopSpecial(){ + return this.mSpecial; + } + + /** + * 检查商店是否合法,比如重载后文件不存在 + */ + public boolean isShopValid(){ + return this.mValid; + } + + private void loadItems(){ + ConfigurationSection shop_sec=this.mConfig.getConfigurationSection("shop"); + for(String key : shop_sec.getKeys(false)){ + ConfigurationSection sec=shop_sec.getConfigurationSection(key); + if(sec==null){ + this.mPlugin.severe("创建商品["+shop_sec.getName()+"]时发生了错误,文件:"+this.mShopName); + continue; + } + List menusec=sec.getStringList("MenuItem"); + if(menusec==null){ + this.mPlugin.severe("创建商品["+shop_sec.getName()+"]时发生了错误,菜单物品不存在"); + continue; + } + ItemStack menu=((BossShop)this.mPlugin).getManager(ItemStackCreator.class).createItemStackS(menusec,true); + BSBuy items=((BossShop)this.mPlugin).getManager(BuyItemHandler.class).createBuyItem(this,sec); + if(items==null) continue; + if(items.hasOwner()){ + if(items.getLimit()==0){ + this.removeItem(items.getName(),false); + shop_sec.set(key,null); + continue; + } + if(!items.isBuyTime()){ + ((BossShop)this.mPlugin).getManager(MailManager.class).addMail(items,this.mPlugin.getLangManager().getNode("Mail.FromOutDateSale")); + shop_sec.set(key,null); + continue; + } + } + this.addShopItem(items,menu); + } + } + + /** + * 删除商店配置文件中的商品 + * @param pname 商品名字 + * @param update 是否保存并重载配置 + */ + public ConfigurationSection removeItem(String pname,boolean update){ + ConfigurationSection sec=this.mConfig.getConfigurationSection("shop."+pname); + this.mConfig.set("shop."+pname,null); + if(update) this.saveConfig(); + BSBuy tBuy=this.getShopItem(pname); + if(tBuy!=null){ + this.mMenuItems.remove(tBuy.getInventoryLocation()); + this.mShopItems.remove(tBuy.getInventoryLocation()); + this.mTotalWeight-=tBuy.getWeight()>0?tBuy.getWeight():0; + } + return sec; + } + + /** + * 重新载入商店 + * @return + */ + public boolean reloadShop(){ + if(!this.mFile.exists()||this.mFile.isDirectory()){ + warn(C("Console.ShopFileNotExist")+"["+this.mShopName+"]"); + this.mValid=false; + this.mMenuItems.clear(); + this.mShopItems.clear(); + this.mConfig.set("shop",null); + return false; + } + this.reloadConfig(); + return true; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/core/BSShopHolder.java b/src/main/java/org/black_ixx/bossshop/core/BSShopHolder.java new file mode 100644 index 0000000..a291b54 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/core/BSShopHolder.java @@ -0,0 +1,82 @@ +package org.black_ixx.bossshop.core; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.managers.ShopCustomizer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; + +/** + * used by Inventory of BSShop + */ + +public class BSShopHolder implements InventoryHolder{ + + protected BossShop mPlugin; + protected final BSShop mShop; + protected final Player mOwner; + protected Inventory mInv; + + public BSShopHolder(BossShop pPlugin,Player pOwner,BSShop pShop){ + this.mPlugin=pPlugin; + this.mOwner=pOwner; + this.mShop=pShop; + } + + /** + * 获取holder所持有的背包 + */ + @Override + public Inventory getInventory(){ + return mInv; + } + + /** + * 设置holder的背包,必须调用 + * @param pInv 要设置成的背包 + */ + public void setInventory(Inventory pInv){ + this.mInv=pInv; + } + + /** + * 获取背包内的物品 + *

+ * 函数内部已经做过过滤,对于不符合条件的物品一律返回null + *

+ * @param pPos 位置 + * @return 如果不存在则为null + */ + public BSBuy getShopItem(int pPos){ + return mShop.getShopItem(pPos); + } + + /** + * 获取holder所关联的商店 + */ + public BSShop getShop(){ + return mShop; + } + + /** + * 获取holder所关联的玩家 + */ + public Player getOwner(){ + return this.mOwner; + } + + /** + * 将定制的物品放入到背包中 + * @return 如果商店不存在false,否则true + */ + public boolean updateInventory(){ + if(!this.mShop.isShopValid()){ + this.mInv.clear(); + this.mOwner.closeInventory(); + return false; + } + BossShop.getInstance().getManager(ShopCustomizer.class).updateInventory(this.mShop,this.mOwner,this.mInv,this.mShop.getMenuItemSet()); + return true; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/core/BSShopManager.java b/src/main/java/org/black_ixx/bossshop/core/BSShopManager.java new file mode 100644 index 0000000..3ec8deb --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/core/BSShopManager.java @@ -0,0 +1,203 @@ +package org.black_ixx.bossshop.core; + +import java.io.File; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.managers.DefaultCreator; +import org.black_ixx.bossshop.nbt.NBTEditManager; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; + +import cc.util.bossshop.FBukkit; +import cc.util.bossshop.pluginmodel.INeedReload; + +public class BSShopManager implements INeedReload{ + + private long mLastReloadTime=0; + private BossShop mPlugin; + private File folder; + private final HashMap shops=new HashMap(); + + /** + * 商店管理器 + * @param plugin + */ + public BSShopManager(BossShop plugin){ + this.mPlugin=plugin; + this.folder=new File(plugin.getDataFolder().getAbsolutePath()+"/shops/"); + this.mPlugin.registerReloadModel(this); + } + + /** + * 重载或载入所有商店 + */ + @Override + public boolean reloadConfig(){ + if(!folder.isDirectory()) createDefaults(); + Iterator> it=this.shops.entrySet().iterator(); + while(it.hasNext()){ + Map.Entry entry=it.next(); + BSShop shop=entry.getValue(); + if(!shop.reloadShop()){ + this.mPlugin.debug("商店["+shop.mShopName+"]由于配置文件不存在,移除"); + it.remove(); + } + } + for(File file : folder.listFiles()){ + if(file==null||!file.isFile()) continue; + String name=file.getName(); + if(name.toLowerCase().endsWith(".yml")) this.loadShop(file); + } + this.mPlugin.info("载入了 "+shops.size()+" Shops!"); + this.mLastReloadTime=System.currentTimeMillis(); + // 一定要在调用清理函数之前调用时间 + if(this.mPlugin.getConfigManager().isAutoClearNBT()) + this.mPlugin.getManager(NBTEditManager.class).clearNBT(); + this.updataAllInventory(); + return true; + } + + public long getLastReloadTime(){ + return this.mLastReloadTime; + } + + /** + * 重载所有由BS插件生成的背包 + *

商店配置重载后悔自动调用该函数

+ */ + public void updataAllInventory(){ + for(Player player : FBukkit.getOnlinePlayers()){ + Inventory inv=player.getOpenInventory().getTopInventory(); + if(inv.getHolder() instanceof BSShopHolder){ + BSShopHolder holder=(BSShopHolder)inv.getHolder(); + holder.getShop().updateInventory(inv,player); + } + } + } + + /** + * 检查根据机读名称(文件名)对应的商店是否存在 + * @param name 商店机读名称,忽略大小写 + * @return 是否存在 + */ + public boolean isShopExist(String name){ + return !(this.getShop(name)==null); + } + + /** + * 检查指定的商店是否存在(多用于配置重载后的商店数据检验) + * @param shop 商店 + * @return 是否存在 + */ + public boolean isShopExist(BSShop shop){ + return this.shops.containsValue(shop); + } + + /** + * 根据商店机读名称(文件名)获取指定的商店,不存在则返回null + * @param name 商店机读名称,忽略大小写 + * @return 商店 + */ + public BSShop getShop(String name){ + for(Map.Entry entry : this.shops.entrySet()){ + if(entry.getKey().equalsIgnoreCase(name)) return entry.getValue(); + } + return null; + } + + /** + * 增加一个商店到管理器中 + */ + public void addShop(BSShop pShop){ + this.shops.put(pShop.getShopName(),pShop); + } + + public BSShop removeShop(BSShop pShop){ + return this.shops.remove(pShop); + } + + /** + * 根据指定的文件载入所需的商店 + *

如果商店已经载入,则重新载入但是商店对象不变,如果商店文件不存在,则去除该商店

+ * @param file 要载入商店的文件 + * @return 载入后的商店,自动添加到商店列表 + */ + private BSShop loadShop(File file){ + String fileName=file.getName(); + String shopName=getName(fileName); + BSShop shop=this.getShop(shopName); + if(shop!=null) return shop; + else{ + shop=new BSShop(this.mPlugin,fileName,shopName); + shop.reloadConfig(); + this.shops.put(shopName,shop); + } + return shop; + } + + /** + * 根据文件名生成商店名 + * @param filename 文件名 + * @return 去掉后缀的字符串 + */ + public static String getName(String filename){ + int pos=filename.lastIndexOf('.'); + if(pos!=-1) return filename.substring(0,pos); + else return filename; + } + + /** + * 根据机读名字移除指定的商店 + * @param name 机读名字 + * @return 被移除的商店,如果不存在则为null + */ + private BSShop removeShop(String name){ + Iterator> it=this.shops.entrySet().iterator(); + while(it.hasNext()){ + Map.Entry entry=it.next(); + if(entry.getKey().equalsIgnoreCase(name)){ + it.remove(); + return entry.getValue(); + } + } + return null; + } + + /** + * 根据机读名称为玩家打开指定的商店 + * @param player 玩家 + * @param name 商店机读名 + */ + public void openShop(Player player,String name){ + BSShop shop=this.getShop(name); + if(shop==null){ + this.mPlugin.send(player,this.mPlugin.getLangManager().getNode("Main.ShopNotExisting")); + return; + } + this.openShop(player,shop); + } + + /** + * 为玩家打开指定的商店 + * @param player 玩家 + * @param shop 商店 + */ + public void openShop(Player player,BSShop shop){ + shop.openInventory(player); + this.mPlugin.send(player,shop.getEnterMessage()); + } + + public HashMap getShops(){ + return shops; + } + + /** + * 创建默认的商店 + */ + public void createDefaults(){ + new DefaultCreator().addAllExamples(); + } +} diff --git a/src/main/java/org/black_ixx/bossshop/events/BSDisplayItemEvent.java b/src/main/java/org/black_ixx/bossshop/events/BSDisplayItemEvent.java new file mode 100644 index 0000000..ea5295b --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/events/BSDisplayItemEvent.java @@ -0,0 +1,52 @@ +package org.black_ixx.bossshop.events; +import org.black_ixx.bossshop.core.BSBuy; +import org.black_ixx.bossshop.core.BSShop; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class BSDisplayItemEvent extends Event implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Player player; + private final BSBuy buy; + private final BSShop shop; + private boolean cancelled = false; + public BSDisplayItemEvent(Player player, BSShop shop, BSBuy buy) { + this.player=player; + this.buy=buy; + this.shop=shop; + } + + public Player getPlayer() { + return player; + } + + public BSBuy getShopItem(){ + return buy; + } + + public BSShop getShop(){ + return shop; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + +} \ No newline at end of file diff --git a/src/main/java/org/black_ixx/bossshop/events/BSPlayerPurchaseEvent.java b/src/main/java/org/black_ixx/bossshop/events/BSPlayerPurchaseEvent.java new file mode 100644 index 0000000..7b3cfb7 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/events/BSPlayerPurchaseEvent.java @@ -0,0 +1,55 @@ +package org.black_ixx.bossshop.events; + +import org.black_ixx.bossshop.core.BSBuy; +import org.black_ixx.bossshop.core.BSShop; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class BSPlayerPurchaseEvent extends Event implements Cancellable{ + + private static final HandlerList handlers=new HandlerList(); + private final Player player; + private final BSBuy buy; + private final BSShop shop; + private boolean cancelled=false; + + public BSPlayerPurchaseEvent(Player player,BSShop shop,BSBuy buy){ + this.player=player; + this.buy=buy; + this.shop=shop; + } + + public Player getPlayer(){ + return player; + } + + public BSBuy getShopItem(){ + return buy; + }; + + public BSShop getShop(){ + return shop; + } + + @Override + public boolean isCancelled(){ + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled){ + this.cancelled=cancelled; + } + + public static HandlerList getHandlerList(){ + return handlers; + } + + @Override + public HandlerList getHandlers(){ + return handlers; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/events/BSPlayerPurchasedEvent.java b/src/main/java/org/black_ixx/bossshop/events/BSPlayerPurchasedEvent.java new file mode 100644 index 0000000..7bbe57b --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/events/BSPlayerPurchasedEvent.java @@ -0,0 +1,43 @@ +package org.black_ixx.bossshop.events; + +import org.black_ixx.bossshop.core.BSBuy; +import org.black_ixx.bossshop.core.BSShop; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class BSPlayerPurchasedEvent extends Event{ + + private static final HandlerList handlers=new HandlerList(); + private final Player player; + private final BSBuy buy; + private final BSShop shop; + + public BSPlayerPurchasedEvent(Player player,BSShop shop,BSBuy buy){ + this.player=player; + this.buy=buy; + this.shop=shop; + } + + public Player getPlayer(){ + return player; + } + + public BSBuy getShopItem(){ + return buy; + } + + public BSShop getShop(){ + return shop; + } + + public static HandlerList getHandlerList(){ + return handlers; + } + + @Override + public HandlerList getHandlers(){ + return handlers; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/events/BSReloadedEvent.java b/src/main/java/org/black_ixx/bossshop/events/BSReloadedEvent.java new file mode 100644 index 0000000..6f39080 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/events/BSReloadedEvent.java @@ -0,0 +1,26 @@ +package org.black_ixx.bossshop.events; +import org.black_ixx.bossshop.BossShop; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class BSReloadedEvent extends Event { + private static final HandlerList handlers = new HandlerList(); + private BossShop plugin; + public BSReloadedEvent(BossShop plugin) { + this.plugin=plugin; + } + + public BossShop getBossShop(){ + return plugin; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + +} \ No newline at end of file diff --git a/src/main/java/org/black_ixx/bossshop/listeners/InventoryListener.java b/src/main/java/org/black_ixx/bossshop/listeners/InventoryListener.java new file mode 100644 index 0000000..eb80efe --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/listeners/InventoryListener.java @@ -0,0 +1,108 @@ +package org.black_ixx.bossshop.listeners; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.UUID; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSBuy; +import org.black_ixx.bossshop.core.BSEnums; +import org.black_ixx.bossshop.core.BSShopHolder; +import org.black_ixx.bossshop.lottery.LotteryHolder; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.Event.Result; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryType.SlotType; +import org.bukkit.inventory.ItemStack; + +import cc.util.bossshop.pluginmodel.IClearAble; + +public class InventoryListener implements Listener,IClearAble{ + + private BossShop mPlugin; + private long mLastClear=0; + private long mClearDis=3600000; + private HashMap mWait=new HashMap<>(); + + public InventoryListener(BossShop pPlugin){ + this.mPlugin=pPlugin; + this.mPlugin.getServer().getPluginManager().registerEvents(this,this.mPlugin); + this.mPlugin.registerClearModel(this); + } + + @Override + public boolean clearStatus(){ + this.mWait.clear(); + return true; + } + + /** + * 在一定的时间后清理掉无用的等待列表 + */ + private void clearWaitList(){ + if((this.mLastClear+this.mClearDis)>System.currentTimeMillis()) return; + for(Iterator> it=this.mWait.entrySet().iterator();it.hasNext();){ + if((it.next().getValue()+this.mPlugin.getConfigManager().getCoolTime()*1000)System.currentTimeMillis()){ + this.mPlugin.send(pPlayer,this.mPlugin.getLangManager().get("Main.YouClickTooQuick")); + return; + } + + if(!buy.canBuy(pPlayer,holder)) return; + buy.stockOut(pPlayer,holder); + + }catch(Throwable exp){ + this.mPlugin.severe(exp.getMessage(),exp); + this.mPlugin.send(pPlayer,this.mPlugin.getLangManager().get("Main.ErrorHappend")); + } + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/listeners/PlayerListener.java b/src/main/java/org/black_ixx/bossshop/listeners/PlayerListener.java new file mode 100644 index 0000000..da8a82d --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/listeners/PlayerListener.java @@ -0,0 +1,67 @@ +package org.black_ixx.bossshop.listeners; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSShopManager; +import org.black_ixx.bossshop.mail.MailManager; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.inventory.ItemStack; + +import cc.util.bossshop.config.CommentedYamlConfig; +import cc.util.bossshop.pluginmodel.INeedConfig; + +public class PlayerListener implements Listener,INeedConfig{ + + private BossShop mPlugin; + private Material mOpenItem=Material.WATCH; + private boolean mSneakNeed=true; + + public PlayerListener(BossShop pPlugin){ + this.mPlugin=pPlugin; + this.mPlugin.getServer().getPluginManager().registerEvents(this,this.mPlugin); + this.mPlugin.registerConfigModel(this); + } + + @EventHandler(priority=EventPriority.HIGHEST) + public void onPlayerInteractItem(PlayerInteractEvent e){ + Player player=e.getPlayer(); + ItemStack item=player.getItemInHand(); + if(item==null||item.getType()==Material.AIR||item.getType()!=this.mOpenItem) return; + if(!player.hasPermission("BossShop.open.item")) return; + if(!mSneakNeed||player.isSneaking()){ + e.setCancelled(true); + this.mPlugin.getManager(BSShopManager.class).openShop(player,this.mPlugin.getConfigManager().getMainShop()); + } + } + + @EventHandler + public void onPlayerJoin(final PlayerJoinEvent pEvent){ + Bukkit.getScheduler().runTaskLater(this.mPlugin,new Runnable(){ + @Override + public void run(){ + PlayerListener.this.mPlugin.getManager(MailManager.class).noticeExistMail(pEvent.getPlayer()); + } + },100); + } + + @Override + public void setConfig(){ + CommentedYamlConfig tConfig=this.mPlugin.getConfigManager().getConfig(); + int bind_menn_item=tConfig.getInt("OpenMainShop.BindItem",347); + this.mOpenItem=Material.getMaterial(bind_menn_item); + if(this.mOpenItem==null){ + this.mPlugin.warn("绑定打开BossShop主商店的的物品["+bind_menn_item+"]不是一个有效的物品,将设置为默认值的347(钟表)"); + tConfig.set("OpenMainShop.BindItem",347); + this.mPlugin.getConfigManager().saveConfig(); + this.mOpenItem=Material.WATCH; + }else this.mPlugin.info("使用"+this.mOpenItem.name()+"作为打开BossShop主商店的的物品"); + this.mSneakNeed=tConfig.getBoolean("OpenMainShop.SneakNeed"); + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/listeners/SignListener.java b/src/main/java/org/black_ixx/bossshop/listeners/SignListener.java new file mode 100644 index 0000000..ccc6d4a --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/listeners/SignListener.java @@ -0,0 +1,97 @@ +package org.black_ixx.bossshop.listeners; + +import java.util.HashMap; +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSShop; +import org.black_ixx.bossshop.core.BSShopManager; +import org.black_ixx.bossshop.managers.StringManager; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Sign; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +import cc.util.bossshop.pluginmodel.INeedConfig; + +public class SignListener implements Listener,INeedConfig{ + + private boolean mEnable; + private BossShop mPlugin; + + public SignListener(BossShop plugin){ + this.mPlugin=plugin; + this.mPlugin.getServer().getPluginManager().registerEvents(this,this.mPlugin); + this.mPlugin.registerConfigModel(this); + } + + @Override + public void setConfig(){ + this.mEnable=this.mPlugin.getConfigManager().signsEnabled(); + } + + private BSShop getBossShopSign(String line){ + if(line==null||line=="") return null; + line=line.toLowerCase(); + HashMap set=this.mPlugin.getManager(BSShopManager.class).getShops(); + for(String s : set.keySet()){ + BSShop shop=set.get(s); + if(line.endsWith(shop.getSignText().toLowerCase())) return shop; + } + return null; + } + + @EventHandler + public void createSign(SignChangeEvent e){ + if(!this.mEnable) return; + BSShop shop=getBossShopSign(e.getLine(0)); + if(shop!=null){ + if(shop.needPermToCreateSign()){ + if(!e.getPlayer().hasPermission("BossShop.createSign")){ + this.mPlugin.send(e.getPlayer(),this.mPlugin.getLangManager().getNode("Main.NoPermitToCreateSignShop")); + e.setCancelled(true); + return; + } + } + if(e.getLine(0)!=""){ + e.setLine(0,StringManager.transform(e.getLine(0))); + } + if(e.getLine(1)!=""){ + e.setLine(1,StringManager.transform(e.getLine(1))); + } + if(e.getLine(2)!=""){ + e.setLine(2,StringManager.transform(e.getLine(2))); + } + if(e.getLine(3)!=""){ + e.setLine(3,StringManager.transform(e.getLine(3))); + } + } + } + + @EventHandler + public void interactSign(PlayerInteractEvent e){ + if(!this.mEnable) return; + if(e.getClickedBlock()!=null){ + if(e.getAction()==Action.RIGHT_CLICK_BLOCK){ + Block b=e.getClickedBlock(); + if(b.getType()==Material.SIGN||b.getType()==Material.SIGN_POST||b.getType()==Material.WALL_SIGN){ + if(b.getState() instanceof Sign){ + Sign s=(Sign)b.getState(); + BSShop shop=getBossShopSign(s.getLine(0)); + if(shop!=null){ + if(e.getPlayer().hasPermission("BossShop.open")||e.getPlayer().hasPermission("BossShop.open.sign")){ + this.mPlugin.getManager(BSShopManager.class).openShop(e.getPlayer(),shop); + return; + } + this.mPlugin.send(e.getPlayer(),this.mPlugin.getLangManager().getNode("Main.NoPermitToOpenSignShop")); + return; + } + } + } + } + } + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/lottery/LotteryHolder.java b/src/main/java/org/black_ixx/bossshop/lottery/LotteryHolder.java new file mode 100644 index 0000000..5540681 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/lottery/LotteryHolder.java @@ -0,0 +1,224 @@ +package org.black_ixx.bossshop.lottery; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Random; +import java.util.Set; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSBuy; +import org.black_ixx.bossshop.core.BSEnums.BSBuyType; +import org.black_ixx.bossshop.managers.ShopCustomizer; +import org.black_ixx.bossshop.core.BSShop; +import org.black_ixx.bossshop.core.BSShopHolder; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +public class LotteryHolder extends BSShopHolder{ + + private int mStopCount=4; + private LotteryStatus mStatus=LotteryStatus.UNOPENED; + private final Set mNowShow=new HashSet<>(); + private Random mItemRandom=new Random(System.currentTimeMillis()); + + public LotteryHolder(BossShop pPlugin,Player pOwner,BSShop pShop){ + super(pPlugin,pOwner,pShop); + } + + /** + * 获取当前乐透状态 + */ + public LotteryStatus getStatus(){ + synchronized(this){ + return this.mStatus; + } + } + + /** + * 切换抽奖背包状态为LotteryStatus.RUNNING,并刷新一次 + */ + public void startLottery(){ + this.mStatus=LotteryStatus.RUNNING; + this.mStopCount=LotteryManager.mRefalshFastCount; + this.gotoNextFrame(); + } + + /** + * 重置乐透,切换抽奖背包状态为{@link LotteryStatus#WAIT} + */ + public void resetLottery(){ + this.mStatus=LotteryStatus.UNOPENED; + this.mNowShow.clear(); + this.mInv.clear(); + this.updateInventory(); + } + + /** + * 关闭乐透,在背包关闭时调用,此时切换背包状态为{@link LotteryStatus#CLOSE} + */ + public void closeLottery(){ + this.mInv.clear(); + this.mStatus=LotteryStatus.CLOSE; + } + + @Override + public BSBuy getShopItem(int pPos){ + synchronized(this){ + if(this.mStatus==LotteryStatus.WAIT){ + BSBuy tBuy=super.getShopItem(pPos); + if(tBuy!=null&&tBuy.getBuyType()==BSBuyType.Lottery&&tBuy.getWeight()<=0) return tBuy; + else return null; + }else if(this.mStatus==LotteryStatus.RUNNING){ + return null; + }else if(this.mStatus==LotteryStatus.RESULT){ + return super.getShopItem(pPos); + } + return null; + } + } + + /** + * 只在配置重载,或者背包打开时调用 + *

+ *1.同步更新配置到物品
+ *2.用于在背包打开的时候切换holder从{@link LotteryStatus#UNOPENED}到{@link LotteryStatus#WAIT} + *

+ */ + @Override + public boolean updateInventory(){ + synchronized(this){ + if(!this.mShop.isShopValid()){ + this.mInv.clear(); + this.mOwner.closeInventory(); + return false; + } + if(this.mStatus==LotteryStatus.UNOPENED){ + this.mStatus=LotteryStatus.WAIT; + this.mStopCount=4; + if(!LotteryManager.existTask(this)){ + ReflashInventoryTask tTask=new ReflashInventoryTask(this); + int tTaskId=Bukkit.getScheduler().scheduleAsyncRepeatingTask(BossShop.getInstance(),tTask,LotteryManager.mReflashBaseTicks,LotteryManager.mReflashBaseTicks); + LotteryManager.addTask(this,tTaskId); + this.mPlugin.debug("新增抽奖背包刷新任务"); + } + return this.gotoNextFrame(); + } + this.mPlugin.getManager(ShopCustomizer.class).updateInventory(this.mShop,this.mOwner,this.mInv,this.mNowShow); + return true; + } + } + + /** + * 显示抽奖背包的下一帧动画 + *

+ * 在背包状态为{@link LotteryStatus#WAIT}和{@link LotteryStatus#RUNNING}的时候调用
+ * 在{@link LotteryStatus#RUNNING}状态的最后一帧动画时,由该函数切换状态为{@link LotteryStatus#RESULT} + *

+ */ + public boolean gotoNextFrame(){ + synchronized(this){ + if(!this.mShop.isShopValid()){ + this.mInv.clear(); + this.mOwner.closeInventory(); + return false; + } + if(this.mStatus==LotteryStatus.WAIT){ + if(this.mStopCount<=0){ + this.mInv.clear(); + this.reselectMenuItem(); + this.mPlugin.getManager(ShopCustomizer.class).updateInventory(this.mShop,this.mOwner,this.mInv,this.mNowShow); + this.mStopCount=LotteryManager.mReflashLowMultiple; + } + this.mStopCount--; + }else if(this.mStatus==LotteryStatus.RUNNING&&this.mStopCount>0){ + this.mInv.clear(); + this.reselectMenuItem(); + this.mPlugin.getManager(ShopCustomizer.class).updateInventory(this.mShop,this.mOwner,this.mInv,this.mNowShow); + this.mStopCount--; + }else if(this.mStopCount<=0||this.mStatus==LotteryStatus.RESULT){ + if(this.mStatus!=LotteryStatus.RESULT){ + //一定要先清背包,再切换状态 + this.mInv.clear(); + this.mNowShow.clear(); + this.mStatus=LotteryStatus.RESULT; + int result=this.getResultItem(); + this.mNowShow.add(result); + this.mPlugin.debug("生成抽奖结果["+result+"]"); + this.mPlugin.getManager(ShopCustomizer.class).updateInventory(this.mShop,this.mOwner,this.mInv,this.mNowShow); + } + } + return true; + } + } + + /** + * 重新设置{@link LotteryHolder#mNowShow}的内容 + *

+ * 此函数不会自动刷新背包,需要自己调用刷新函数
+ * 如果背包当前状态为{@link LotteryStatus#WAIT}状态时,会自动添加一个抽奖按钮到当前显示列表 + * 该抽奖按钮的设置条件为类型={@link BSBuyType#Lottery}且weight为0,且是第一个,该物品的添加 + * 不会影响显示比例
+ * 如果概率显示物品的数量除按钮外为零个,那么如果存在可以显示的物品,显示物品会自少添加一个到显示列表 + *

+ */ + private void reselectMenuItem(){ + HashSet tOldItem=new HashSet<>(); + tOldItem.addAll(this.mNowShow); + this.mNowShow.clear(); + ArrayList canShowItem=new ArrayList<>(); + //第一遍循环找出所有可以显示给玩家的商品,如果是等待状态,添加抽奖按钮到显示列表 + boolean find=false; + Random random=new Random(); + int total=0; + for(Integer sPos : this.mShop.getMenuItemSet()){ + BSBuy buy=this.mShop.getShopItem(sPos); + if(buy==null||!buy.disaplyToPlayer(this.mOwner)) continue; + if(buy.getBuyType()==BSBuyType.Lottery&&buy.getWeight()<=0){ + if(this.mStatus==LotteryStatus.WAIT&&!find){ + find=true; + this.mNowShow.add(sPos); + continue; + }else if(this.mStatus==LotteryStatus.RUNNING){ + continue; + } + + } + total++; + if(tOldItem.contains(sPos)) continue; + int tPos=random.nextInt(canShowItem.size()+1); + canShowItem.add(tPos,sPos); + } + int showCount=total*LotteryManager.mShowPrecent/100; + if(showCount<=0) showCount=1; + for(int index=0;index如果没有符合条件的物品将返回-1

+ * @return 物品背包位置 + */ + private int getResultItem(){ + LinkedHashMap weigthQueue=new LinkedHashMap<>(); + //设置权重队列值 + int addWeight=0; + for(Integer sPos : this.mShop.getMenuItemSet()){ + BSBuy buy=this.mShop.getShopItem(sPos); + if(buy==null||buy.getWeight()<=0||!buy.disaplyToPlayer(this.mOwner)) continue; + addWeight+=buy.getWeight(); + weigthQueue.put(addWeight,sPos); + } + if(weigthQueue.size()==0) return -1; + int findWeight=mItemRandom.nextInt(addWeight+1); + //确定是哪个物品 + for(int sWeight : weigthQueue.keySet()){ + if(sWeight50) mShowPrecent=34; + this.mPlugin.debug("已经重载抽奖配置:"+mReflashBaseTicks+"|"+mReflashLowMultiple+"|"+mRefalshFastCount+"|"+mShowPrecent); + } + + + /** + * 任务列表 + *

+ * 第一个参数为任务目标背包的持有者
+ * 第二个参数为Bukkit任务ID + *

+ */ + private final static HashMap mTasks=new HashMap<>(); + + /** + * 添加一个任务到列表中 + * @param pHolder 任务关联的holder + * @param pTaskId Bukkit任务Id + */ + public static void addTask(LotteryHolder pHolder,int pTaskId){ + LotteryManager.mTasks.put(pHolder,pTaskId); + } + + /** + * 尝试停止一个列表中的任务 + * @param pHolder 任务关联的holder + */ + public static void stopTask(LotteryHolder pHolder){ + Integer tTaskId=LotteryManager.mTasks.remove(pHolder); + if(tTaskId!=null){ + Bukkit.getScheduler().cancelTask(tTaskId); + } + } + + /** + * 检查holder是否已经有关联的任务存在 + * @param pHolder 任务关联的holder + * @return 是否存在 + */ + public static boolean existTask(LotteryHolder pHolder){ + return LotteryManager.mTasks.containsKey(pHolder); + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/lottery/LotteryStatus.java b/src/main/java/org/black_ixx/bossshop/lottery/LotteryStatus.java new file mode 100644 index 0000000..29bd7a9 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/lottery/LotteryStatus.java @@ -0,0 +1,25 @@ +package org.black_ixx.bossshop.lottery; + + +public enum LotteryStatus{ + /** + * 玩家未打开背包时 + */ + UNOPENED, + /** + * 等待用户运行抽奖系统,当前状态为默认的,慢速循环显示物品的状态 + */ + WAIT, + /** + * 正在运行抽奖状态,快速循环显示物品 + */ + RUNNING, + /** + * 抽奖结果显示状态,只有一个物品被显示 + */ + RESULT, + /** + * 背包在打开后被关闭状态 + */ + CLOSE +} diff --git a/src/main/java/org/black_ixx/bossshop/lottery/ReflashInventoryTask.java b/src/main/java/org/black_ixx/bossshop/lottery/ReflashInventoryTask.java new file mode 100644 index 0000000..a399e90 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/lottery/ReflashInventoryTask.java @@ -0,0 +1,31 @@ +package org.black_ixx.bossshop.lottery; + +import org.bukkit.inventory.Inventory; + +public class ReflashInventoryTask implements Runnable{ + + private final LotteryHolder mHolder; + private final Inventory mInv; + + public ReflashInventoryTask(LotteryHolder pHolder){ + this.mHolder=pHolder; + this.mInv=this.mHolder.getInventory(); + } + + @Override + public void run(){ + LotteryStatus status=this.mHolder.getStatus(); + if(this.mInv.getViewers().isEmpty()) this.stopTask(); + if(status==LotteryStatus.WAIT||status==LotteryStatus.RUNNING){ + this.mHolder.gotoNextFrame(); + }else if(status==LotteryStatus.CLOSE){ + this.stopTask(); + } + //status==LotteryStatus.RESULT时等待,不关闭任务,可以为下次连续抽奖做准备 + } + + protected void stopTask(){ + LotteryManager.stopTask(this.mHolder); + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/mail/MailItem.java b/src/main/java/org/black_ixx/bossshop/mail/MailItem.java new file mode 100644 index 0000000..c4156b8 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/mail/MailItem.java @@ -0,0 +1,94 @@ +package org.black_ixx.bossshop.mail; + +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSEnums.BSBuyType; +import org.black_ixx.bossshop.managers.ItemNameManager; +import org.bukkit.inventory.ItemStack; + +public class MailItem{ + + private BSBuyType mItemType; + /** + * 过期物品 + * item-->ItemStack,money->int,point->int + */ + private Object mItem; + private String mSource; + /** + * 物品预览 + */ + private String mPreview=null; + + /** + * 构造一个Mail物品 + * @param pItem 物品,允许Integer,List ItemStack + * @param pSaleType 物品类型,只支持Item,points,money + * @param pCount 数量 + * @param pSource 来源,用于生成预览 + */ + public MailItem(Object pItem,BSBuyType pSaleType,int pCount,String pSource){ + this.mItem=pItem; + this.mItemType=pSaleType; + this.mSource=pSource; + this.mPreview=this.resetItem(pCount); + } + + protected String C(String pNode){ + return BossShop.getInstance().getLangManager().getNode(pNode); + } + + /** + * 创建邮件物品的预览 + * @param pCount 数量 + * @return 预览 + */ + protected String resetItem(int pCount){ + switch(this.mItemType){ + case Money: + if(pCount>=1) this.mItem=((Integer)this.mItem)*pCount; + this.mPreview=this.mItem+C("Word.Money"); + break; + case Points: + if(pCount>=1) this.mItem=((Integer)this.mItem)*pCount; + this.mPreview=this.mItem+C("Word.Points"); + break; + case Item: + ItemStack tItem=null; + if(this.mItem instanceof List) tItem=((List)this.mItem).get(0); + else tItem=(ItemStack)this.mItem; + this.mItem=tItem; + if(pCount>=1) tItem.setAmount(tItem.getAmount()*pCount); + String itemname=BossShop.getInstance().getManager(ItemNameManager.class).getDisPlayName(tItem); + this.mPreview=tItem.getAmount()+C("Word.Count")+itemname; + break; + default: + BossShop.getInstance().warn(C("CFG.UnsupportMailItemType")+"["+this.mItemType.name()+"]"); + } + this.mPreview=(StringUtils.isEmpty(mSource)?C("Mail.FromUnknow")+": ":this.mSource+": ")+this.mPreview; + return this.mPreview; + } + + public String getPreview(){ + return this.mPreview; + } + + public String getSource(){ + return this.mSource; + } + + /** + * 邮件列表中的物品统一为一件 + * @return + */ + public Object getItem(){ + return this.mItem; + } + + public BSBuyType getItemType(){ + return this.mItemType; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/mail/MailManager.java b/src/main/java/org/black_ixx/bossshop/mail/MailManager.java new file mode 100644 index 0000000..c7ca9da --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/mail/MailManager.java @@ -0,0 +1,310 @@ +package org.black_ixx.bossshop.mail; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.UUID; +import java.util.Map.Entry; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSBuy; +import org.black_ixx.bossshop.core.BSEnums; +import org.black_ixx.bossshop.core.BSEnums.BSBuyType; +import org.black_ixx.bossshop.managers.ItemStackCreator; +import org.black_ixx.bossshop.managers.WorthHandler; +import org.black_ixx.bossshop.nbt.NBTEditManager; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import cc.util.bossshop.config.CommentedYamlConfig; +import cc.util.bossshop.filemanager.AFileManager; +import cc.util.bossshop.pluginmodel.INeedConfig; +import cc.util.bossshop.pluginmodel.INeedReload; + +public class MailManager extends AFileManager implements INeedConfig,INeedReload{ + + public static final String CFG_MAIN="MailSystem"; + public static final String SEC_ItemType="Type"; + public static final String SEC_ItemContent="Content"; + public static final String SEC_Source="Source"; + public static final String SEC_MaxSize="MaxSize"; + public static final String SEC_SendCost="SendCost"; + private int mMaxSize=10; + private int mSendCost=1000; + private final HashMap> mMailItems=new HashMap<>(); + private long mLastReloadTime=0; + + public MailManager(BossShop pPlugin){ + super(pPlugin,"mail_item.yml","1.0"); + this.mPlugin.registerConfigModel(this); + this.mPlugin.registerReloadModel(this); + } + + public long getLastReloadTime(){ + return this.mLastReloadTime; + } + + @Override + public void setConfig(){ + CommentedYamlConfig tConfig=this.mPlugin.getConfigManager().getConfig(); + this.mMaxSize=tConfig.getInt(CFG_MAIN+"."+SEC_MaxSize,mMaxSize); + this.mSendCost=tConfig.getInt(CFG_MAIN+"."+SEC_SendCost,mSendCost); + } + + @Override + public boolean reloadConfig(){ + if(!super.reloadConfig()) return false; + this.mLastReloadTime=System.currentTimeMillis(); + this.loadMailItem(); + return true; + } + + /** + * 从配置文件中载入邮件物品 + */ + protected void loadMailItem(){ + this.mMailItems.clear(); + for(String player_key : this.mConfig.getKeys(false)){ + ConfigurationSection player_sec=this.mConfig.getConfigurationSection(player_key); + if(player_sec==null){ + this.mConfig.set(player_key,null); + continue; + } + UUID tUuid=null; + try{ + tUuid=UUID.fromString(player_key); + }catch(Exception exp){ + this.mPlugin.severe(ChatColor.RED+"配置文件["+this.mFileName+"]存在错误,["+player_key+"] 必须为UUID"); + continue; + } + ArrayList list=new ArrayList<>(); + for(String item_key : player_sec.getKeys(false)){ + try{ + ConfigurationSection item_sec=player_sec.getConfigurationSection(item_key); + if(item_sec==null) continue; + BSBuyType tItemType=BSEnums.getBSBuyType(item_sec.getString(SEC_ItemType)); + Object tItem=null; + if(tItemType==null){ + this.warn(C("CFG.UnsupportMailItemType")+"["+item_sec.getString(SEC_ItemType)+"]"); + continue; + } + switch(tItemType){ + case Money: + tItem=item_sec.get(SEC_ItemContent); + break; + case Points: + tItem=item_sec.get(SEC_ItemContent); + break; + case Item: + tItem=((BossShop)this.mPlugin).getManager(ItemStackCreator.class).createItemStackS(item_sec.getStringList(SEC_ItemContent),false); + if(tItem==null){ + this.warn(C("CannotCreatMailItem")); + continue; + } + break; + default: + this.warn(C("CFG.UnsupportMailItemType")+"["+tItemType.name()+"]"); + continue; + } + list.add(new MailItem(tItem,tItemType,1,item_sec.getString(SEC_Source))); + if(list.size()>=this.mMaxSize) break; + }catch(Exception exp){ + this.mPlugin.severe(exp.getMessage(),exp); + } + } + this.mMailItems.put(tUuid,list); + } + } + + /** + * 添加一封来自他人的邮件,调用前请先检查邮件是否已经满了 + *

如果玩家在线,会提醒玩家有新邮件

+ * @param pFrom 来自谁 + * @param pTo 给谁 + * @param pItem 物品 + */ + public MailItem addMail(Player pFrom,Player pTo,ItemStack pItem){ + MailItem tItem=new MailItem(pItem,BSBuyType.Item,1,C("Mail.FromPlayer")+pFrom.getName()); + if(!this.addMailToFile(pTo.getUniqueId(),tItem)) return null; + this.addMail(pTo.getUniqueId(),tItem); + return tItem; + } + + /** + * 添加过期物品到邮件列表,如果玩家在线会发送提醒 + * @return 失败返回空 + */ + public MailItem addMail(BSBuy pBuy,String pSource){ + if(!pBuy.hasOwner()||pBuy.getLimit()<=0) return null; + if(this.isMailFull(pBuy.getOwner())){ + this.noticeMailFull(pBuy.getOwner()); + return null; + } + MailItem tItem=new MailItem(pBuy.getReward(),pBuy.getBuyType(),pBuy.getLimit(),pSource); + if(!this.addMailToFile(pBuy.getOwner(),tItem)) return null; + this.addMail(pBuy.getOwner(),tItem); + return tItem; + } + + /** + * 添加邮件到邮件列表,调用前必须检查isMailFull
+ * 在此函数中统一发送通知消息 + */ + private void addMail(UUID pUUID,MailItem pItem){ + ArrayList tItems=this.mMailItems.get(pUUID); + if(tItems==null){ + tItems=new ArrayList<>(); + this.mMailItems.put(pUUID,tItems); + } + tItems.add(pItem); + Player tPlayer=Bukkit.getPlayer(pUUID); + if(tPlayer!=null&&tPlayer.isOnline()){ + this.mPlugin.send(tPlayer,C("Mail.YouReciveMail").replace("%where%",pItem.getSource())); + //tPlayer.playSound(tPlayer.getLocation(),org.bukkit.Sound.ENTITY_PLAYER_LEVELUP,1,1); + } + } + + /** + * 玩家邮箱是否达到了数量上限 + */ + public boolean isMailFull(UUID pUUID){ + if(this.mMaxSize<=0) return true; + ConfigurationSection tUserSec=this.mConfig.getConfigurationSection(pUUID.toString()); + if(tUserSec==null) return false; + return tUserSec.getKeys(false).size()>=this.mMaxSize; + } + + /** + * 通知玩家邮箱已经满了 + * @param pUUID + */ + public void noticeMailFull(UUID pUUID){ + Player tPlayer=Bukkit.getPlayer(pUUID); + if(tPlayer==null||!tPlayer.isOnline()) return; + this.mPlugin.send(tPlayer,C("YouMailIsFull")); + } + + public void noticeExistMail(Player pPlayer){ + if(pPlayer==null||!pPlayer.isOnline()) return; + int mailCount=this.getMailCount(pPlayer.getUniqueId()); + if(mailCount<=0) ;//TODO 你没有邮件 + else ; //TODO 你有邮件 + } + + /** + * 添加邮件到文件,调用前必须检查isMailFull + * @param pUUID 玩家UUID + * @param pItem 邮件 + */ + protected boolean addMailToFile(UUID pUUID,MailItem pItem){ + ConfigurationSection tUserSec=this.mConfig.getConfigurationSection(pUUID.toString()); + if(tUserSec==null) tUserSec=this.mConfig.createSection(pUUID.toString()); + long time=System.currentTimeMillis()/1000; + String tItemLabel="item_"+time; + while(tUserSec.contains(tItemLabel)) + tItemLabel="item_"+(++time); + ConfigurationSection tItemSec=tUserSec.createSection(tItemLabel); + tItemSec.set(SEC_ItemType,pItem.getItemType().name()); + switch(pItem.getItemType()){ + case Money: + case Points: + tItemSec.set(SEC_ItemContent,pItem.getItem()); + break; + case Item: + ItemStack item=(ItemStack)pItem.getItem(); + ArrayList tItemInfo=new ArrayList<>(); + tItemInfo.add("id:"+item.getTypeId()); + tItemInfo.add("durability:"+item.getDurability()); + tItemInfo.add("amount:"+item.getAmount()); + String nbtlabel=((BossShop)this.mPlugin).getManager(NBTEditManager.class).addItemNBTToStock(item,true); + if(nbtlabel!=null) tItemInfo.add("nbt:"+nbtlabel); + tItemSec.set(SEC_ItemContent,tItemInfo); + break; + default: + this.warn(ChatColor.RED+"["+pItem.getItemType().name()+"]"); + this.warn(C("Mail.UnsupportMailItemType")); + tUserSec.set(tItemLabel,null); + return false; + } + tItemSec.set(SEC_Source,pItem.getSource()); + this.saveConfig(); + return true; + } + + /** + * 返回玩家所有过期几首物品的预览 + * @param pUUID 玩家UUID + * @return 过期物品预览列表,或没有邮件的消息 + */ + public ArrayList checkMail(UUID pUUID){ + ArrayList items_preview=new ArrayList<>(); + for(Entry> entry : this.mMailItems.entrySet()){ + if(entry.getKey().equals(pUUID)){ + int i=1; + for(MailItem sitem : entry.getValue()){ + items_preview.add(i+". "+sitem.getPreview()); + i++; + } + break; + } + } + if(items_preview.size()!=0){ + items_preview.add(0,C("Mail.MailMaxSaveNumb").replace("%numb%",items_preview.size()+"/"+this.mMaxSize)); + items_preview.add(items_preview.size(),"===================="); + }else items_preview.add(C("Mail.YouHaveNoMail")); + return items_preview; + } + + public int getMailCount(UUID pUUID){ + for(Entry> entry : this.mMailItems.entrySet()){ + if(entry.getKey().equals(pUUID)){ + return entry.getValue().size(); + } + } + return 0; + } + + /** + * 返还寄售过期的物品 + */ + public void reciveMail(Player player){ + if(player==null) return; + UUID tUuid=player.getUniqueId(); + WorthHandler wHandler=((BossShop)this.mPlugin).getManager(WorthHandler.class); + for(UUID key : this.mMailItems.keySet()){ + if(key.equals(tUuid)){ + boolean tIsItem=false; + for(MailItem item : this.mMailItems.get(key)){ + switch(item.getItemType()){ + case Money: + wHandler.giveRewardMoney(player,(Integer)item.getItem()); + break; + case Points: + wHandler.giveRewardPoints(player,(Integer)item.getItem()); + break; + case Item: + tIsItem=true; + wHandler.giveRewardItem(player,(ItemStack)item.getItem()); + break; + default: + break; + } + this.mPlugin.send(player,ChatColor.GREEN+C("Word.Revive")+item.getPreview()); + } + if(tIsItem&&((BossShop)this.mPlugin).getConfigManager().isAutoClearNBT()) + ((BossShop)this.mPlugin).getManager(NBTEditManager.class).clearNBT(); + this.mMailItems.remove(key); + this.mConfig.set(tUuid.toString(),null); + this.saveConfig(); + return; + } + } + } + + public int getMailSendCost(){ + return this.mSendCost; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/managers/BuyItemHandler.java b/src/main/java/org/black_ixx/bossshop/managers/BuyItemHandler.java new file mode 100644 index 0000000..6bd59d9 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/managers/BuyItemHandler.java @@ -0,0 +1,355 @@ +package org.black_ixx.bossshop.managers; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +import org.apache.commons.lang.StringUtils; +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSBuy; +import org.black_ixx.bossshop.core.BSBuyParameter; +import org.black_ixx.bossshop.core.BSShop; +import org.black_ixx.bossshop.core.BSEnums.BSBuyType; +import org.black_ixx.bossshop.core.BSEnums.BSPriceType; +import org.black_ixx.bossshop.misc.Enchant; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; + +/** + * 用于从商品节点上创建菜单物品和回报物品 + * @author 聪聪 + * + */ +public class BuyItemHandler{ + + private BossShop mPlugin; + private final static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm"); + + + public BuyItemHandler(BossShop pPlugin){ + this.mPlugin=pPlugin; + } + + private static Date getDate(String str){ + if(str==null) return null; + Date date=null; + try{ + date=sdf.parse(str); + }catch(ParseException pexp){} + return date; + } + + @SuppressWarnings({"unchecked","deprecation"}) + public BSBuy createBuyItem(BSShop shop,ConfigurationSection c){ + String stage="Basic Data"; + String name=c.getName(); + ItemStackCreator itemCteator=this.mPlugin.getManager(ItemStackCreator.class); + try{ + BSBuyParameter parameter=new BSBuyParameter(); + parameter.name=name; + parameter.shop=shop; + parameter.msg=c.getString("Message"); + + if(parameter.weight<0) parameter.weight=0; + String str_uuid=c.getString("Owner"); + if(str_uuid!=null){ + try{ + parameter.owner=UUID.fromString(str_uuid); + }catch(Exception exp){ + this.mPlugin.severe("配置文件"+shop.getConfigFilename()+"存在错误,["+str_uuid+"]不是一个合法的UUID",exp); + } + } + parameter.hideNoStock=c.getBoolean("HideNoStock",false); + parameter.hideNotTime=c.getBoolean("HideNotTime",false); + if(c.contains("HideItemFlag")) + parameter.hideItemFlag=c.getInt("HideItemFlag"); + else parameter.hideItemFlag=this.mPlugin.getConfigManager().getHideItemFlag(); + parameter.permission=c.getString("ExtraPermission"); + parameter.weight=c.getInt("Weight",0); + if(StringUtils.isEmpty(parameter.permission)) parameter.permission=null; + parameter.numberLimit=c.getInt("NumberLimit",-1); + parameter.personalLimit=c.getInt("PersonalLimit",-1); + ConfigurationSection time=c.getConfigurationSection("TimeLimit"); + if(time!=null){ + parameter.startTime=BuyItemHandler.getDate(time.getString("start")); + parameter.stopTime =BuyItemHandler.getDate(time.getString("stop")); + } + parameter.location=c.getInt("InventoryLocation")-1; + if(parameter.location<0){ + parameter.location=0; + this.mPlugin.warn("商店物品["+name+"]的背包位置["+parameter.location+"]不正确,已经设置为1号位置"); + } + stage="获取商品类型"; + String rewardType=c.getString("RewardType"); + for(BSBuyType type : BSBuyType.values()){ + if(rewardType.equalsIgnoreCase(type.name())){ + parameter.buyT=type; + break; + } + } + if(parameter.buyT==null){ + this.mPlugin.severe("无法创建商品["+name+"],["+rewardType+"]不是受支持商品类型"); + this.mPlugin.severe("支持的商品类型有"); + for(BSBuyType type : BSBuyType.values()){ + this.mPlugin.severe("-"+type.name()); + } + return null; + } + stage="获取价格类型"; + String priceType=c.getString("PriceType"); + for(BSPriceType type : BSPriceType.values()){ + if(priceType.equalsIgnoreCase(type.name())){ + parameter.priceT=type; + break; + } + } + if(parameter.priceT==null){ + this.mPlugin.severe("无法创建商品["+name+"],["+priceType+"]不是受支持的价格类型"); + this.mPlugin.severe("支持的价格类型又:"); + for(BSPriceType type : BSPriceType.values()){ + this.mPlugin.severe("-"+type.name()); + } + return null; + } + parameter.price=c.get("Price"); + parameter.reward=c.get("Reward"); + stage="创建奖励物品"; + switch(parameter.buyT){ + case Item: + if(!(parameter.reward instanceof List)){ + this.mPlugin.severe("无法创建商品["+name+"],["+parameter.reward+"]不是List,物品类型的商品必须为List,参见 ( dev.bukkit.org/bukkit-plugins/bossshop/pages/setup-guide/ )"); + return null; + } + List l=(List)parameter.reward; + boolean b=false; + for(Object o : l){ + if(o instanceof List){ + b=true; + break; + } + } + // Wenn Liste korrekt ist: List> + if(b){ + List items=new ArrayList(); + for(List s : (List>)parameter.reward){ + items.add(itemCteator.createItemStackS(s,false)); + } + parameter.reward=items; + }else{ + // Wenn es eine Liste gibt, in der aber keine weitere Liste + // ist + List items=new ArrayList(); + items.add(itemCteator.createItemStackS((List)parameter.reward,false)); + parameter.reward=items; + } + break; + case TimeCommand: + HashMap cmds=new HashMap(); + // Wenn reward = List + if(parameter.reward instanceof List){ + for(String s : (List)parameter.reward){ + try{ + String[] parts=s.split(":",2); + String a1=parts[0].trim(); + int i=Integer.parseInt(a1); + String cmd=parts[1].trim(); + cmds.put(i,cmd); + }catch(Exception e){ + this.mPlugin.severe("无法创建商品["+name+"],["+s+"]不是一个合法的(TimeCommand),类型商品,正确的格式应该是: <时间>:<命令> 例如 600:ban %name%"); + return null; + } + } + }else{ + if(!(parameter.reward instanceof String)){ + this.mPlugin.severe("无法创建商品["+name+"],["+parameter.reward+"]不是一个合法的(TimeCommand)类型商品,正确的格式应该是: <时间>:<命令> 例如 600:ban %name%"); + return null; + } + String s=(String)parameter.reward; + String[] parts=s.split(":",2); + String a1=parts[0].trim(); + int i=Integer.parseInt(a1); + String cmd=parts[1].trim(); + cmds.put(i,cmd); + } + parameter.reward=cmds; + break; + case Shop: + if(parameter.reward instanceof String){ + String x=(String)parameter.reward; + parameter.reward=x.toLowerCase(); + }else{ + this.mPlugin.severe("无法创建商品["+name+"],["+parameter.reward+"]不是正确的(shop)类型商品"); + return null; + } + break; + case Command: + if(!(parameter.reward instanceof List)){ + if(!(parameter.reward instanceof String)){ + this.mPlugin.severe("无法创建商品["+name+"],["+parameter.reward+" 不是正确的(Command)类型商品"); + return null; + } + List li=new ArrayList(); + li.add((String)parameter.reward); + parameter.reward=li; + } + break; + case PlayerCommand: + if(!(parameter.reward instanceof List)){ + if(!(parameter.reward instanceof String)){ + this.mPlugin.severe("无法创建商品["+name+"! "+parameter.reward+" (PlayerCommand) is no valid Reward list! It needs to be a list of commands!"); + return null; + } + List li=new ArrayList(); + li.add((String)parameter.reward); + parameter.reward=li; + } + break; + case Money: + if(!(parameter.reward instanceof Double)&!(parameter.reward instanceof Integer)){ + try{ + parameter.reward=Double.parseDouble((String)(parameter.reward)); + }catch(Exception exc){ + this.mPlugin.severe("无法创建商品["+name+"! "+parameter.reward+" (Money) is no valid Reward number!"); + return null; + } + } + break; + case Points: + if(!(parameter.reward instanceof Integer)){ + try{ + parameter.reward=Integer.parseInt((String)(parameter.reward)); + }catch(Exception exc){ + this.mPlugin.severe("无法创建商品["+name+"! "+parameter.reward+" (Points) is no valid parameter.reward number!"); + return null; + } + } + break; + case Permission: + if(!(parameter.reward instanceof List)){ + if(!(parameter.reward instanceof String)){ + this.mPlugin.severe("无法创建商品["+name+"! "+parameter.reward+" (Permissions) is no valid parameter.reward list! It needs to be a list of permissions!"); + return null; + } + List li=new ArrayList(); + li.add((String)parameter.reward); + parameter.reward=li; + } + break; + case Enchantment: + if(!(parameter.reward instanceof String)){ + this.mPlugin.severe("无法创建商品["+name+"! "+parameter.reward+" (Enchantment) is no valid parameter.reward line! It needs to be a text line looking like this: #!"); + return null; + } + String line=(String)parameter.reward; + String parts[]=line.split("#",2); + if(parts.length!=2){ + this.mPlugin.severe("无法创建商品["+name+"! "+parameter.reward+" (Enchantment) is no valid parameter.reward line! It needs to be a text line looking like this: #!"); + return null; + } + String p_a=parts[0].trim(); + String p_b=parts[1].trim(); + int lvl=0; + Enchantment enchantment=null; + try{ + lvl=Integer.parseInt(p_b); + }catch(Exception e){ + this.mPlugin.severe("无法创建商品["+name+"! "+parameter.reward+" (Enchantment) is no valid parameter.reward line! Invalid level! It needs to be a number!"); + return null; + } + int ench=-1; + try{ + ench=Integer.parseInt(p_a); + }catch(Exception e){} + if(ench>=0){ + enchantment=Enchantment.getById(ench); + } + if(enchantment==null){ + enchantment=Enchantment.getByName(p_a); + } + if(enchantment==null){ + this.mPlugin.severe("无法创建商品["+name+"! "+parameter.reward+" (Enchantment) is no valid Reward line! Enchantment not found! Here you can find the right enchantment names: http://jd.bukkit.org/doxygen/db/dbe/namespaceorg_1_1bukkit_1_1enchantments.html"); + return null; + } + parameter.reward=new Enchant(enchantment,lvl); + break; + default: + break; + } + stage="创建价格"; + switch(parameter.priceT){ + case Item: + if(!(parameter.price instanceof List)){ + this.mPlugin.severe("无法创建商品["+name+"! "+parameter.price+" (Item) is no valid parameter.price Item List! There has to be a list of ItemData ( dev.bukkit.org/bukkit-plugins/bossshop/pages/setup-guide/ )"); + return null; + } + List l=(List)parameter.price; + boolean b=false; + for(Object o : l){ + if(o instanceof List){ + b=true; + } + } + // Wenn Liste korrekt ist: List> + if(b){ + List items=new ArrayList(); + for(List s : (List>)parameter.price){ + items.add(itemCteator.createItemStackS(s,false)); + } + parameter.price=items; + }else{ + // Wenn es eine Liste gibt, in der aber keine weitere Liste + // ist + List items=new ArrayList(); + items.add(itemCteator.createItemStackS((List)parameter.price,false)); + parameter.price=items; + } + break; + case Free: + break; + case Nothing: + break; + case Exp: + if(!(parameter.price instanceof Integer)){ + try{ + parameter.price=Integer.parseInt((String)(parameter.price)); + }catch(Exception exc){ + this.mPlugin.severe("无法创建商品["+name+"! "+parameter.price+" (Exp) is no valid parameter.price number!"); + return null; + } + } + break; + case Money: + if(!(parameter.price instanceof Double)&!(parameter.price instanceof Integer)){ + try{ + parameter.price=Double.parseDouble((String)(parameter.price)); + }catch(Exception exc){ + this.mPlugin.severe("无法创建商品["+name+"! "+parameter.price+" (Money) is no valid parameter.price number!"); + return null; + } + } + break; + case Points: + if(!(parameter.price instanceof Integer)){ + try{ + parameter.price=Integer.parseInt((String)(parameter.price)); + }catch(Exception exp){ + this.mPlugin.severe("无法创建商品["+name+"! "+parameter.price+" (Points) is no valid parameter.price number!"); + return null; + } + } + break; + } + return new BSBuy(this.mPlugin,parameter); + }catch(Throwable exp){ + this.mPlugin.severe("无法创建商品 "+name+"! 在步骤 '"+stage+"',可能是由于错误的插件配置",exp); + return null; + } + } + + +} diff --git a/src/main/java/org/black_ixx/bossshop/managers/ConfigManager.java b/src/main/java/org/black_ixx/bossshop/managers/ConfigManager.java new file mode 100644 index 0000000..046b9b2 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/managers/ConfigManager.java @@ -0,0 +1,165 @@ +package org.black_ixx.bossshop.managers; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.lottery.LotteryManager; +import org.black_ixx.bossshop.mail.MailManager; +import org.black_ixx.bossshop.sale.SaleManager; + +import cc.util.bossshop.filemanager.AConfigManager; + +public class ConfigManager extends AConfigManager{ + + private boolean signs; + private boolean ic_hide; + private boolean transaction_log; + private boolean allow_unsafe_enchantments; + private boolean debug; + private String mainshop; + private int mCoolTime=5; + private boolean mAutoClearNBT=true; + private int mHideItemFlag=63; + private int mShopOpenDelay=0; + + private BossShop plugin; + + public ConfigManager(BossShop pplugin){ + super(pplugin,"1.4"); + this.reloadConfig(); + } + + @Override + public boolean reloadConfig(){ + if(!super.reloadConfig()) return false; + this.checkUpdate(); + this.addDefaults(); + this.saveConfig(); + this.signs=this.mConfig.getBoolean("signs.enabled")||this.mConfig.getBoolean("EnableSigns"); + this.mainshop=this.mConfig.getString("MainShop","Menu"); + this.ic_hide=this.mConfig.getBoolean("HideItemsPlayersDoNotHavePermissionsFor",false); + this.transaction_log=this.mConfig.getBoolean("EnableTransactionLog",false); + this.allow_unsafe_enchantments=this.mConfig.getBoolean("AllowUnsafeEnchantments",false); + this.mCoolTime=this.mConfig.getInt("CoolTime",5); + this.debug=this.mConfig.getBoolean("debug",false); + this.mAutoClearNBT=this.mConfig.getBoolean("AutoClearNBT",true); + this.mHideItemFlag=this.mConfig.getInt("HideItemFlag",63); + this.mShopOpenDelay=this.mConfig.getInt("OpenShopDelay",0); + this.mShopOpenDelay/=50; + return true; + } + + protected boolean checkUpdate(){ + String tVersion=this.mConfig.getString(SEC_CFG_VERSION,"1.0"); + if(tVersion.compareToIgnoreCase(this.mVersion)>=0) return false; + if(tVersion.compareToIgnoreCase("1.0")==0){// 1.0-->1.1 + tVersion="1.1"; + Object obj=this.mConfig.get("SaleSystem.OutDateItemNumbLimit"); + if(obj!=null) this.mConfig.set("MailSystem.MaxSize",obj); + this.mConfig.set("SaleSystem.OutDateItemNumbLimit",null); + obj=this.mConfig.get("SaleSystem.AutoClearNBT"); + if(obj!=null) this.mConfig.set("AutoClearNBT",true); + this.mConfig.set("SaleSystem.AutoClearNBT",null); + } + if(tVersion.compareToIgnoreCase("1.1")==0){// 1.1-->1.2 + tVersion="1.2"; + int coolTime=this.mConfig.getInt("CoolTime",1); + this.mConfig.set("CoolTime",coolTime*1000); + } + if(tVersion.compareToIgnoreCase("1.2")==0){// 1.2-->1.3 + tVersion="1.3"; + int item=this.mConfig.getInt("BindMenuItem"); + this.mConfig.set("BindMenuItem",null); + this.mConfig.set("OpenMainShop.BindItem",item); + } + if(tVersion.compareToIgnoreCase("1.3")==0){// 1.3-->1.4 + tVersion="1.4"; + this.mConfig.set("AutoClearNBT",null); + this.mConfig.set("DisableUpdateNotifications",null); + } + this.mConfig.set(SEC_CFG_VERSION,this.mVersion); + return true; + } + + public void addDefaults(){ + super.addDefaults(); + this.mConfig.addDefault("EnableSigns",true,"启用贴牌创建BossShop商店入口"); + this.mConfig.addDefault("LogPrefix","[BossShop]","用于日志输出前缀,基本不用"); + this.mConfig.addDefault("MsgPrefix","&c[&7&l商店&c]&b","前台聊天以及后台大部分输出的前缀"); + this.mConfig.addDefault("MainShop","Menu","BossShop主商店"); + this.mConfig.addDefault("ItemNameLang","zh_CN","物品名字翻译语言,用于寄售物品和邮件 时候使用","当前支持zh_CN,zh_TW,en_US,其中en_US翻译不需要依赖文件,且翻译准确","更多翻译支持请到.minecraft/assets/objects下找到对应的翻译文件,更改名字格式然后复制到插件目录就可以了","此项配置的原版物品名字需要靠ItemName_xx_XX文件,mod物品名字会自动获取","部分mod或mod物品可能存在翻译问题,比如怪物蛋(但是英文是肯定对的)","如果整个mod不能翻译,可能是该mod里没有对应的语言文件","此项配置需要重载整个插件才能使原版翻译生效","可以在手上拿着物品时输入/BS name查看手上的物品的翻译"); + this.mConfig.addDefault("OpenMainShop.BindItem",347,"打开BossShop主商店的物品"); + this.mConfig.addDefault("OpenMainShop.SneakNeed",true,"使用物品打开商店时是否需要潜行"); + this.mConfig.addDefault("OpenShopDelay",0,"命令打开商店的延迟","用来兼容例如时钟命令等插件"); + this.mConfig.addDefault("HideItemsPlayersDoNotHavePermissionsFor",false,"隐藏玩家没有权限购买的物品"); + this.mConfig.addDefault("EnableTransactionLog",false,"启用交易记录"); + this.mConfig.addDefault("AllowUnsafeEnchantments",false,"允许不安全的附魔"); + this.mConfig.addDefault("MultiplierGroups.Enabled",false); + this.mConfig.addDefault("MultiplierGroups.List",new String[]{"Permission.Node::","BossShop.PriceMultiplier.Points1:points:0.75","BossShop.PriceMultiplier.Points1:points:0.5","BossShop.PriceMultiplier.Money1:money:0.75","BossShop.PriceMultiplier.Money2:money:0.5","BossShop.PriceMultiplier.MoneyNegative:money:2.0","BossShop.PriceMultiplier.Exp:exp:0.8",}); + this.mConfig.addDefault(SaleManager.CFG_MAIN+"."+SaleManager.CFG_ShopBaseName,"SYSTEM_SALE","寄售系统主配置"); + this.mConfig.addDefault(SaleManager.CFG_MAIN+"."+SaleManager.CFG_ShopSize,54,"寄售商店大小","必须为9的整数,不超过81,而且商店最后两格需要留空用来制作导航按钮","一旦设置过大小并创建了寄售商店后,就不要改这个值了,除非同时修改商店文件,不然导航功能会失效"); + this.mConfig.addDefault(SaleManager.CFG_MAIN+"."+SaleManager.CFG_ShopMaxNumb,2,"寄售商店最大数量"); + this.mConfig.addDefault(SaleManager.CFG_MAIN+"."+SaleManager.CFG_MoneyPoundage,5,"金币收入手续费,百分比0-99"); + this.mConfig.addDefault(SaleManager.CFG_MAIN+"."+SaleManager.CFG_PointsPoundage,5,"点券收入手续费,百分比0-99"); + this.mConfig.addDefault(SaleManager.CFG_MAIN+"."+SaleManager.CFG_MoneyItem,9,"金币寄售默认显示的物品,只支持数值,不支持12:3的类型"); + this.mConfig.addDefault(SaleManager.CFG_MAIN+"."+SaleManager.CFG_PointsItem,10,"点券寄售默认显示的物品,只支持数值,不支持12:3的类型"); + this.mConfig.addDefault(SaleManager.CFG_MAIN+"."+SaleManager.CFG_SaleTime,86400,"默认寄售时长(秒),这里是一天"); + this.mConfig.addDefault(SaleManager.CFG_MAIN+"."+SaleManager.CFG_UnsaleCost,1000,"下架需要花费的金币数量,默认没有下架权限","权限BossShop.unsale.user: 下架自己物品的权限","权限BossShop.unsale.admin: 下架所有人物品的权限,并且不要支付下架花费"); + this.mConfig.addDefault(SaleManager.CFG_MAIN+"."+SaleManager.CFG_UnsaleCount,3,"每天的下架数量限制,拥有BossShop.unsale.admin的使人可以无视该权限","下架记录保存在buyRecord.yml文件中"); + this.mConfig.addDefault(SaleManager.CFG_MAIN+"."+SaleManager.CFG_PerOneSaleLimit,3,"个人允许最多同时寄售物品数量的上限,如果玩家有BossShop.sale.unlimited权限可以无限寄售"); + this.mConfig.addDefault(SaleManager.CFG_MAIN+"."+SaleManager.CFG_SaleItemName,"&2寄售: %bs_sale_item_sigle_count% %bs_sale_item_name%","设置寄售物品的显示格式","如果一行中有空格的话需要用单引号把整句括起来","可用的变量,SaleItemLore中也可以使用这些变量","%bs_sale_owner_name% 寄售者的名字","%bs_sale_item_id% 寄售物品的编号","%bs_sale_item_sigle_count% 寄售物品单份数量","%bs_sale_item_part_count% 寄售物品份数","%bs_sale_item_type% 寄售物品类型,[金币,点券,物品],如果要修改这几个请转到语言列表的Word节点","%bs_sale_item_name% 寄售物品名字,[金币,点券,物品英文名],前两项同上,最后一项没办法,获取不到中文显示用的名字","%bs_sale_price% 寄售物品价格,这个只显示数字","%bs_sale_price_type% 寄售物品类型,[金币,点券],要修改同上上"); + this.mConfig.addDefault(SaleManager.CFG_MAIN+"."+SaleManager.CFG_SaleItemLore,new String[]{"&3寄售者: %bs_sale_owner_name%","&2寄售编号: %bs_sale_item_id%","&c价格: %bs_sale_price% %bs_sale_price_type%"},"设置寄售物品的lore格式,时间限制和数量限制为默认lore,如果需要修改请转到语言文件","lore最好加上颜色,不然默认是斜体紫色,丑!","lore默认显示在物品属性下面,过期信息,数量限制等信息的上面"); + this.mConfig.addDefault(MailManager.CFG_MAIN+"."+MailManager.SEC_MaxSize,10,"邮箱大小,超过此大小的物品将不会保存"); + this.mConfig.addDefault(MailManager.CFG_MAIN+"."+MailManager.SEC_SendCost,1000,"发送邮件的花费(金币)"); + this.mConfig.addDefault(LotteryManager.CFG_MAIN+"."+LotteryManager.CFG_ReflashBaseTicks,5,"基础Ticks,多少Ticks快速刷新一次背包"); + this.mConfig.addDefault(LotteryManager.CFG_MAIN+"."+LotteryManager.CFG_ReflashLowMultiple,4,"预览抽奖时,过了多少倍ReflashBaseTicks后刷新一次背包"); + this.mConfig.addDefault(LotteryManager.CFG_MAIN+"."+LotteryManager.CFG_RefalshFastCount,20,"进行抽奖时,刷新背包多少次"); + this.mConfig.addDefault(LotteryManager.CFG_MAIN+"."+LotteryManager.CFG_ShowPrecent,34,"抽奖以及未抽奖时,物品显示比例,最高50,交替显示(50的话就只有两个显示样子了)"); + this.mConfig.addDefault("AutoClear.NBTExpiredDays",7,"NBT过期天数,超过该天数未被引用而且是自动创建的NBT会被清理"); + this.mConfig.addDefault("HideItemFlag",63,"隐藏寄售物品的攻击效果等属性"," 1|0.HIDE_ENCHANTS 附魔"," 2|1.HIDE_ATTRIBUTES 前缀"," 4|2.HIDE_UNBREAKABLE 不可破坏"," 8|3.HIDE_DESTROYS, 受损度","16|4.HIDE_PLACED_ON, 放置","32|5.HIDE_POTION_EFFECTS 药水效果","如何隐藏物品的状态??如果想隐藏相应的属性,只要把前面的数字加起来就可以了,如果要全部隐藏,就是63(默认)","注意此项设置只在1.8.3及其以后的版本有效,低版本的只有设置0和非零来开启全部或关闭全部显示,不能选择关闭什么","更低版本的可能不支持关闭","上面的表也可能存在错误就是前面的数字对应的不是这个属性的隐藏"); + this.mConfig.addDefault("PointsPlugin","auto-detect","可选[PlayerPoints,PointsAPI,EnjinMinecraftPlugin,CommandPoints]","如果是[auto-detect],将会自动选择"); + this.mConfig.addDefault("CoolTime",200,"玩家点击商品的间隔(毫秒),无论是否购买成功,都会重复点击会重新计时"); + } + + public boolean signsEnabled(){ + return signs; + } + + public String getMainShop(){ + return mainshop; + } + + public boolean hideNoPermitItem(){ + return ic_hide; + } + + public boolean transactionLogEnabled(){ + return transaction_log; + } + + public boolean allowUnsafeEnchantments(){ + return allow_unsafe_enchantments; + } + + public boolean debug(){ + return this.debug; + } + + public int getCoolTime(){ + return this.mCoolTime; + } + + public boolean isAutoClearNBT(){ + return this.mAutoClearNBT; + } + + public int getHideItemFlag(){ + return this.mHideItemFlag; + } + + /** + * 商店打开延迟,单位为tick + */ + public int getShopOpenDelay(){ + return this.mShopOpenDelay<0?0:this.mShopOpenDelay; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/managers/DefaultCreator.java b/src/main/java/org/black_ixx/bossshop/managers/DefaultCreator.java new file mode 100644 index 0000000..f64f4a6 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/managers/DefaultCreator.java @@ -0,0 +1,519 @@ +package org.black_ixx.bossshop.managers; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSShop; +import org.black_ixx.bossshop.core.BSShopManager; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; + +import com.mysql.jdbc.StringUtils; + +import cc.util.bossshop.config.CommentedYamlConfig;; + +public class DefaultCreator{ + + public static void addDefault(CommentedYamlConfig config,String name,String rewardType,String priceType,Object reward,Object price,List menuitem,String message,int loc,String permission){ + ConfigurationSection c=config.createSection("shop."+name); + c.set("RewardType",rewardType); + c.set("PriceType",priceType); + c.set("Price",price); + c.set("Reward",reward); + c.set("MenuItem",menuitem); + c.set("Message",message); + c.set("InventoryLocation",loc); + c.set("ExtraPermission",permission); + } + + public static void addDefault(CommentedYamlConfig config,String name,String rewardType,String priceType,Object reward,Object price,List menuitem,String message,int loc,String permission,String extra_node,Object extra_object){ + ConfigurationSection c=config.createSection("shop."+name); + c.set("RewardType",rewardType); + c.set("PriceType",priceType); + c.set("Price",price); + c.set("Reward",reward); + c.set("MenuItem",menuitem); + c.set("Message",message); + c.set("InventoryLocation",loc); + c.set("ExtraPermission",permission); + c.set(extra_node,extra_object); + } + + private File getFile(String name){ + return new File(BossShop.getInstance().getDataFolder().getAbsolutePath()+"/shops/"+name); + } + + private CommentedYamlConfig getConfig(File f){ + CommentedYamlConfig tConfig=new CommentedYamlConfig(); + tConfig.loadFromFile(f); + return tConfig; + } + + public static List createMenuItem(String name,String lore,String material,String add){ + List item=new ArrayList(); + item.add("type:"+material); + item.add("amount:1"); + item.add("name:"+name); + if(!StringUtils.isNullOrEmpty(lore)) item.add("lore:"+lore); + if(!StringUtils.isNullOrEmpty(add)) item.add(add); + return item; + } + + public static List createMenuItem(String name,String lore,String material,String add,String add2){ + List item=new ArrayList(); + item.add("type:"+material); + item.add("amount:1"); + item.add("name:"+name); + item.add("lore:"+lore); + if(add!=""){ + item.add(add); + item.add(add2); + } + return item; + } + + public static List createMenuItem(String name,String lore,String material,String add,String add2,String add3){ + List item=new ArrayList(); + item.add("type:"+material); + item.add("amount:1"); + item.add("name:"+name); + item.add("lore:"+lore); + if(add!=""){ + item.add(add); + item.add(add2); + item.add(add3); + } + return item; + } + + public static List> createItem(String name,String lore,String material,String add,String add2){ + List> x=new ArrayList>(); + List item=new ArrayList(); + item.add("type:"+material); + item.add("amount:1"); + item.add("name:"+name); + item.add("lore:"+lore); + if(add!=""){ + item.add(add); + item.add(add2); + } + x.add(item); + return x; + } + + public static List> createItem(String name,String lore,String material,String add,String add2,String add3){ + List> x=new ArrayList>(); + List item=new ArrayList(); + item.add("type:"+material); + item.add("amount:1"); + item.add("name:"+name); + item.add("lore:"+lore); + if(add!=""){ + item.add(add); + item.add(add2); + item.add(add3); + } + x.add(item); + return x; + } + + public static List> createPoorItem(String amount,String material,String add){ + List> x=new ArrayList>(); + List item=new ArrayList(); + item.add("type:"+material); + item.add("amount:"+amount); + if(add!=""){ + item.add(add); + } + x.add(item); + return x; + } + + public static List createOneLineList(String text){ + List l=new ArrayList(); + l.add(text); + return l; + } + + public static List createMenuItemSpell(String name,String lore,String add){ + List item=new ArrayList(); + item.add("type:WRITTEN_BOOK"); + item.add("amount:1"); + item.add("name:"+name); + item.add("lore:"+lore); + if(add!=""){ + item.add(add); + } + return item; + } + + public static void setSettings(CommentedYamlConfig config,String shopName,String displayname){ + config.set("DisplayName",displayname); + config.set("signs.text","["+shopName+"]"); + config.set("signs.NeedPermissionToCreateSign",true); + config.createSection("shop"); + } + + /** + * 为商店添加一个物品项用于链接到其他商店 + * @param config 配置文件 + * @param pLocation 商店物品放置位置 + * @param itemID 菜单显示物品ID + * @param pTarget 目标商店 + * @param pName 配置文件中存储的物品名字 + */ + public static void setGuidItem(CommentedYamlConfig config,int pLocation,int itemID,String pTarget,String pName){ + String tTargetDisplay=pTarget; + BSShop tShop=BossShop.getInstance().getManager(BSShopManager.class).getShop(pTarget); + if(tShop!=null) tTargetDisplay=tShop.getDisplayName(); + setGuidItem(config,pLocation,itemID,pTarget,tTargetDisplay,pName); + } + + /** + * 为商店添加一个物品项用于链接到其他商店 + * @param config 配置文件 + * @param pLocation 商店物品放置位置 + * @param itemID 菜单显示物品ID + * @param pTarget 目标商店 + * @param pTargetDisplay 目标名字,用于合成菜单物品名 + * @param pName 配置文件中存储的物品名字 + */ + public static void setGuidItem(CommentedYamlConfig config,int pLocation,int itemID,String pTarget,String pTargetDisplay,String pName){ + String display=BossShop.getInstance().getLangManager().getNode("Word.GoTo")+" "+pTargetDisplay; + String tShowItem=Material.REDSTONE.name(); + Material mate=Material.getMaterial(itemID); + if(mate!=null) tShowItem=mate.name(); + addDefault(config,pName,"shop","free",pTarget,null,createMenuItem("&9&l"+display,"",tShowItem,""),"",pLocation,""); + } + + + public void addAllExamples(){ + // ShopMenu + { + String name="Menu"; + File f=getFile(name+".yml"); + CommentedYamlConfig config=getConfig(f); + setSettings(config,name,"&lMenu"); + addDefault(config,"MenuBuyShop","shop","free","BuyShop",null,createMenuItem("&9&lBuyShop &6[+]","&8Here you can buy Items","GLASS",""),"",1,""); + addDefault(config,"MenuSellShop","shop","free","SellShop",null,createMenuItem("&b&lSellShop &6[+]","&8Here you can sell Items","COBBLESTONE",""),"",3,""); + addDefault(config,"MenuPotions","shop","free","Potions",null,createMenuItem("&5&lPotions &6[**]","&8Here you can buy Potions","POTION","potioneffect:SPEED#1#1"),"",5,""); + addDefault(config,"MenuSpells","shop","free","Spells",null,createMenuItem("&4&lSpells &6[*]","&8Here you can buy magical Spells","WRITTEN_BOOK",""),"",7,""); + String mpsl="&8Here you can buy Points #"+(simplePointsPluginCheck()?"#&b&lYour Points: &r&9%playerpoints_points%":"")+"#&b&lYour Money: &r&9%vault_eco_balance% ##&cOnly for VIPs"; + addDefault(config,"MenuPointShop","shop","free","PointShop",null,createMenuItem("&6&lPointShop &a[v]",mpsl,"DIAMOND",""),"",9,"Permission.Vip"); + addDefault(config,"MenuWarps","shop","free","Warps",null,createMenuItem("&a&lWarps &6[x]","&8Free Teleportation","PAPER",""),"",10,""); + config.saveToFile(f); + } + // Items + { + String name="BuyShop"; + File f=getFile(name+".yml"); + CommentedYamlConfig config=getConfig(f); + setSettings(config,name,"&9&lBuyShop"); + List> changeRewardItems=new ArrayList>(); + List changeRewardItem1=new ArrayList(); + changeRewardItem1.add("type:WOOL"); + changeRewardItem1.add("amount:10"); + changeRewardItem1.add("durability:14"); + List changeRewardItem2=new ArrayList(); + changeRewardItem2.add("type:WOOL"); + changeRewardItem2.add("amount:10"); + changeRewardItem2.add("durability:11"); + List changeRewardItem3=new ArrayList(); + changeRewardItem3.add("type:WOOL"); + changeRewardItem3.add("amount:10"); + changeRewardItem3.add("durability:4"); + List changeRewardItem4=new ArrayList(); + changeRewardItem4.add("type:WOOL"); + changeRewardItem4.add("amount:10"); + changeRewardItem4.add("durability:5"); + changeRewardItems.add(changeRewardItem1); + changeRewardItems.add(changeRewardItem2); + changeRewardItems.add(changeRewardItem3); + changeRewardItems.add(changeRewardItem4); + addDefault(config,"Wool","item","money",changeRewardItems,400,createMenuItem("&9Colored Wool","&c10 Red, &110 Blue, &e10 Yellow and &a10 Green Wool #&cPrice: 400 Money","WOOL","durability:11","amount:40"),"&eYou bought colored Wool! Money left: &c%left%",1,""); + addDefault(config,"Diamonds","item","money",createPoorItem("5","DIAMOND",""),5000,createMenuItem("&95 Diamonds","&cPrice: 5000 Money","DIAMOND","amount:5"),"&eYou bought %reward%! Money left: &c%left%",3,""); + addDefault(config,"ShadowArmor","item","money",createItem("&5ShadowArmor","","LEATHER_CHESTPLATE","color:110#10#140","enchantment:PROTECTION_ENVIRONMENTAL#10"),1500,createMenuItem("&5%itemname%","&cPrice: 1500 Money","LEATHER_CHESTPLATE","color:110#10#140","enchantment:PROTECTION_ENVIRONMENTAL#10"),"&eYou bought 1 %itemname%! Money left: &c%left%",5,""); + addDefault(config,"Obsidian","item","money",createPoorItem("64","OBSIDIAN",""),10000,createMenuItem("&964 Obsidian","&cPrice: 10000 Money","OBSIDIAN","amount:64"),"&eYou bought %reward%! Money left: &c%left%",7,""); + addDefault(config,"GodApple","item","money",createPoorItem("1","GOLDEN_APPLE","durability:1"),10000,createMenuItem("&9%itemname%","&cPrice: 10000 Money","GOLDEN_APPLE","durability:1"),"&eYou bought 1 %itemname%! Money left: &c%left%",9,""); + addDefault(config,"EnchantUnbreaking","enchantment","exp","DURABILITY#3",25,createMenuItem("&4[crown] &cEnchantment &4[crown]","&8Enchants the Item in your hand. #&cPrice: 25 Exp Level ##&cOnly for VIPs!","ENCHANTED_BOOK","enchantment:DURABILITY#3"),"&eThe Enchantment Unbreaking III was added to your item!",19,"Permission.Vip"); + addDefault(config,"BossSword","item","points",createItem("&cBossSword","&8&o%player_name%'s Sword","DIAMOND_SWORD","enchantment:DAMAGE_ALL#5","enchantment:FIRE_ASPECT#2"),2000,createMenuItem("&4[*] &c%itemname% &4[*]","&cPrice: 2000 Points","DIAMOND_SWORD","enchantment:DAMAGE_ALL#5","enchantment:FIRE_ASPECT#2"),"&eYou bought 1 %itemname%! Money left: &c%left%",21,""); + addDefault(config,"Back","shop","free","menu",null,createMenuItem("&cBack","&8Back to Menu","REDSTONE",""),"&6Leaving the ItemShop...",27,""); + config.saveToFile(f); + } + // SellShop + { + String name="SellShop"; + File f=getFile(name+".yml"); + CommentedYamlConfig config=getConfig(f); + setSettings(config,name,"&b&lSellShop"); + addDefault(config,"Diamond","money","item",100,createPoorItem("1","DIAMOND",""),createMenuItem("&b1 Diamond","&2Reward: 100 Money","DIAMOND","amount:1"),"&eYou sold %price% for %reward% %rewardtype%!",1,""); + addDefault(config,"Cobblestone","money","item",20,createPoorItem("32","COBBLESTONE",""),createMenuItem("&b32 Cobblestone","&2Reward: 20 Money","COBBLESTONE","amount:32"),"&eYou sold &eYou sold %price% for %reward% %rewardtype%!",2,""); + addDefault(config,"GLASS","money","item",30,createPoorItem("32","GLASS",""),createMenuItem("&b32 Glass","&2Reward: 30 Money","GLASS","amount:32"),"&eYou sold %price% for %reward% %rewardtype%!",3,""); + addDefault(config,"Back","shop","free","menu",null,createMenuItem("&cBack","&8Back to Menu","REDSTONE",""),"&6Leaving the ItemShop...",27,""); + config.saveToFile(f); + } + // Potions + { + String name="Potions"; + File f=getFile(name+".yml"); + CommentedYamlConfig config=getConfig(f); + setSettings(config,name,"&5&lPotions"); + addDefault(config,"NinjaPotion","item","item",createItem("&5NinjaPotion","&8No Barrier can stop you!","POTION","potioneffect:SPEED#2#600","potioneffect:JUMP#0#600","amount:3"),createPoorItem("5","EMERALD",""),createMenuItem("&5NinjaPotion","&8No Barrier can stop you! #&cPrice: 5 Emeralds","POTION","potioneffect:SPEED#2#600","potioneffect:JUMP#0#600","amount:3"),"&eYou bought 3 NinjaPotions!",1,""); + addDefault(config,"BeserkerPotion","item","item",createItem("&5BeserkerPotion","&8Cut em down!!!","POTION","potioneffect:INCREASE_DAMAGE#2#600","potioneffect:CONFUSION#0#600","amount:3"),createPoorItem("1","GHAST_TEAR",""),createMenuItem("&5BeserkerPotion","&8Cut em down!!! #&cPrice: 1 Ghast Tear","POTION","potioneffect:INCREASE_DAMAGE#2#600","potioneffect:CONFUSION#0#600","amount:3"),"&eYou bought 3 BeserkerPotions!",2,""); + addDefault(config,"GhostPotion","item","item",createItem("&5GhostPotion","&8Where are you? I can't see you!","POTION","potioneffect:INVISIBILITY#0#600","potioneffect:NIGHT_VISION#0#600","amount:3"),createPoorItem("30","SOUL_SAND",""),createMenuItem("&5GhostPotion","&8Where are you? I can't see you! #&cPrice: 30 SoulSand","POTION","potioneffect:INVISIBILITY#0#600","potioneffect:NIGHT_VISION#0#600","amount:3"),"&eYou bought 3 GhostPotions!",3,""); + addDefault(config,"TitanPotion","item","points",createItem("&cTitanPotion","&8Ahaha only Gods can defeat you!!!","POTION","potioneffect:REGENERATION#1#600","amount:3"),750,createMenuItem("&4[*] &cTitanPotion &4[*]","&8Ahaha only Gods can defeat you!!! #&cPrice: 750 Points","POTION","potioneffect:REGENERATION#1#600","amount:3"),"&eYou bought 3 TitanPotions!",5,""); + addDefault(config,"Back","shop","free","menu",null,createMenuItem("&cBack","&8Back to Menu","REDSTONE",""),"&6Leaving the ItemShop...",9,""); + config.saveToFile(f); + } + // Spells + { + String name="Spells"; + File f=getFile(name+".yml"); + CommentedYamlConfig config=getConfig(f); + setSettings(config,name,"&4&lSpells"); + addDefault(config,"SpellFireball","permission","exp",createOneLineList("Permission.Fireball"),10,createMenuItemSpell("&4Fireball","&8Allows you to shoot Fireballs #&cPrice: 10 Levels",""),"&eYou bought the Fireball Spell!",1,""); + addDefault(config,"SpellPoison","permission","exp",createOneLineList("Permission.Poison"),20,createMenuItemSpell("&4Poison &2[radioactive]","&8Allows you to poison your enemies #&cPrice: 20 Levels",""),"&eYou bought the Poison Spell!",2,""); + addDefault(config,"SpellBolt","permission","exp",createOneLineList("Permission.Bolt"),30,createMenuItemSpell("&4Bolt","&8Allows you to strike your enemies with Bolts #&cPrice: 30 Levels",""),"&eYou bought the Bolt Spell!",3,""); + addDefault(config,"SpellVanish","permission","exp",createOneLineList("Permission.Vanish"),40,createMenuItemSpell("&4Vanish","&8Allows you to vanish for a few seconds #&cPrice: 40 Levels",""),"&eYou bought the Vanish Spell!",4,""); + addDefault(config,"SpellFreeze","permission","points",createOneLineList("Permission.Freeze"),3500,createMenuItemSpell("&4[*] &cFreeze &4[*]","&8Allows you to freeze your enemies #&cPrice: 3500 Points",""),"&eYou bought the Freeze Spell!",6,""); + addDefault(config,"Back","shop","free","menu",null,createMenuItem("&cBack","&8Back to Menu","REDSTONE",""),"&6Leaving the SpellShop...",9,""); + config.saveToFile(f); + } + // PointShop + { + String name="PointShop"; + File f=getFile(name+".yml"); + CommentedYamlConfig config=getConfig(f); + setSettings(config,name,"&6&lPointShop &2[v]"); + addDefault(config,"100Points","points","money",100,1000,createMenuItem("&6100 Points","&cPrice: 1000 Money","DIAMOND","amount:1"),"&eYou bought 100 Points! Money left: &c%left%",1,""); + addDefault(config,"500Points","points","money",500,5000,createMenuItem("&6500 Points","&cPrice: 5000 Money","DIAMOND","amount:5"),"&eYou bought 500 Points! Money left: &c%left%",2,""); + addDefault(config,"1000Points","points","money",1000,10000,createMenuItem("&61000 Points","&cPrice: 10000 Money","DIAMOND","amount:10"),"&eYou bought 1000 Points! Money left: &c%left%",3,""); + addDefault(config,"500Money","money","points",500,100,createMenuItem("&6500 Money","&cPrice: 100 Points","EMERALD","amount:5"),"&eYou bought 500 Money! Points left: &c%left%",19,""); + addDefault(config,"1000Money","money","points",1000,200,createMenuItem("&61000 Money","&cPrice: 200 Points","EMERALD","amount:10"),"&eYou bought 1000 Money! Points left: &c%left%",20,""); + addDefault(config,"5000Money","money","points",5000,1000,createMenuItem("&65000 Money","&cPrice: 1000 Points","EMERALD","amount:50"),"&eYou bought 5000 Money! Points left: &c%left%",21,""); + addDefault(config,"Back","shop","free","menu",null,createMenuItem("&cBack","&8Back to Menu","REDSTONE",""),"&6Leaving the PointShop...",27,""); + config.saveToFile(f); + } + // Warps + { + String name="Warps"; + File f=getFile(name+".yml"); + CommentedYamlConfig config=getConfig(f); + setSettings(config,name,"&a&lWarps"); + ArrayList w1=new ArrayList(); + w1.add("warp spawn"); + ArrayList w2=new ArrayList(); + w2.add("warp pvp"); + ArrayList w3=new ArrayList(); + w3.add("warp shop"); + addDefault(config,"Spawn","playercommand","free",w1,null,createMenuItem("&aSpawn","&8Warp to the Spawn","COMPASS","amount:1"),null,1,""); + addDefault(config,"PvP","playercommand","free",w2,null,createMenuItem("&aPvP","&8Warp to the PvP Arena","DIAMOND_SWORD","amount:1"),null,2,""); + addDefault(config,"Shop","playercommand","free",w3,null,createMenuItem("&aShop","&8Warp to the Shop","GOLD_INGOT","amount:1"),null,3,""); + addDefault(config,"Back","shop","free","menu",null,createMenuItem("&cBack","&8Back to Menu","REDSTONE",""),"&6Leaving the ItemShop...",27,""); + config.saveToFile(f); + } + } + + public void createShopItemsExample(File f,CommentedYamlConfig config){ + config.set("ShopName","ExampleShop"); + config.set("signs.text","[ExampleShop]"); + config.set("signs.NeedPermissionToCreateSign",false); + config.createSection("shop"); + List> diaswordRewardItems=new ArrayList>(); + List diaswordRewardItem=new ArrayList(); + diaswordRewardItem.add("id:276"); + diaswordRewardItem.add("amount:1"); + diaswordRewardItem.add("name:&bDiamond Sword"); + diaswordRewardItem.add("lore:Line1#Line2#Line3#Line4#This is an Example"); + diaswordRewardItem.add("enchantment:DAMAGE_ALL#3"); + diaswordRewardItem.add("enchantment:FIRE_ASPECT#2"); + diaswordRewardItems.add(diaswordRewardItem); + List diaswordMenuItem=new ArrayList(); + diaswordMenuItem.add("type:DIAMOND_SWORD"); + diaswordMenuItem.add("amount:1"); + diaswordMenuItem.add("name:&bDiamond Sword"); + diaswordMenuItem.add("lore:Line1#Line2#Line3#Line4#This is an Example"); + diaswordMenuItem.add("enchantmentId:16#3"); + diaswordMenuItem.add("enchantmentId:20#2"); + addDefault(config,"DiamondSword","item","money",diaswordRewardItems,5000,diaswordMenuItem,"&6You bought a Diamond Sword! &cMoney left: %left%",1,""); + List pointsMenuItem=new ArrayList(); + pointsMenuItem.add("type:WRITTEN_BOOK"); + pointsMenuItem.add("amount:1"); + pointsMenuItem.add("name:&6PointSet"); + pointsMenuItem.add("lore:&e1000 Points#&cPrice: 500 Money!"); + addDefault(config,"PointSet","points","money",1000,500,pointsMenuItem,"&6You bought 1000 Points! &cMoney left: %left%",3,""); + List moneyMenuItem=new ArrayList(); + moneyMenuItem.add("type:WRITTEN_BOOK"); + moneyMenuItem.add("amount:1"); + moneyMenuItem.add("name:&6MoneySet"); + moneyMenuItem.add("lore:&e500 Money#&cPrice: 1000 Points!"); + addDefault(config,"MoneySet","money","points",500,1000,moneyMenuItem,"&6You bought 500 Money! &cPoints left: %left%",4,""); + List sMenuItem=new ArrayList(); + sMenuItem.add("type:WRITTEN_BOOK"); + sMenuItem.add("amount:1"); + sMenuItem.add("name:&4Kick"); + sMenuItem.add("lore:&cPrice: Free"); + List sCmds=new ArrayList(); + sCmds.add("kick %name%"); + addDefault(config,"Kick","command","free",sCmds,null,sMenuItem,"",5,""); + List opMenuItem=new ArrayList(); + opMenuItem.add("type:BEDROCK"); + opMenuItem.add("amount:1"); + opMenuItem.add("name:&5OP"); + opMenuItem.add("lore:&eYou are from PlanetMinecraft and you want to #&ebecome OP to be able to review the server? #&eNo problem! #&cJust buy it for 5 Levels!"); + List opcmd=new ArrayList(); + opcmd.add("op %name%"); + opcmd.add("say Yay! %name% will review the server now!"); + addDefault(config,"OP","command","exp",opcmd,5,opMenuItem,"",9,""); + List flyMenuItem=new ArrayList(); + flyMenuItem.add("type:FEATHER"); + flyMenuItem.add("amount:1"); + flyMenuItem.add("name:&5Fly"); + flyMenuItem.add("lore:&eAllows you to fly#&cPrice: 64 diamonds and 10 emeralds! #&cOnly for VIPs!"); + List> flyPriceItems=new ArrayList>(); + List flyPriceItem1=new ArrayList(); + flyPriceItem1.add("type:DIAMOND"); + flyPriceItem1.add("amount:64"); + List flyPriceItem2=new ArrayList(); + flyPriceItem2.add("type:EMERALD"); + flyPriceItem2.add("amount:10"); + flyPriceItems.add(flyPriceItem1); + flyPriceItems.add(flyPriceItem2); + List flyPermissions=new ArrayList(); + flyPermissions.add("essentials.fly"); + addDefault(config,"Fly","permission","item",flyPermissions,flyPriceItems,flyMenuItem,"&6You bought Fly Permissions!",10,"VIP.Access"); + List> evilCookieRewardItems=new ArrayList>(); + List evilCookieRewardItem=new ArrayList(); + evilCookieRewardItem.add("type: COOKIE"); + evilCookieRewardItem.add("amount:1"); + evilCookieRewardItem.add("name:&4Evil Cookie"); + evilCookieRewardItem.add("lore:&0&l*_* #&cPrice: 40 Levels"); + evilCookieRewardItem.add("enchantment:KNOCKBACK#10"); + evilCookieRewardItem.add("enchantment:FIRE_ASPECT#2"); + evilCookieRewardItem.add("enchantment:DAMAGE_ALL#5"); + evilCookieRewardItems.add(evilCookieRewardItem); + List evilCookieMenuItem=new ArrayList(); + evilCookieMenuItem.add("type: COOKIE"); + evilCookieMenuItem.add("amount:1"); + evilCookieMenuItem.add("name:&4Evil Cookie"); + evilCookieMenuItem.add("lore:&0&l*_* #&cPrice: 40 Levels #&cOnly for VIPs!"); + evilCookieMenuItem.add("enchantment:KNOCKBACK#10"); + evilCookieMenuItem.add("enchantment:FIRE_ASPECT#2"); + evilCookieMenuItem.add("enchantment:DAMAGE_ALL#5"); + addDefault(config,"EvilCookie","item","exp",evilCookieRewardItems,40,evilCookieMenuItem,"&4Nothing will stop you now!",12,"VIP.Access"); + List npMenuItem=new ArrayList(); + npMenuItem.add("type:POTION"); + npMenuItem.add("amount:1"); + npMenuItem.add("name:&1NinjaPotion"); + npMenuItem.add("lore:&e1 NinjaPotion #&cPrice: 400 Money"); + npMenuItem.add("potioneffect:SPEED#4#600"); + npMenuItem.add("potioneffect:JUMP#0#600"); + npMenuItem.add("potioneffect:NIGHT_VISION#0#600"); + List> npItems=new ArrayList>(); + List np2MenuItem=new ArrayList(); + np2MenuItem.add("type:POTION"); + np2MenuItem.add("amount:1"); + np2MenuItem.add("name:&1NinjaPotion"); + np2MenuItem.add("lore:&e1 NinjaPotion #&cPrice: 400 Money #&cOnly for VIPs!"); + np2MenuItem.add("potioneffect:SPEED#4#600"); + np2MenuItem.add("potioneffect:JUMP#0#600"); + np2MenuItem.add("potioneffect:NIGHT_VISION#0#600"); + npItems.add(np2MenuItem); + addDefault(config,"NinjaPotion","item","money",npItems,400,npMenuItem,"&6You bought 1 NinjaPotion! &cMoney left: %left%",13,"VIP.Access"); + List> changePriceItems=new ArrayList>(); + List changePriceItem=new ArrayList(); + changePriceItem.add("type:DIAMOND"); + changePriceItem.add("amount:2"); + changePriceItems.add(changePriceItem); + List> changeRewardItems=new ArrayList>(); + List changeRewardItem1=new ArrayList(); + changeRewardItem1.add("type:WOOL"); + changeRewardItem1.add("amount:10"); + changeRewardItem1.add("durability:14"); + List changeRewardItem2=new ArrayList(); + changeRewardItem2.add("type:WOOL"); + changeRewardItem2.add("amount:10"); + changeRewardItem2.add("durability:11"); + List changeRewardItem3=new ArrayList(); + changeRewardItem3.add("type:WOOL"); + changeRewardItem3.add("amount:10"); + changeRewardItem3.add("durability:4"); + List changeRewardItem4=new ArrayList(); + changeRewardItem4.add("type:WOOL"); + changeRewardItem4.add("amount:10"); + changeRewardItem4.add("durability:5"); + changeRewardItems.add(changeRewardItem1); + changeRewardItems.add(changeRewardItem2); + changeRewardItems.add(changeRewardItem3); + changeRewardItems.add(changeRewardItem4); + List changeMenuItem=new ArrayList(); + changeMenuItem.add("type:WOOL"); + changeMenuItem.add("amount:40"); + changeMenuItem.add("durability:10"); + changeMenuItem.add("name:&bColored Wool"); + changeMenuItem.add("lore:&c10 Red, &110 Blue, &e10 Yellow and &a10 Green Wool #&cPrice: 2 Diamonds"); + addDefault(config,"WoolSet","item","item",changeRewardItems,changePriceItems,changeMenuItem,"&6You bought a set of wool!",14,""); + List backMenuItem=new ArrayList(); + backMenuItem.add("type:REDSTONE"); + backMenuItem.add("amount:1"); + backMenuItem.add("name:&4Back"); + backMenuItem.add("lore:&8Go back to the Menu"); + addDefault(config,"Back","shop","free","ShopMenu",null,backMenuItem,"&6Leaving the ExampleShop...",18,""); + config.saveToFile(f); + } + + public void createShopItemsMenu(File f,CommentedYamlConfig config){ + config.set("ShopName","ShopMenu"); + config.set("signs.text","[ShopMenu]"); + config.set("signs.NeedPermissionToCreateSign",false); + config.createSection("shop"); + List exampleShopMenuItem=new ArrayList(); + exampleShopMenuItem.add("type:TNT"); + exampleShopMenuItem.add("amount:1"); + exampleShopMenuItem.add("name:&1ExampleShop"); + exampleShopMenuItem.add("lore:&6Click me!"); + addDefault(config,"ExampleShop","shop","free","ExampleShop",null,exampleShopMenuItem,"&6Opening ExampleShop...",1,""); + List spellShopMenuItem=new ArrayList(); + spellShopMenuItem.add("type:WRITTEN_BOOK"); + spellShopMenuItem.add("amount:1"); + spellShopMenuItem.add("name:&5SpellShop"); + spellShopMenuItem.add("lore:&8Click me!"); + addDefault(config,"SpellShop","shop","free","SpellShop",null,spellShopMenuItem,"&6Opening SpellShop...",9,""); + config.saveToFile(f); + } + + public void createShopItemsSpell(File f,CommentedYamlConfig config){ + config.set("ShopName","SpellShop"); + config.set("signs.text","[SpellShop]"); + config.set("signs.NeedPermissionToCreateSign",false); + config.createSection("shop"); + List exampleShopMenuItem=new ArrayList(); + exampleShopMenuItem.add("type:WRITTEN_BOOK"); + exampleShopMenuItem.add("amount:1"); + exampleShopMenuItem.add("name:&4Fireball"); + exampleShopMenuItem.add("lore:&8Allows you to shoot Fireballs! #&cPrice: 10 Levels"); + List perms=new ArrayList(); + perms.add("Plugin.Fireball"); + addDefault(config,"SpellFireball","permission","exp",perms,10,exampleShopMenuItem,"&6Opening ExampleShop...",1,""); + List backMenuItem=new ArrayList(); + backMenuItem.add("type:REDSTONE"); + backMenuItem.add("amount:1"); + backMenuItem.add("name:&4Back"); + backMenuItem.add("lore:&8Go back to the Menu"); + addDefault(config,"Back","shop","free","ShopMenu",null,backMenuItem,"&6Leaving the SpellShop...",9,""); + config.saveToFile(f); + } + + private boolean simplePointsPluginCheck(){ + if(Bukkit.getPluginManager().getPlugin("PlayerPoints")!=null){ return true; } + if(Bukkit.getPluginManager().getPlugin("PointsAPI")!=null){ return true; } + if(Bukkit.getPluginManager().getPlugin("CommandPoints")!=null){ return true; } + if(Bukkit.getPluginManager().getPlugin("EnjinMinecraftPlugin")!=null){ return true; } + if(Bukkit.getPluginManager().getPlugin("BossAPI")!=null){ return true; } + return false; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/managers/ItemNameManager.java b/src/main/java/org/black_ixx/bossshop/managers/ItemNameManager.java new file mode 100644 index 0000000..a3670be --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/managers/ItemNameManager.java @@ -0,0 +1,137 @@ +package org.black_ixx.bossshop.managers; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; + +import org.apache.commons.lang.StringUtils; +import org.black_ixx.bossshop.BossShop; +import org.bukkit.inventory.ItemStack; + +import cc.util.bossshop.ClassHelper; +import cc.util.bossshop.LocalLanguage; +import cc.util.bossshop.NMSHelper; +import cc.util.bossshop.config.CommentedYamlConfig; +import cc.util.bossshop.pluginmodel.INeedConfig; +import cc.util.bossshop.pluginmodel.INeedReload; + + +public class ItemNameManager extends LocalLanguage implements INeedConfig,INeedReload{ + + private final HashMap mItemNames=new HashMap<>(); + private String mLang="zh_CN"; + + public ItemNameManager(BossShop pPlugin){ + super(pPlugin); + this.mPlugin.registerConfigModel(this); + this.mPlugin.registerReloadModel(this); + } + + public String getLang(){ + return this.mLang; + } + + public String getDisPlayName(ItemStack pItem){ + return this.getDisplayName(pItem,this.mLang); + } + + @Override + public String getDisplayName(ItemStack pItem,String pLang){ + if(pItem==null) return ""; + if(this.hasSetName(pItem)) return pItem.getItemMeta().getDisplayName(); + if(pLang.equals("en_US")){ //如果是en_US 直接使用ItemStack的getName + Object NMSItemStack=ClassHelper.invokeStaticMethod(NMSHelper.method_CraftItemStack_asNMSCopy,pItem); + if(NMSItemStack==null) return this.getIDFormatName(pItem); + return (String)ClassHelper.invokeMethod(NMSItemStack,method_NMSItemStack_getName); + } + String unlocalName=this.getUnlocalizedName(pItem); + String itemName=this.mItemNames.get(unlocalName); + if(StringUtils.isNotEmpty(itemName)) return itemName; + itemName=this.mItemNames.get(unlocalName+".name"); + if(StringUtils.isNotEmpty(itemName)) return itemName; + return super.getDisplayName(pItem,pLang); + } + + @Override + public boolean reloadConfig(){ + if(this.mLang.equals("en_US")){ + this.mItemNames.clear(); + this.mPlugin.info("en_US语言无需配置物品翻译"); + return true; + } + InputStream fis=null; + String fileName="ItemName_"+this.mLang+".lang"; + File existItemName=new File(this.mPlugin.getDataFolder(),fileName); + if(!existItemName.isFile()){ + fis=this.mPlugin.getResource("lang/"+fileName); + if(fis==null){ + this.mPlugin.warn("未找到物品翻译文件["+fileName+"]"); + return false; + } + try{ + if(!existItemName.getParentFile().isDirectory()) + existItemName.getParentFile().mkdirs(); + existItemName.createNewFile(); + }catch(IOException ioexp){ + try{ + fis.close(); + }catch(IOException e){} + this.mPlugin.severe("创建物品翻译文件["+fileName+"]时发生了错误: "+ioexp.getMessage(),ioexp); + return false; + } + FileOutputStream fos=null; + byte[] buffer=new byte[1024]; + try{ + fos=new FileOutputStream(existItemName); + int readCount=-1; + while((readCount=fis.read(buffer))!=-1) + fos.write(buffer,0,readCount); + }catch(IOException ioexp){ + this.mPlugin.severe("向物品翻译文件["+fileName+"]写入内容时发生了错误: "+ioexp.getMessage(),ioexp); + return false; + }finally{ + if(fos!=null) try{ + fos.close(); + }catch(IOException e){} + try{ + fis.close(); + fis=null; + }catch(IOException e){} + } + } + String[] contentLines=new String[0]; + try{ + fis=new FileInputStream(existItemName); + byte[] contents=new byte[fis.available()]; + fis.read(contents); + contentLines=new String(contents,"UTF-8").split("[\\r]?\n"); + }catch(IOException ioexp){ + this.mPlugin.severe("从文件["+fileName+"]读入物品翻译是发生了错误"+ioexp.getMessage(),ioexp); + return false; + }finally{ + if(fis!=null) try{ + fis.close(); + }catch(IOException e){} + } + this.mItemNames.clear(); + for(String sLine : contentLines){ + String[] t=sLine.split("=",2); + if(t.length<2) continue; + this.mItemNames.put(t[0],t[1]); + } + this.mPlugin.info("已经成功载入原版物品翻译"); + return true; + } + + @Override + public void setConfig(){ + CommentedYamlConfig tConfig=this.mPlugin.getConfigManager().getConfig(); + this.mLang=tConfig.getString("ItemNameLang",this.mLang).trim(); + if(this.mLang.isEmpty()) this.mLang="zh_CN"; + this.mPlugin.info("设置物品翻译语言为["+this.mLang+"]"); + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/managers/ItemStackChecker.java b/src/main/java/org/black_ixx/bossshop/managers/ItemStackChecker.java new file mode 100644 index 0000000..93e5bd4 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/managers/ItemStackChecker.java @@ -0,0 +1,75 @@ +package org.black_ixx.bossshop.managers; + +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import cc.util.bossshop.ClassHelper; +import cc.util.bossshop.NMSHelper; + +/** + * 用于背包物品检查,背包物品收取 + * @author 聪聪 + * + */ +public class ItemStackChecker{ + + public static boolean isSame(ItemStack pItem1,ItemStack pItem2){ + if(pItem1==null&&pItem2==null) return true; + if(pItem1==null||pItem2==null) return false; + if(pItem1.getType()!=pItem2.getType()) return false; + if(pItem1.getDurability()!=pItem2.getDurability()) return false; + Object tNMSItemStack_mcitem1=NMSHelper.getNMSItem(pItem1); + Object tNMSItemStack_mcitem2=NMSHelper.getNMSItem(pItem2); + if(tNMSItemStack_mcitem1==null&&tNMSItemStack_mcitem2==null) return true; + if(tNMSItemStack_mcitem1==null||tNMSItemStack_mcitem2==null) return false; + Object tag1=ClassHelper.getFieldValue(tNMSItemStack_mcitem1,NMSHelper.field_NMSItemStack_tag); + Object tag2=ClassHelper.getFieldValue(tNMSItemStack_mcitem2,NMSHelper.field_NMSItemStack_tag); + return (tag1==null&&tag2==null)||(tag1!=null&&tag2!=null&&tag1.equals(tag2)); + } + + public static boolean inventoryContainsItem(Player p,ItemStack item){ + int a=0; + int rcount=item.getAmount(); + for(ItemStack ii : p.getInventory().getContents()){ + if(ii==null) continue; + if(item.getType()!=ii.getType()) continue; + if(item.getDurability()!=ii.getDurability()) continue; + if(isSame(ii,item)) + a+=ii.getAmount(); + if(rcount<=a) return true; + } + return false; + } + + public static void takeItem(ItemStack item,Player p){ + int a=0; + int rcount=item.getAmount(); + ItemStack[] items=p.getInventory().getContents(); + for(int i=0;i=rcount){ + p.getInventory().setContents(items); + return; + } + } + } + + public static boolean isValidEnchantment(ItemStack item,Enchantment enchantment,int level){ + try{ + item.clone().addEnchantment(enchantment,level); + } + catch(Exception e){ + return false; + } + return true; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/managers/ItemStackCreator.java b/src/main/java/org/black_ixx/bossshop/managers/ItemStackCreator.java new file mode 100644 index 0000000..b54c53b --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/managers/ItemStackCreator.java @@ -0,0 +1,304 @@ +package org.black_ixx.bossshop.managers; + +import java.util.ArrayList; +import java.util.List; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.nbt.NBTEditManager; +import org.bukkit.Color; +import org.bukkit.DyeColor; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.LeatherArmorMeta; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.material.Colorable; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import com.mysql.jdbc.StringUtils; + +import cc.util.bossshop.FBukkit; + +public class ItemStackCreator{ + + private BossShop mPlugin; + + public ItemStackCreator(BossShop pPlugin){ + this.mPlugin=pPlugin; + } + + public ItemStack createItemStack(List itemData){ + return this.createItemStackS(itemData,false); + } + + @SuppressWarnings("deprecation") + public ItemStack createItemStackS(List itemData,boolean pOverrideName){ + String tdisplayName=null; + ItemStack i=new ItemStack(Material.STONE); + for(String x : itemData){ + String parts[]=x.split(":",2); + String s=parts[0].trim(); + String a=parts[1].trim(); + if(s.equalsIgnoreCase("id")){ + a=stringFix(a); + if(a.contains(":")){ + String pa[]=a.split(":",2); + String type=pa[0].trim(); + String dur=pa[1].trim(); + try{ + short dura=Short.parseShort(dur); + i.setDurability(dura); + a=type; + }catch(Exception e){ + // Do not change anything + } + } + if(!isInteger(a)){ + this.mPlugin.severe("Mistake in Config: "+a+" (id) needs to be a number!"); + continue; + } + if(Material.getMaterial(Integer.parseInt(a))==null){ + this.mPlugin.severe("Mistake in Config: "+a+" (id) is no valid Material!"); + continue; + } + i.setTypeId(Integer.parseInt(a)); + continue; + } + if(s.equalsIgnoreCase("nbt")){ + a=stringFix(a); + ItemStack ti=this.mPlugin.getManager(NBTEditManager.class).setItemNBT(i,a); + if(ti!=null) i=ti; + } + if(s.equalsIgnoreCase("type")){ + a=stringFix(a); + if(a.contains(":")){ + String pa[]=a.split(":",2); + String type=pa[0].trim(); + String dur=pa[1].trim(); + try{ + short dura=Short.parseShort(dur); + i.setDurability(dura); + a=type; + }catch(Exception e){ + // Do not change anything + } + } + a=a.toUpperCase(); + if(Material.getMaterial(a)==null){ + this.mPlugin.severe("Mistake in Config: "+a+" (type) is no valid Material!"); + continue; + } + i.setType(Material.getMaterial(a)); + continue; + } + if(s.equalsIgnoreCase("amount")){ + a=stringFix(a); + if(!isInteger(a)){ + this.mPlugin.severe("Mistake in Config: "+a+" (amount) needs to be a number!"); + continue; + } + i.setAmount(Integer.parseInt(a)); + continue; + } + if(s.equalsIgnoreCase("durability")||s.equalsIgnoreCase("damage")){ + a=stringFix(a); + if(!isShort(a)){ + this.mPlugin.severe("Mistake in Config: "+a+" (durability) needs to be a number!"); + continue; + } + i.setDurability(Short.parseShort(a)); + continue; + } + if(s.equalsIgnoreCase("name")){ + tdisplayName=StringManager.transform(a); + continue; + } + if(s.equalsIgnoreCase("lore")){ + ItemMeta meta=i.getItemMeta(); + String par[]=a.split("#"); + List lore=meta.getLore(); + if(lore==null) lore=new ArrayList(); + for(String b : par) + lore.add(StringManager.transform(b)); + meta.setLore(lore); + if(!FBukkit.isItemMetaEmpty(meta)) + i.setItemMeta(meta); + continue; + } + if(s.equalsIgnoreCase("enchantment")){ + a=stringFix(a); + try{ + String par[]=a.split("#"); + String eType=par[0].trim().toUpperCase(); + String eLvl=par[1].trim(); + Enchantment e=Enchantment.DURABILITY; + if(isInteger(eType)){ + e=Enchantment.getById((Integer)Integer.parseInt(eType)); + }else{ + e=Enchantment.getByName(eType); + } + if(e==null){ + this.mPlugin.severe("Mistake in Config: "+a+" (enchantment) contains mistakes: Invalid Enchantment name"); + continue; + } + if(i.getType()==Material.ENCHANTED_BOOK){ + EnchantmentStorageMeta meta=(EnchantmentStorageMeta)i.getItemMeta(); + meta.addStoredEnchant(e,Integer.parseInt(eLvl),true); + if(!FBukkit.isItemMetaEmpty(meta)) + i.setItemMeta(meta); + continue; + } + i.addUnsafeEnchantment(e,Integer.parseInt(eLvl)); + }catch(Exception e){ + this.mPlugin.severe("Mistake in Config: "+a+" (enchantment) contains mistakes!"); + } + continue; + } + if(s.equalsIgnoreCase("enchantmentid")){ + a=stringFix(a); + try{ + String par[]=a.split("#"); + String eType=par[0].trim(); + String eLvl=par[1].trim(); + Enchantment e=Enchantment.getById(Integer.parseInt(eType)); + if(e==null){ + this.mPlugin.severe("Mistake in Config: "+a+" (enchantmentid) contains mistakes: Invalid Enchantment id"); + continue; + } + if(i.getType()==Material.ENCHANTED_BOOK){ + EnchantmentStorageMeta meta=(EnchantmentStorageMeta)i.getItemMeta(); + meta.addStoredEnchant(e,Integer.parseInt(eLvl),true); + // meta.addEnchant(e, Integer.parseInt(eLvl), true); + if(!FBukkit.isItemMetaEmpty(meta)) + i.setItemMeta(meta); + continue; + } + i.addUnsafeEnchantment(e,Integer.parseInt(eLvl)); + }catch(Exception e){ + this.mPlugin.severe("Mistake in Config: "+a+" (enchantmentid) contains mistakes!"); + } + continue; + } + if(s.equalsIgnoreCase("color")){ + a=stringFix(a); + Color c=Color.AQUA; + try{ + String par[]=a.split("#"); + String c1=par[0].trim(); + String c2=par[1].trim(); + String c3=par[2].trim(); + Integer i1=Integer.parseInt(c1); + Integer i2=Integer.parseInt(c2); + Integer i3=Integer.parseInt(c3); + c=Color.fromRGB(i1,i2,i3); + }catch(Exception e){ + this.mPlugin.severe("Mistake in Config: "+a+" (color) contains mistakes! A color Line should look like this: \"color:##\". You can find a list of RGB Colors here: http://www.farb-tabelle.de/de/farbtabelle.htm"); + continue; + } + if(!(i.getItemMeta() instanceof Colorable)&!(i.getItemMeta() instanceof LeatherArmorMeta)){ + this.mPlugin.severe("Mistake in Config: The item "+a+" (Type "+i.getType()+")"+"(color) can't be colored/dyed! Tip: Always define the Material Type before you color the item!"); + continue; + } + if(i.getItemMeta() instanceof Colorable){ + Colorable ic=(Colorable)i.getItemMeta(); + DyeColor color=DyeColor.getByColor(c); + ic.setColor(color); + i.setItemMeta((ItemMeta)ic); + continue; + } + if(i.getItemMeta() instanceof LeatherArmorMeta){ + LeatherArmorMeta ic=(LeatherArmorMeta)i.getItemMeta(); + ic.setColor(c); + i.setItemMeta((ItemMeta)ic); + continue; + } + continue; + } + if(s.equalsIgnoreCase("playerhead")){ + if(i.getType()!=Material.SKULL_ITEM){ + this.mPlugin.severe("Mistake in Config: "+a+" (playerhead) You can't use \"PlayerHead\" on items which are not skulls..."); + continue; + } + SkullMeta meta=(SkullMeta)i.getItemMeta(); + meta.setOwner(a); + if(!FBukkit.isItemMetaEmpty(meta)) + i.setItemMeta(meta); + continue; + } + if(s.equalsIgnoreCase("potioneffect")){ + a=stringFix(a); + if(i.getType()!=Material.POTION){ + this.mPlugin.severe("Mistake in Config: "+a+" (potioneffect) You can't add PotionEffects to items which are not potions..."); + continue; + } + PotionMeta meta=(PotionMeta)i.getItemMeta(); + try{ + String par[]=a.split("#"); + String pType=par[0].trim().toUpperCase(); + String pLvl=par[1].trim(); + String pTime=par[2].trim(); + PotionEffectType type=null; + if(isInteger(pType)){ + type=PotionEffectType.getById(Integer.parseInt(pType)); + }else{ + type=PotionEffectType.getByName(pType); + } + meta.addCustomEffect(new PotionEffect(type,getTicksFromSeconds(pTime),Integer.parseInt(pLvl)),true); + if(!FBukkit.isItemMetaEmpty(meta)) + i.setItemMeta(meta); + }catch(Exception e){ + this.mPlugin.severe("Mistake in Config: "+a+" (potioneffect) contains mistakes!"); + } + continue; + } + } + if((pOverrideName||!i.getItemMeta().hasDisplayName())&&!StringUtils.isNullOrEmpty(tdisplayName)){ + ItemMeta meta=i.getItemMeta(); + meta.setDisplayName(tdisplayName); + i.setItemMeta(meta); + } + return i; + } + + private static boolean isInteger(String str){ + try{ + Integer.parseInt(str); + }catch(NumberFormatException nfe){ + return false; + } + return true; + } + + private static boolean isShort(String str){ + try{ + Short.parseShort(str); + }catch(NumberFormatException nfe){ + return false; + } + return true; + } + + private static String stringFix(String s){ + if(s.contains(" ")){ + s=s.replaceAll(" ",""); + } + return s; + } + + private static int getTicksFromSeconds(String s){ + try{ + Double d=Double.parseDouble(s); + return (int)(d*20); + }catch(Exception e){} + try{ + Integer i=Integer.parseInt(s); + return (int)(i*20); + }catch(Exception e){} + return 0; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/managers/LangManager.java b/src/main/java/org/black_ixx/bossshop/managers/LangManager.java new file mode 100644 index 0000000..bc54950 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/managers/LangManager.java @@ -0,0 +1,212 @@ +package org.black_ixx.bossshop.managers; + +import org.black_ixx.bossshop.BossShop; +import org.bukkit.ChatColor; + +import cc.util.bossshop.filemanager.ALangManager; + +public class LangManager extends ALangManager{ + + /** + * 语言翻译系统 + * @param plugin + */ + public LangManager(BossShop pplugin){ + super(pplugin,"messages.yml","1.1"); + } + + @Override + public boolean reloadConfig(){ + if(!super.reloadConfig()) return false; + this.checkUpdate(); + this.addDefaults(); + this.saveConfig(); + return false; + } + + public boolean checkUpdate(){ + boolean update=super.checkUpdate(); + if(!update) return false; + String tVersion=this.mConfig.getString(SEC_CFG_VERSION,"1.0"); + if(tVersion.equalsIgnoreCase("1.0")){// 1.0-->1.1 + tVersion="1.1"; + this.mConfig.set("Mail.MailMaxSaveNumb","====邮件最大保存数量[%numb%]===="); + String tMsg=this.mConfig.getString("Mail.YouReciveMailFrom"); + if(tMsg!=null){ + tMsg=tMsg.replace("你收到了来自","你收到了"); + this.mConfig.set("Mail.YouReciveMailFrom",null); + this.mConfig.set("Mail.YouReciveMail",tMsg); + } + } + this.mConfig.set(SEC_CFG_VERSION,this.mVersion); + return true; + } + + @Override + public void addDefaults(){ + this.mConfig.addDefault("CFG.CannotCreatMailItem","&c无法创建邮件物品"); + this.mConfig.addDefault("CFG.UnableFoundSaleOwner","&c无法找到寄售商品的所有者"); + this.mConfig.addDefault("CFG.UnknowOutDateSaleItemType","&c未知的过期寄售物品类型"); + this.mConfig.addDefault("CFG.UnknowSalePriceType","&c未知的寄售价格类型,无法给寄售物品所有者所得"); + this.mConfig.addDefault("CFG.UnsupportMailItemType","&c不支持的邮件物品类型"); + this.mConfig.addDefault("CFG.UnsupportSaleItemParam","&c不支持的寄售物品变量设置"); + this.mConfig.addDefault("Cmd.AlreadyReloadShop","已经重载%shop%商店"); + this.mConfig.addDefault("Cmd.ClearedModelStatus","&c清理了%numb%个模块的状态"); + this.mConfig.addDefault("Cmd.ClearedNoUsefulNBT","共清理了%numb%个未被引用且自动生成的NBT库存"); + this.mConfig.addDefault("Cmd.ConfigReloacded","已经重载配置"); + this.mConfig.addDefault("Cmd.ConsoleNotAllow","&c控制台不能执行该命令"); + this.mConfig.addDefault("Cmd.ErrorHappedWhenHandlerCmd","&c插件在处理命令的时候发生了异常"); + this.mConfig.addDefault("Cmd.ForSaleSuccessAndTisID","&c寄售物品成功,寄售物品的编号是: "); + this.mConfig.addDefault("Cmd.GetHelp","&c获取帮助"); + this.mConfig.addDefault("Cmd.ItemMayDonotHaveNBT","&c物品可能没有nbt信息"); + this.mConfig.addDefault("Cmd.LangReloacded","&2已经重载语言文件"); + this.mConfig.addDefault("Cmd.MustAboveZero","&c必须大于零"); + this.mConfig.addDefault("Cmd.MustBeNumb","&c必须为数字"); + this.mConfig.addDefault("Cmd.NBTHasExistInStockAndItsID","NBT已经存在在NBT库存中,它的ID是: "); + this.mConfig.addDefault("Cmd.NBTHaveAddAndItsID","NBT已经添加到NBT库存,它的ID是: "); + this.mConfig.addDefault("Cmd.NoPermission","&c你没有权限"); + this.mConfig.addDefault("Cmd.NoPlayerFound","&c未发现该玩家"); + this.mConfig.addDefault("Cmd.NoShopFound","&c未发现该商店"); + this.mConfig.addDefault("Cmd.OpenShopForPlayer","&2为玩家%player%开启商店%shop%"); + this.mConfig.addDefault("Cmd.PluginReloaded","已经重载插件"); + this.mConfig.addDefault("Cmd.SaleItemShouldNotSameWithPrice","&c寄售物品类型和价格类型不能相同"); + this.mConfig.addDefault("Cmd.SaleReachPreOneLimit","&c寄售商品已达个人寄售上线"); + this.mConfig.addDefault("Cmd.SaleShopIsFull","&c寄售商店寄售数量已经达到上限"); + this.mConfig.addDefault("Cmd.ServerDisablePoints","&c服务器不支持点券或未启用点券"); + this.mConfig.addDefault("Cmd.UnknowCommand","&c未知指令,输入/BossShop help 获取帮助"); + this.mConfig.addDefault("Cmd.UnknowPriceType","&c未知的价格类型"); + this.mConfig.addDefault("Cmd.UnknowSaleItemType","&c未知的寄售物品类型"); + this.mConfig.addDefault("Cmd.UnsupportPriceType","&c不支持的价格类型"); + this.mConfig.addDefault("Cmd.UnsupportSaleItemType","&c不支持的寄售物品类型"); + this.mConfig.addDefault("Cmd.YouHaveNotOutDateSaleItem","&c你没有过期的寄售物品"); + this.mConfig.addDefault("Cmd.YouSaleHasSellOne","你的寄售物品售出一份"); + this.mConfig.addDefault("Cmd.YouShouldTakeItemInHand","&c你的手上必须拿着物品"); + this.mConfig.addDefault("Cmd.unknowChildCommand","&c未知子命令指令"); + this.mConfig.addDefault("Console.ErrorCreateSaleFile","&c创建寄售商店文件是发生了错误"); + this.mConfig.addDefault("Console.ShopFileNotExist","&c商店配置文件不存在"); + this.mConfig.addDefault("Console.StartReloadShop","开始重载商店"); + this.mConfig.addDefault("Console.MissingNBTNode","&cNBT库丢失NBT节点"); + this.mConfig.addDefault("Economy.NoAccount","&c你还没有开户"); + this.mConfig.addDefault("Enchantment.Invalid","&c这个物品不能附这个魔"); + this.mConfig.addDefault("Help.ClearModelStatus","&2清理插件中一些模块的状态"); + this.mConfig.addDefault("Help.ClosePlayerShop","&2关闭玩家打开的BossShop商店"); + this.mConfig.addDefault("Help.HelpOpen","&2获取有关/BossShop open的帮助指令"); + this.mConfig.addDefault("Help.MailCheck","&2检查你的邮件列表"); + this.mConfig.addDefault("Help.MailHelp","&2获取邮件命令的帮助"); + this.mConfig.addDefault("Help.MailRevice","&2接受邮件中的物品"); + this.mConfig.addDefault("Help.MailSend","&2发送物品到玩家的邮箱"); + this.mConfig.addDefault("Help.NBTAdd","&2添加当前手上物品的NBT信息到NBT库存"); + this.mConfig.addDefault("Help.NBTClear","&2清理由插件自动生成且引用次数为0的NBT库存"); + this.mConfig.addDefault("Help.NBTHelp","&2获取有关/BossShop nbt 的帮助指令"); + this.mConfig.addDefault("Help.OpenOtherShop","&2为别人打开商店"); + this.mConfig.addDefault("Help.OpenShop","&2为自己打开商店"); + this.mConfig.addDefault("Help.Reload","&2重载BossShop配置"); + this.mConfig.addDefault("Help.ReloadConfig","&2重载插件主配置"); + this.mConfig.addDefault("Help.ReloadHelp","&2获取有关/BossShop reload的帮助指令"); + this.mConfig.addDefault("Help.ReloadLang","&2重载语言文件,部分语言可能无法更新"); + this.mConfig.addDefault("Help.ReloadShop","&2重载指定商店配置,商店名需要文件名"); + this.mConfig.addDefault("Help.SaleHelp","&2获取有关/BossShop sale 的帮助命令"); + this.mConfig.addDefault("Help.StartSaleWithChat","&2使用聊天栏开始图形化寄售"); + this.mConfig.addDefault("Help.StopSaleWithChat","&2退出聊天栏寄售"); + this.mConfig.addDefault("Help.SaleForsale","&2寄售物品"); + this.mConfig.addDefault("Help.SaleOther","&2其他参数均为整数"); + this.mConfig.addDefault("Help.SalePriceType","&2支持的价格类型 money,points"); + this.mConfig.addDefault("Help.SaleSellType","&2支持的售卖类型 item,money,points"); + this.mConfig.addDefault("Help.Unsale","&2下架指定编号的寄售物品"); + this.mConfig.addDefault("Lore.AlreadySoldOut","&c商品已经售罄"); + this.mConfig.addDefault("Lore.BuyIsNotTime","§c商品未到购买时间,将于%time%开放购买"); + this.mConfig.addDefault("Lore.EveryoneLimit","§2每人限购%numb%件,你还可以购买%left%件"); + this.mConfig.addDefault("Lore.GoodsOutOfDate","&c商品已经过了购买日期"); + this.mConfig.addDefault("Lore.GoodsStockLeft","&2库存剩余%numb%件!"); + this.mConfig.addDefault("Lore.NowCanBuyGoods","&2现在可以购买该商品"); + this.mConfig.addDefault("Lore.WillCloseInTime","&c将于%time%结束开放购买"); + this.mConfig.addDefault("Mail.FromOutDateSale","来自过期寄售"); + this.mConfig.addDefault("Mail.FromPlayer","来自玩家"); + this.mConfig.addDefault("Mail.FromUnknow","来自未知"); + this.mConfig.addDefault("Mail.MailHasSend","邮件已经发送"); + this.mConfig.addDefault("Mail.MailMaxSaveNumb","====邮件最大保存数量===="); + this.mConfig.addDefault("Mail.NoPermitToCreateSignShop","没有权限创建牌子商店"); + this.mConfig.addDefault("Mail.NoPermitToOpenSignShop","没有权限打开牌子商店"); + this.mConfig.addDefault("Mail.PlayerMailIsFull","&c玩家[%player%]的邮箱已经满了"); + this.mConfig.addDefault("Mail.YouCannotSendMailToYouSelf","&c你不能给自己发邮件"); + this.mConfig.addDefault("Mail.YouHaveNoMail","你没有任何邮件"); + this.mConfig.addDefault("Mail.YouMailIsFull","&c你的邮箱已经满了"); + this.mConfig.addDefault("Mail.YouNeed%%MoneyToSendMail","&c你需要%numb%的金币来发送邮件"); + this.mConfig.addDefault("Mail.YouReciveMail","你收到了%where%的邮件,输入/BS mail check查看"); + this.mConfig.addDefault("Main.AlreadyBought","&c这东西你已经买过了"); + this.mConfig.addDefault("Main.AlreadyClosePlayerShop","&2已经关闭玩家商店"); + this.mConfig.addDefault("Main.AlreadySoldOut","&c商品已经售罄"); + this.mConfig.addDefault("Main.CannotBuyYourselfGoods","&c你不能购买自己的寄售物品"); + this.mConfig.addDefault("Main.CloseShop","&6下次再来??"); + this.mConfig.addDefault("Main.ErrorHappend","&c商店发生了一点小错误,请通知管理员"); + this.mConfig.addDefault("Main.FromHandUnsale","来自手动下架"); + this.mConfig.addDefault("Main.GoodsNotArriveTime","&c商品未到购买时间"); + this.mConfig.addDefault("Main.GoodsOutOfDate","&c商品已经过了购买时间"); + this.mConfig.addDefault("Main.ItemCannotPileMoreThan","&c此物品的堆叠数量不能超过"); + this.mConfig.addDefault("Main.NoPermitToBuyThisItem","&c你没有权限购买该物品"); + this.mConfig.addDefault("Main.NoPlayerFound","&c未找到该玩家"); + this.mConfig.addDefault("Main.NoSaleItemFound","&c未找到该寄售物品"); + this.mConfig.addDefault("Main.OpenShop","&6开启小店"); + this.mConfig.addDefault("Main.OutOfPersonalLimit","&c已经达到个人购买物品数量上限"); + this.mConfig.addDefault("Main.PlayerNotOpenBSShop","&c玩家没有打开BossShop商店"); + this.mConfig.addDefault("Main.PlayerOffline","&c玩家未在线"); + this.mConfig.addDefault("Main.PlayerSaleItem","&2玩家%player_name%寄售了%item%"); + this.mConfig.addDefault("Main.SuccessUnsaleItem","成功下架寄售物品"); + this.mConfig.addDefault("Main.YouCannotUnsaleOther","&c你不能下架别人的寄售的物品"); + this.mConfig.addDefault("Main.YouClickTooQuick","&c你点击的太快了"); + this.mConfig.addDefault("Main.YouNeed%%MoneyToUnsale","&c你需要%numb%的金币来下架物品"); + this.mConfig.addDefault("Main.YouTodayCannotUnsaleMore","&c你今天已经不能再下架更多物品了"); + this.mConfig.addDefault("NotEnough.Exp","&c你的经验太少了"); + this.mConfig.addDefault("NotEnough.Item","&c兑换需要的物品不足"); + this.mConfig.addDefault("NotEnough.Money","&c你钱不够了"); + this.mConfig.addDefault("NotEnough.Points","&c你点券不足"); + this.mConfig.addDefault("Sale.ChatSaleStart","你已经进入寄售物品状态,输入/BS sale stop 来退出"); + this.mConfig.addDefault("Sale.ChatSaleRepartStart","&c你已经开始寄售了,请勿重复开始"); + this.mConfig.addDefault("Sale.ChatSaleExit","你已经退出聊天栏寄售"); + this.mConfig.addDefault("Sale.ChatSaleFinish","你已经设置所有寄售参数"); + this.mConfig.addDefault("Sale.NowSaleParameter","当前寄售参数(§6可以重复使用)"); + this.mConfig.addDefault("Sale.NowSaleParameterColor","&a"); + this.mConfig.addDefault("Sale.ClickSaleButtonFor","点击寄售按钮来进行"); + this.mConfig.addDefault("Sale.YouSetSaleParamTo","&a你设置了%param%为%value%"); + this.mConfig.addDefault("Sale.IntroduceSellType","点击选择你要寄售的商品类型: "); + this.mConfig.addDefault("Sale.IntroduceSingleNumb","请输入你要寄售物品每份的数量: "); + this.mConfig.addDefault("Sale.IntroducePartNumb","请输入你要寄售物品的份数: "); + this.mConfig.addDefault("Sale.IntroducePriceType","点击选择购买商品的支付方式: "); + this.mConfig.addDefault("Sale.IntroducePrice","请输入每份商品的价格: "); + this.mConfig.addDefault("Sale.IntroduceSingleNumb","请输入你要寄售物品每份的数量: "); + this.mConfig.addDefault("Word.All","所有"); + this.mConfig.addDefault("Word.Count","个"); + this.mConfig.addDefault("Word.ForSale","寄售"); + this.mConfig.addDefault("Word.GoTo","前往"); + this.mConfig.addDefault("Word.Input","输入"); + this.mConfig.addDefault("Word.Item","物品"); + this.mConfig.addDefault("Word.Money","金币"); + this.mConfig.addDefault("Word.PartNumb","份数"); + this.mConfig.addDefault("Word.PlayerName","玩家名"); + this.mConfig.addDefault("Word.Points","点券"); + this.mConfig.addDefault("Word.Price","价格"); + this.mConfig.addDefault("Word.PriceType","价格类型"); + this.mConfig.addDefault("Word.Revive","接收"); + this.mConfig.addDefault("Word.SaleID","物品编号"); + this.mConfig.addDefault("Word.SaleShop","寄售商店"); + this.mConfig.addDefault("Word.SellType","寄售类型"); + this.mConfig.addDefault("Word.ShopName","商店名称"); + this.mConfig.addDefault("Word.SigleNumb","每份数量"); + this.mConfig.addDefault("Word.YouBought","你购买了"); + this.mConfig.options().copyDefaults(true); + } + + /** + * 获取消息节点(不做替换) + * @param node + * @return + */ + public String get(String node){ + if(!this.mConfig.contains(node)){ + this.mPlugin.warn("missing language node(丢失语言节点)["+node+"]"); + return node; + } + return ChatColor.translateAlternateColorCodes('&',this.mConfig.getString(node,node)); + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/managers/MultiplierHandler.java b/src/main/java/org/black_ixx/bossshop/managers/MultiplierHandler.java new file mode 100644 index 0000000..84c6a51 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/managers/MultiplierHandler.java @@ -0,0 +1,71 @@ +package org.black_ixx.bossshop.managers; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSMultiplier; +import org.black_ixx.bossshop.core.BSEnums.BSPriceType; +import org.bukkit.entity.Player; + +import cc.util.bossshop.config.CommentedYamlConfig; +import cc.util.bossshop.pluginmodel.INeedConfig; + +public class MultiplierHandler implements INeedConfig{ + + private BossShop plugin; + private Set multipliers=new HashSet(); + + /** + * 会员价格系统 + * @param plugin + */ + public MultiplierHandler(BossShop plugin){ + this.plugin=plugin; + this.plugin.registerConfigModel(this); + } + + + @Override + public void setConfig(){ + CommentedYamlConfig tConfig=this.plugin.getConfigManager().getConfig(); + if(!tConfig.getBoolean("MultiplierGroups.Enabled")) return; + List lines=tConfig.getStringList("MultiplierGroups.List"); + if(lines==null) return; + multipliers.clear(); + for(String s : lines){ + BSMultiplier m=new BSMultiplier(plugin,s); + if(m.isValid()){ + multipliers.add(m); + } + } + } + + public double calculateWithMultiplier(Player p,BSPriceType type,double d){ + for(BSMultiplier m : multipliers){ + if(m.getType()==type){ + if(m.hasPermission(p)){ + d=d*m.getMultiplier(); + } + } + } + return d; + } + + public int calculateWithMultiplier(Player p,BSPriceType type,int d){ + double x=d; + for(BSMultiplier m : multipliers){ + if(m.getType()==type){ + if(m.hasPermission(p)){ + x=x*m.getMultiplier(); + } + } + } + return (int)x; + } + + public Set getMultipliers(){ + return multipliers; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/managers/RecordManager.java b/src/main/java/org/black_ixx/bossshop/managers/RecordManager.java new file mode 100644 index 0000000..f08a190 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/managers/RecordManager.java @@ -0,0 +1,152 @@ +package org.black_ixx.bossshop.managers; + +import java.util.Calendar; +import java.util.UUID; + +import org.black_ixx.bossshop.BossShop; +import org.bukkit.Bukkit; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; + +import cc.util.bossshop.filemanager.AFileManager; +import cc.util.bossshop.pluginmodel.INeedReload; + +/** + * 用于记录玩家购买限制数量物品的记录 + * @author 聪聪 + * + */ +public class RecordManager extends AFileManager implements INeedReload{ + + public static final String SEC_UNSALE="_UnsaleRecord_"; + public static final String SEC_BUY="_BuyRecord_"; + public static final String SEC_UNSALE_COUNT="Count"; + public static final String SEC_UNSALE_TIME="Time"; + + /** + * 无初始化限制条件 + * @param mPlugin + */ + public RecordManager(BossShop pplugin){ + super(pplugin,"buyRecord.yml","1.2"); + this.mPlugin.registerReloadModel(this); + } + + @Override + public boolean reloadConfig(){ + if(!super.reloadConfig()) return false; + if(this.checkUpdate()) this.saveConfig(); + return true; + } + + protected boolean checkUpdate(){ + boolean update=super.checkUpdate(); + if(!update) return false; + String tVersion=this.mConfig.getString(SEC_CFG_VERSION,"1.0"); + if(tVersion.equalsIgnoreCase("1.0")){ + tVersion="1.1"; + for(String shop_key : this.mConfig.getKeys(false)){ + ConfigurationSection shop_sec=this.mConfig.getConfigurationSection(shop_key); + if(shop_sec==null) continue; + for(String item_key : shop_sec.getKeys(false)){ + ConfigurationSection item_sec=shop_sec.getConfigurationSection(item_key); + if(item_sec==null) continue; + for(String player_key : item_sec.getKeys(false)){ + int numb=item_sec.getInt(player_key); + item_sec.set(player_key,null); + if(numb<=0) continue; + Player tPlayer=Bukkit.getPlayer(player_key); + if(tPlayer==null) continue; + item_sec.set(tPlayer.getUniqueId().toString(),numb); + } + if(item_sec.getKeys(false).size()==0) shop_sec.set(item_key,null); + } + if(shop_sec.getKeys(false).size()==0) this.mConfig.set(shop_key,null); + } + } + if(tVersion.equalsIgnoreCase("1.1")){ + for(String shop_key : this.mConfig.getKeys(false)){ + if(shop_key.equalsIgnoreCase(SEC_BUY)) continue; + ConfigurationSection shop_sec=this.mConfig.getConfigurationSection(shop_key); + this.mConfig.set(shop_key,null); + this.mConfig.set(SEC_BUY+"."+shop_key,shop_sec); + } + } + this.mConfig.set(SEC_CFG_VERSION,this.mVersion); + return true; + } + + /** + * 增加一次玩家购买该物品的次数 + * @param shopname 商店名 + * @param buyname 物品名 + * @param player 用户名 + */ + public void addBuyRecord(String shopname,String buyname,UUID pUUID){ + String path=(shopname+"."+buyname+"."+pUUID.toString()).toLowerCase(); + int buy=this.mConfig.getInt(path,0); + this.mConfig.set(path,buy+1); + this.saveConfig(); + } + + /** + * 获取玩家购买该物品的次数 + * @param shopname 商店名 + * @param buyname 物品名 + * @param player 用户名 + */ + public int getBuyRecord(String shopname,String buyname,UUID pUUID){ + String path=(shopname+"."+buyname+"."+pUUID.toString()).toLowerCase(); + return this.mConfig.getInt(path,0); + } + + public int addUnsaleRecord(UUID pUUID){ + String path=SEC_UNSALE+"."+pUUID.toString(); + ConfigurationSection tSec=this.mConfig.getConfigurationSection(path); + int count=0; + if(tSec!=null){ + long time=this.mConfig.getLong(path+"."+SEC_UNSALE_TIME,System.currentTimeMillis()); + if(dxDays(time)==0){ + count=this.mConfig.getInt(path+"."+SEC_UNSALE_COUNT,0); + count++; + + } + }else count=1; + this.mConfig.set(path+"."+SEC_UNSALE_COUNT,count); + this.mConfig.set(path+"."+SEC_UNSALE_TIME,System.currentTimeMillis()); + this.saveConfig(); + return count; + } + + public int getUnsaleRecord(UUID pUUID){ + String path=SEC_UNSALE+"."+pUUID.toString(); + ConfigurationSection tSec=this.mConfig.getConfigurationSection(path); + if(tSec==null) return 0; + long time=this.mConfig.getLong(path+"."+SEC_UNSALE_TIME,System.currentTimeMillis()); + if(dxDays(time)!=0) return 0; + return this.mConfig.getInt(path+"."+SEC_UNSALE_COUNT,0); + } + + /** + * 计算所给时间和现在的相差的天数,结果肯定>=0 + * @param date 给定的时间 + * @return 相差的天数 + */ + public static int dxDays(long date){ + return dxDays(System.currentTimeMillis(),date); + } + + /** + * 计算所给连个时间相差的天数,结果肯定>=0 + * @param data 给定的时间 + * @return 相差的天数 + */ + public static int dxDays(long dateb,long datef){ + int ptime=Calendar.getInstance().getTimeZone().getRawOffset(); + int ndays=(int)((dateb+ptime)/86400000); + int tdays=(int)((datef+ptime)/86400000); + return Math.abs(ndays-tdays); + } + + +} diff --git a/src/main/java/org/black_ixx/bossshop/managers/ShopCustomizer.java b/src/main/java/org/black_ixx/bossshop/managers/ShopCustomizer.java new file mode 100644 index 0000000..0181fe0 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/managers/ShopCustomizer.java @@ -0,0 +1,134 @@ +package org.black_ixx.bossshop.managers; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Set; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSBuy; +import org.black_ixx.bossshop.core.BSShop; +import org.black_ixx.bossshop.core.BSShopHolder; +import org.black_ixx.bossshop.events.BSDisplayItemEvent; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +import cc.util.bossshop.FBukkit; + +/** + * 定制专属于某个玩家的商店 + * @author 聪聪 + * + */ +public class ShopCustomizer{ + + private final static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm"); + private BossShop mPlugin; + + public ShopCustomizer(BossShop pPlugin){ + this.mPlugin=pPlugin; + } + + private String C(String key,Player player){ + return BossShop.replaceParam(player,this.mPlugin.getLangManager().getNode(key)); + } + + /** + * 根据指定的玩家,商店创建物品到指定的背包 + * @param pShop 商店 + * @param pPlayer 玩家 + * @param pInventory 背包 + * @param pPoss 要显示的物品的位置 + */ + public Inventory updateInventory(BSShop pShop,Player pPlayer,Inventory pInventory,Set pPoss){ + if(!(pInventory.getHolder() instanceof BSShopHolder)) return pInventory; + pInventory.clear(); + for(Integer pos : pPoss){ + BSBuy buy=pShop.getShopItem(pos); + if(buy==null||!buy.disaplyToPlayer(pPlayer)) continue; + ItemStack menu_item=pShop.getMenuItem(pos); + if(!buy.isNeedEdit()){ + pInventory.setItem(pos,menu_item); + continue; + } + pInventory.setItem(pos,createPersonalMenuItem(pPlayer,buy,menu_item)); + } + return pInventory; + } + + /** + * 根据玩家信息,商品信息设置菜单物品Lore + *

此函数只管设置lore,不会检测是否隐藏物品

+ * @param pPlayer 玩家 + * @param pBuy 商品 + * @param pMenuItem 菜单物品 + * @return 肯定不是null + */ + public ItemStack createPersonalMenuItem(Player pPlayer,BSBuy pBuy,ItemStack pMenuItem){ + BSShop pShop=pBuy.getShop(); + BSDisplayItemEvent event=new BSDisplayItemEvent(pPlayer,pShop,pBuy); + Bukkit.getPluginManager().callEvent(event); + if(event.isCancelled()) return null; + ItemMeta meta=pMenuItem.getItemMeta(); + //设置骷髅所有者 + if(meta instanceof SkullMeta){ + SkullMeta skullMeta=(SkullMeta)meta; + if(skullMeta.hasOwner()) skullMeta.setOwner(BossShop.replaceParam(pPlayer,skullMeta.getOwner())); + } + //设置lore和物品名字 + if(meta.hasDisplayName()) + meta.setDisplayName(BossShop.replaceParam(pPlayer,meta.getDisplayName())); + List lore=meta.getLore(); + if(lore!=null){ + int co=0; + for(String line : lore){ + lore.set(co,BossShop.replaceParam(pPlayer,line)); + co++; + } + }else lore=new ArrayList(); + // 设置剩余数量,售卖时间 + int limit=pBuy.getLimit(); + int perLimit=pBuy.getPersonalLimit(); + Date start_t=pBuy.getStartTime(); + Date stop_t=pBuy.getStopTime(); + ArrayList addlore=new ArrayList<>(); + String faillore=null; + //设置剩余数量 + if(limit>-1){ + if(limit>0) addlore.add(C("Lore.GoodsStockLeft",pPlayer).replace("%numb%",limit+"")); + else faillore=C("Lore.AlreadySoldOut",pPlayer); + } + //设置个人剩余数量 + if(faillore==null&&perLimit>-1){ + int leftbuy=perLimit-this.mPlugin.getManager(RecordManager.class).getBuyRecord(pShop.getShopName(),pBuy.getName(),pPlayer.getUniqueId()); + if(leftbuy<0) leftbuy=0; + if(leftbuy==0) faillore=C("Main.OutOfPersonalLimit",pPlayer); + else addlore.add(C("Lore.EveryoneLimit",pPlayer).replace("%numb%",perLimit+"").replace("%left%",leftbuy+"")); + } + //设置开始和结束时间 + if(faillore==null&&(start_t!=null||stop_t!=null)){ + Date date=new Date(); + if(start_t==null||date.after(start_t)){ + if(stop_t==null||date.before(stop_t)){ + addlore.add(C("Lore.NowCanBuyGoods",pPlayer)); + if(stop_t!=null) addlore.add(C("Lore.WillCloseInTime",pPlayer).replaceAll("%time%",getTimeString(stop_t))); + }else faillore=C("Lore.GoodsOutOfDate",pPlayer); + }else faillore=C("Lore.BuyIsNotTime",pPlayer).replaceAll("%time%",getTimeString(start_t)); + } + if(faillore==null) lore.addAll(addlore); + else lore.add(faillore); + if(lore.size()!=0) meta.setLore(lore); + if(!FBukkit.isItemMetaEmpty(meta)) pMenuItem.setItemMeta(meta); + return pMenuItem; + } + + private static String getTimeString(Date date){ + return sdf.format(date); + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/managers/StringManager.java b/src/main/java/org/black_ixx/bossshop/managers/StringManager.java new file mode 100644 index 0000000..dbff40c --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/managers/StringManager.java @@ -0,0 +1,29 @@ +package org.black_ixx.bossshop.managers; + +import org.bukkit.ChatColor; + +/** + * 用于转换字符串中的特殊字符 + * @author 聪聪 + * + */ +public class StringManager{ + + public static String transform(String s){ + s=s.replace("[<3]","❤"); + s=s.replace("[*]","★"); + s=s.replace("[**]","✹"); + s=s.replace("[o]","●"); + s=s.replace("[v]","✔"); + s=s.replace("[+]","♦"); + s=s.replace("[x]","✦"); + s=s.replace("[%]","♠"); + s=s.replace("[%%]","♣"); + s=s.replace("[radioactive]","☢"); + s=s.replace("[peace]","☮"); + s=s.replace("[moon]","☾"); + s=s.replace("[crown]","♔"); + s=s.replace("[note]","♩ "); + return ChatColor.translateAlternateColorCodes('&',s); + } +} diff --git a/src/main/java/org/black_ixx/bossshop/managers/TimeHandler.java b/src/main/java/org/black_ixx/bossshop/managers/TimeHandler.java new file mode 100644 index 0000000..8ef5852 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/managers/TimeHandler.java @@ -0,0 +1,32 @@ +package org.black_ixx.bossshop.managers; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.timedCommands.TimedCommands; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; + +public class TimeHandler{ + + private BossShop mPlugin; + private TimedCommands tc; + + public TimeHandler(BossShop pPlugin){ + this.mPlugin=pPlugin; + } + + private void buildPlugin(){ + Plugin plugin=Bukkit.getServer().getPluginManager().getPlugin("TimedCommands"); + if(plugin!=null){ + tc=(TimedCommands.class.cast(plugin)); + }else{ + this.mPlugin.warn("未发现TimedCommands插件,如果你需要使用该插件,请前往: http://dev.bukkit.org/bukkit-plugins/scheduledcommands/"); + return; + } + } + + public TimedCommands getTimedCommands(){ + if(tc==null) this.buildPlugin(); + return tc; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/managers/TransactionLog.java b/src/main/java/org/black_ixx/bossshop/managers/TransactionLog.java new file mode 100644 index 0000000..db1644d --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/managers/TransactionLog.java @@ -0,0 +1,38 @@ +package org.black_ixx.bossshop.managers; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSBuy; +import org.black_ixx.bossshop.core.BSEnums.BSBuyType; +import org.bukkit.entity.Player; + +import cc.util.bossshop.filemanager.AFileManager; +import cc.util.bossshop.pluginmodel.INeedReload; + +public class TransactionLog extends AFileManager implements INeedReload{ + + private SimpleDateFormat formatter=new SimpleDateFormat("yyyy dd-MM 'at' hh:mm:ss a (E)"); + + public TransactionLog(BossShop pPlugin){ + super(pPlugin,"TransactionLog.yml","1.0"); + this.mPlugin.registerReloadModel(this); + } + + public String getDate(){ + Date dNow=new Date(); + return formatter.format(dNow); + } + + public void addTransaction(String message){ + this.mConfig.set(getDate(),message); + this.saveConfig(); + } + + public void addTransaction(Player p,BSBuy buy){ + if(buy.getBuyType()==BSBuyType.Shop) return; + addTransaction("Player "+p.getName()+" bought "+buy.getName()+"("+buy.getBuyType().name()+") for "+buy.getPriceType().name()+"."); + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/managers/VaultHandler.java b/src/main/java/org/black_ixx/bossshop/managers/VaultHandler.java new file mode 100644 index 0000000..731881c --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/managers/VaultHandler.java @@ -0,0 +1,59 @@ +package org.black_ixx.bossshop.managers; + +import net.milkbowl.vault.economy.Economy; +import net.milkbowl.vault.permission.Permission; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.misc.NoEconomy; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.RegisteredServiceProvider; + +public class VaultHandler{ + + private BossShop mPlugin; + private Permission perms; + private Economy economy; + + public VaultHandler(BossShop pPlugin){ + this.mPlugin=pPlugin; + Plugin VaultPlugin=Bukkit.getServer().getPluginManager().getPlugin("Vault"); + if(VaultPlugin==null){ + this.mPlugin.warn("未发现Vault插件,如果你想使用经济系统,请前往: http://dev.bukkit.org/server-mods/vault/ 下载"); + return; + } + this.mPlugin.info("Vault found."); + setupEconomy(); + setupPermissions(); + } + + public Permission getPermission(){ + if(perms==null) this.setupPermissions(); + return perms; + } + + public Economy getEconomy(){ + if(economy==null) this.setupEconomy(); + return economy; + } + + private void setupEconomy(){ + RegisteredServiceProvider economyProvider=Bukkit.getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class); + if(economyProvider==null){ + this.mPlugin.warn("未发现Economy类型插件... 如果你需要用到经济系统: http://plugins.bukkit.org/."); + economy=new NoEconomy(this.mPlugin); + return; + } + economy=economyProvider.getProvider(); + } + + private void setupPermissions(){ + RegisteredServiceProvider rsp=Bukkit.getServer().getServicesManager().getRegistration(Permission.class); + perms=rsp.getProvider(); + if(perms==null){ + this.mPlugin.warn("未发现Permissions类型插件... 如果你需要用到权限系统: http://plugins.bukkit.org/"); + return; + } + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/managers/WorthHandler.java b/src/main/java/org/black_ixx/bossshop/managers/WorthHandler.java new file mode 100644 index 0000000..c6fe286 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/managers/WorthHandler.java @@ -0,0 +1,226 @@ +package org.black_ixx.bossshop.managers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSShopManager; +import org.black_ixx.bossshop.misc.Enchant; +import org.black_ixx.bossshop.points.PointsManager; +import org.black_ixx.timedCommands.StoreHandler; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +import cc.util.bossshop.FBukkit; +import net.milkbowl.vault.permission.Permission; + +public class WorthHandler{ + + private BossShop mPlugin; + + public WorthHandler(BossShop pPlugin){ + this.mPlugin=pPlugin; + } + + public String C(String pNode){ + return this.mPlugin.getLangManager().getNode(pNode); + } + + public void giveRewardCommand(Player pPlayer,List pCommands){ + for(String scmd : pCommands){ + scmd=StringManager.transform(BossShop.replaceParam(pPlayer,scmd)); + boolean tOp=pPlayer.isOp(); + try{ + pPlayer.setOp(true); + pPlayer.performCommand(scmd); + }catch(Throwable exp){ + }finally { + pPlayer.setOp(tOp); + } + } + } + + public void giveRewardPlayerCommand(Player pPlayer,List pCommands){ + for(String scmd : pCommands){ + String command=StringManager.transform(BossShop.replaceParam(pPlayer,scmd)); + PlayerCommandPreprocessEvent event=new PlayerCommandPreprocessEvent(pPlayer,"/"+command); + Bukkit.getPluginManager().callEvent(event); + if(event.isCancelled()) continue; + pPlayer.performCommand(event.getMessage().substring(1)); + } + } + + public void giveRewardTimeCommand(Player pPlayer,HashMap pCommands){ + StoreHandler s=this.mPlugin.getManager(TimeHandler.class).getTimedCommands().getStoreHandler(); + for(int i : pCommands.keySet()){ + String cmd=BossShop.replaceParam(pPlayer,pCommands.get(i)); + s.addCommandToHashMap(System.currentTimeMillis()+1000*i,cmd); + } + } + + public void giveRewardItem(Player p,ItemStack item){ + item=transformRewardItem(item.clone(),p); + if(p.getInventory().firstEmpty()==-1){ // Inventory full + if(item.getMaxStackSize()==1){ // Max Stack Size == 1 + p.getWorld().dropItem(p.getLocation(),item); // -> Drop Item + return; + } + } + int free=0; + for(ItemStack sitem : p.getInventory().getContents()){ + if(sitem!=null&&sitem.getTypeId()!=0){ + if(ItemStackChecker.isSame(sitem,item)){ + free+=sitem.getMaxStackSize()-sitem.getAmount(); + } + }else{ + free+=item.getMaxStackSize(); + } + if(free>=item.getAmount()) break; + } + if(free items){ + for(ItemStack i : items){ + this.giveRewardItem(p,i); + } + } + + public void giveRewardPermission(Player p,List permissions){ + Permission per=this.mPlugin.getManager(VaultHandler.class).getPermission(); + for(String s : permissions){ + per.playerAdd(p,s); + } + } + + public void giveRewardMoney(Player p,double money){ + VaultHandler vaultMan=this.mPlugin.getManager(VaultHandler.class); + if(!vaultMan.getEconomy().hasAccount(p.getName())){ + this.mPlugin.send(p,C("Economy.NoAccount")); + this.mPlugin.severe("Unable to give "+p.getName()+" his/her money: He/She does not have an Economy Account"); + return; + } + vaultMan.getEconomy().depositPlayer(p.getName(),money); + } + + public void giveRewardPoints(Player p,Integer points){ + this.mPlugin.getManager(PointsManager.class).givePoints(p,points); + } + + public void giveRewardShop(Player p,String shopName){ + if(shopName==null||shopName==""||shopName.length()<1){ + p.closeInventory(); + return; + } + this.mPlugin.getManager(BSShopManager.class).openShop(p,shopName); + } + + public void giveRewardEnchantment(Player p,Enchant e){ + if(p.getItemInHand()!=null){ + p.getItemInHand().addUnsafeEnchantment(e.getType(),e.getLevel()); + } + } + + public boolean hasExp(Player p,int price){ + if((p.getLevel()<(Integer)price)){ + this.mPlugin.send(p,C("NotEnough.Exp")); + return false; + } + return true; + } + + public boolean hasItems(Player p,List items){ + for(ItemStack i : items){ + if(!ItemStackChecker.inventoryContainsItem(p,i)){ + this.mPlugin.send(p,C("NotEnough.Item")); + return false; + } + } + return true; + } + + public boolean hasMoney(Player p,double money){ + VaultHandler vaultMan=this.mPlugin.getManager(VaultHandler.class); + if(!vaultMan.getEconomy().hasAccount(p.getName())){ + this.mPlugin.send(p,C("Economy.NoAccount")); + return false; + } + if(vaultMan.getEconomy().getBalance(p.getName()) items){ + for(ItemStack i : items){ + ItemStackChecker.takeItem(i,p); + } + return null; + } + + public String takeMoney(Player p,double money){ + VaultHandler vaultMan=this.mPlugin.getManager(VaultHandler.class); + if(!vaultMan.getEconomy().hasAccount(p.getName())){ + this.mPlugin.severe("Unable to take money! No economy account existing! ("+p.getName()+", "+money+")"); + return ""; + } + vaultMan.getEconomy().withdrawPlayer(p.getName(),money); + return ""+vaultMan.getEconomy().getBalance(p.getName()); + } + + public String takePoints(Player p,Integer d){ + return ""+this.mPlugin.getManager(PointsManager.class).takePoints(p,d); + } + + private ItemStack transformRewardItem(ItemStack i,Player p){ + if(i.hasItemMeta()){ + ItemMeta meta=i.getItemMeta(); + //设置骷髅所有者 + if(meta instanceof SkullMeta){ + SkullMeta skullMeta=(SkullMeta)meta; + if(skullMeta.hasOwner()) skullMeta.setOwner(BossShop.replaceParam(p,skullMeta.getOwner())); + } + if(meta.hasDisplayName()){ + meta.setDisplayName(BossShop.replaceParam(p,meta.getDisplayName())); + } + if(meta.hasLore()){ + List new_lore=new ArrayList(); + for(String s : meta.getLore()){ + new_lore.add(BossShop.replaceParam(p,s)); + } + meta.setLore(new_lore); + } + if(!FBukkit.isItemMetaEmpty(meta)) + i.setItemMeta(meta); + } + return i; + } + +} + diff --git a/src/main/java/org/black_ixx/bossshop/misc/Enchant.java b/src/main/java/org/black_ixx/bossshop/misc/Enchant.java new file mode 100644 index 0000000..175f71a --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/misc/Enchant.java @@ -0,0 +1,20 @@ +package org.black_ixx.bossshop.misc; +import org.bukkit.enchantments.Enchantment; + +public class Enchant { + private Enchantment type; + private int lvl; + public Enchant(Enchantment type, int lvl){ + this.type=type; + this.lvl=lvl; + } + + public Enchantment getType(){ + return type; + } + + public int getLevel(){ + return lvl; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/misc/NoEconomy.java b/src/main/java/org/black_ixx/bossshop/misc/NoEconomy.java new file mode 100644 index 0000000..caf3e15 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/misc/NoEconomy.java @@ -0,0 +1,276 @@ +package org.black_ixx.bossshop.misc; + +import java.util.List; + +import org.black_ixx.bossshop.BossShop; +import org.bukkit.OfflinePlayer; +import net.milkbowl.vault.economy.Economy; +import net.milkbowl.vault.economy.EconomyResponse; + +public class NoEconomy implements Economy{ + + private BossShop mPlugin; + + public NoEconomy(BossShop pPlugin){ + this.mPlugin=pPlugin; + } + + @Override + public EconomyResponse bankBalance(String arg0){ + this.mPlugin.warn("No Economy Plugin found!"); + return null; + } + + @Override + public EconomyResponse bankDeposit(String arg0,double arg1){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public EconomyResponse bankHas(String arg0,double arg1){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public EconomyResponse bankWithdraw(String arg0,double arg1){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public EconomyResponse createBank(String arg0,String arg1){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public boolean createPlayerAccount(String arg0){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return false; + } + + @Override + public boolean createPlayerAccount(String arg0,String arg1){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return false; + } + + @Override + public String currencyNamePlural(){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public String currencyNameSingular(){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public EconomyResponse deleteBank(String arg0){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public EconomyResponse depositPlayer(String arg0,double arg1){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public EconomyResponse depositPlayer(String arg0,String arg1,double arg2){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public String format(double arg0){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public int fractionalDigits(){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return 0; + } + + @Override + public double getBalance(String arg0){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return 0; + } + + @Override + public double getBalance(String arg0,String arg1){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return 0; + } + + @Override + public List getBanks(){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public String getName(){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public boolean has(String arg0,double arg1){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return false; + } + + @Override + public boolean has(String arg0,String arg1,double arg2){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return false; + } + + @Override + public boolean hasAccount(String arg0){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return false; + } + + @Override + public boolean hasAccount(String arg0,String arg1){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return false; + } + + @Override + public boolean hasBankSupport(){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return false; + } + + @Override + public EconomyResponse isBankMember(String arg0,String arg1){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public EconomyResponse isBankOwner(String arg0,String arg1){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public boolean isEnabled(){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return false; + } + + @Override + public EconomyResponse withdrawPlayer(String arg0,double arg1){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public EconomyResponse withdrawPlayer(String arg0,String arg1,double arg2){ + this.mPlugin.warn("No Economy Plugin was found... You need one if you want to work with Money! Get it there: http://plugins.bukkit.org/."); + return null; + } + + @Override + public EconomyResponse createBank(String arg0,OfflinePlayer arg1){ + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean createPlayerAccount(OfflinePlayer arg0){ + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean createPlayerAccount(OfflinePlayer arg0,String arg1){ + // TODO Auto-generated method stub + return false; + } + + @Override + public EconomyResponse depositPlayer(OfflinePlayer arg0,double arg1){ + // TODO Auto-generated method stub + return null; + } + + @Override + public EconomyResponse depositPlayer(OfflinePlayer arg0,String arg1,double arg2){ + // TODO Auto-generated method stub + return null; + } + + @Override + public double getBalance(OfflinePlayer arg0){ + // TODO Auto-generated method stub + return 0; + } + + @Override + public double getBalance(OfflinePlayer arg0,String arg1){ + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean has(OfflinePlayer arg0,double arg1){ + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean has(OfflinePlayer arg0,String arg1,double arg2){ + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean hasAccount(OfflinePlayer arg0){ + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean hasAccount(OfflinePlayer arg0,String arg1){ + // TODO Auto-generated method stub + return false; + } + + @Override + public EconomyResponse isBankMember(String arg0,OfflinePlayer arg1){ + // TODO Auto-generated method stub + return null; + } + + @Override + public EconomyResponse isBankOwner(String arg0,OfflinePlayer arg1){ + // TODO Auto-generated method stub + return null; + } + + @Override + public EconomyResponse withdrawPlayer(OfflinePlayer arg0,double arg1){ + // TODO Auto-generated method stub + return null; + } + + @Override + public EconomyResponse withdrawPlayer(OfflinePlayer arg0,String arg1,double arg2){ + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/nbt/NBT.java b/src/main/java/org/black_ixx/bossshop/nbt/NBT.java new file mode 100644 index 0000000..936410d --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/nbt/NBT.java @@ -0,0 +1,18 @@ +package org.black_ixx.bossshop.nbt; + + +public class NBT{ + /** + * 此处的值类型为NMS的NBTTagCompound + */ + public final String mLabel; + protected final Object nbt; + protected boolean autoadd=false; + protected long lastUseTime=System.currentTimeMillis(); + + NBT(String pLabel,Object pnbt,boolean pauto){ + this.mLabel=pLabel; + this.nbt=pnbt; + this.autoadd=pauto; + } +} \ No newline at end of file diff --git a/src/main/java/org/black_ixx/bossshop/nbt/NBTEditManager.java b/src/main/java/org/black_ixx/bossshop/nbt/NBTEditManager.java new file mode 100644 index 0000000..4301e2d --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/nbt/NBTEditManager.java @@ -0,0 +1,538 @@ +package org.black_ixx.bossshop.nbt; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.lang.StringUtils; +import org.black_ixx.bossshop.BossShop; +import org.black_ixx.bossshop.core.BSShopManager; +import org.black_ixx.bossshop.mail.MailManager; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import cc.util.bossshop.ClassHelper; +import cc.util.bossshop.FBukkit; +import cc.util.bossshop.NMSHelper; +import cc.util.bossshop.config.CommentedSection; +import cc.util.bossshop.config.CommentedYamlConfig; +import cc.util.bossshop.filemanager.AFileManager; +import cc.util.bossshop.pluginmodel.INeedConfig; +import cc.util.bossshop.pluginmodel.INeedReload; + +/** + * NBT管理系统,用于NBT的创建,保存和恢复 + * @author 聪聪 + * + */ +public class NBTEditManager extends AFileManager implements INeedConfig,INeedReload{ + + public static final String SEC_NBT_MAIN="NBTS"; + public static final String SEC_NBT_CONTENT="Content"; + public static final String SEC_AUTO_ADD="AutoAdd"; + public static final String SEC_LAST_USE_TIME="LastUseTime"; + /** + * 用于NBT节点中名中"."符号的替换 + */ + private static char re='$'; + private final HashMap nbtmap=new HashMap<>(); + private int mExpiredDays=7; + + /** + * 无初始化限制条件 + * @param plugin + */ + public NBTEditManager(BossShop pplugin){ + super(pplugin,"nbt.yml","1.1"); + pplugin.registerConfigModel(this); + pplugin.registerReloadModel(this); + } + + @Override + public void setConfig(){ + CommentedYamlConfig tConfig=this.mPlugin.getConfigManager().getConfig(); + this.mExpiredDays=tConfig.getInt("AutoClear.NBTExpiredDays",this.mExpiredDays); + if(this.mExpiredDays<=0) this.mExpiredDays=Integer.MAX_VALUE; + } + + /** + * 切勿单独调用,防止NBT引用次数被重置 + */ + public boolean reloadConfig(){ + super.reloadConfig(); + this.checkUpdate(); + this.generateNBT(); + this.saveConfig(); + return true; + } + + /** + * 检查配置文件版本,用以更新配置文件 + * 当前更新范围 无版本->1.1 + */ + protected boolean checkUpdate(){ + boolean update=super.checkUpdate(); + if(!update) return false; + String tversion=this.mConfig.getString(SEC_CFG_VERSION,"1.0"); + if(tversion.equalsIgnoreCase("1.0")){ + tversion="1.1"; + this.mConfig.set("index",null); + if(tversion.compareToIgnoreCase("1.0")==0){ + ConfigurationSection sec=this.mConfig.getConfigurationSection(SEC_NBT_MAIN); + if(sec!=null){ + for(String label : sec.getKeys(false)){ + ConfigurationSection nbtsec=sec.getConfigurationSection(label); + sec.set(label,null); + sec.set(label+"."+SEC_NBT_CONTENT,nbtsec); + sec.set(label+"."+SEC_AUTO_ADD,false); + } + } + } + } + this.mConfig.set(SEC_CFG_VERSION,this.mVersion); + return true; + } + + /** + * 清理自动生成且引用次数为0的NBT节点 + * @return 清理的个数 + */ + public int clearNBT(){ + ConfigurationSection sec=this.mConfig.getConfigurationSection(SEC_NBT_MAIN); + if(sec==null) return 0; + + //先检查商店和邮件是否在范围时间内引用了NBT,如果没有则重载来增加NBT最后引用时间 + MailManager mailMan=((BossShop)this.mPlugin).getManager(MailManager.class); + if(System.currentTimeMillis()-mailMan.getLastReloadTime()>=this.mExpiredDays*86400000) + mailMan.reloadConfig(); + BSShopManager shopMan=((BossShop)this.mPlugin).getManager(BSShopManager.class); + if(System.currentTimeMillis()-shopMan.getLastReloadTime()>=this.mExpiredDays*86400000) + shopMan.reloadConfig(); + + int count=0; + Iterator> it=this.nbtmap.entrySet().iterator(); + while(it.hasNext()){ + Entry entry=it.next(); + NBT nbt=entry.getValue(); + if(nbt.autoadd&&System.currentTimeMillis()-nbt.lastUseTime>this.mExpiredDays*86400000){ + sec.set(entry.getKey(),null); + it.remove(); + count++; + } + } + if(count>0){ + this.saveConfig(); + this.debug("NBT库清理函数被调用,共清理 " +count+" 个过期NBT"); + } + return count; + } + + /** + * 添加物品的NBT到库存 + * @param item 要获取NBT的物品 + * @param auto 是否是自动添加的 + * @return 添加成功后存储在文件中的NBT节点名,如果物品没有nbt信息,将返回null + */ + public String addItemNBTToStock(ItemStack item,boolean auto){ + Object tObj_NBTTagCompound_tag=NMSHelper.getNBTTagCompound(item); + if(tObj_NBTTagCompound_tag==null) return null; + Object tNBTTagCompound_tagClone=ClassHelper.invokeMethod(tObj_NBTTagCompound_tag,NMSHelper.clazz_NBTBase); //克隆方法 + Map tagContent=NMSHelper.getNBTTagMapFromTag(tNBTTagCompound_tagClone); + if(tagContent==null||tagContent.isEmpty()) + return null; + NBT addNBT=this.getNBTByTag(tNBTTagCompound_tagClone); + if(addNBT==null){ + long time=System.currentTimeMillis()/1000; + String label=time+"_"+item.getType().name(); + while(this.mConfig.contains(label)){ + time++; + label=time+"_"+item.getType().name(); + } + addNBT=new NBT(label,tNBTTagCompound_tagClone,auto); + this.nbtmap.put(label,addNBT); + } + this.updateNBTLastTime(addNBT); + return addNBT.mLabel; + } + + /** + * 更新NBT最后引用时间(配置文件和自身实例) + *

如果NBT配置节点不存在会自动创建

+ * @param pNBT 要更新的NBT + */ + public void updateNBTLastTime(NBT pNBT){ + ConfigurationSection sec=this.mConfig.getConfigurationSection(SEC_NBT_MAIN); + if(sec==null) sec=this.mConfig.createSection(SEC_NBT_MAIN); + ConfigurationSection nsec=sec.getConfigurationSection(pNBT.mLabel); + if(nsec==null){ + nsec=sec.createSection(pNBT.mLabel); + nsec.set(SEC_AUTO_ADD,pNBT.autoadd); + ConfigurationSection content_sec=nsec.createSection(SEC_NBT_CONTENT); + this.putNBTToSection(content_sec,pNBT.nbt); + } + nsec.set(SEC_LAST_USE_TIME,System.currentTimeMillis()); + pNBT.lastUseTime=System.currentTimeMillis(); + this.saveConfig(); + } + + /** + * 根据pItem来获取NBT类实例 + * @param pItem Bukkit物品 + * @return 找到的NBl类实例或null + */ + public NBT getNBTByItem(ItemStack pItem){ + return this.getNBTByTag(NMSHelper.getNBTTagCompound(pItem)); + } + + /** + * 根据NBTTagCompound来获取NBT类实例 + * @param pTag + * @return 找到的NBT类实例或null + */ + private NBT getNBTByTag(Object pTag){ + if(pTag==null) return null; + for(NBT sNBT : this.nbtmap.values()){ + if(sNBT.nbt.equals(pTag)) return sNBT; + } + return null; + } + + /** + * 将NBT信息存储到配置文件指定的节点中 + */ + private void putNBTToSection(ConfigurationSection sec,Object tObj_NBTBase_nbt){ + if(NMSHelper.clazz_NBTTagCompound.isInstance(tObj_NBTBase_nbt)){ + Map nbtContents=NMSHelper.getNBTTagMapFromTag(tObj_NBTBase_nbt); + for(Map.Entry entry : nbtContents.entrySet()){ + String cname=entry.getKey().replace('.',re); + Object tObj_NBTBase_tnbt=entry.getValue(); + if(NMSHelper.clazz_NBTTagCompound.isInstance(tObj_NBTBase_tnbt)||NMSHelper.clazz_NBTTagList.isInstance(tObj_NBTBase_tnbt)){ + ConfigurationSection nsec=sec.createSection(cname); + putNBTToSection(nsec,tObj_NBTBase_tnbt); + }else sec.set(cname,this.getStringValue(tObj_NBTBase_tnbt)); + } + }else if(NMSHelper.clazz_NBTTagList.isInstance(tObj_NBTBase_nbt)){ + List contentList=(List)ClassHelper.getFieldValue(tObj_NBTBase_nbt,NMSHelper.field_NBTTagList_value); + sec.set("_type_",9); + int i=0; + for(Object sNBTBase: contentList){ + String name=i+"";i++; + if(NMSHelper.clazz_NBTTagCompound.isInstance(sNBTBase)||NMSHelper.clazz_NBTTagList.isInstance(sNBTBase)){ + ConfigurationSection nsec=sec.createSection(name); + putNBTToSection(nsec,sNBTBase); + }else{ + String str=sNBTBase.toString(); + if(NMSHelper.clazz_NBTTagString.isInstance(sNBTBase)) + str=(String)ClassHelper.getFieldValue(sNBTBase,NMSHelper.field_NBTTagString_value); + sec.set(name,str); + } + } + if(sec.getKeys(false).size()==1) sec.set("_type_",null); + }else{ + warn("NBT Edit maybe make a bug"); + } + } + + /** + * 从配置文件中生成NBT + */ + private void generateNBT(){ + this.nbtmap.clear(); + CommentedSection sec=this.mConfig.getConfigurationSection(SEC_NBT_MAIN); + if(sec==null){ + sec=this.mConfig.createSection(SEC_NBT_MAIN); + return ; + } + for(String label : sec.getKeys(false)){ + Object tObj_NBTTagCompound=ClassHelper.getInstance(NMSHelper.clazz_NBTTagCompound); + CommentedSection nbtSec=sec.getConfigurationSection(label); + if(label==null) continue; + boolean auto=nbtSec.getBoolean(NBTEditManager.SEC_AUTO_ADD,false); + CommentedSection contentSec=nbtSec.getConfigurationSection(NBTEditManager.SEC_NBT_CONTENT); + if(contentSec==null){ + this.mPlugin.warn("NBT配置节点["+label+"]没有["+NBTEditManager.SEC_NBT_CONTENT+"]节点,这是一个配置错误"); + continue; + } + this.setTagContent(contentSec,tObj_NBTTagCompound); + NBT cfgNBT=new NBT(label,tObj_NBTTagCompound,auto); + cfgNBT.lastUseTime=nbtSec.getLong(NBTEditManager.SEC_LAST_USE_TIME,System.currentTimeMillis());; + this.nbtmap.put(label,cfgNBT); + } + } + + /** + * 使用该方法是必须接受返回结果 + * @param item 要设置nbt信息的物品 + * @param nbtLabel nbt标签,所有标签在数据文件夹下的nbt.yml中的NBTS节点下 + * @return 设置完nbt信息后的物品 + */ + public ItemStack setItemNBT(ItemStack item,String nbtLabel){ + NBT nbtcom=this.nbtmap.get(nbtLabel); + if(nbtcom==null){ + if(StringUtils.isNotEmpty(nbtLabel)) + warn(C("Console.MissingNBTNode")+"["+nbtLabel+"]"); + return null; + } + this.updateNBTLastTime(nbtcom); + ItemMeta meta=item.getItemMeta(); + List lore_old=new ArrayList(); + if(meta.hasLore()) lore_old.addAll(meta.getLore()); + Object tObj_NMSItemStack=ClassHelper.invokeStaticMethod(NMSHelper.method_CraftItemStack_asNMSCopy,item); + if(tObj_NMSItemStack==null) return item; + Object tObj_NBTTagCompound_tag=ClassHelper.getFieldValue(tObj_NMSItemStack,NMSHelper.field_NMSItemStack_tag);//获取物品tag + if(tObj_NBTTagCompound_tag!=null){ + //清空原lore + Map tagContent=NMSHelper.getNBTTagMapFromTag(tObj_NBTTagCompound_tag); + Object tNBTTagCompound_display=tagContent.get("display"); + if(tNBTTagCompound_display!=null){ + Map tagDisplayContent=NMSHelper.getNBTTagMapFromTag(tNBTTagCompound_display); + tagDisplayContent.remove("Lore"); + } + this.mixNBT(tObj_NBTTagCompound_tag,nbtcom.nbt); + } + else{ + Object tNBTTagCompound_tagClone=ClassHelper.invokeMethod(nbtcom.nbt,NMSHelper.method_NBTTagCompound_clone);//克隆 nbt + ClassHelper.setFieldValue(tObj_NMSItemStack,NMSHelper.field_NMSItemStack_tag,tNBTTagCompound_tagClone);//设置tag + } + ItemStack ritem=(ItemStack)ClassHelper.invokeStaticMethod(NMSHelper.method_CraftItemStack_asCraftMirror,tObj_NMSItemStack); + meta=ritem.getItemMeta(); + List lore_nbt=meta.getLore(); + if(lore_nbt==null) lore_nbt=new ArrayList<>(); + if(lore_old!=null) lore_nbt.addAll(lore_old); + meta.setLore(lore_nbt); + if(!FBukkit.isItemMetaEmpty(meta)){//防止空meta清空nbt + ritem.setItemMeta(meta); + } + return ritem; + } + + /** + * 混合两个 NBTTagCompound,如果同时存在节点名相同但属性不同,优先保存前一个的数据 + */ + private void mixNBT(Object tObj_NBTTagCompound_to,Object tObj_NBTTagCompound_from){ + Object tNBTBase_toChild=null; + Map tagFromContent=NMSHelper.getNBTTagMapFromTag(tObj_NBTTagCompound_from);//获取from com的map + Map tagToContent=NMSHelper.getNBTTagMapFromTag(tObj_NBTTagCompound_to);//获取to com的map + for(Object obj : tagFromContent.keySet()){ + String name=obj.toString(); + tNBTBase_toChild=tagToContent.get(name); //尝试to com中是否有该节点 + Object tNBTBase_fromChild=tagFromContent.get(name); //获取from com中的该值 + if(tNBTBase_fromChild==null) continue; //如果复制源为null就没必要继续了 + if(tNBTBase_toChild==null){ //to com中不存在直接put + tagToContent.put(name,tNBTBase_fromChild); + }else{ //to com中存在,分类 + if(tNBTBase_toChild.getClass()==tNBTBase_fromChild.getClass()){//如果同是com + if(NMSHelper.clazz_NBTTagCompound.isInstance(tNBTBase_toChild))//如果是com,混合 + mixNBT(tNBTBase_toChild,tNBTBase_fromChild); + else if(NMSHelper.clazz_NBTTagList.isInstance(tNBTBase_toChild)){//如果list,相加 + List from_contentList=(List)ClassHelper.getFieldValue(tNBTBase_fromChild,NMSHelper.field_NBTTagList_value);//获取from taglist中的list + int tListSize=from_contentList.size(); + for(int i=0;i tObj_content=NMSHelper.getNBTTagMapFromTag(pObj_NBTBase); + for(String n : sec.getKeys(false)){ + Object value=sec.get(n); + String tname=n.replace(re,'.'); + if(value instanceof String){ + Object tObj_NBTBase_ntag=getNBTNode((String)value); + if(tObj_NBTBase_ntag!=null) tObj_content.put(tname,tObj_NBTBase_ntag); + }else if(value instanceof CommentedSection){ + CommentedSection nsec=(CommentedSection)value; + Object tObj_NBTBase_nbt=null; + if(nsec.getInt("_type_")==9) tObj_NBTBase_nbt=ClassHelper.getInstance(NMSHelper.clazz_NBTTagList); + else tObj_NBTBase_nbt=ClassHelper.getInstance(NMSHelper.clazz_NBTTagCompound); + tObj_content.put(tname,tObj_NBTBase_nbt); + setTagContent(nsec,tObj_NBTBase_nbt); + }else{ + warn("NBT配置存在错误,无法处理["+sec.getCurrentPath()+"]的值"+value); + } + } + }else warn("你的NBT配置文件可能存在问题,节点"+sec.getCurrentPath()+"下\"_type_\"标识符错误"); + } + + /** + * 根据字符串格式生成指定的NBT节点 + * @return 返回类型为NBTBase + */ + private Object getNBTNode(String value){ + String[] vals=value.split("[|]",2); + if(vals.length<2) return ClassHelper.getInstance(NMSHelper.clazz_NBTTagString,String.class,value); + int type=0; + try{ + type=Integer.parseInt(vals[0]); + } + catch(NumberFormatException nbfexp){ + warn("NBT.yml file at "+value+" is uncorrect,value before '|' must be a int"); + return null; + } + switch(type){ + case 1: + try{ + byte tByteValue=Byte.parseByte(vals[1]); + return ClassHelper.getInstance(NMSHelper.clazz_NBTTagByte,byte.class,tByteValue); + } + catch(NumberFormatException nbfexp){ + warn("NBT.yml file at "+value+" is uncorrect,value after '|' must be a byte value"); + return null; + } + case 2: + try{ + short tShortValue=Short.parseShort(vals[1]); + return ClassHelper.getInstance(NMSHelper.clazz_NBTTagShort,short.class,tShortValue); + } + catch(NumberFormatException nbfexp){ + warn("NBT.yml file at "+value+" is uncorrect,value after '|' must be a short value"); + return null; + } + case 3: + try{ + int tIntValue=Integer.parseInt(vals[1]); + return ClassHelper.getInstance(NMSHelper.clazz_NBTTagInt,int.class,tIntValue); + } + catch(NumberFormatException nbfexp){ + warn("NBT.yml file at "+value+" is uncorrect,value after '|' must be a int value"); + return null; + } + case 4: + try{ + long tLongValue=Long.parseLong(vals[1]); + return ClassHelper.getInstance(NMSHelper.clazz_NBTTagLong,long.class,tLongValue); + } + catch(NumberFormatException nbfexp){ + warn("NBT.yml file at "+value+" is uncorrect,value after '|' must be a long value"); + return null; + } + case 5: + try{ + float tFloatValue=Float.parseFloat(vals[1]); + return ClassHelper.getInstance(NMSHelper.clazz_NBTTagFloat,float.class,tFloatValue); + } + catch(NumberFormatException nbfexp){ + warn("NBT.yml file at "+value+" is uncorrect,value after '|' must be a float value"); + return null; + } + case 6: + try{ + double tDoubleValue=Double.parseDouble(vals[1]); + return ClassHelper.getInstance(NMSHelper.clazz_NBTTagDouble,double.class,tDoubleValue); + } + catch(NumberFormatException nbfexp){ + warn("NBT.yml file at "+value+" is uncorrect,value after '|' must be a double value"); + return null; + } + case 7: + String[] bvals=vals[1].trim().split(","); + List blist=new ArrayList<>(); + for(String v : bvals){ + try{ + blist.add(Byte.parseByte(v)); + } + catch(NumberFormatException nbfexp){ + warn("NBT.yml file at "+value+" is uncorrect,"+v+" is not a short value"); + } + } + byte[] mbvals=new byte[blist.size()]; + for(int i=0;i ilist=new ArrayList<>(); + for(String v : ivals){ + try{ + ilist.add(Integer.parseInt(v)); + } + catch(NumberFormatException nbfexp){ + warn("NBT.yml file at "+value+" is uncorrect,"+v+" is not a int value"); + } + } + int[] mivals=new int[ilist.size()]; + for(int i=0;i interfaces = new HashMap(); + public static void register(IPointsAPI points) { + interfaces.put(points.getName(), points); + } + + public static IPointsAPI get(String name) { + return interfaces.get(name); + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/points/PointsAPIPlugin.java b/src/main/java/org/black_ixx/bossshop/points/PointsAPIPlugin.java new file mode 100644 index 0000000..b1d4440 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/points/PointsAPIPlugin.java @@ -0,0 +1,43 @@ +package org.black_ixx.bossshop.points; + +import org.black_ixx.bossshop.BossShop; +import org.bukkit.OfflinePlayer; +import org.bukkit.plugin.Plugin; +import me.BukkitPVP.PointsAPI.PointsAPI; + +public class PointsAPIPlugin extends IPointsAPI{ + + private PointsAPI pp; + + public PointsAPIPlugin(BossShop pPlugin){ + super(pPlugin,"PointsAPI"); + Plugin pointsApi=this.mPlugin.getServer().getPluginManager().getPlugin("PointsAPI"); + if(pointsApi!=null){ + pp=((PointsAPI)pointsApi); + } + } + + @Override + public int getPoints(OfflinePlayer player){ + return pp.getPoints(player); + } + + @Override + public int setPoints(OfflinePlayer player,int points){ + pp.setPoints(player.getPlayer(),points); + return getPoints(player); + } + + @Override + public int takePoints(OfflinePlayer player,int points){ + pp.removePoints(player,points); + return getPoints(player); + } + + @Override + public int givePoints(OfflinePlayer player,int points){ + pp.addPoints(player,points); + return getPoints(player); + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/points/PointsManager.java b/src/main/java/org/black_ixx/bossshop/points/PointsManager.java new file mode 100644 index 0000000..6c64250 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/points/PointsManager.java @@ -0,0 +1,142 @@ +package org.black_ixx.bossshop.points; + +import org.black_ixx.bossshop.BossShop; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; + +import cc.util.bossshop.pluginmodel.INeedConfig; +import cc.util.bossshop.pluginmodel.INeedReload; + +public class PointsManager extends IPointsAPI implements INeedConfig,INeedReload{ + + private IPointsAPI pa; + private PointsPlugin mPointPlugin; + private String mPointsPlugin="auto-detect"; + + public enum PointsPlugin{ + PLAYERPOINTS,COMMANDPOINTS,ENJIN_MINECRAFT_PLUGIN,POINTSAPI,CUSTOM; + + private String name; + + public void setCustom(String name){ + this.name=name; + } + + public String getCustom(){ + return name; + } + } + + /** + * 点券管理系统 + */ + public PointsManager(BossShop pPlugin){ + super(pPlugin,""); + this.mPlugin.registerConfigModel(this); + this.mPlugin.registerReloadModel(this); + } + + public String getPluginName(){ + return this.mPointPlugin==null?"":this.mPointPlugin.name(); + } + + @Override + public void setConfig(){ + this.mPointsPlugin=this.mPlugin.getConfigManager().getConfig().getString("PointsPlugin",this.mPointsPlugin); + } + + public boolean reloadConfig(){ + mPointPlugin=null; + if(mPointsPlugin!=null){ + if(Bukkit.getPluginManager().getPlugin("PlayerPoints")!=null){ + mPointPlugin=PointsPlugin.PLAYERPOINTS; + } + if(Bukkit.getPluginManager().getPlugin("CommandPoints")!=null){ + if(mPointPlugin==null||mPointsPlugin.equalsIgnoreCase("CommandPoints")) + mPointPlugin=PointsPlugin.COMMANDPOINTS; + } + if(Bukkit.getPluginManager().getPlugin("PointsAPI")!=null){ + if(mPointPlugin==null||mPointsPlugin.equalsIgnoreCase("PointsAPI")) + mPointPlugin=PointsPlugin.POINTSAPI; + } + if(Bukkit.getPluginManager().getPlugin("EnjinMinecraftPlugin")!=null){ + if(mPointPlugin==null||mPointsPlugin.equalsIgnoreCase("EnjinMinecraftPlugin")) + mPointPlugin=PointsPlugin.ENJIN_MINECRAFT_PLUGIN; + } + if(PointsAPI.get(mPointsPlugin)!=null){ + mPointPlugin=PointsPlugin.CUSTOM; + mPointPlugin.setCustom(mPointsPlugin); + } + } + if (mPointPlugin==null) { + this.mPlugin.severe("未找到任何支持的点券插件"); + pa = new FailedPointsAPI(this.mPlugin); + return false; + }else if(mPointsPlugin.equalsIgnoreCase("auto-detect")){ + this.mPlugin.info("自动选择"+mPointPlugin.toString()+"点券插件"); + }else if(!mPointPlugin.toString().equalsIgnoreCase(mPointsPlugin)){ + this.mPlugin.severe("未找到配置文件中指定的"+mPointsPlugin+"点券插件,但是发现了支持的点券插件"+mPointPlugin.toString()+",将此插件作为默认"); + }else{ + this.mPlugin.info("自定义点券插件为 " + mPointPlugin.name() + "."); + } + switch (mPointPlugin) { + case COMMANDPOINTS: + if (Bukkit.getPluginManager().getPlugin("CommandPoints") == null) { + this.mPlugin.severe("You defined CommandPoints as the Points Plugin... BUT IT WAS NOT FOUND?! Please download it at Bukkit.org!"); + return false; + } + pa = new CommandPointsAPI(this.mPlugin); + return false; + case ENJIN_MINECRAFT_PLUGIN: + if (Bukkit.getPluginManager().getPlugin("EnjinMinecraftPlugin") == null) { + this.mPlugin.severe("You defined Enjin Minecraft Plugin as the Points Plugin... BUT IT WAS NOT FOUND?! Please download it at Bukkit.org!"); + return false; + } + pa = new EnjinPointsAPI(this.mPlugin); + return false; + case PLAYERPOINTS: + if (Bukkit.getPluginManager().getPlugin("PlayerPoints") == null) { + this.mPlugin.severe("You defined PlayerPoints as the Points Plugin... BUT IT WAS NOT FOUND?! Please download it at Bukkit.org!"); + return false; + } + pa = new PlayerPointsAPI(this.mPlugin); + return false; + case POINTSAPI: + if (Bukkit.getPluginManager().getPlugin("PointsAPI") == null) { + this.mPlugin.severe("You defined PointsAPI as the Points Plugin... BUT IT WAS NOT FOUND?! Please download it at Bukkit.org!"); + return false; + } + pa = new PointsAPIPlugin(this.mPlugin); + return false; + case CUSTOM: + IPointsAPI customPoints = PointsAPI.get(mPointPlugin.getCustom()); + if (customPoints != null) { + pa = customPoints; + return false; + } + break; + } + this.mPlugin.warn("PlayerPoints/CommandPoints was not found... You need one of those plugins if you want this plugin to work with Points! Get PlayerPoints here: http://dev.bukkit.org/server-mods/playerpoints/"); + pa = new FailedPointsAPI(this.mPlugin); + return true; + } + + public int getPoints(OfflinePlayer player){ + return pa.getPoints(player); + } + + public int setPoints(OfflinePlayer player,int points){ + return pa.setPoints(player,points); + } + + public int givePoints(OfflinePlayer player,int points){ + return pa.givePoints(player,points); + } + + public int takePoints(OfflinePlayer player,int points){ + return pa.takePoints(player,points); + } + + + +} diff --git a/src/main/java/org/black_ixx/bossshop/sale/SaleListener.java b/src/main/java/org/black_ixx/bossshop/sale/SaleListener.java new file mode 100644 index 0000000..f2129b2 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/sale/SaleListener.java @@ -0,0 +1,243 @@ +package org.black_ixx.bossshop.sale; + +import java.util.HashMap; + +import org.apache.commons.lang.StringUtils; +import org.black_ixx.bossshop.BossShop; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; + +import cc.util.bossshop.JsonExtra; +import cc.util.bossshop.JsonExtra.ClickAction; +import cc.util.bossshop.JsonExtra.Color; +import cc.util.bossshop.JsonExtra.HoverAction; +import cc.util.bossshop.JsonExtra.Style; + + +public class SaleListener implements Listener{ + + private BossShop mPlugin; + private HashMap mForSale=new HashMap<>(); + + public SaleListener(BossShop pPlugin){ + this.mPlugin=pPlugin; + this.mPlugin.getServer().getPluginManager().registerEvents(this,this.mPlugin); + } + + private String C(String pNode){ + return this.mPlugin.C(pNode); + } + + @EventHandler(priority=EventPriority.LOW) + public void onPlayerChat(AsyncPlayerChatEvent pEvent){ + synchronized(this){ + if(!this.needHandle(pEvent.getPlayer(),pEvent.getMessage())) return; + pEvent.setCancelled(true); + this.handlePlayerMsg(pEvent.getPlayer(),pEvent.getMessage()); + } + } + + protected boolean needHandle(Player sender,String pMsg){ + if(sender==null||StringUtils.isEmpty(pMsg)) return false; + if(pMsg.startsWith("/")) return false; //跳过命令 + SaleParameter tParam=this.mForSale.get(sender); + if(tParam==null) return false; + if(tParam.mStep==SaleStep.Finish){ + this.stopSaleListenrer(sender); + return false; + } + return true; + } + + /** + * 处理玩家发送的消息 + *

调用前,请先使用{@link SaleListener#needHandle(Player, String)}检查是否因该处理

+ * @param pSender 消息发送者 + * @param pMsg 发送的消息 + */ + protected void handlePlayerMsg(Player pSender,String pMsg){ + SaleParameter tParam=this.mForSale.get(pSender); + if(this.handleStep(pSender,tParam,pMsg)){ + tParam.mStep=SaleStep.getNext(tParam.mStep); + } + this.showStepIntroduce(pSender,tParam); + if(tParam.mStep==SaleStep.Finish) + this.mForSale.remove(pSender); + } + + public SaleParameter stopSaleListenrer(Player pPlayer){ + SaleParameter removeParam= this.mForSale.remove(pPlayer); + if(removeParam==null){ + //this.mPlugin.send(pPlayer,"你没有开始寄售"); + }else{ + this.mPlugin.send(pPlayer,C("Sale.ChatSaleExit")); + } + return removeParam; + } + + public void startSaleListenrer(Player pPlayer){ + SaleParameter tp=this.mForSale.get(pPlayer); + if(tp!=null){ + if(tp.mStep==SaleStep.Finish){ + tp.mStep=SaleStep.GoodsType; + }else{ + this.mPlugin.send(pPlayer,C("Sale.ChatSaleRepartStart")); + return; + } + }else{ + tp=new SaleParameter(); + this.mForSale.put(pPlayer,tp); + } + this.mPlugin.send(pPlayer,C("Sale.ChatSaleStart")); + this.showStepIntroduce(pPlayer,tp); + } + + /** + * 显示当前寄售步奏的提示 + * @param pPlayer 用户名 + * @param pStep 参数 + */ + public void showStepIntroduce(Player pPlayer,SaleParameter pParam){ + switch(pParam.mStep){ + case GoodsType: + this.showSelect(pPlayer,this.mPlugin.getChatPrefix()+" "+C("Sale.IntroduceSellType") + ,new String[]{C("Word.Item"),C("Word.Money"),C("Word.Points")} + ,new String[]{"item","money","points"} + ); + break; + case SingleNumb: + this.mPlugin.send(pPlayer,C("Sale.IntroduceSingleNumb")); + break; + case PartNumb: + this.mPlugin.send(pPlayer,C("Sale.IntroducePartNumb")); + break; + case PriceType: + this.showSelect(pPlayer,this.mPlugin.getChatPrefix()+" "+C("Sale.IntroducePriceType") + ,new String[]{C("Word.Money"),C("Word.Points")} + ,new String[]{"money","points"} + ); + break; + case Price: + this.mPlugin.send(pPlayer,C("Sale.IntroducePrice")); + break; + case Finish: + this.mPlugin.send(pPlayer,C("Sale.ChatSaleFinish")); + JsonExtra chat=new JsonExtra(this.mPlugin.getChatPrefix()+" "+C("Sale.ClickSaleButtonFor")); + JsonExtra saleButton=new JsonExtra(C("Word.ForSale"),Color.blue); + saleButton.setStyle(Style.bold,Style.underline); + saleButton.setClickEvent(ClickAction.run_command,this.getSaleCommand(pParam)); + saleButton.setHoverEvent(HoverAction.show_text,this.getSaleHover(pParam)); + chat.addExtra(saleButton); + chat.sendToPlayer(pPlayer); + break; + } + } + + private String getSaleCommand(SaleParameter pParam){ + StringBuilder builder=new StringBuilder("/BS sale "); + builder.append(pParam.saleType.name()+" "); + builder.append(pParam.singleNumb+" "); + builder.append(pParam.partNumb+" "); + builder.append(pParam.priceType.name()+" "); + builder.append(pParam.price); + return builder.toString(); + } + + private String getSaleHover(SaleParameter pParam){ + String color=C("Sale.NowSaleParameterColor"); + if(!color.startsWith("§")) color="§a"; + StringBuilder builder=new StringBuilder(color+C("Sale.NowSaleParameter")+"\n"); + builder.append(color+C("Word.SellType")+": "+C("Word."+pParam.saleType.name())+"\n"); + builder.append(color+C("Word.SigleNumb")+": "+pParam.singleNumb+"\n"); + builder.append(color+C("Word.PartNumb")+": "+pParam.partNumb+"\n"); + builder.append(color+C("Word.Price")+": "+pParam.price+C("Word."+pParam.priceType.name())); + return builder.toString(); + } + + /** + * 处理当前寄售步奏的玩家输入 + *

+ * 错误消息会在本函数中立刻显示 + *

+ * @param pPlayer 用户名 + * @param pParam 已经处理的参数 + * @param pMsg 当前参数的输入 + * @return 是否处理成功 + */ + public boolean handleStep(Player pPlayer,SaleParameter pParam,String pMsg){ + SaleManager saleMan=this.mPlugin.getManager(SaleManager.class); + boolean result=false; + String successMsg=C("Sale.YouSetSaleParamTo"); + switch(pParam.mStep){ + case GoodsType: + if(!(result=saleMan.handleGoodsTypeInput(pParam,pPlayer,pMsg))) + this.mPlugin.send(pPlayer,pParam.errorInfo); + else{ + successMsg=successMsg.replace("%param%",C("Word.SellType")).replace("%value%",C("Word."+pParam.saleType.name())); + this.mPlugin.send(pPlayer,successMsg); + } + return result; + case SingleNumb: + if(!(result=saleMan.handleSingleNumbInput(pParam,pPlayer,pMsg))) + this.mPlugin.send(pPlayer,pParam.errorInfo); + else{ + successMsg=successMsg.replace("%param%",C("Word.SigleNumb")).replace("%value%",pParam.singleNumb+""); + this.mPlugin.send(pPlayer,successMsg); + } + return result; + case PartNumb: + if(!(result=saleMan.handlePartNumbInput(pParam,pPlayer,pMsg))) + this.mPlugin.send(pPlayer,pParam.errorInfo); + else{ + successMsg=successMsg.replace("%param%",C("Word.PartNumb")).replace("%value%",pParam.partNumb+""); + this.mPlugin.send(pPlayer,successMsg); + } + return result; + case PriceType: + if(!(result=saleMan.handlePriceTypeInput(pParam,pPlayer,pMsg))) + this.mPlugin.send(pPlayer,pParam.errorInfo); + else{ + successMsg=successMsg.replace("%param%",C("Word.PriceType")).replace("%value%",C("Word."+pParam.priceType.name())); + this.mPlugin.send(pPlayer,successMsg); + } + return result; + case Price: + if(!(result=saleMan.handlePriceInput(pParam,pPlayer,pMsg))) + this.mPlugin.send(pPlayer,pParam.errorInfo); + else{ + successMsg=successMsg.replace("%param%",C("Word.Price")).replace("%value%",pParam.price+""); + this.mPlugin.send(pPlayer,successMsg); + } + return result; + case Finish: + throw new IllegalArgumentException("在聊天栏寄售状态为Finish时,不能处理输入参数"); + } + return false; + } + + /** + * 使用指定参数显示一个可点击的选项消息 + * @param pPlayer 发送给谁 + * @param pMsg 消息前缀 + * @param pSelectDisName 选项显示名字 + * @param pSelect 选项值 + */ + protected void showSelect(Player pPlayer,String pMsg,String[] pSelectDisName,String[] pSelect){ + JsonExtra main=new JsonExtra(pMsg); + JsonExtra goosType=new JsonExtra("",Color.gold); + goosType.setStyle(Style.bold,Style.underline); + for(int i=0;i mSaleItemLore; + private final HashMap mLang=new HashMap<>(); + + public SaleManager(BossShop pplugin){ + this.mPlugin=pplugin; + this.mSaleItemLore=new ArrayList<>(); + this.mSaleItemLore.add("&2寄售编号: %bs_sale_item_id%"); + this.mSaleItemLore.add("&3寄售者: %bs_sale_owner_name%"); + this.mSaleItemLore.add("&c价格: %bs_sale_price% %bs_sale_price_type%"); + this.mPlugin.registerConfigModel(this); + } + + @Override + public void setConfig(){ + //配置文件 + CommentedYamlConfig tConfig=this.mPlugin.getConfigManager().getConfig(); + ConfigurationSection sec=tConfig.getConfigurationSection(CFG_MAIN); + if(sec==null) sec=tConfig.createSection(CFG_MAIN); + this.mShopBaseName=sec.getString(CFG_ShopBaseName); + this.mMoneyItem=sec.getInt(CFG_MoneyItem,0); + if(this.mMoneyItem==0||org.bukkit.Material.getMaterial(this.mMoneyItem)==null){ + this.mMoneyItem=9; + sec.set(CFG_MoneyItem,this.mMoneyItem); + } + this.mPointsItem=sec.getInt(CFG_PointsItem,0); + if(this.mPointsItem==0||org.bukkit.Material.getMaterial(this.mPointsItem)==null){ + this.mPointsItem=10; + sec.set(CFG_PointsItem,this.mPointsItem); + } + this.mSaleTime=sec.getInt(CFG_SaleTime,this.mSaleTime); + this.mPerOneSaleLimit=sec.getInt(CFG_PerOneSaleLimit,this.mPerOneSaleLimit); + this.mShopSize=sec.getInt(CFG_ShopSize,this.mShopSize); + this.mShopMaxNumb=sec.getInt(CFG_ShopSize,this.mShopMaxNumb); + this.mMoneyPoundage=sec.getInt(CFG_MoneyPoundage,this.mMoneyPoundage); + this.mPointsPoundage=sec.getInt(CFG_PointsPoundage,this.mPointsPoundage); + this.mUnsaleCount=sec.getInt(CFG_UnsaleCount,this.mUnsaleCount); + this.mUnsaleCost=sec.getInt(CFG_UnsaleCost,this.mUnsaleCost); + this.mSaleItemName=sec.getString(CFG_SaleItemName,this.mSaleItemName); + List list=sec.getStringList(CFG_SaleItemLore); + if(list!=null&&!list.isEmpty()){ + this.mSaleItemLore.clear(); + this.mSaleItemLore.addAll(list); + } + this.mPlugin.getConfigManager().saveConfig(); + //语言文件 + LangManager tLang=this.mPlugin.getLangManager(); + this.mLang.clear(); + this.mLang.put(BSBuyType.Money,tLang.get("Word.Money")); + this.mLang.put(BSBuyType.Points,tLang.get("Word.Points")); + this.mLang.put(BSBuyType.Item,tLang.get("Word.Item")); + } + + /** + * 给予上架物品出售后的收获,如果所有者在线,通知所有者物品被购买 + * @param pPlayer 玩家 + * @param pBuy 售出的物品 + */ + public void giveSaleReward(BSBuy pBuy){ + if(pBuy==null) return; + if(!pBuy.hasOwner()) return; + Player player=Bukkit.getPlayer(pBuy.getOwner()); + if(player==null){ + this.mPlugin.warn(C("CFG.UnableFoundSaleOwner")); + return; + } + int numb=0; + WorthHandler wHandler=this.mPlugin.getManager(WorthHandler.class); + switch(pBuy.getPriceType()){ + case Money: + numb=SaleManager.getActualySaleReward((Integer)pBuy.getPrice(),this.mMoneyPoundage); + wHandler.giveRewardMoney(player,numb); + break; + case Points: + numb=SaleManager.getActualySaleReward((Integer)pBuy.getPrice(),this.mPointsPoundage); + wHandler.giveRewardPoints(player,numb); + break; + default: + this.mPlugin.warn(C("CFG.UnknowSalePriceType")); + break; + } + this.mPlugin.send(player,C("Cmd.YouSaleHasSellOne")+"["+pBuy.getName()+"]"); + } + + private String C(String pNode){ + return this.mPlugin.getLangManager().getNode(pNode); + } + + /** + * 采用四舍五入发计算收取手续费后的收入 + * @param pNowReward + * @param pPoundage + * @return + */ + public static int getActualySaleReward(int pNowReward,int pPoundage){ + if(pPoundage<0||pPoundage>=100) return pNowReward; + int t=pNowReward*(100-pPoundage); + int unSolve=t%100; + if(unSolve==0){ + return t/100; + }else{ + return t/100+(unSolve<50?0:1); + } + } + + public int getShopMaxCount(){ + return this.mShopMaxNumb; + } + + public String getShopBaseName(){ + return this.mShopBaseName; + } + + public int getUnsaleCount(){ + return this.mUnsaleCount; + } + + public int getUnSaleCost(){ + return this.mUnsaleCost; + } + + public BSBuy getSaleItem(String pName){ + for(int i=1;i getSaleItemsName(){ + return this.getSaleItemsName(null); + } + + /** + * 获取指定玩家所有在寄售物品编号 + *

如果玩家未null,将返回所有在寄售物品编号

+ * @param pPlayer 玩家,可以为null + */ + public ArrayList getSaleItemsName(Player pPlayer){ + ArrayList allSale=new ArrayList<>(); + for(int i=1;i如果shop为null,说明无可用的商店用于上架

+ */ + public SaleParameter getAvailableShop(SaleParameter pParam){ + BSShop taregt_shop=null; + BSShopManager shopMan=this.mPlugin.getManager(BSShopManager.class); + BSShop frontShop=shopMan.getShop(this.mPlugin.getConfigManager().getMainShop()); + int target_loc=0,i; + for(i=1;i<=this.mShopMaxNumb;i++){ + String tShopname=this.mShopBaseName+i; + taregt_shop=shopMan.getShop(tShopname); + if(taregt_shop!=null){ + frontShop=taregt_shop; + target_loc=taregt_shop.getNextAvailableLoc(); + if(target_loc<=0||target_loc>this.mShopMaxNumb-2) continue; + break; + }else{ + taregt_shop=this.createShop(frontShop,tShopname,i); + target_loc=1; + break; + } + } + if(i<=this.mShopMaxNumb){ + pParam.shop=taregt_shop; + pParam.location=target_loc; + } + return pParam; + } + + /** + * 创建一个寄售商店 + * @param pShopname 商店名 + * @param pIndex 寄售商店索引 + * @return 创建好的商店 + */ + private BSShop createShop(BSShop pFrontShop,String pShopname,int pIndex){ + File tFile=new File(this.mPlugin.getDataFolder().getAbsolutePath()+"/shops/"+pShopname+".yml"); + CommentedYamlConfig tConfig=new CommentedYamlConfig(); + tConfig.loadFromFile(tFile); + String tname=C("Word.SaleShop")+" "+pIndex; + DefaultCreator.setSettings(tConfig,tname,tname); + //设置返回键 + if(pIndex==1){ + String tMainShop=this.mPlugin.getConfigManager().getMainShop(); + DefaultCreator.setGuidItem(tConfig,this.mShopSize-1,348,tMainShop,"RETURN_TO_FROUNT"); + }else{ + DefaultCreator.setGuidItem(tConfig,this.mShopSize-1,348,this.mShopBaseName+(pIndex-1),"RETURN_TO_FROUNT"); + if(pFrontShop!=null){ + CommentedYamlConfig tFrontConfig=pFrontShop.getConfig(); + DefaultCreator.setGuidItem(tFrontConfig,this.mShopSize,331,pShopname,tname,"GOTO_TO_NEXT"); + pFrontShop.saveConfig(); + pFrontShop.reloadConfig(); + } + } + //设置下一个按键 + BSShopManager shopMan=this.mPlugin.getManager(BSShopManager.class); + if(pIndex!=this.mShopMaxNumb&&shopMan.getShop("this.mShopBaseName+(pIndex+1)")!=null) + DefaultCreator.setGuidItem(tConfig,this.mShopSize,331,this.mShopBaseName+(pIndex+1),"GOTO_TO_NEXT"); + tConfig.saveToFile(tFile); + BSShop tshop=new BSShop(this.mPlugin,pShopname+".yml",pShopname); + shopMan.addShop(tshop); + return tshop; + } + + /** + * 物品上架接口 + * @param player 命令发送人 + * @param args 参数 + * @return 是否成功上架,错误消息会立刻反馈给玩家 + */ + public boolean sale(Player player,String[] args){ + if(args.length!=5) return this.getHelp(player); + //检查寄售上限 + if(this.getForSaleNumb(player)>=this.mPerOneSaleLimit&&!player.hasPermission("BossShop.sale.unlimited")) + return this.mPlugin.send(player,C("Cmd.SaleReachPreOneLimit")); + SaleParameter param=new SaleParameter(); + //获取可用的商店 + this.getAvailableShop(param); + if(param.shop==null) + return this.mPlugin.send(player,C("Cmd.SaleShopIsFull")); + //格式化参数 + this.formatSale(param,player,args); + if(param.errorInfo!=null) + return this.mPlugin.send(player,ChatColor.RED+param.errorInfo); + ItemStack item=player.getItemInHand(); + if(param.saleType==BSBuyType.Item&&(item==null||item.getTypeId()==0)) + return this.mPlugin.send(player,C("Cmd.YouShouldTakeItemInHand")); + //是否有足够的资本.此处会检查物品是否可以堆叠 + if(!this.enough(param)) return false; + //查找可用的物品名字节点 + ConfigurationSection sec_item_main=param.shop.getConfig().getConfigurationSection("shop"); + ConfigurationSection sec_item=null; + long time=System.currentTimeMillis()/1000; + String sec_item_name=param.owner.getName()+"_"+time; + if(sec_item_main!=null){ + do{ + sec_item_name=param.owner.getName()+"_"+time; + time++; + }while(sec_item_main.contains(sec_item_name)); + sec_item=sec_item_main.createSection(sec_item_name); + }else sec_item=param.shop.getConfig().createSection("shop."+sec_item_name); + param.mID=sec_item_name; + //替换文本变量 + this.replaceInfoParam(param); + //设置上架物品到文件,并同时收取价格 + //设置菜单物品到上架参数中,用于发送全服公告 + this.setConfigSection(sec_item,param); + this.mPlugin.send(player,C("Cmd.ForSaleSuccessAndTisID")+sec_item_name); + //全服公告 + this.sendSaleAnnoun(param); + return true; + } + + /** + * 发送全服公告 + */ + private void sendSaleAnnoun(SaleParameter param){ + if(!param.owner.hasPermission("BossShop.sale.announce.other")) + return; + BSBuy saleBuy=param.shop.getShopItem(param.location-1); + for(Player sPlayer : FBukkit.getOnlinePlayers()){ + saleBuy.getPersonalChatMenuItem(sPlayer,param.owner).sendToPlayer(sPlayer); + } + } + + /** + * 根据上架参数,添加物品到商店配置文件中,并同时收取价格 + * @param sec 要设置的节点 + * @param param 上架参数 + * @param pos 背包位置 + */ + private void setConfigSection(ConfigurationSection sec,SaleParameter param){ + ArrayList item_info=new ArrayList<>(); + String lore_prefix="&2"; + for(String sStr : param.mLore) lore_prefix+="#"+sStr; + item_info.add("name:"+param.mDisplayName); + item_info.add("lore:"+lore_prefix); + ItemStack item=new ItemStack(Material.STONE); + ItemMeta meta=item.getItemMeta(); + WorthHandler wHandler=this.mPlugin.getManager(WorthHandler.class); + switch(param.saleType){ + case Money: + wHandler.takeMoney(param.owner,param.singleNumb*param.partNumb); + meta.setDisplayName(param.singleNumb+" "+C("Word.Money")); + item_info.add("id:"+this.mMoneyItem); + item.setTypeId(this.mMoneyItem); + item_info.add("amount:1"); + sec.set("MenuItem",item_info); + sec.set("Reward",param.singleNumb); + break; + case Points: + wHandler.takePoints(param.owner,param.singleNumb*param.partNumb); + meta.setDisplayName(param.singleNumb+" "+C("Word.Points")); + item_info.add("id:"+this.mPointsItem); + item_info.add("amount:1"); + sec.set("MenuItem",item_info); + sec.set("Reward",param.singleNumb); + break; + case Item: + item=param.owner.getItemInHand().clone(); + ItemStack itemTake=item.clone(); + itemTake.setAmount(param.singleNumb*param.partNumb); + ArrayList listItem=new ArrayList<>(); + listItem.add(itemTake); + wHandler.takeItems(param.owner,listItem); + item_info.add("type:"+item.getType().name()); + item_info.add("durability:"+item.getDurability()); + item_info.add("amount:"+param.singleNumb); + String nbtlabel=this.mPlugin.getManager(NBTEditManager.class).addItemNBTToStock(item,true); + if(nbtlabel!=null) item_info.add("nbt:"+nbtlabel); + sec.set("MenuItem",item_info); + item_info=new ArrayList<>(); + if(item.getItemMeta().hasDisplayName()) item_info.add("name:"+item.getItemMeta().getDisplayName()); + item_info.add("id:"+item.getTypeId()); + item_info.add("durability:"+item.getDurability()); + item_info.add("amount:"+param.singleNumb); + if(nbtlabel!=null) item_info.add("nbt:"+nbtlabel); + sec.set("Reward",item_info); + break; + default: break; + } + List tLores=meta.getLore(); + if(tLores==null) tLores=new ArrayList(); + tLores.add("&2"+C("Word.SaleID")+":"+param.mID); + Date date=new Date(System.currentTimeMillis()+this.mSaleTime*1000); + sec.set("TimeLimit.stop",sdf.format(date)); + sec.set("HideItemFlag",this.mPlugin.getConfigManager().getHideItemFlag()); + sec.set("HideNotTime",true); + sec.set("NumberLimit",param.partNumb); + tLores.add("&2"+C("Word.PartNumb")+":"+param.partNumb); + sec.set("HideNoStock",true); + sec.set("InventoryLocation",param.location); + sec.set("RewardType",param.saleType.toString()); + sec.set("PriceType",param.priceType.toString()); + sec.set("Price",param.price); + tLores.add("&2"+C("Word.Price")+":"+param.price+(param.priceType==BSBuyType.Money?C("Word.Money"):C("Word.Points"))); + sec.set("Message",C("Word.YouBought")+param.mDisplayName); + sec.set("Owner",param.owner.getUniqueId().toString()); + sec.set("OwnerOnlyForLook",param.owner.getName()); + meta.setLore(tLores); + item.setItemMeta(meta); + param.shop.saveConfig(); + param.shop.reloadConfig(); + } + + /** + * 玩家是否有足够的资本上架,如果不足会立刻发送错误消息给玩家 + */ + private boolean enough(SaleParameter param){ + WorthHandler wHandler=this.mPlugin.getManager(WorthHandler.class); + switch(param.saleType){ + case Money: + return wHandler.hasMoney(param.owner,param.singleNumb*param.partNumb); + case Points: + return wHandler.hasPoints(param.owner,param.singleNumb*param.partNumb); + case Item: + ItemStack item=param.owner.getItemInHand().clone(); + int maxCount=item.getMaxStackSize(); + if(maxCount!=-1&&maxCount items=new ArrayList<>(); + items.add(item); + return wHandler.hasItems(param.owner,items); + default: + break; + } + return false; + } + + /** + * 从命令参数中生成上架物品的参数 + *

如果参数错误,错误消息存储在SaleParameter.errorInfo中, + * 如果SaleParameter.errorInfo为空说明没有错误

+ * @param pPlayer 玩家 + * @param args 参数 + * @return 上架物品参数 + */ + private boolean formatSale(SaleParameter pParam,Player pPlayer,String[] args){ + pParam.owner=pPlayer; + if(!this.handleGoodsTypeInput(pParam,pPlayer,args[0])) return false; + //单次的数量 + if(!this.handleSingleNumbInput(pParam,pPlayer,args[1])) return false; + //几份 + if(!this.handlePartNumbInput(pParam,pPlayer,args[2])) return false; + //价格类型 + if(!this.handlePriceTypeInput(pParam,pPlayer,args[3])) return false; + //价格 + if(!this.handlePriceInput(pParam,pPlayer,args[4])) return false; + return true; + } + + protected boolean handleGoodsTypeInput(SaleParameter pParam,Player pPlayer,String pInput){ + pParam.saleType=BSEnums.getBSBuyType(pInput); + if(pParam.saleType==null){ + pParam.errorInfo=C("Cmd.UnknowSaleItemType")+"["+pInput+"]"; + return false; + } + if(pParam.saleType!=BSBuyType.Item&&pParam.saleType!=BSBuyType.Money&&pParam.saleType!=BSBuyType.Points){ + pParam.errorInfo=C("Cmd.UnsupportSaleItemType")+"["+pParam.saleType.toString()+"]"; + return false; + } + return true; + } + + protected boolean handleSingleNumbInput(SaleParameter pParam,Player pPlayer,String pInput){ + try{ + pParam.singleNumb=Integer.parseInt(pInput); + }catch(NumberFormatException nfexp){ + pParam.errorInfo=ChatColor.RED+C("Word.SigleNumb")+" "+C("Cmd.MustBeNumb"); + return false; + } + if(pParam.singleNumb<=0){ + pParam.errorInfo=ChatColor.RED+C("Word.SigleNumb")+" "+C("Cmd.MustAboveZero"); + return false; + } + return true; + } + + protected boolean handlePartNumbInput(SaleParameter pParam,Player pPlayer,String pInput){ + try{ + pParam.partNumb=Integer.parseInt(pInput); + }catch(NumberFormatException nfexp){ + pParam.errorInfo=ChatColor.RED+C("Word.PartNumb")+" "+C("Cmd.MustBeNumb"); + return false; + } + if(pParam.partNumb<=0){ + pParam.errorInfo=ChatColor.RED+C("Word.PartNumb")+" "+C("Cmd.MustAboveZero"); + return false; + } + return true; + } + + /** + * 处理价格类型输入 + *

+ * 处理失败,失败结果会存储在{@link SaleParameter#errorInfo}中
+ * 函数内同时会处理寄售类型和价格类型相同的情况 + *

+ * @param pParam 已经处理的参数 + * @param pPlayer 输入者 + * @param pInput 输入字符 + * @return 是否处理成功 + */ + protected boolean handlePriceTypeInput(SaleParameter pParam,Player pPlayer,String pInput){ + pParam.priceType=BSEnums.getBSBuyType(pInput); + if(pParam.priceType==null){ + pParam.errorInfo=C("Cmd.UnknowPriceType")+"["+pInput+"]"; + return false; + } + if(pParam.priceType!=BSBuyType.Money&&pParam.priceType!=BSBuyType.Points){ + pParam.errorInfo=C("Cmd.UnsupportPriceType")+"["+pParam.priceType.name()+"]"; + return false; + } + //点券插件是否启用 + if(pParam.priceType==BSBuyType.Points){ + if(this.mPlugin.getManager(PointsManager.class)==null){ + pParam.errorInfo=C("Cmd.ServerDisablePoints"); + return false; + } + } + //不允许寄售物品和价格类型相同 + if(pParam.priceType.name().equals(pParam.saleType.name())){ + pParam.errorInfo=C("Cmd.SaleItemShouldNotSameWithPrice"); + return false; + } + return true; + } + + protected boolean handlePriceInput(SaleParameter pParam,Player pPlayer,String pInput){ + try{ + pParam.price=Integer.parseInt(pInput); + }catch(NumberFormatException nfexp){ + pParam.errorInfo=ChatColor.RED+"<"+C("Word.Price")+">"+C("Cmd.MustBeNumb"); + return false; + } + if(pParam.price<=0){ + pParam.errorInfo=ChatColor.RED+"<"+C("Word.Price")+">"+C("Cmd.MustAboveZero"); + return false; + } + return true; + } + + /** + * 上架系统命令帮助 + */ + public boolean getHelp(Player player){ + return this.mPlugin.getCommandExc().getCommand(CommandSale.class).help(player); + } + + /** + * 将寄售模板中的字符串变量替换之后放到寄售参数中 + */ + public void replaceInfoParam(SaleParameter pParam){ + pParam.mDisplayName=this.formatString(this.mSaleItemName,pParam); + pParam.mLore=new ArrayList<>(); + for(String sStr : this.mSaleItemLore) + pParam.mLore.add(this.formatString(sStr,pParam)); + } + + /** + * 替换字符串中的变量,所有支持的变量:
+ * %bs_sale_owner_name%
+ * %bs_sale_item_id%
+ * %bs_sale_item_sigle_count%
+ * %bs_sale_item_part_count%
+ * %bs_sale_item_type%
+ * %bs_sale_item_name%
+ * %bs_sale_price%
+ * %bs_sale_price_type%
+ */ + protected String formatString(String pStr,SaleParameter pSaleParam){ + Matcher tMatch=PATTERN.matcher(pStr); + while(tMatch.find()){ + String tParam=tMatch.group(1); + String value=tParam; + if(tParam.equalsIgnoreCase("bs_sale_owner_name")){ + value=pSaleParam.owner.getName(); + }else if(tParam.equalsIgnoreCase("bs_sale_item_id")){ + value=pSaleParam.mID; + }else if(tParam.equalsIgnoreCase("bs_sale_item_sigle_count")){ + value=pSaleParam.singleNumb+""; + }else if(tParam.equalsIgnoreCase("bs_sale_item_part_count")){ + value=pSaleParam.partNumb+""; + }else if(tParam.equalsIgnoreCase("bs_sale_item_type")){ + value=this.mLang.get(pSaleParam.saleType); + }else if(tParam.equalsIgnoreCase("bs_sale_item_name")){ + String tName=""; + if(pSaleParam.saleType==BSBuyType.Item){ + ItemStack tItem=pSaleParam.owner.getPlayer().getItemInHand(); + tName=this.mPlugin.getManager(ItemNameManager.class).getDisPlayName(tItem); + } + else tName=this.mLang.get(pSaleParam.saleType); + value=tName; + }else if(tParam.equalsIgnoreCase("bs_sale_price")){ + value=pSaleParam.price+""; + }else if(tParam.equalsIgnoreCase("bs_sale_price_type")){ + value=this.mLang.get(pSaleParam.priceType); + }else this.mPlugin.warn(C("CFG.UnsupportSaleItemParam")); + if(tParam!=value) + pStr=pStr.replaceAll("%"+tParam+"%",value); + } + return pStr; + } + +} + + + diff --git a/src/main/java/org/black_ixx/bossshop/sale/SaleParameter.java b/src/main/java/org/black_ixx/bossshop/sale/SaleParameter.java new file mode 100644 index 0000000..20b0d47 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/sale/SaleParameter.java @@ -0,0 +1,41 @@ +package org.black_ixx.bossshop.sale; + +import java.util.ArrayList; + +import org.black_ixx.bossshop.core.BSShop; +import org.black_ixx.bossshop.core.BSEnums.BSBuyType; +import org.bukkit.entity.Player; + +class SaleParameter{ + /**当前寄售步奏*/ + public SaleStep mStep; + /**寄售物品的编号*/ + public String mID; + /**所有者*/ + public Player owner; + /**寄售物品类型*/ + public BSBuyType saleType; + /**价格类型*/ + public BSBuyType priceType; + /**价格*/ + public int price; + /**单份售卖数量*/ + public int singleNumb=0; + /**多少份*/ + public int partNumb=0; + /**错误消息,如果无错误,此值为空*/ + public String errorInfo=null; + /**寄售的商店,如果为空说明无位置可用*/ + public BSShop shop=null; + /**商品放置位置,从1开始到81-2,剩余两个格子用作导航*/ + public int location=1; + /**寄售物品显示的名字*/ + public String mDisplayName; + /**寄售物品的lore*/ + public ArrayList mLore; + + public SaleParameter(){ + this.mStep=SaleStep.GoodsType; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/sale/SaleStep.java b/src/main/java/org/black_ixx/bossshop/sale/SaleStep.java new file mode 100644 index 0000000..f52e212 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/sale/SaleStep.java @@ -0,0 +1,33 @@ +package org.black_ixx.bossshop.sale; + +public enum SaleStep{ + // /BossShop sale <售卖类型> <单次数量> <份数> <价格类型> <价格> + GoodsType(1), + SingleNumb(2), + PartNumb(3), + PriceType(4), + Price(5), + Finish(6); + + private final int mStepNumb; + + private SaleStep(int pStepNumb){ + this.mStepNumb=pStepNumb; + } + + /** + * 获取基于当前步奏的下一步奏 + * @param pStep + * @return + */ + public static SaleStep getNext(SaleStep pStep){ + if(pStep==null) throw new IllegalArgumentException("获取下一步寄售步奏时,当前步奏不能为null"); + if(pStep==SaleStep.Finish) return null; + int next=pStep.mStepNumb+1; + for(SaleStep sStep : SaleStep.values()){ + if(sStep.mStepNumb==next) return sStep; + } + return null; + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/util/AttributeRemover.java b/src/main/java/org/black_ixx/bossshop/util/AttributeRemover.java new file mode 100644 index 0000000..322bb9e --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/util/AttributeRemover.java @@ -0,0 +1,67 @@ +package org.black_ixx.bossshop.util; + +import java.util.Collection; +import java.util.Map; + +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import cc.util.bossshop.ClassHelper; +import cc.util.bossshop.FBukkit; +import cc.util.bossshop.NMSHelper; + +public class AttributeRemover{ + + private static boolean useItemFlags; + private static boolean useReflection; + + public static boolean init(){ + + if(ClassHelper.isClassLoaded("org.bukkit.inventory.ItemFlag")){ + useItemFlags=true; + }else{ + FBukkit.severe("此服务器版本"+ClassHelper.getServerVersion()+"可能无法启用属性移除"); + useReflection=true; + } + return true; + } + + public static ItemStack hideAttributes(ItemStack item,int mFlags){ + if(item==null) return null; + mFlags=mFlags & 0xFFFFFFFF; + if(useItemFlags){ + ItemMeta meta=item.getItemMeta(); + for(ItemFlag sFlag : ItemFlag.values()){ + int pos=sFlag.ordinal(); + int value=((1<>pos; + if(value==1) meta.addItemFlags(sFlag); + else meta.removeItemFlags(sFlag); + //removeItemFlags + } + if(!FBukkit.isItemMetaEmpty(meta)) + item.setItemMeta(meta); + return item; + }else if(useReflection&&mFlags>0){ + try{ + Object nmsItemstack=ClassHelper.invokeStaticMethod(NMSHelper.clazz_CraftItemStack,"asNMSCopy",ItemStack.class,item); + if(nmsItemstack==null) return item; + Object nbtCompound=ClassHelper.getFieldValue(nmsItemstack,NMSHelper.clazz_NBTTagCompound,-1); + if(nbtCompound==null){ + nbtCompound=ClassHelper.getInstance(NMSHelper.clazz_NBTTagCompound); + ClassHelper.setFiledValue(nmsItemstack,NMSHelper.clazz_NBTTagCompound,nbtCompound,-1); + } + Object nbtList=ClassHelper.getInstance(NMSHelper.clazz_NBTTagList); + Map map=ClassHelper.getFieldValue(nbtCompound,Map.class,-1); + map.put("AttributeModifiers",nbtList); + return (ItemStack)ClassHelper.invokeStaticMethod(NMSHelper.clazz_CraftItemStack,"asCraftMirror",NMSHelper.clazz_NMSItemStack,nmsItemstack); + }catch(Exception e){} + } + return item; + } + + private static boolean isNullOrEmpty(Collection coll){ + return coll==null||coll.isEmpty(); + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/util/Function.java b/src/main/java/org/black_ixx/bossshop/util/Function.java new file mode 100644 index 0000000..2f1d3e2 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/util/Function.java @@ -0,0 +1,13 @@ +package org.black_ixx.bossshop.util; + + +public class Function{ + + public static void printStackTrace(){ + StackTraceElement[] elements=Thread.currentThread().getStackTrace(); + for(StackTraceElement sElement : elements){ + System.out.println(sElement.toString()); + } + } + +} diff --git a/src/main/java/org/black_ixx/bossshop/util/MyYamlConfig.java b/src/main/java/org/black_ixx/bossshop/util/MyYamlConfig.java new file mode 100644 index 0000000..645db87 --- /dev/null +++ b/src/main/java/org/black_ixx/bossshop/util/MyYamlConfig.java @@ -0,0 +1,342 @@ +package org.black_ixx.bossshop.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConstructor; +import org.bukkit.configuration.file.YamlRepresenter; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.error.YAMLException; +import org.yaml.snakeyaml.representer.Representer; + +/** + *

自定义的Yml配置文件读取的实现

+ *

读取的配置文件必须为utf8类型

+ *

可显式的保存中文

+ *

不能保存注释

+ * @author 聪聪 + * + */ +public class MyYamlConfig extends FileConfiguration{ + + protected static final String COMMENT_PREFIX="# "; + protected static final String BLANK_CONFIG="{}\n"; + private static Yaml yaml; + + static{ + DumperOptions dumpOptions=new DumperOptions(); + Representer yamlRepresenter=new YamlRepresenter(); + dumpOptions.setIndent(2); + dumpOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + dumpOptions.setAllowUnicode(true); + if(!dumpOptions.isAllowUnicode()){ + Class clazz=DumperOptions.class; + try{ + Field field=clazz.getDeclaredField("allowUnicode"); + field.setAccessible(true); + field.setBoolean(dumpOptions,true); + }catch(Exception exp){ + MyYamlConfig.log("错误,无法设置文件存储为unicode编码",exp); + } + } + yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + MyYamlConfig.yaml=new Yaml(new YamlConstructor(),yamlRepresenter,dumpOptions); + } + + public MyYamlConfig(){} + + /** + * 在发生异常时用于输出调试信息,或输出警告信息到控制台 + * @param level 错误等级 + * @param msg 错误的提示消息 + * @param thrown 要抛出的异常 + */ + private static void log(String msg,Throwable thrown){ + if(Bukkit.getServer()!=null){ + ConsoleCommandSender tSender=Bukkit.getConsoleSender(); + tSender.sendMessage(ChatColor.RED+"[ERROR] "+msg); + String[] lines= org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(thrown).split("\n"); + for(String sLine : lines) tSender.sendMessage(ChatColor.RED+"[ERROR] "+sLine); + }else Logger.getLogger("minecraft").log(Level.SEVERE,msg,thrown); + } + + @Override + public void addDefault(String path,Object value){ + if(this.get(path)==null) this.set(path,value); + super.addDefault(path,value); + } + + /** + * 保存当前配置内存中的数据为字符串 + */ + public String saveToString(){ + String header=buildHeader(); + String dump=MyYamlConfig.yaml.dump(getValues(false)); + if(dump.equals(BLANK_CONFIG)) dump=""; + return header+dump; + } + + /** + * 从字符串中载入配置 + */ + public void loadFromString(String contents) throws InvalidConfigurationException{ + Validate.notNull(contents,"字符串不能未null"); + Map input=null; + try{ + input=(Map)MyYamlConfig.yaml.load(contents); + }catch(YAMLException e){ + throw new InvalidConfigurationException(e); + }catch(ClassCastException e){ + throw new InvalidConfigurationException("配置文件顶级节点不是Map"); + } + String header=parseHeader(contents); + if(header.length()>0){ + options().header(header); + } + if(input!=null) convertMapsToSections(input,this); + } + + /** + * 将从字符串生成的配置节点数据格式化复制到本实例中 + * @param input 格式化后的数据 + * @param section 本实例,用于递归 + */ + protected void convertMapsToSections(Map input,ConfigurationSection section){ + for(Map.Entry entry : input.entrySet()){ + String key=entry.getKey().toString(); + Object value=entry.getValue(); + if((value instanceof Map)) convertMapsToSections((Map)value,section.createSection(key)); + else section.set(key,value); + } + } + + /** + * 构建配置文件注释头,保存到内存中 + * @param input 文件内容字符串 + * @return 构建完的头 + */ + protected String parseHeader(String input){ + String[] lines=input.split("\r?\n",-1); + StringBuilder result=new StringBuilder(); + boolean readingHeader=true; + boolean foundHeader=false; + for(int i=0;(i0) result.append("\n"); + if(line.length()>COMMENT_PREFIX.length()){ + result.append(line.substring(COMMENT_PREFIX.length())); + } + foundHeader=true; + }else if((foundHeader)&&(line.length()==0)){ + result.append("\n"); + }else if(foundHeader){ + readingHeader=false; + } + } + return result.toString(); + } + + /** + * 保留配置文件的头部注释输出 + */ + protected String buildHeader(){ + String header=options().header(); + if(header==null) return ""; + StringBuilder builder=new StringBuilder(); + String[] lines=header.split("\r?\n",-1); + boolean startedHeader=false; + for(int i=lines.length-1;i>=0;i--){ + builder.insert(0,"\r\n"); + if((startedHeader)||(lines[i].length()!=0)){ + builder.insert(0,lines[i]); + builder.insert(0,COMMENT_PREFIX); + startedHeader=true; + } + } + return builder.toString(); + } + + /** + * 保存指定的数据到指定的文件,如果文件不存在将会自动创建 + *

保存数据过程中的任何错误都会被记录到控制台然后忽视

+ * @param file 指定的文件 + * @param yml 指定的数据 + */ + @Override + public void save(File file){ + Validate.notNull(file,"配置文件不能为null"); + MyYamlConfig.save(file,this); + } + + /** + * 保存指定的数据到指定的文件,如果文件不存在将会自动创建 + *

保存数据过程中的任何错误都会被记录到控制台然后忽视

+ * @param file 指定的文件 + * @param yml 指定的数据 + */ + public static void save(File file,MyYamlConfig yml){ + Validate.notNull(yml,"配置文件读取器不能为null"); + Validate.notNull(file,"配置文件不能为null"); + FileOutputStream output=null; + try{ + if(!file.exists()) file.createNewFile(); + output=new FileOutputStream(file,false); + output.write(yml.saveToString().getBytes("UTF-8")); + }catch(FileNotFoundException ex){ + MyYamlConfig.log("未找到文件["+file+"]",ex); + }catch(IOException ex){ + MyYamlConfig.log("无法保存文件["+file+"]",ex); + }finally{ + if(output!=null) try{ + output.close(); + }catch(IOException exp){} + } + } + + /** + * 为当前实例从给定的文件路径中载入数据 + *

+ * 载入配置文件过程中的任何错误都会被记录到控制台然后忽视 + * 如果输入的文件不符合配置文件的格式规范,结果将返回一个空白文件 + *

+ * 编码默认使用 UTF-8 + * + * @param filename 输入的文件路径 + * @throws NullPointerException 如果文件名为空 + */ + @Override + public void load(String filename){ + MyYamlConfig.load(new File(filename),this); + } + + /** + * 为当前实例从给定的文件中载入数据 + *

+ * 载入配置文件过程中的任何错误都会被记录到控制台然后忽视 + * 如果输入的文件不符合配置文件的格式规范,结果将返回一个空白文件 + *

+ * 编码默认使用 UTF-8 + * + * @param file 输入文件 + * @throws IllegalArgumentException 如果文件为空 + */ + @Override + public void load(File file){ + MyYamlConfig.load(file,this); + } + + /** + * 为当前实例从给定的流中载入数据,流不会自动关闭 + *

+ * 载入配置文件过程中的任何错误都会被记录到控制台然后忽视 + * 如果输入的流不符合配置文件的格式规范,结果将返回一个空白文件 + *

+ * 编码默认使用 UTF-8 + * + * @param stream 输入的数据流 + * @param config 需要载入到的目标,可以为null + * @return 处理完后的内存中的配置文件 + * @throws IllegalArgumentException 如果输入流为空 + */ + @Override + public void load(InputStream stream){ + MyYamlConfig.load(stream,this); + } + + /** + * 创建一个新的 {@link MyYamlConfig}实例, 并从给定的文件中载入数据 + *

+ * 载入配置文件过程中的任何错误都会被记录到控制台然后忽视 + * 如果输入的文件不符合配置文件的格式规范,结果将返回一个空白文件 + *

+ * 编码默认使用 UTF-8 + * + * @param file 输入文件 + * @param config 需要载入到的目标,可以为null + * @return 处理完后的内存中的配置文件 + * @throws IllegalArgumentException 如果文件为空 + */ + public static MyYamlConfig load(File file,MyYamlConfig config){ + Validate.notNull(file,"文件不能为null"); + if(config==null) config=new MyYamlConfig(); + FileInputStream input=null; + byte[] contents=new byte[(int)file.length()]; + try{ + input=new FileInputStream(file); + input.read(contents); + config.loadFromString(new String(contents,"UTF-8")); + }catch(FileNotFoundException ex){ + MyYamlConfig.log("无法找到文件["+file+"]",ex); + }catch(IOException ex){ + MyYamlConfig.log("无法加载文件["+file+"]",ex); + }catch(InvalidConfigurationException ex){ + MyYamlConfig.log("无法加载文件["+file+"] 配置文件格式错误",ex); + }finally{ + if(input!=null) try{ + input.close(); + }catch(IOException exp){} + } + return config; + } + + /** + * 创建一个新的 {@link MyYamlConfig}实例, 并从给定的流中载入数据,流不会自动关闭 + *

+ * 载入配置文件过程中的任何错误都会被记录到控制台然后忽视 + * 如果输入的流不符合配置文件的格式规范,结果将返回一个空白文件 + *

+ * 编码默认使用 UTF-8 + * + * @param stream 输入的数据流 + * @param config 需要载入到的目标,可以为null + * @return 处理完后的内存中的配置文件 + * @throws IllegalArgumentException 如果输入流为空 + */ + public static MyYamlConfig load(InputStream stream,MyYamlConfig config){ + Validate.notNull(stream,"输入流不能为空"); + if(config==null) config=new MyYamlConfig(); + try{ + byte[] contents=new byte[stream.available()]; + config.loadFromString(new String(contents,"UTF-8")); + }catch(IOException ex){ + MyYamlConfig.log("无法从输入流加载配置",ex); + }catch(InvalidConfigurationException ex){ + MyYamlConfig.log("无法从输入流加载配置",ex); + } + return config; + } + + /** + * 设置输入路径的分割用字符,默认为"." + * @param value 作为分割路径用的字符 + */ + public void pathSeparator(char value){ + this.options().pathSeparator(value); + } + + /** + * 获取路径分割用字符 + * @return 分割路径用的字符 + */ + public char pathSeparator(){ + return this.options().pathSeparator(); + } + +} diff --git a/src/main/resource/lang/ItemName_zh_CN.lang b/src/main/resource/lang/ItemName_zh_CN.lang new file mode 100644 index 0000000..089ed0a --- /dev/null +++ b/src/main/resource/lang/ItemName_zh_CN.lang @@ -0,0 +1,1609 @@ +achievement.acquireIron=来硬的 +achievement.acquireIron.desc=冶炼出一块铁锭 +achievement.bakeCake=蛋糕是个谎言 +achievement.bakeCake.desc=小麦、糖、牛奶和鸡蛋! +achievement.blazeRod=与火共舞 +achievement.blazeRod.desc=得到烈焰人的烈焰棒 +achievement.bookcase=图书管理员 +achievement.bookcase.desc=建造一些书架来强化你的附魔台 +achievement.breedCow=种群恢复 +achievement.breedCow.desc=喂小麦来繁殖出1头牛 +achievement.buildBetterPickaxe=获得升级 +achievement.buildBetterPickaxe.desc=制作一把更好的镐 +achievement.buildFurnace=“热”门话题 +achievement.buildFurnace.desc=用八个圆石来制作一个熔炉 +achievement.buildHoe=耕种时间到! +achievement.buildHoe.desc=使用木板和木棍来制作木锄 +achievement.buildPickaxe=采矿时间到! +achievement.buildPickaxe.desc=使用木板和木棍来制作木镐 +achievement.buildSword=出击时间到! +achievement.buildSword.desc=使用木板和木棍来制作一把木剑! +achievement.buildWorkBench=这是? 工作台! +achievement.buildWorkBench.desc=用四个木板来制作一个工作台 +achievement.cookFish=美味的鱼儿 +achievement.cookFish.desc=抓了鱼儿然后烤! +achievement.diamonds=钻石! +achievement.diamonds.desc=用你的铁镐,挖一些钻石吧 +achievement.diamondsToYou=给你钻石! +achievement.diamondsToYou.desc=向一名玩家投掷钻石 +achievement.enchantments=附魔师 +achievement.enchantments.desc=使用书、黑曜石以及钻石来制作一个附魔台 +achievement.exploreAllBiomes=探索的时光 +achievement.exploreAllBiomes.desc=发现所有的生物群系 +achievement.flyPig=当猪飞的时候 +achievement.flyPig.desc=骑着猪从悬崖上飞下 +achievement.fullBeacon=信标工程师 +achievement.fullBeacon.desc=制造最高级的信标 +achievement.get=获得成就! +achievement.ghast=见鬼去吧 +achievement.ghast.desc=用一团火球干掉一只恶魂 +achievement.killCow=斗牛士 +achievement.killCow.desc=获得一些皮革 +achievement.killEnemy=怪物猎人 +achievement.killEnemy.desc=攻击并消灭一只怪物 +achievement.killWither=开始了 +achievement.killWither.desc=杀死凋灵 +achievement.makeBread=烤面包 +achievement.makeBread.desc=用小麦来做面包 +achievement.mineWood=获得木头 +achievement.mineWood.desc=摧毁树木直到跳出一个木头方块 +achievement.onARail=在铁路上 +achievement.onARail.desc=通过矿车旅行,移动到至少离出发点1公里的位置 +achievement.openInventory=打开物品栏 +achievement.openInventory.desc=按“%1$s”来打开你的物品栏 +achievement.overkill=赶尽杀绝 +achievement.overkill.desc=制造一击8颗心的伤害 +achievement.portal=我们需要再深入些 +achievement.portal.desc=建造一个通往下界的传送门 +achievement.potion=本地的酿造厂 +achievement.potion.desc=酿造一瓶药水 +achievement.requires=需要“%1$s” +achievement.snipeSkeleton=狙击手的对决 +achievement.snipeSkeleton.desc=用弓箭从50米外干掉一只骷髅 +achievement.spawnWither=开始了? +achievement.spawnWither.desc=生成凋灵 +achievement.taken=已得到! +achievement.theEnd=结束了? +achievement.theEnd.desc=找到“末地” +achievement.theEnd2=结束了。 +achievement.theEnd2.desc=击败末影龙 +achievement.unknown=??? +addServer.add=完成 +addServer.enterIp=服务器地址 +addServer.enterName=服务器名称 +addServer.hideAddress=隐藏地址 +addServer.resourcePack=服务器资源包 +addServer.resourcePack.disabled=禁用 +addServer.resourcePack.enabled=启用 +addServer.resourcePack.prompt=提示 +addServer.title=编辑服务器信息 +advMode.allPlayers=用“@a”来代表全部玩家 +advMode.command=控制台指令 +advMode.nearestPlayer=用“@p”来代表最近的玩家 +advMode.notAllowed=必须是处于创造模式的管理员 +advMode.notEnabled=命令方块没有在此服务器上启用 +advMode.previousOutput=上一个输出 +advMode.randomPlayer=用“@r”来代表随机玩家 +advMode.setCommand=设置此方块的控制台指令 +advMode.setCommand.success=成功设置:%s +attribute.modifier.plus.0=%2$s +%1$s +attribute.modifier.plus.1=%2$s +%1$s%% +attribute.modifier.plus.2=%2$s +%1$s%% +attribute.modifier.take.0=%2$s -%1$s +attribute.modifier.take.1=%2$s -%1$s%% +attribute.modifier.take.2=%2$s -%1$s%% +attribute.name.generic.attackDamage=攻击伤害 +attribute.name.generic.followRange=生物跟随距离 +attribute.name.generic.knockbackResistance=击退抗性 +attribute.name.generic.maxHealth=最大生命值 +attribute.name.generic.movementSpeed=速度 +attribute.name.horse.jumpStrength=马匹跳跃能力 +attribute.name.zombie.spawnReinforcements=僵尸增援 +book.byAuthor=——%1$s +book.editTitle=输入书名: +book.finalizeButton=签名并关闭 +book.finalizeWarning=注意!在你签名后,它将不能再被修改。 +book.pageIndicator=页面:%1$s/%2$s +book.signButton=签名 +build.tooHigh=建筑高度限制是 %s 个方块 +chat.cannotSend=无法发送聊天信息 +chat.copy=复制到剪贴板 +chat.link.confirm=你确定你要打开以下网页? +chat.link.confirmTrusted=你想要打开这个链接或复制到你的剪贴板吗? +chat.link.open=在浏览器中打开 +chat.link.warning=永远不要打开从你不信任的人得到的链接! +chat.stream.emote=(%s) * %s %s +chat.stream.text=(%s) <%s> %s +chat.type.achievement=%s 刚刚获得了 %s 成就! +chat.type.admin=[%s: %s] +chat.type.announcement=[%s] %s +chat.type.emote=* %s %s +chat.type.text=<%s> %s +commands.achievement.give.success.all=成功将所有成就授予 %s +commands.achievement.give.success.one=成功地给予了 %s 统计数据 %s +commands.achievement.statTooLow=玩家 %s 并没有统计数据 %s +commands.achievement.unknownAchievement=未知的成就或统计 '%s' +commands.achievement.usage=/achievement give <数据名> [玩家] +commands.ban.failed=无法封禁玩家 %s +commands.ban.success=已封禁玩家 %s +commands.ban.usage=/ban <名称> [原因...] +commands.banip.invalid=你输入了一个无效的IP地址或者此玩家不在线 +commands.banip.success=已封禁IP地址 %s +commands.banip.success.players=已封禁的IP地址 %s 是属于 %s 的 +commands.banip.usage=/ban-ip <地址|名称> [原因...] +commands.banlist.ips=总共有 %s 个被封禁的IP地址: +commands.banlist.players=总共有 %s 个被封禁的玩家 +commands.banlist.usage=/banlist [ips] +commands.clear.failure=无法清除%s的物品栏,没有可清除的物品 +commands.clear.success=清除了%s的物品栏,删除了%s个物品 +commands.clear.usage=/clear <玩家> [物品] [数值] +commands.debug.notStarted=不能在开始前就停止分析! +commands.debug.start=开始调试分析 +commands.debug.stop=在 %s 秒 (%s 刻) 后停止调试分析 +commands.debug.usage=/debug +commands.defaultgamemode.success=世界的默认游戏模式现为%s +commands.defaultgamemode.usage=/defaultgamemode <模式> +commands.deop.failed=无法取消 %s 的管理员权限 +commands.deop.success=已夺去 %s 的OP权限 +commands.deop.usage=/deop <玩家> +commands.difficulty.success=将游戏难度设置为%s +commands.difficulty.usage=/difficulty <新的难度> +commands.downfall.success=天气已切换 +commands.downfall.usage=/toggledownfall +commands.effect.failure.notActive=无法从 %2$s 身上消除 %1$s 因为他们没有此效果 +commands.effect.failure.notActive.all=无法从 %s 身上移除任何效果,因为他们身上什么效果都没有 +commands.effect.notFound=没有ID为%s的状态效果 +commands.effect.success=给予 %4$s 时长为 %5$s 秒的 %1$s (ID%2$s) * %3$s 效果 +commands.effect.success.removed=消除了 %2$s 身上的 %1$s +commands.effect.success.removed.all=已将 %s 身上的全部效果移除 +commands.effect.usage=/effect <玩家> <效果> [时间] [倍数] +commands.enchant.cantCombine=%1$s 无法和 %2$s 结合 +commands.enchant.cantEnchant=该物品不能添加此魔咒 +commands.enchant.noItem=目标没有拿着任何物品 +commands.enchant.notFound=没有ID为 %s 的魔咒 +commands.enchant.success=附魔成功 +commands.enchant.usage=/enchant <玩家> <魔咒ID> [等级] +commands.gamemode.success.other=将 %s 的游戏模式改为 %s +commands.gamemode.success.self=您的游戏模式已被设置为 %s +commands.gamemode.usage=/gamemode <模式> [玩家] +commands.gamerule.norule=不存在叫做'%s'的游戏规则 +commands.gamerule.success=游戏规则已更新 +commands.gamerule.usage=/gamerule <规则名称> <数值> 或 /gamerule <规则名称> +commands.generic.boolean.invalid='%s'不是个布尔值参数 +commands.generic.deprecatedId=注意:未来版本将不再支持数字ID的使用。请使用例如 '%s' 的名称 +commands.generic.double.tooBig=你输入的数字 (%s) 太大了,它最高只能为%s +commands.generic.double.tooSmall=你输入的数字 (%s) 太小了,它至少需要为%s +commands.generic.exception=在执行此指令时出现了未知的错误 +commands.generic.notFound=未知指令。请使用/help来查看指令列表。 +commands.generic.num.invalid='%s'不是一个有效的数字 +commands.generic.num.tooBig=你输入的数字 (%s) 太大了,它最高只能为%s +commands.generic.num.tooSmall=你输入的数字 (%s) 太小了,它至少需要为 %s +commands.generic.permission=您没有使用此命令的权限 +commands.generic.player.notFound=无法找到该玩家 +commands.generic.syntax=无效的指令语法 +commands.generic.usage=用法:%s +commands.give.notFound=ID为 %s 的物品不存在 +commands.give.success=成功将 %s * %s 给予 %s +commands.give.tagError=数据标签分析失败:%s +commands.give.usage=/give <玩家> <物品> [数量] [数据值] [数据标签] +commands.help.footer=小提示: 在输入命令时可以使用 键来自动补全命令或参数。 +commands.help.header=--- 显示帮助手册总 %s 页中的第 %s 页(/help <页码>) --- +commands.help.usage=/help [页码|指令名称] +commands.kick.success=把 %s 从游戏中踢出 +commands.kick.success.reason=%s 被踢出游戏:‘%s’ +commands.kick.usage=/kick <玩家> [原因...] +commands.kill.success=哎哟!那看起来很疼 +commands.kill.usage=/kill +commands.me.usage=/me <动作...> +commands.message.display.incoming=%s悄悄地对你说:%s +commands.message.display.outgoing=你悄悄地对%s说:%s +commands.message.sameTarget=你不能向自己发出私人信息! +commands.message.usage=/tell <玩家> <私密信息...> +commands.op.failed=无法给予 %s 管理员权限 +commands.op.success=已将 %s 设为OP +commands.op.usage=/op +commands.players.list=目前有%s/%s个玩家在线: +commands.players.usage=/list +commands.playsound.playerTooFar=玩家 %s 离得太远了,无法听到这声音 +commands.playsound.success=向 %2$s 播放声音 '%2$s' +commands.playsound.usage=/playsound <声音> <玩家> [x] [y] [z] [音量] [音调] [最低音量] +commands.publish.failed=无法建立本地游戏 +commands.publish.started=本地游戏已在端口 %s 上开启 +commands.publish.usage=/publish +commands.save-off.alreadyOff=已经关闭世界保存 +commands.save-off.usage=/save-off +commands.save-on.alreadyOn=已经打开世界保存 +commands.save-on.usage=/save-on +commands.save.disabled=世界自动保存已关闭 +commands.save.enabled=世界自动保存已启用 +commands.save.failed=保存失败:%s +commands.save.start=保存中…… +commands.save.success=已保存世界 +commands.save.usage=/save-all +commands.say.usage=/say <信息...> +commands.scoreboard.objectiveNotFound=没有找到名称为'%s'的目标 +commands.scoreboard.objectiveReadOnly=无法设置只读目标 '%s' +commands.scoreboard.objectives.add.alreadyExists=已经存在名为'%s'的目标 +commands.scoreboard.objectives.add.displayTooLong='%s' 作为目标的显示名太长了,它最多只能有 %s 个字 +commands.scoreboard.objectives.add.success=成功地添加了新目标'%s' +commands.scoreboard.objectives.add.tooLong='%s' 作为目标名太长了,它最多只能有 %s 个字 +commands.scoreboard.objectives.add.usage=/scoreboard objectives add <名称> <准则类型> [显示的名称…] +commands.scoreboard.objectives.add.wrongType=无效的目标准则类型'%s' +commands.scoreboard.objectives.list.count=记分板正在显示%s个目标 +commands.scoreboard.objectives.list.empty=记分板上没有目标 +commands.scoreboard.objectives.list.entry=——%s:显示名为'%s',类型为'%s' +commands.scoreboard.objectives.remove.success=成功地移除了目标'%s' +commands.scoreboard.objectives.remove.usage=/scoreboard objectives remove <名字> +commands.scoreboard.objectives.setdisplay.invalidSlot=没有名称为'%s'的显示位置 +commands.scoreboard.objectives.setdisplay.successCleared=清空了'%s'目标显示区 +commands.scoreboard.objectives.setdisplay.successSet=将目标显示区从'%s'设置为'%s' +commands.scoreboard.objectives.setdisplay.usage=/scoreboard objectives setdisplay <位置> [目标] +commands.scoreboard.objectives.usage=/scoreboard objectives +commands.scoreboard.players.add.usage=/scoreboard players add <玩家> <目标> <记分> +commands.scoreboard.players.list.count=记分板上正在显示 %s 个被追踪的玩家 +commands.scoreboard.players.list.empty=记分板上没有追踪的玩家 +commands.scoreboard.players.list.player.count=正在给 %s 显示 %s 个被追踪的目标 +commands.scoreboard.players.list.player.empty=玩家 %s 没有分数记录 +commands.scoreboard.players.list.player.entry=- %2$s: %1$s (%3$s) +commands.scoreboard.players.list.usage=/scoreboard players list [名字] +commands.scoreboard.players.remove.usage=/scoreboard players remove <玩家> <目标> <记分> +commands.scoreboard.players.reset.success=重置玩家 %s 所有的分数 +commands.scoreboard.players.reset.usage=/scoreboard players reset <玩家> +commands.scoreboard.players.set.success=将玩家 %2$s 的 %1$s 分数的设置为 %s +commands.scoreboard.players.set.usage=/scoreboard players set <玩家> <目标> <分数> +commands.scoreboard.players.usage=/scoreboard players <设置(set)|添加(add)|移除(remove)|重置(reset)|列表(list)> +commands.scoreboard.teamNotFound=没有找到队名为'%s'的队伍 +commands.scoreboard.teams.add.alreadyExists=名为'%s'的队伍已经存在 +commands.scoreboard.teams.add.displayTooLong='%s' 作为队伍的显示名太长了,它最多只能有 %s 个字 +commands.scoreboard.teams.add.success=成功地添加了新队伍'%s' +commands.scoreboard.teams.add.tooLong='%s' 作为队名太长了,它最多只能有 %s 个字 +commands.scoreboard.teams.add.usage=/scoreboard teams add <名字> [显示的名字...] +commands.scoreboard.teams.empty.alreadyEmpty=队伍%s已空,无法移除不存在的玩家 +commands.scoreboard.teams.empty.success=移除了队伍%s内的全部%s个玩家 +commands.scoreboard.teams.empty.usage=/scoreboard teams empty +commands.scoreboard.teams.join.failure=无法给队伍 %s 添加 %s 个玩家:%s +commands.scoreboard.teams.join.success=将%s个玩家添加至队伍%s:%s +commands.scoreboard.teams.join.usage=/scoreboard teams join <队名> [玩家] +commands.scoreboard.teams.leave.failure=无法从他们队伍中移除 %s 个玩家:%s +commands.scoreboard.teams.leave.noTeam=你不在一个队伍中 +commands.scoreboard.teams.leave.success=从他们的队伍里移除了 %s 个玩家:%s +commands.scoreboard.teams.leave.usage=/scoreboard teams leave [玩家] +commands.scoreboard.teams.list.count=记分板上正在显示 %s 个队伍 +commands.scoreboard.teams.list.empty=记分板上无注册的队伍 +commands.scoreboard.teams.list.entry=——%1$s:'%2$s' 有 %3$s 个玩家 +commands.scoreboard.teams.list.player.count=正在显示队伍 %s 中的 %s 个玩家 +commands.scoreboard.teams.list.player.empty=队伍%s没有玩家 +commands.scoreboard.teams.list.player.entry=- %2$s: %1$s (%3$s) +commands.scoreboard.teams.list.usage=/scoreboard teams list [名字] +commands.scoreboard.teams.option.noValue=选项 %s 的有效数值有:%s +commands.scoreboard.teams.option.success=将队伍 %2$s 的选项 %1$s 设置为 %3$s +commands.scoreboard.teams.option.usage=/scoreboard teams option <队名> <数值> +commands.scoreboard.teams.remove.success=移除了队伍%s +commands.scoreboard.teams.remove.usage=/scoreboard teams remove <名字> +commands.scoreboard.teams.usage=/scoreboard teams +commands.scoreboard.usage=/scoreboard +commands.seed.success=种子: %s +commands.seed.usage=/seed +commands.setblock.failed=无法放置方块 +commands.setblock.noChange=无法放置方块 +commands.setblock.notFound=找不到数据值或名称为 %s 的方块 +commands.setblock.outOfWorld=无法在世界外放置方块 +commands.setblock.success=方块已放置 +commands.setblock.tagError=数据标签分析失败:%s +commands.setblock.usage=/setblock <方块名> [数据值] [旧方块处理方式] [数据标签] +commands.setidletimeout.success=成功地将空闲时限设为 %s 分钟 +commands.setidletimeout.usage=/setidletimeout <闲置时限(分钟)> +commands.setworldspawn.success=已将世界出生点设置为(%s,%s,%s) +commands.setworldspawn.usage=/setworldspawn 或 /setworldspawn +commands.spawnpoint.success=将%s的出生点设置到(%s,%s,%s) +commands.spawnpoint.usage=/spawnpoint 或 /spawnpoint <玩家> 或 /spawnpoint <玩家> +commands.spreadplayers.failure.players=无法将 %s 个玩家扩散至 (%s,%s)周围 (玩家数量过多 - 最大玩家数为 %s) +commands.spreadplayers.failure.teams=无法将%s个队伍扩散至%s个方块远离%s,%s (玩家数量过多 - 最大玩家数为%s) +commands.spreadplayers.info.players=(玩家间的平均距离在迭代 %2$s 次后为 %1$s 个方块) +commands.spreadplayers.info.teams=(队伍间的平均距离在迭代 %2$s 次后为 %1$s 个方块) +commands.spreadplayers.spreading.players=已将 %1$s 个玩家扩散至离(%3$s,%4$s) %2$s 个方块远的地方 (至少相隔 %5$s 个方块) +commands.spreadplayers.spreading.teams=已将 %1$s 个队伍扩散至离(%3$s,%4$s%) 2$s 个方块远的地方 (至少相隔 %5$s 个方块) + +commands.spreadplayers.success.players=成功地将 %s 个玩家在 (%s,%s) 周围扩散 +commands.spreadplayers.success.teams=成功地将 %s 个队伍在 (%s,%s) 周围扩散 +commands.spreadplayers.usage=/spreadplayers <扩散距离> <最远距离> <分组 true|false> <玩家 ...> +commands.stop.start=正在关闭服务器 +commands.stop.usage=/stop +commands.summon.failed=无法召唤实体 +commands.summon.outOfWorld=无法在世界外召唤实体 +commands.summon.success=成功地召唤了实体 +commands.summon.tagError=数据标签分析失败:%s +commands.summon.usage=/summon <实体名> [x] [y] [z] [数据标签] +commands.tellraw.jsonException=无效的json:%s +commands.tellraw.usage=/tellraw <玩家> <原json消息> +commands.testfor.failed=/testfor 只能由于命令方块上来输出模拟信号 +commands.testfor.usage=/testfor <玩家> +commands.testforblock.failed.data=位于%s,%s,%s的方块拥有数据值 %s ( 预定: %s ) +commands.testforblock.failed.nbt=位于%s,%s,%s的方块并没有所需的NBT标签 +commands.testforblock.failed.tile=位于%s,%s,%s的方块为%s ( 预定: %s ) +commands.testforblock.failed.tileEntity=位于%s,%s,%s的方块不拥有实体附加值且不支持标签配对 +commands.testforblock.outOfWorld=无法测试位于世界外的方块 +commands.testforblock.success=成功找到了位于%s,%s,%s的方块 +commands.testforblock.usage=/testforblock <方块名> [数据值] [数据标签] +commands.time.added=将时间调快了 %s +commands.time.set=将时间设为 %s +commands.time.usage=/time <数值> +commands.tp.notSameDimension=无法传送,因为目标玩家不存在于同一个空间 +commands.tp.success=已将 %s 传送至 %s +commands.tp.success.coordinates=将 %s 传送到 %s,%s,%s +commands.tp.usage=/tp [目标玩家] <目的地玩家> 或 /tp [目标玩家] +commands.unban.failed=无法解除玩家 %s 的封禁 +commands.unban.success=已解封玩家 %s +commands.unban.usage=/pardon <用户名> +commands.unbanip.invalid=你输入了一个无效的IP地址 +commands.unbanip.success=已解封IP地址 %s +commands.unbanip.usage=/pardon-ip <地址> +commands.weather.clear=更换到无天气 +commands.weather.rain=更换到下雨天 +commands.weather.thunder=更换到雷雨天 +commands.weather.usage=/weather [持续的秒数] +commands.whitelist.add.failed=无法将 %s 添加到白名单中 +commands.whitelist.add.success=已将 %s 加入白名单 +commands.whitelist.add.usage=/whitelist add <玩家> +commands.whitelist.disabled=白名单验证已关闭 +commands.whitelist.enabled=白名单验证已启用 +commands.whitelist.list=现在白名单中有 %s 个 ( 于可见的 %s 人中 ) 玩家: +commands.whitelist.reloaded=已重新读取白名单 +commands.whitelist.remove.failed=无法从白名单中移除 %s +commands.whitelist.remove.success=已将 %s 移出白名单 +commands.whitelist.remove.usage=/whitelist remove <玩家> +commands.whitelist.usage=/whitelist +commands.xp.failure.widthdrawXp=无法给玩家为负数的经验值 +commands.xp.success=已将 %s 的经验给予 %s +commands.xp.success.levels=给予 %s %s 级经验 +commands.xp.success.negative.levels=从 %s 身上拿走了 %s 级经验 +commands.xp.usage=/xp <经验数量> [玩家] 或 /xp <等级数量>L [玩家] +connect.authorizing=登入中... +connect.connecting=正在连接到服务器... +connect.failed=无法连接至服务器 +container.brewing=酿造台 +container.chest=箱子 +container.chestDouble=大型箱子 +container.crafting=合成 +container.creative=物品选栏 +container.dispenser=发射器 +container.dropper=投掷器 +container.enchant=附魔 +container.enderchest=末影箱 +container.furnace=熔炉 +container.hopper=漏斗 +container.inventory=物品栏 +container.minecart=矿车 +container.repair=物品修复和命名 +container.repair.cost=魔咒花费:%1$s +container.repair.expensive=过于昂贵! +controls.reset=重置 +controls.resetAll=重置按键 +controls.title=按键控制 +createWorld.customize.flat.addLayer=添加层面 +createWorld.customize.flat.editLayer=编辑层面 +createWorld.customize.flat.height=高度 +createWorld.customize.flat.layer=%s +createWorld.customize.flat.layer.bottom=底层-%s +createWorld.customize.flat.layer.top=顶层-%s +createWorld.customize.flat.removeLayer=移除层面 +createWorld.customize.flat.tile=此层的材料 +createWorld.customize.flat.title=超平坦世界个性化 +createWorld.customize.presets=预设 +createWorld.customize.presets.list=另外,这里是些我们早期制作好的! +createWorld.customize.presets.select=使用预设 +createWorld.customize.presets.share=想要与别人分享你的预设方案吗?使用下面的输入框吧! +createWorld.customize.presets.title=选择一个预设 +death.attack.anvil=%1$s 被坠落的铁砧压扁了 +death.attack.arrow=%1$s 被 %2$s 射杀 +death.attack.arrow.item=%1$s 被 %2$s 用 %3$s 射杀 +death.attack.cactus=%1$s 被戳死了 +death.attack.cactus.player=%1$s 在试图逃离 %2$s 时撞入了仙人掌中 +death.attack.drown=%1$s 淹死了 +death.attack.drown.player=%1$s 在试图逃离 %2$s 时淹死了 +death.attack.explosion=%1$s 爆炸了 +death.attack.explosion.player=%1$s 被 %2$s 炸死了 +death.attack.fall=%1$s落地过猛 +death.attack.fallingBlock=%1$s 被坠落的方块压扁了 +death.attack.fireball=%1$s 被 %2$s 用火球烤死了 +death.attack.fireball.item=%1$s 被 %2$s 用 %3$s 发射的火球烤死了 +death.attack.generic=%1$s 死了 +death.attack.inFire=%1$s 浴火焚身 +death.attack.inFire.player=%1$s 在与 %2$s 战斗中不慎走入了火中 +death.attack.inWall=%1$s 在墙里窒息而亡 +death.attack.indirectMagic=%1$s 被 %2$s 使用的魔法杀死了 +death.attack.indirectMagic.item=%1$s 被 %2$s 用 %3$s 杀死了 +death.attack.lava=%1$s 试图在岩浆里游泳 +death.attack.lava.player=%1$s 在逃离 %2$s 时试图在岩浆里游泳 +death.attack.magic=%1$s 被魔法杀死了 +death.attack.mob=%1$s 被 %2$s 杀死了 +death.attack.onFire=%1$s 被烧死了 +death.attack.onFire.player=%1$s 在试图与 %2$s 战斗时被烤的酥脆 +death.attack.outOfWorld=%1$s 掉出了这个世界 +death.attack.player=%1$s 被 %2$s 杀死了 +death.attack.player.item=%1$s 被 %2$s 用 %3$s 杀死了 +death.attack.starve=%1$s 饿死了 +death.attack.thorns=%1$s 在试图伤害 %2$s 时被杀 +death.attack.thrown=%1$s 被 %2$s 给砸死了 +death.attack.thrown.item=%1$s 被 %2$s 用 %3$s 给砸死了 +death.attack.wither=%1$s 凋谢了 +death.fell.accident.generic=%1$s 从高处摔了下来 +death.fell.accident.ladder=%1$s 从梯子上摔了下来 +death.fell.accident.vines=%1$s 从一些藤蔓上摔了下来 +death.fell.accident.water=%1$s 从水中掉了下来 +death.fell.assist=%1$s 因为 %2$s 注定要摔死 +death.fell.assist.item=%1$s 因为 %2$s 使用了 %3$s 注定要摔死 +death.fell.finish=%1$s 摔伤得太重并被 %2$s 完结了生命 +death.fell.finish.item=%1$s 摔伤得太重并被 %2$s 用 %3$s 完结了生命 +death.fell.killer=%1$s 注定要摔死 +deathScreen.deleteWorld=删除世界 +deathScreen.hardcoreInfo=你不可在极限模式中重生! +deathScreen.leaveServer=退出服务器 +deathScreen.quit.confirm=你确定要退出吗? +deathScreen.respawn=重生 +deathScreen.score=分数 +deathScreen.title=你死了! +deathScreen.title.hardcore=游戏结束! +deathScreen.titleScreen=标题画面 +demo.day.1=此试玩版只有5天游戏内时间, 尽力而为吧! +demo.day.2=第二天 +demo.day.3=第三天 +demo.day.4=第四天 +demo.day.5=这是你游戏内的最后一天! +demo.day.6=你已经度过了5天的试玩时间,按F2来截图留念 +demo.day.warning=您的试玩时间即将结束! +demo.demoExpired=试玩的时间结束了! +demo.help.buy=即刻购买! +demo.help.fullWrapped=这个试玩将会持续游戏内5天的时间 (现实时间大约为1小时40分钟) 。查看成就来获得提示!祝您玩得开心! +demo.help.inventory=按 %1$s 打开你的道具栏 +demo.help.jump=按 %1$s 跳跃 +demo.help.later=继续玩! +demo.help.movement=按 %1$s, %2$s, %3$s, %4$s 以及鼠标来移动 +demo.help.movementMouse=使用鼠标来查看四周 +demo.help.movementShort=通过按下%1$s,%2$s,%3$s,%4$s来移动 +demo.help.title=Minecraft试玩模式 +demo.remainingTime=剩余时间:%s +demo.reminder=试玩时间已经结束,请购买游戏来继续或开始一个新的世界! +disconnect.closed=连接已关闭 +disconnect.disconnected=被服务器中断连接 +disconnect.endOfStream=数据流终止 +disconnect.genericReason=%s +disconnect.kicked=您已被踢出游戏 +disconnect.loginFailed=登入失败 +disconnect.loginFailedInfo=登入失败:%s +disconnect.loginFailedInfo.invalidSession=无效的会话 (请尝试重启你的游戏) +disconnect.loginFailedInfo.serversUnavailable=身份验证目前正在停机维护。 +disconnect.lost=连接已丢失 +disconnect.overflow=缓冲区溢出 +disconnect.quitting=退出 +disconnect.spam=由于滥发信息而被踢出游戏 +disconnect.timeout=连接超时 +enchantment.arrowDamage=力量 +enchantment.arrowFire=火矢 +enchantment.arrowInfinite=无限 +enchantment.arrowKnockback=冲击 +enchantment.damage.all=锋利 +enchantment.damage.arthropods=节肢杀手 +enchantment.damage.undead=亡灵杀手 +enchantment.digging=效率 +enchantment.durability=耐久 +enchantment.fire=火焰附加 +enchantment.fishingSpeed=饵钓 +enchantment.knockback=击退 +enchantment.level.1=I +enchantment.level.10=X +enchantment.level.2=II +enchantment.level.3=III +enchantment.level.4=IV +enchantment.level.5=V +enchantment.level.6=VI +enchantment.level.7=VII +enchantment.level.8=VIII +enchantment.level.9=IX +enchantment.lootBonus=抢夺 +enchantment.lootBonusDigger=时运 +enchantment.lootBonusFishing=海之眷顾 +enchantment.oxygen=水下呼吸 +enchantment.protect.all=保护 +enchantment.protect.explosion=爆炸保护 +enchantment.protect.fall=摔落保护 +enchantment.protect.fire=火焰保护 +enchantment.protect.projectile=弹射物保护 +enchantment.thorns=荆棘 +enchantment.untouching=精准采集 +enchantment.waterWorker=水下速掘 +entity.Arrow.name=箭 +entity.Bat.name=蝙蝠 +entity.Blaze.name=烈焰人 +entity.Boat.name=船 +entity.Cat.name=猫 +entity.CaveSpider.name=洞穴蜘蛛 +entity.Chicken.name=鸡 +entity.Cow.name=牛 +entity.Creeper.name=爬行者 +entity.EnderDragon.name=末影龙 +entity.Enderman.name=末影人 +entity.EntityHorse.name=马 +entity.FallingSand.name=掉落的方块 +entity.Fireball.name=火球 +entity.Ghast.name=恶魂 +entity.Giant.name=巨人 +entity.Item.name=物品 +entity.LavaSlime.name=岩浆怪 +entity.Minecart.name=矿车 +entity.Mob.name=生物 +entity.Monster.name=怪物 +entity.MushroomCow.name=哞菇 +entity.Ozelot.name=豹猫 +entity.Painting.name=画 +entity.Pig.name=猪 +entity.PigZombie.name=僵尸猪人 +entity.PrimedTnt.name=TNT方块 +entity.Sheep.name=羊 +entity.Silverfish.name=蠹虫 +entity.Skeleton.name=骷髅 +entity.Slime.name=史莱姆 +entity.SmallFireball.name=小火球 +entity.SnowMan.name=雪傀儡 +entity.Snowball.name=雪球 +entity.Spider.name=蜘蛛 +entity.Squid.name=鱿鱼 +entity.Villager.name=村民 +entity.VillagerGolem.name=铁傀儡 +entity.Witch.name=女巫 +entity.WitherBoss.name=凋灵 +entity.Wolf.name=狼 +entity.XPOrb.name=经验球 +entity.Zombie.name=僵尸 +entity.donkey.name=驴 +entity.generic.name=未知 +entity.horse.name=马 +entity.mule.name=骡 +entity.skeletonhorse.name=骷髅马 +entity.zombiehorse.name=僵尸马 +gameMode.adventure=冒险模式 +gameMode.changed=您的游戏模式已更新 +gameMode.creative=创造模式 +gameMode.hardcore=极限模式! +gameMode.survival=生存模式 +generator.amplified=放大化 +generator.amplified.info=注意:只是为了好玩,需要强劲的电脑 +generator.default=默认 +generator.flat=超平坦 +generator.largeBiomes=巨型生物群系 +gui.achievements=成就 +gui.back=返回 +gui.cancel=取消 +gui.done=完成 +gui.down=向下 +gui.no=否 +gui.stats=统计信息 +gui.toMenu=回到标题画面 +gui.up=向上 +gui.yes=是 +inventory.binSlot=摧毁物品 +item.apple.name=苹果 +item.appleGold.name=金苹果 +item.arrow.name=箭 +item.bed.name=床 +item.beefCooked.name=牛排 +item.beefRaw.name=生牛肉 +item.blazePowder.name=烈焰粉 +item.blazeRod.name=烈焰棒 +item.boat.name=船 +item.bone.name=骨头 +item.book.name=书 +item.bootsChain.name=锁链靴子 +item.bootsCloth.name=皮革靴子 +item.bootsDiamond.name=钻石靴子 +item.bootsGold.name=金靴子 +item.bootsIron.name=铁靴子 +item.bow.name=弓 +item.bowl.name=碗 +item.bread.name=面包 +item.brewingStand.name=酿造台 +item.brick.name=红砖 +item.bucket.name=桶 +item.bucketLava.name=岩浆桶 +item.bucketWater.name=水桶 +item.cake.name=蛋糕 +item.carrotGolden.name=金萝卜 +item.carrotOnAStick.name=萝卜钓竿 +item.carrots.name=胡萝卜 +item.cauldron.name=炼药锅 +item.charcoal.name=木炭 +item.chestplateChain.name=锁链胸甲 +item.chestplateCloth.name=皮革外套 +item.chestplateDiamond.name=钻石胸甲 +item.chestplateGold.name=金胸甲 +item.chestplateIron.name=铁胸甲 +item.chickenCooked.name=熟鸡肉 +item.chickenRaw.name=生鸡肉 +item.clay.name=粘土 +item.clock.name=钟 +item.coal.name=煤炭 +item.comparator.name=红石比较器 +item.compass.name=指南针 +item.cookie.name=曲奇 +item.diamond.name=钻石 +item.diode.name=红石中继器 +item.doorIron.name=铁门 +item.doorWood.name=木门 +item.dyePowder.black.name=墨囊 +item.dyePowder.blue.name=青金石 +item.dyePowder.brown.name=可可豆 +item.dyePowder.cyan.name=青色染料 +item.dyePowder.gray.name=灰色染料 +item.dyePowder.green.name=仙人掌绿 +item.dyePowder.lightBlue.name=淡蓝色染料 +item.dyePowder.lime.name=黄绿色染料 +item.dyePowder.magenta.name=品红色染料 +item.dyePowder.orange.name=橙色染料 +item.dyePowder.pink.name=粉红色染料 +item.dyePowder.purple.name=紫色染料 +item.dyePowder.red.name=玫瑰红 +item.dyePowder.silver.name=淡灰色染料 +item.dyePowder.white.name=骨粉 +item.dyePowder.yellow.name=蒲公英黄 +item.dyed=已染色 +item.egg.name=鸡蛋 +item.emerald.name=绿宝石 +item.emptyMap.name=空地图 +item.emptyPotion.name=水瓶 +item.enchantedBook.name=附魔书 +item.enderPearl.name=末影珍珠 +item.expBottle.name=附魔之瓶 +item.eyeOfEnder.name=末影之眼 +item.feather.name=羽毛 +item.fermentedSpiderEye.name=发酵蛛眼 +item.fireball.name=火焰弹 +item.fireworks.flight=飞行时间: +item.fireworks.name=烟花火箭 +item.fireworksCharge.black=黑色 +item.fireworksCharge.blue=蓝色 +item.fireworksCharge.brown=棕色 +item.fireworksCharge.customColor=自定义 +item.fireworksCharge.cyan=青色 +item.fireworksCharge.fadeTo=淡化至 +item.fireworksCharge.flicker=闪烁 +item.fireworksCharge.gray=灰色 +item.fireworksCharge.green=绿色 +item.fireworksCharge.lightBlue=淡蓝色 +item.fireworksCharge.lime=黄绿色 +item.fireworksCharge.magenta=品红色 +item.fireworksCharge.name=烟火之星 +item.fireworksCharge.orange=橙色 +item.fireworksCharge.pink=粉红色 +item.fireworksCharge.purple=紫色 +item.fireworksCharge.red=红色 +item.fireworksCharge.silver=淡灰色 +item.fireworksCharge.trail=踪迹 +item.fireworksCharge.type=未知形状 +item.fireworksCharge.type.0=小型球状 +item.fireworksCharge.type.1=大型球状 +item.fireworksCharge.type.2=星形 +item.fireworksCharge.type.3=爬行者状 +item.fireworksCharge.type.4=爆裂状 +item.fireworksCharge.white=白色 +item.fireworksCharge.yellow=黄色 +item.fish.clownfish.raw.name=小丑鱼 +item.fish.cod.cooked.name=熟鱼 +item.fish.cod.raw.name=生鱼 +item.fish.pufferfish.raw.name=河豚 +item.fish.salmon.cooked.name=熟鲑鱼 +item.fish.salmon.raw.name=生鲑鱼 +item.fishingRod.name=钓鱼竿 +item.flint.name=燧石 +item.flintAndSteel.name=打火石 +item.flowerPot.name=花盆 +item.frame.name=物品展示框 +item.ghastTear.name=恶魂之泪 +item.glassBottle.name=玻璃瓶 +item.goldNugget.name=金粒 +item.hatchetDiamond.name=钻石斧 +item.hatchetGold.name=金斧 +item.hatchetIron.name=铁斧 +item.hatchetStone.name=石斧 +item.hatchetWood.name=木斧 +item.helmetChain.name=锁链头盔 +item.helmetCloth.name=皮革帽子 +item.helmetDiamond.name=钻石头盔 +item.helmetGold.name=金头盔 +item.helmetIron.name=铁头盔 +item.hoeDiamond.name=钻石锄 +item.hoeGold.name=金锄 +item.hoeIron.name=铁锄 +item.hoeStone.name=石锄 +item.hoeWood.name=木锄 +item.horsearmordiamond.name=钻石马铠 +item.horsearmorgold.name=金马铠 +item.horsearmormetal.name=铁马铠 +item.ingotGold.name=金锭 +item.ingotIron.name=铁锭 +item.leash.name=拴绳 +item.leather.name=皮革 +item.leaves.name=树叶 +item.leggingsChain.name=锁链护腿 +item.leggingsCloth.name=皮革裤子 +item.leggingsDiamond.name=钻石护腿 +item.leggingsGold.name=金护腿 +item.leggingsIron.name=铁护腿 +item.magmaCream.name=岩浆膏 +item.map.name=地图 +item.melon.name=西瓜 +item.milk.name=牛奶 +item.minecart.name=矿车 +item.minecartChest.name=运输矿车 +item.minecartCommandBlock.name=命令方块矿车 +item.minecartFurnace.name=动力矿车 +item.minecartHopper.name=漏斗矿车 +item.minecartTnt.name=TNT矿车 +item.monsterPlacer.name=生成 +item.mushroomStew.name=蘑菇煲 +item.nameTag.name=命名牌 +item.netherStalkSeeds.name=地狱疣 +item.netherStar.name=下界之星 +item.netherbrick.name=地狱砖 +item.netherquartz.name=下界石英 +item.painting.name=画 +item.paper.name=纸 +item.pickaxeDiamond.name=钻石镐 +item.pickaxeGold.name=金镐 +item.pickaxeIron.name=铁镐 +item.pickaxeStone.name=石镐 +item.pickaxeWood.name=木镐 +item.porkchopCooked.name=熟猪排 +item.porkchopRaw.name=生猪排 +item.potato.name=马铃薯 +item.potatoBaked.name=烤马铃薯 +item.potatoPoisonous.name=毒马铃薯 +item.potion.name=药水 +item.pumpkinPie.name=南瓜派 +item.record.11.desc=C418 - 11 +item.record.13.desc=C418 - 13 +item.record.blocks.desc=C418 - blocks +item.record.cat.desc=C418 - cat +item.record.chirp.desc=C418 - chirp +item.record.far.desc=C418 - far +item.record.mall.desc=C418 - mall +item.record.mellohi.desc=C418 - mellohi +item.record.name=音乐唱片 +item.record.stal.desc=C418 - stal +item.record.strad.desc=C418 - strad +item.record.wait.desc=C418 - wait +item.record.ward.desc=C418 - ward +item.redstone.name=红石 +item.reeds.name=甘蔗 +item.rottenFlesh.name=腐肉 +item.ruby.name=红宝石 +item.saddle.name=鞍 +item.seeds.name=小麦种子 +item.seeds_melon.name=西瓜种子 +item.seeds_pumpkin.name=南瓜种子 +item.shears.name=剪刀 +item.shovelDiamond.name=钻石锹 +item.shovelGold.name=金锹 +item.shovelIron.name=铁锹 +item.shovelStone.name=石锹 +item.shovelWood.name=木锹 +item.sign.name=告示牌 +item.skull.char.name=头 +item.skull.creeper.name=爬行者的头 +item.skull.player.name=%s的头 +item.skull.skeleton.name=骷髅头颅 +item.skull.wither.name=凋灵骷髅头颅 +item.skull.zombie.name=僵尸的头 +item.slimeball.name=粘液球 +item.snowball.name=雪球 +item.speckledMelon.name=闪烁的西瓜 +item.spiderEye.name=蜘蛛眼 +item.stick.name=木棍 +item.string.name=线 +item.sugar.name=糖 +item.sulphur.name=火药 +item.swordDiamond.name=钻石剑 +item.swordGold.name=金剑 +item.swordIron.name=铁剑 +item.swordStone.name=石剑 +item.swordWood.name=木剑 +item.unbreakable=无法破坏 +item.wheat.name=小麦 +item.writingBook.name=书与笔 +item.writtenBook.name=成书 +item.yellowDust.name=萤石粉 +itemGroup.brewing=酿造 +itemGroup.buildingBlocks=建筑方块 +itemGroup.combat=战斗用品 +itemGroup.decorations=装饰性方块 +itemGroup.food=食物 +itemGroup.inventory=生存模式物品栏 +itemGroup.materials=材料 +itemGroup.misc=杂项 +itemGroup.redstone=红石 +itemGroup.search=搜索物品 +itemGroup.tools=工具 +itemGroup.transportation=交通运输 +key.attack=攻击/摧毁 +key.back=向后移动 +key.categories.gameplay=游戏内容 +key.categories.inventory=道具栏 +key.categories.misc=杂项 +key.categories.movement=移动 +key.categories.multiplayer=多人游戏 +key.categories.stream=直播中 +key.categories.ui=游戏界面 +key.chat=打开聊天栏 +key.command=输入指令 +key.drop=丢弃物品 +key.forward=向前移动 +key.fullscreen=全屏显示切换 +key.hotbar.1=快捷栏1 +key.hotbar.2=快捷栏2 +key.hotbar.3=快捷栏3 +key.hotbar.4=快捷栏4 +key.hotbar.5=快捷栏5 +key.hotbar.6=快捷栏6 +key.hotbar.7=快捷栏7 +key.hotbar.8=快捷栏8 +key.hotbar.9=快捷栏9 +key.inventory=道具栏 +key.jump=跳跃 +key.left=向左移动 +key.mouseButton=鼠标按键%1$s +key.pickItem=选取方块 +key.playerlist=玩家列表 +key.right=向右移动 +key.screenshot=截图 +key.smoothCamera=切换电影视角 +key.sneak=潜行 +key.sprint=疾跑 +key.streamCommercial=显示广告 +key.streamPauseUnpause=暂停/取消暂停直播 +key.streamStartStop=开始/停止直播 +key.streamToggleMic=点击来开启麦克风/静音 +key.togglePerspective=切换视角 +key.use=使用物品/放置方块 +lanServer.otherPlayers=对其他玩家的设置 +lanServer.scanning=正在你的本地网络中寻找游戏 +lanServer.start=创造一个局域网世界 +lanServer.title=局域网世界 +language.code=zh_CN +language.name=简体中文 +language.region=中国 +mcoServer.title=Minecraft Online世界 +menu.convertingLevel=转换世界中 +menu.disconnect=断开连接 +menu.game=游戏菜单 +menu.generatingLevel=生成世界中 +menu.generatingTerrain=生成地形中 +menu.loadingLevel=加载世界中 +menu.multiplayer=多人游戏 +menu.online=Minecraft领域 +menu.options=选项… +menu.playdemo=开始试玩世界 +menu.quit=退出游戏 +menu.resetdemo=重置试玩世界 +menu.respawning=重生中 +menu.returnToGame=回到游戏 +menu.returnToMenu=保存并退回到标题画面 +menu.shareToLan=对局域网开放 +menu.simulating=模拟世界中 +menu.singleplayer=单人游戏 +menu.switchingLevel=转接世界中 +mount.onboard=按下 %1$s 来脱离 +multiplayer.connect=连接 +multiplayer.downloadingStats=正在下载统计和成就中… +multiplayer.downloadingTerrain=下载地形中 +multiplayer.info1=Minecraft 多人模式还未完成,但我们 +multiplayer.info2=正在进行一些早期测试。 +multiplayer.ipinfo=输入服务器的IP来连接: +multiplayer.player.joined=%s 加入了游戏 +multiplayer.player.joined.renamed=%s (之前被称为 %s)加入了游戏 +multiplayer.player.left=%s 退出了游戏 +multiplayer.stopSleeping=起床 +multiplayer.texturePrompt.line1=这个服务器推荐使用自定义的资源包。 +multiplayer.texturePrompt.line2=你想要自动下载和安装它吗? +multiplayer.title=多人游戏 +options.advancedButton=高级视频设置… +options.advancedOpengl=高级 OpenGL +options.advancedOpenglDesc0=已启用遮挡查询。在拥有AMD和Intel硬盘的 +options.advancedOpenglDesc1=计算机上也许会降低性能。 +options.advancedVideoTitle=高级视频设置 +options.anaglyph=3D效果 +options.anisotropicFiltering=各向异性过滤 +options.ao=平滑光照 +options.ao.max=最大 +options.ao.min=最小 +options.ao.off=关 +options.aoDesc0=启用对方块的人造环境光遮蔽 +options.chat.color=颜色 +options.chat.height.focused=聚焦高度 +options.chat.height.unfocused=淡化高度 +options.chat.links=网页链接 +options.chat.links.prompt=链接提示 +options.chat.opacity=透明度 +options.chat.scale=比例 +options.chat.title=聊天设置... +options.chat.visibility=聊天 +options.chat.visibility.full=显示 +options.chat.visibility.hidden=隐藏 +options.chat.visibility.system=仅限指令 +options.chat.width=宽度 +options.controls=控制… +options.difficulty=难度 +options.difficulty.easy=简单 +options.difficulty.hard=困难 +options.difficulty.hardcore=极限 +options.difficulty.normal=普通 +options.difficulty.peaceful=和平 +options.farWarning1=推荐安装64位的Java以使用能见度 +options.farWarning2=选项“高” ( 你目前使用的是32位的JAVA ) +options.fboEnable=启用帧缓冲器 +options.fboEnableDesc0=启用帧缓冲对象的使用。 +options.fboEnableDesc1=这对一些Minecraft功能是必要的。 +options.forceUnicodeFont=强制Unicode字体 +options.fov=视野 +options.fov.max=广角 +options.fov.min=正常 +options.framerateLimit=最大帧率 +options.framerateLimit.max=无限制 +options.framerateLimitDesc0=选择最大帧率: +options.framerateLimitDesc1=35 fps、120 fps、或 200+ fps。 +options.fullscreen=全屏 +options.gamma=亮度 +options.gamma.max=明亮 +options.gamma.min=昏暗 +options.graphics=图像品质 +options.graphics.fancy=高品质 +options.graphics.fast=流畅 +options.graphicsDesc0='高品质':启用额外的透明度 +options.graphicsDesc1='流畅':建议有较低端硬件的计算机使用 +options.guiScale=界面尺寸 +options.guiScale.auto=自动 +options.guiScale.large=大 +options.guiScale.normal=中 +options.guiScale.small=小 +options.hidden=隐藏 +options.invertMouse=鼠标反转 +options.language=语言… +options.languageWarning=语言翻译不一定100%%准确 +options.mipmapLevels=Mipmap 级别 +options.multiplayer.title=多人游戏设置… +options.music=音乐 +options.off=关 +options.on=开 +options.particles=颗粒效果 +options.particles.all=全部 +options.particles.decreased=少量 +options.particles.minimal=最少 +options.particlesDesc0=选择颗粒的总量。 +options.particlesDesc1=对于有较低端硬件的计算机来说,越少越好。 +options.performanceButton=视频性能设置… +options.performanceVideoTitle=视频性能设置 +options.postButton=后期处理设置… +options.postProcessEnable=启用后期处理 +options.postProcessEnableDesc0=启用后期处理。禁用会导致 +options.postProcessEnableDesc1=画质的大量流失。 +options.postVideoTitle=后期处理设置 +options.qualityButton=视频质量设置… +options.qualityVideoTitle=视频质量设置 +options.renderClouds=云 +options.renderCloudsDesc0=启用云的渲染 +options.renderDistance=能见度 +options.renderDistance.far=高 +options.renderDistance.normal=中等 +options.renderDistance.short=低 +options.renderDistance.tiny=很低 +options.renderDistanceDesc0=最大能见度。较低的数值在 +options.renderDistanceDesc1=有较低端硬件的计算机上运行会更流畅。 +options.resourcepack=资源包… +options.saturation=饱和 +options.sensitivity=鼠标灵敏度 +options.sensitivity.max=超高速!!! +options.sensitivity.min=*哈欠* +options.showCape=显示披风 +options.snooper=允许匿名信息反馈 +options.snooper.desc=我们意在通过收集你的设备的相关信息,以帮助我们改进Minecraft。所有的数据都是匿名的,并已经全部列在下方。我们承诺不会用这些数据进行非法行为。但如果你想要取消匿名信息反馈,您随时都可以将其关闭! +options.snooper.title=机器规格集 +options.snooper.view=匿名信息反馈设置… +options.sound=音效 +options.sounds=音乐和声音… +options.sounds.title=音乐和声音选项 +options.stream=直播设置… +options.stream.bytesPerPixel=质量 +options.stream.changes=你可能需要重启直播才能使更改生效。 +options.stream.chat.enabled=启用 +options.stream.chat.enabled.always=总是 +options.stream.chat.enabled.never=从不 +options.stream.chat.enabled.streaming=直播时 +options.stream.chat.title=Twitch聊天设置 +options.stream.chat.userFilter=用户过滤器 +options.stream.chat.userFilter.all=所有观众 +options.stream.chat.userFilter.mods=管理员 +options.stream.chat.userFilter.subs=订阅者 +options.stream.compression=压缩 +options.stream.compression.high=高 +options.stream.compression.low=低 +options.stream.compression.medium=中 +options.stream.estimation=预计分辨率:%sx%s +options.stream.fps=帧率 +options.stream.ingest.reset=重置偏好 +options.stream.ingest.title=Twitch直播服务器 +options.stream.ingestSelection=直播服务器列表 +options.stream.kbps=带宽 +options.stream.micToggleBehavior=按下来 +options.stream.micVolumne=麦克风音量 +options.stream.mic_toggle.mute=静音 +options.stream.mic_toggle.talk=开启 +options.stream.sendMetadata=发送元数据 +options.stream.systemVolume=系统音量 +options.stream.title=Twitch直播设置 +options.title=选项 +options.touchscreen=触屏模式 +options.video=视频设置… +options.videoTitle=视频设置 +options.viewBobbing=视角摇晃 +options.viewBobbingDesc0=启用移动时的视觉摇晃 +options.visible=显示 +options.vsync=使用垂直同步 +potion.absorption=伤害吸收 +potion.absorption.postfix=伤害吸收药水 +potion.blindness=失明 +potion.blindness.postfix=失明药水 +potion.confusion=反胃 +potion.confusion.postfix=反胃药水 +potion.damageBoost=力量 +potion.damageBoost.postfix=力量药水 +potion.digSlowDown=挖掘疲劳 +potion.digSlowDown.postfix=缓掘药水 +potion.digSpeed=急迫 +potion.digSpeed.postfix=速掘药水 +potion.effects.whenDrank=当饮用后: +potion.empty=无效果 +potion.fireResistance=防火 +potion.fireResistance.postfix=抗火药水 +potion.harm=瞬间伤害 +potion.harm.postfix=伤害药水 +potion.heal=瞬间治疗 +potion.heal.postfix=治疗药水 +potion.healthBoost=生命提升 +potion.healthBoost.postfix=生命提升药水 +potion.hunger=饥饿 +potion.hunger.postfix=饥饿药水 +potion.invisibility=隐身 +potion.invisibility.postfix=隐身药水 +potion.jump=跳跃提升 +potion.jump.postfix=跳跃药水 +potion.moveSlowdown=缓慢 +potion.moveSlowdown.postfix=迟缓药水 +potion.moveSpeed=速度 +potion.moveSpeed.postfix=迅捷药水 +potion.nightVision=夜视 +potion.nightVision.postfix=夜视药水 +potion.poison=中毒 +potion.poison.postfix=剧毒药水 +potion.potency.1=II +potion.potency.2=III +potion.potency.3=IV +potion.prefix.acrid=辛辣的 +potion.prefix.artless=朴实的 +potion.prefix.awkward=粗制的 +potion.prefix.bland=温和的 +potion.prefix.bulky=笨重的 +potion.prefix.bungling=笨拙的 +potion.prefix.buttered=脂状的 +potion.prefix.charming=迷人的 +potion.prefix.clear=清澈的 +potion.prefix.cordial=诚挚的 +potion.prefix.dashing=活泼的 +potion.prefix.debonair=快活的 +potion.prefix.diffuse=弥漫的 +potion.prefix.elegant=优雅的 +potion.prefix.fancy=花俏的 +potion.prefix.flat=单调的 +potion.prefix.foul=污秽的 +potion.prefix.grenade=喷溅型 +potion.prefix.gross=恶心的 +potion.prefix.harsh=刺鼻的 +potion.prefix.milky=浑浊的 +potion.prefix.mundane=平凡的 +potion.prefix.odorless=无味的 +potion.prefix.potent=给力的 +potion.prefix.rank=恶臭的 +potion.prefix.refined=精致的 +potion.prefix.smooth=平滑的 +potion.prefix.sparkling=闪亮的 +potion.prefix.stinky=异味的 +potion.prefix.suave=娴雅的 +potion.prefix.thick=浓稠的 +potion.prefix.thin=稀薄的 +potion.prefix.uninteresting=无趣的 +potion.regeneration=生命恢复 +potion.regeneration.postfix=再生药水 +potion.resistance=抗性提升 +potion.resistance.postfix=抵抗药水 +potion.saturation=饱和 +potion.saturation.postfix=饱和药水 +potion.waterBreathing=水下呼吸 +potion.waterBreathing.postfix=水肺药水 +potion.weakness=虚弱 +potion.weakness.postfix=虚弱药水 +potion.wither=凋零 +potion.wither.postfix=衰变药水 +record.nowPlaying=正在播放:%s +resourcePack.available.title=可用的资源包 +resourcePack.folderInfo=(请把资源包文件放在这里) +resourcePack.openFolder=打开资源包文件夹 +resourcePack.selected.title=已选的资源包 +resourcePack.title=选择资源包 +screenshot.failure=无法保存截图:%s +screenshot.success=已将截图保存为%s +selectServer.add=添加服务器 +selectServer.defaultName=Minecraft 服务器 +selectServer.delete=删除 +selectServer.deleteButton=删除 +selectServer.deleteQuestion=确定要删除此服务器吗? +selectServer.deleteWarning=将会永久失去!( 无法恢复!) +selectServer.direct=直接连接 +selectServer.edit=编辑 +selectServer.empty=空 +selectServer.hiddenAddress= (隐藏) +selectServer.refresh=刷新 +selectServer.select=加入服务器 +selectServer.title=选择服务器 +selectWorld.allowCommands=允许作弊: +selectWorld.allowCommands.info=像/gamemode、/xp的指令 +selectWorld.bonusItems=奖励箱: +selectWorld.cheats=作弊 +selectWorld.conversion=必须进行转换! +selectWorld.create=创建新的世界 +selectWorld.createDemo=进入新的试玩世界 +selectWorld.customizeType=个性化 +selectWorld.delete=删除 +selectWorld.deleteButton=删除 +selectWorld.deleteQuestion=确定要删除这个世界吗? +selectWorld.deleteWarning=将会永久失去!( 真的很久!) +selectWorld.empty=空 +selectWorld.enterName=世界名称 +selectWorld.enterSeed=世界生成器的种子 +selectWorld.gameMode=游戏模式 +selectWorld.gameMode.adventure=冒险 +selectWorld.gameMode.adventure.line1=与生存模式相同,但是方块 +selectWorld.gameMode.adventure.line2=不能被添加或者移除 +selectWorld.gameMode.creative=创造 +selectWorld.gameMode.creative.line1=无限的资源、自由地飞翔 +selectWorld.gameMode.creative.line2=并且能够瞬间破坏方块 +selectWorld.gameMode.hardcore=极限 +selectWorld.gameMode.hardcore.line1=难度锁定在困难的生存模式 +selectWorld.gameMode.hardcore.line2=且只有一条生命 +selectWorld.gameMode.survival=生存 +selectWorld.gameMode.survival.line1=探索世界、收集资源、合成道具、 +selectWorld.gameMode.survival.line2=提高等级、补充体力和生命值 +selectWorld.hardcoreMode=极限: +selectWorld.hardcoreMode.info=死亡后世界将被删除 +selectWorld.mapFeatures=生成建筑: +selectWorld.mapFeatures.info=村庄、地牢等等 +selectWorld.mapType=世界类型: +selectWorld.mapType.normal=普通 +selectWorld.moreWorldOptions=更多世界的选项… +selectWorld.newWorld=新的世界 +selectWorld.newWorld.copyOf=%s的拷贝 +selectWorld.recreate=重建 +selectWorld.rename=重命名 +selectWorld.renameButton=重命名 +selectWorld.renameTitle=重命名世界 +selectWorld.resultFolder=将会保存于: +selectWorld.seedInfo=留空以生成随机种子 +selectWorld.select=进入选中的世界 +selectWorld.title=选择世界 +selectWorld.world=世界 +sign.edit=修改告示牌信息 +soundCategory.ambient=环境 +soundCategory.block=方块 +soundCategory.hostile=敌对生物 +soundCategory.master=主音量 +soundCategory.music=音乐 +soundCategory.neutral=友好生物 +soundCategory.player=玩家 +soundCategory.record=唱片机/音符盒 +soundCategory.weather=天气 +stat.animalsBred=繁殖动物次数 +stat.blocksButton=方块 +stat.boatOneCm=坐船移动距离 +stat.breakItem=消耗了%1$s个工具 +stat.climbOneCm=已攀爬距离 +stat.craftItem=合成了 %1$s 个物品 +stat.crafted=合成次数 +stat.createWorld=已创建的世界数 +stat.damageDealt=造成伤害 +stat.damageTaken=受到伤害 +stat.deaths=死亡次数 +stat.depleted=工具消耗数 +stat.diveOneCm=水下移动距离 +stat.drop=物品掉落 +stat.entityKilledBy=%s 杀死了你 %s 次 +stat.entityKilledBy.none=你从来没有被 %s 杀死过 +stat.entityKills=你杀死了 %s只 %s +stat.entityKills.none=你从来没有杀死过 %s +stat.fallOneCm=摔落高度 +stat.fishCaught=捕鱼数 +stat.flyOneCm=飞行距离 +stat.generalButton=通用 +stat.horseOneCm=骑马移动距离 +stat.itemsButton=物品 +stat.joinMultiplayer=加入多人游戏次数 +stat.jump=跳跃次数 +stat.junkFished=钓到垃圾次数 +stat.leaveGame=游戏退出次数 +stat.loadWorld=读取存档次数 +stat.mineBlock=挖掘了 %1$s 个方块 +stat.minecartOneCm=坐矿车移动距离 +stat.mined=开采次数 +stat.mobKills=生物击杀数 +stat.mobsButton=生物 +stat.pigOneCm=骑猪移动距离 +stat.playOneMinute=游戏时间(分钟) +stat.playerKills=玩家击杀数 +stat.startGame=游戏次数 +stat.swimOneCm=游泳距离 +stat.treasureFished=钓到宝藏次数 +stat.useItem=使用了 %1$s 个物品 +stat.used=使用次数 +stat.walkOneCm=行走距离 +stats.tooltip.type.achievement=成就 +stats.tooltip.type.statistic=统计 +stream.confirm_start=你确定你要开始直播吗? +stream.unavailable.account_not_bound=在通过Twitch直播Minecraft之前,你需要在mojang.com上绑定你的Twitch账户。是否马上开启? +stream.unavailable.account_not_bound.okay=连接账户 +stream.unavailable.account_not_migrated=在您通过Twitch直播Minecraft之前,您需要将您的Minecraft账号转移至一个Mojang账号。是否马上开始? +stream.unavailable.account_not_migrated.okay=迁移帐户 +stream.unavailable.failed_auth=Twitch验证失败。请到mojang.com重新绑定您的Twitch账户。 +stream.unavailable.failed_auth.okay=重新绑定账户 +stream.unavailable.failed_auth_error=无法连接到Twitch验证。请稍后再试。 +stream.unavailable.initialization_failure=无法初始化Twitch SDK。 +stream.unavailable.initialization_failure.extra=(原因:%s) +stream.unavailable.library_arch_mismatch=您用于启动Minecraft的自定义Java版本与运行启动器的Java版本结构不同。请确保两者相同,即都是32位或都是64位。 +stream.unavailable.library_failure=无法载入集成的Twitch直播服务的库文件。 +stream.unavailable.no_fbo=您的显卡至少需要支持OpenGL 3.0或通过扩展支持帧缓冲对象才能使用集成的Twitch直播功能。 +stream.unavailable.no_fbo.arb=通过ARB的帧缓冲器对象支持为:%s +stream.unavailable.no_fbo.blend=通过EXT的单独混合支持是:%s +stream.unavailable.no_fbo.ext=支持通过的EXT显卡芯片的帧缓冲对象型号为:%s +stream.unavailable.no_fbo.version=您正在使用:%s +stream.unavailable.not_supported.mac=很不幸,集成的Twitch直播功能需要更新的Mac OS X。你需要10.7 (Mac OS X Mountain Lion) 或更新的版本。您想访问apple.com来了解关于升级的信息吗? +stream.unavailable.not_supported.mac.okay=升级 +stream.unavailable.not_supported.other=很不幸,集成的Twitch直播服务需要Windows (Vista或更新) 或Mac OS X (10.7/Lion或更新) +stream.unavailable.not_supported.windows=很不幸,集成的Twitch直播功能需要更新的Windows版本。你需要Windows Vista或更新的版本。 +stream.unavailable.report_to_mojang=向Mojang报告 +stream.unavailable.soundflower.chat=你需要Soundflower才能在Mac上直播。%s +stream.unavailable.soundflower.chat.link=请点击这里安装。 +stream.unavailable.title=Twitch直播不可用 +stream.unavailable.unknown=很不幸,由于未知原因,您现在无法在Twitch上同步直播 :'( +stream.unavailable.unknown.chat=无法开始直播:%s +stream.user.mode.administrator=Twitch管理员 +stream.user.mode.banned=被封禁 +stream.user.mode.banned.other=在 %s 的频道上被封禁 +stream.user.mode.banned.self=在你的频道上被封禁 +stream.user.mode.broadcaster=实况者 +stream.user.mode.broadcaster.other=实况者 +stream.user.mode.broadcaster.self=实况者 (你!) +stream.user.mode.moderator=管理员 +stream.user.mode.moderator.other=%s 的频道上的管理员 +stream.user.mode.moderator.self=您的频道上的管理员 +stream.user.mode.staff=Twitch工作人员 +stream.user.subscription.subscriber=订阅者 +stream.user.subscription.subscriber.other=%s 的频道的订阅者 +stream.user.subscription.subscriber.self=你的频道的订阅者 +stream.user.subscription.turbo=Twitch Turbo +stream.userinfo.ban=封禁 +stream.userinfo.chatTooltip=点击以管理用户 +stream.userinfo.mod=升级至管理员 +stream.userinfo.timeout=超时 +stream.userinfo.unban=解禁 +stream.userinfo.unmod=取消管理员资格 +tile.activatorRail.name=激活铁轨 +tile.anvil.intact.name=铁砧 +tile.anvil.name=铁砧 +tile.anvil.slightlyDamaged.name=轻微损坏的铁砧 +tile.anvil.veryDamaged.name=严重损坏的铁砧 +tile.beacon.name=信标 +tile.beacon.primary=主效果 +tile.beacon.secondary=辅助效果 +tile.bed.name=床 +tile.bed.noSleep=你只能在晚上睡觉 +tile.bed.notSafe=你现在不能休息,周围有怪物在游荡 +tile.bed.notValid=你的床已遗失或被阻挡 +tile.bed.occupied=这张床已被占用 +tile.bedrock.name=基岩 +tile.blockCoal.name=煤炭块 +tile.blockDiamond.name=钻石块 +tile.blockEmerald.name=绿宝石块 +tile.blockGold.name=金块 +tile.blockIron.name=铁块 +tile.blockLapis.name=青金石块 +tile.blockRedstone.name=红石块 +tile.bookshelf.name=书架 +tile.brick.name=砖块 +tile.button.name=按钮 +tile.cactus.name=仙人掌 +tile.cake.name=蛋糕 +tile.carrots.name=胡萝卜 +tile.cauldron.name=炼药锅 +tile.chest.name=箱子 +tile.chestTrap.name=陷阱箱 +tile.clay.name=粘土块 +tile.clayHardened.name=硬化粘土 +tile.clayHardenedStained.black.name=黑色染色粘土 +tile.clayHardenedStained.blue.name=蓝色染色粘土 +tile.clayHardenedStained.brown.name=棕色染色粘土 +tile.clayHardenedStained.cyan.name=青色染色粘土 +tile.clayHardenedStained.gray.name=灰色染色粘土 +tile.clayHardenedStained.green.name=绿色染色粘土 +tile.clayHardenedStained.lightBlue.name=淡蓝色染色粘土 +tile.clayHardenedStained.lime.name=黄绿色染色粘土 +tile.clayHardenedStained.magenta.name=品红色染色粘土 +tile.clayHardenedStained.orange.name=橙色染色粘土 +tile.clayHardenedStained.pink.name=粉色染色粘土 +tile.clayHardenedStained.purple.name=紫色染色粘土 +tile.clayHardenedStained.red.name=红色染色粘土 +tile.clayHardenedStained.silver.name=淡灰色染色粘土 +tile.clayHardenedStained.white.name=白色染色粘土 +tile.clayHardenedStained.yellow.name=黄色染色粘土 +tile.cloth.black.name=黑色羊毛 +tile.cloth.blue.name=蓝色羊毛 +tile.cloth.brown.name=棕色羊毛 +tile.cloth.cyan.name=青色羊毛 +tile.cloth.gray.name=灰色羊毛 +tile.cloth.green.name=绿色羊毛 +tile.cloth.lightBlue.name=淡蓝色羊毛 +tile.cloth.lime.name=黄绿色羊毛 +tile.cloth.magenta.name=品红色羊毛 +tile.cloth.name=羊毛 +tile.cloth.orange.name=橙色羊毛 +tile.cloth.pink.name=粉红色羊毛 +tile.cloth.purple.name=紫色羊毛 +tile.cloth.red.name=红色羊毛 +tile.cloth.silver.name=淡灰色羊毛 +tile.cloth.white.name=羊毛 +tile.cloth.yellow.name=黄色羊毛 +tile.cobbleWall.mossy.name=苔石墙 +tile.cobbleWall.normal.name=圆石墙 +tile.cocoa.name=可可果 +tile.commandBlock.name=命令方块 +tile.crops.name=农作物 +tile.daylightDetector.name=阳光传感器 +tile.deadbush.name=枯死的灌木 +tile.detectorRail.name=探测铁轨 +tile.dirt.default.name=泥土 +tile.dirt.podzol.name=灰化土 +tile.dispenser.name=发射器 +tile.doorIron.name=铁门 +tile.doorWood.name=木门 +tile.doublePlant.fern.name=大型蕨 +tile.doublePlant.grass.name=高草丛 +tile.doublePlant.paeonia.name=牡丹 +tile.doublePlant.rose.name=玫瑰丛 +tile.doublePlant.sunflower.name=向日葵 +tile.doublePlant.syringa.name=丁香 +tile.dragonEgg.name=龙蛋 +tile.dropper.name=投掷器 +tile.enchantmentTable.name=附魔台 +tile.endPortalFrame.name=末地传送门 +tile.enderChest.name=末影箱 +tile.farmland.name=耕地 +tile.fence.name=栅栏 +tile.fenceGate.name=栅栏门 +tile.fenceIron.name=铁栏杆 +tile.fire.name=火 +tile.flower1.dandelion.name=蒲公英 +tile.flower2.allium.name=绒球葱 +tile.flower2.blueOrchid.name=兰花 +tile.flower2.houstonia.name=茜草花 +tile.flower2.oxeyeDaisy.name=滨菊 +tile.flower2.poppy.name=罂粟 +tile.flower2.tulipOrange.name=橙色郁金香 +tile.flower2.tulipPink.name=粉红色郁金香 +tile.flower2.tulipRed.name=红色郁金香 +tile.flower2.tulipWhite.name=白色郁金香 +tile.furnace.name=熔炉 +tile.glass.name=玻璃 +tile.goldenRail.name=动力铁轨 +tile.grass.name=草方块 +tile.gravel.name=沙砾 +tile.hayBlock.name=干草块 +tile.hellrock.name=地狱岩 +tile.hellsand.name=灵魂沙 +tile.hopper.name=漏斗 +tile.ice.name=冰 +tile.icePacked.name=浮冰 +tile.jukebox.name=唱片机 +tile.ladder.name=梯子 +tile.lava.name=岩浆 +tile.leaves.acacia.name=金合欢树叶 +tile.leaves.big_oak.name=深色橡树树叶 +tile.leaves.birch.name=白桦树叶 +tile.leaves.jungle.name=丛林树叶 +tile.leaves.name=树叶 +tile.leaves.oak.name=橡树树叶 +tile.leaves.spruce.name=云杉树叶 +tile.lever.name=拉杆 +tile.lightgem.name=萤石 +tile.litpumpkin.name=南瓜灯 +tile.lockedchest.name=上锁的箱子 +tile.log.acacia.name=金合欢木 +tile.log.big_oak.name=深色橡木 +tile.log.birch.name=白桦木 +tile.log.jungle.name=丛林木 +tile.log.name=木头 +tile.log.oak.name=橡木 +tile.log.spruce.name=云杉木 +tile.melon.name=西瓜 +tile.mobSpawner.name=刷怪箱 +tile.monsterStoneEgg.brick.name=石砖怪物蛋 +tile.monsterStoneEgg.chiseledbrick.name=錾制石砖怪物蛋 +tile.monsterStoneEgg.cobble.name=圆石怪物蛋 +tile.monsterStoneEgg.crackedbrick.name=裂石砖怪物蛋 +tile.monsterStoneEgg.mossybrick.name=苔石砖怪物蛋 +tile.monsterStoneEgg.stone.name=石头怪物蛋 +tile.mushroom.name=蘑菇 +tile.musicBlock.name=音符盒 +tile.mycel.name=菌丝 +tile.netherBrick.name=地狱砖块 +tile.netherFence.name=地狱砖栅栏 +tile.netherStalk.name=地狱疣 +tile.netherquartz.name=下界石英矿石 +tile.notGate.name=红石火把 +tile.obsidian.name=黑曜石 +tile.oreCoal.name=煤矿石 +tile.oreDiamond.name=钻石矿石 +tile.oreEmerald.name=绿宝石矿石 +tile.oreGold.name=金矿石 +tile.oreIron.name=铁矿石 +tile.oreLapis.name=青金石矿石 +tile.oreRedstone.name=红石矿石 +tile.oreRuby.name=红宝石矿石 +tile.pistonBase.name=活塞 +tile.pistonStickyBase.name=粘性活塞 +tile.portal.name=传送门 +tile.potatoes.name=马铃薯 +tile.pressurePlate.name=压力板 +tile.pumpkin.name=南瓜 +tile.quartzBlock.chiseled.name=錾制石英块 +tile.quartzBlock.default.name=石英块 +tile.quartzBlock.lines.name=竖纹石英块 +tile.rail.name=铁轨 +tile.redstoneDust.name=红石粉 +tile.redstoneLight.name=红石灯 +tile.reeds.name=甘蔗 +tile.sand.default.name=沙子 +tile.sand.red.name=红沙 +tile.sandStone.chiseled.name=錾制沙石 +tile.sandStone.default.name=沙石 +tile.sandStone.name=沙石 +tile.sandStone.smooth.name=平滑沙石 +tile.sapling.acacia.name=金合欢树苗 +tile.sapling.birch.name=白桦树苗 +tile.sapling.jungle.name=丛林树苗 +tile.sapling.oak.name=橡树树苗 +tile.sapling.roofed_oak.name=深色橡树树苗 +tile.sapling.spruce.name=云杉树苗 +tile.sign.name=告示牌 +tile.snow.name=雪 +tile.sponge.name=海绵 +tile.stainedGlass.black.name=黑色染色玻璃 +tile.stainedGlass.blue.name=蓝色染色玻璃 +tile.stainedGlass.brown.name=棕色染色玻璃 +tile.stainedGlass.cyan.name=青色染色玻璃 +tile.stainedGlass.gray.name=灰色染色玻璃 +tile.stainedGlass.green.name=绿色染色玻璃 +tile.stainedGlass.lightBlue.name=淡蓝色染色玻璃 +tile.stainedGlass.lime.name=黄绿色染色玻璃 +tile.stainedGlass.magenta.name=品红色染色玻璃 +tile.stainedGlass.name=染色玻璃 +tile.stainedGlass.orange.name=橙色染色玻璃 +tile.stainedGlass.pink.name=粉红色染色玻璃 +tile.stainedGlass.purple.name=紫色染色玻璃 +tile.stainedGlass.red.name=红色染色玻璃 +tile.stainedGlass.silver.name=淡灰色染色玻璃 +tile.stainedGlass.white.name=白色染色玻璃 +tile.stainedGlass.yellow.name=黄色染色玻璃 +tile.stairsBrick.name=砖楼梯 +tile.stairsNetherBrick.name=地狱砖楼梯 +tile.stairsQuartz.name=石英楼梯 +tile.stairsSandStone.name=沙石楼梯 +tile.stairsStone.name=石楼梯 +tile.stairsStoneBrickSmooth.name=石砖楼梯 +tile.stairsWood.name=橡木楼梯 +tile.stairsWoodAcacia.name=金合欢木楼梯 +tile.stairsWoodBirch.name=桦木楼梯 +tile.stairsWoodDarkOak.name=深色橡木楼梯 +tile.stairsWoodJungle.name=丛林木楼梯 +tile.stairsWoodSpruce.name=云杉木楼梯 +tile.stone.name=石头 +tile.stoneMoss.name=苔石 +tile.stoneSlab.brick.name=砖台阶 +tile.stoneSlab.cobble.name=圆石台阶 +tile.stoneSlab.netherBrick.name=地狱砖台阶 +tile.stoneSlab.quartz.name=石英台阶 +tile.stoneSlab.sand.name=沙石台阶 +tile.stoneSlab.smoothStoneBrick.name=石砖台阶 +tile.stoneSlab.stone.name=石台阶 +tile.stoneSlab.wood.name=木台阶 +tile.stonebrick.name=圆石 +tile.stonebricksmooth.chiseled.name=錾制石砖 +tile.stonebricksmooth.cracked.name=裂石砖 +tile.stonebricksmooth.default.name=石砖 +tile.stonebricksmooth.mossy.name=苔石砖 +tile.stonebricksmooth.name=石砖 +tile.tallgrass.fern.name=蕨 +tile.tallgrass.grass.name=草 +tile.tallgrass.name=草 +tile.tallgrass.shrub.name=灌木 +tile.thinGlass.name=玻璃板 +tile.thinStainedGlass.black.name=黑色染色玻璃板 +tile.thinStainedGlass.blue.name=蓝色染色玻璃板 +tile.thinStainedGlass.brown.name=棕色染色玻璃板 +tile.thinStainedGlass.cyan.name=青色染色玻璃板 +tile.thinStainedGlass.gray.name=灰色染色玻璃板 +tile.thinStainedGlass.green.name=绿色染色玻璃板 +tile.thinStainedGlass.lightBlue.name=淡蓝色染色玻璃板 +tile.thinStainedGlass.lime.name=黄绿色染色玻璃板 +tile.thinStainedGlass.magenta.name=品红色染色玻璃板 +tile.thinStainedGlass.name=染色玻璃板 +tile.thinStainedGlass.orange.name=橙色染色玻璃板 +tile.thinStainedGlass.pink.name=粉红色染色玻璃板 +tile.thinStainedGlass.purple.name=紫色染色玻璃板 +tile.thinStainedGlass.red.name=红色染色玻璃板 +tile.thinStainedGlass.silver.name=淡灰色染色玻璃板 +tile.thinStainedGlass.white.name=白色染色玻璃板 +tile.thinStainedGlass.yellow.name=黄色染色玻璃板 +tile.tnt.name=TNT +tile.torch.name=火把 +tile.trapdoor.name=活板门 +tile.tripWire.name=绊线 +tile.tripWireSource.name=绊线钩 +tile.vine.name=藤蔓 +tile.water.name=水 +tile.waterlily.name=睡莲 +tile.web.name=蜘蛛网 +tile.weightedPlate_heavy.name=测重压力板 (重质) +tile.weightedPlate_light.name=测重压力板 (轻质) +tile.whiteStone.name=末地石 +tile.wood.acacia.name=金合欢木板 +tile.wood.big_oak.name=深色橡木木板 +tile.wood.birch.name=白桦木板 +tile.wood.jungle.name=丛林木板 +tile.wood.name=木板 +tile.wood.oak.name=橡木木板 +tile.wood.spruce.name=云杉木板 +tile.woodSlab.acacia.name=金合欢木台阶 +tile.woodSlab.big_oak.name=深色橡木台阶 +tile.woodSlab.birch.name=桦木台阶 +tile.woodSlab.jungle.name=丛林台阶 +tile.woodSlab.oak.name=橡木台阶 +tile.woodSlab.spruce.name=云杉台阶 +tile.woolCarpet.black.name=黑色地毯 +tile.woolCarpet.blue.name=蓝色地毯 +tile.woolCarpet.brown.name=棕色地毯 +tile.woolCarpet.cyan.name=青色地毯 +tile.woolCarpet.gray.name=灰色地毯 +tile.woolCarpet.green.name=绿色地毯 +tile.woolCarpet.lightBlue.name=淡蓝色地毯 +tile.woolCarpet.lime.name=黄绿色地毯 +tile.woolCarpet.magenta.name=品红色地毯 +tile.woolCarpet.orange.name=橙色地毯 +tile.woolCarpet.pink.name=粉红色地毯 +tile.woolCarpet.purple.name=紫色地毯 +tile.woolCarpet.red.name=红色地毯 +tile.woolCarpet.silver.name=淡灰色地毯 +tile.woolCarpet.white.name=地毯 +tile.woolCarpet.yellow.name=黄色地毯 +tile.workbench.name=工作台 +title.oldgl1=检测到旧式显卡;由于需要OpenGL 2.0, +title.oldgl2=这可能会在未来阻止您进行游戏。 +translation.test.args=%s %s +translation.test.complex=前缀,%s%2$s 然后是 %s 和 %1$s 最后是 %s 还有 %1$s! +translation.test.escape=%%s %%%s %%%%s %%%%%s +translation.test.invalid=% 你好 +translation.test.invalid2=%s 你好 +translation.test.none=你好,世界! +translation.test.world=世界 diff --git a/src/main/resource/lang/ItemName_zh_TW.lang b/src/main/resource/lang/ItemName_zh_TW.lang new file mode 100644 index 0000000..4930688 --- /dev/null +++ b/src/main/resource/lang/ItemName_zh_TW.lang @@ -0,0 +1,1605 @@ +achievement.acquireIron=來硬的 +achievement.acquireIron.desc=熔煉一塊鐵錠 +achievement.bakeCake=蛋糕是個謊言 +achievement.bakeCake.desc=小麥、糖、牛奶和雞蛋! +achievement.blazeRod=與火共舞 +achievement.blazeRod.desc=從烈焰使者手中獲得烈焰桿 +achievement.bookcase=圖書管理員 +achievement.bookcase.desc=在周圍放置書櫃,增強您的附魔台等級 +achievement.breedCow=增產報國 +achievement.breedCow.desc=用小麥餵兩頭牛 +achievement.buildBetterPickaxe=工具升级 +achievement.buildBetterPickaxe.desc=合成一把更好的鎬 +achievement.buildFurnace=『熱』門『焦』點 +achievement.buildFurnace.desc=將八塊碎石合成一個熔爐 +achievement.buildHoe=農耕時間! +achievement.buildHoe.desc=使用木材與木棒合成一把木鋤 +achievement.buildPickaxe=採礦時間! +achievement.buildPickaxe.desc=使用木材和木棒合成一把木鎬 +achievement.buildSword=殺戮時間! +achievement.buildSword.desc=用木材及木枝打造一把劍 +achievement.buildWorkBench=基礎 +achievement.buildWorkBench.desc=將四塊木材合成一個工作台 +achievement.cookFish=優質魚肉 +achievement.cookFish.desc=捕捉並烤魚! +achievement.diamonds=鑽石! +achievement.diamonds.desc=使用鐵製工具挖出鑽石 +achievement.diamondsToYou=送你的鑽石! +achievement.diamondsToYou.desc=拿鑽石砸其他玩家。 +achievement.enchantments=附魔師 +achievement.enchantments.desc=使用書、黑曜石和鑽石合成一個附魔台 +achievement.exploreAllBiomes=環遊世界 +achievement.exploreAllBiomes.desc=發現所有的生態域 +achievement.flyPig=等哪天豬會飛了…噢! +achievement.flyPig.desc=騎著豬從懸崖衝下去 +achievement.fullBeacon=引導者 +achievement.fullBeacon.desc=建造一個完整的烽火台 +achievement.get=獲得成就! +achievement.ghast=以牙還牙 +achievement.ghast.desc=讓地獄幽靈被自己的火焰球毀滅. +achievement.killCow=牛不只有牛奶 +achievement.killCow.desc=獲得一些皮革 +achievement.killEnemy=魔物獵人 +achievement.killEnemy.desc=攻擊並殺死一隻怪物 +achievement.killWither=真正的開始 +achievement.killWither.desc=殺死凋靈 +achievement.makeBread=烤麵包 +achievement.makeBread.desc=把小麥變成麵包 +achievement.mineWood=獲得木頭 +achievement.mineWood.desc=持續破壞樹木直到木頭掉落為止. +achievement.onARail=暢行無阻 +achievement.onARail.desc=從開始處乘坐礦車至少 1 公里 +achievement.openInventory=盤點庫存 +achievement.openInventory.desc=按「%1$s」打開你的背包 +achievement.overkill=太殺了 +achievement.overkill.desc=一擊製造八顆心的傷害 +achievement.portal=我們必須更深入一點 +achievement.portal.desc=建造一個地獄傳送門 +achievement.potion=私人釀坊 +achievement.potion.desc=釀造一罐藥水 +achievement.requires=需要:%1$s +achievement.snipeSkeleton=狙擊手對決 +achievement.snipeSkeleton.desc=使用弓箭射殺距離五十公尺外的骷髏 +achievement.spawnWither=一切的開端? +achievement.spawnWither.desc=召喚凋靈 +achievement.taken=已完成! +achievement.theEnd=結束了? +achievement.theEnd.desc=進入終界 +achievement.theEnd2=結束了。 +achievement.theEnd2.desc=擊敗終界龍 +achievement.unknown=??? +addServer.add=完成 +addServer.enterIp=伺服器位址 +addServer.enterName=伺服器名稱 +addServer.hideAddress=隱藏位址 +addServer.resourcePack=伺服端資源包 +addServer.resourcePack.disabled=已禁用 +addServer.resourcePack.enabled=已啟用 +addServer.resourcePack.prompt=提示 +addServer.title=編輯伺服器資訊 +advMode.allPlayers=使用"@a"來指定全部玩家 +advMode.command=控制台指令 +advMode.nearestPlayer=使用"@p"來指定最近的玩家 +advMode.notAllowed=必須是創造模式中的伺服器工作人員 +advMode.notEnabled=這個伺服器不允許使用指令方塊 +advMode.previousOutput=預覽輸出 +advMode.randomPlayer=使用"@r"來指定隨機一位玩家 +advMode.setCommand=設置指令方塊的指令 +advMode.setCommand.success=指令設為:%s +attribute.modifier.plus.0=+ %s%s +attribute.modifier.plus.1=+ %s%%%s +attribute.modifier.plus.2=+ %s%%%s +attribute.modifier.take.0=-%s%s +attribute.modifier.take.1=- %s%%%s +attribute.modifier.take.2=- %s%%%s +attribute.name.generic.attackDamage=攻擊傷害 +attribute.name.generic.followRange=生物追蹤範圍 +attribute.name.generic.knockbackResistance=擊退抗性 +attribute.name.generic.maxHealth=最高生命值 +attribute.name.generic.movementSpeed=速度 +attribute.name.horse.jumpStrength=馬匹跳躍力 +attribute.name.zombie.spawnReinforcements=殭屍增援隊 +book.byAuthor=作者 %1$s +book.editTitle=輸入書名: +book.finalizeButton=署名並完成此書 +book.finalizeWarning=注意!當您在這本書簽署後,將無法修改書中內容。 +book.pageIndicator=第 %1$s 頁/共 %2$s 頁 +book.signButton=署名 +build.tooHigh=最大建築高度限制為 %s 格 +chat.cannotSend=無法發出聊天信息 +chat.copy=複製到剪貼簿 +chat.link.confirm=你確定你要打開以下網頁嗎? +chat.link.confirmTrusted=確定要打開此連結或複製到剪貼簿嗎? +chat.link.open=以瀏覽器開啟 +chat.link.warning=千萬不要打開任何你不信任的人給你的網址! +chat.stream.emote=(%s) * %s%s +chat.stream.text=(%s)<%s> %s +chat.type.achievement=%s 獲得成就 %s +chat.type.admin=[%s: %s] +chat.type.announcement=[%s] %s +chat.type.emote=* %s %s +chat.type.text=<%s> %s +commands.achievement.give.success.all=成功地給所有成就到 %s +commands.achievement.give.success.one=成功給予 %s 統計值 %s +commands.achievement.statTooLow=玩家 %s 沒有 %s 統計值 +commands.achievement.unknownAchievement=未知的成就或統計 '%s' +commands.achievement.usage=/achievement give <成就名稱> [玩家] +commands.ban.failed=無法ban玩家 %s +commands.ban.success=封鎖 %s 玩家。 +commands.ban.usage=/ban <名稱> [理由...] +commands.banip.invalid=你輸入了一個無效的IP位址或是玩家並不在線上。 +commands.banip.success=封鎖IP位址 %s 。 +commands.banip.success.players=封鎖IP位址 %s (%s 的來源位址)。 +commands.banip.usage=/ban-ip [理由...] +commands.banlist.ips=共有 %s 個被封鎖的IP位置: +commands.banlist.players=共有 %s 個被封鎖的玩家: +commands.banlist.usage=/banlist [ips(IP位址)|players(玩家)] +commands.clear.failure=無法清空 %s 的背包,無物可清除 +commands.clear.success=已清空 %s 的背包,移除了 %s 個物品 +commands.clear.usage=/clear <玩家> [物品] [物品數據值] +commands.debug.notStarted=不能在開始分析以前就停止它! +commands.debug.start=開始除錯分析 +commands.debug.stop=%s秒(%s刻)後停止除錯分析 +commands.defaultgamemode.success=現在這世界的預設遊戲模式為 %s +commands.defaultgamemode.usage=/defaultgamemode <模式代碼> +commands.deop.failed=無法去除 %s 的op權限 +commands.deop.success=%s 的管理員權限已被取消 +commands.deop.usage=/deop <玩家> +commands.difficulty.success=已將遊戲難度設至 %s +commands.difficulty.usage=/difficulty <新的難度> +commands.downfall.success=已切換天氣 +commands.downfall.usage=/toogledownfall +commands.effect.failure.notActive=無法從 %1$s 身上移除 %2$s,因為其身上無此效果 +commands.effect.failure.notActive.all=無法移除效果因為%s身上沒有任何效果 +commands.effect.notFound=ID為 %s 的特殊效果並不存在 +commands.effect.success=對 %4$s 加上了 %5$s 秒的 %1$s (ID %2$s) * %3$s +commands.effect.success.removed=從 %1$s 身上移除了 %2$s +commands.effect.success.removed.all=已解除 %s 身上所有特殊狀態 +commands.effect.usage=/effect <玩家> <效果> [秒數] [增幅] +commands.enchant.cantCombine=%1$s 無法和 %2$s 結合 +commands.enchant.cantEnchant=此附魔無法加到該物品 +commands.enchant.noItem=該目標未持有物品 +commands.enchant.notFound=並不存在ID為 %s 的附魔 +commands.enchant.success=附魔成功 +commands.enchant.usage=/enchant <玩家> <附魔 ID> [等級] +commands.gamemode.success.other=將 %s 的遊戲模式切換為 %s +commands.gamemode.success.self=將自己的遊戲模式切換至 %s 。 +commands.gamemode.usage=/gamemode <模式> [玩家] +commands.gamerule.norule=沒有名為'%s'的遊戲規則 +commands.gamerule.success=遊戲規則已經變更 +commands.gamerule.usage=/gamerule <規則名稱> <數值> 或 /gamerule <規則名稱> +commands.generic.boolean.invalid=‘%s’ 不是 true 或 false +commands.generic.deprecatedId=警告:未來遊戲將不支援數字型 ID 。請改用名稱取代,例如「%s」 +commands.generic.double.tooBig=您所輸入的數字(%s)過大,最大為%s +commands.generic.double.tooSmall=您所輸入的數字(%s)過小,最少為%s +commands.generic.exception=嘗試執行此指令時發生未知錯誤 +commands.generic.notFound=未知的指令。請嘗試 /help 來顯示指令列表。 +commands.generic.num.invalid='%s' 並不是一個有效的數字。 +commands.generic.num.tooBig=您所輸入的數字(%s)過大,最大為 %s +commands.generic.num.tooSmall=您所輸入的數目(%s)過小,最少為 %s 。 +commands.generic.permission=你沒有權限使用此指令 +commands.generic.player.notFound=找不到該玩家 +commands.generic.syntax=無效的指令語法 +commands.generic.usage=用法:%s +commands.give.notFound=ID為 %s 的物品並不存在。 +commands.give.success=將 %s * %s 給 %s +commands.give.tagError=資料標籤解析失敗: %s +commands.give.usage=/give <玩家> <物品ID> [數量] [資料值] [資料標記] +commands.help.footer=提示:輸入指令時請使用鍵來自動完成指令或參數 +commands.help.header=--- 顯示說明第 %s/%s 頁 (/help <頁數>) --- +commands.help.usage=/help [頁數|指令名稱] +commands.kick.success=%s 從遊戲中被踢出 +commands.kick.success.reason=%s 從遊戲中被踢出:「%s」 +commands.kick.usage=/kick <玩家> [原因...] +commands.kill.success=哎唷 !這看起來好疼哦 +commands.kill.usage=/kill +commands.me.usage=/me <行動...> +commands.message.display.incoming=%s 悄悄對您說: %s +commands.message.display.outgoing=您悄悄對%s說:%s +commands.message.sameTarget=您不能傳送私訊給您自己! +commands.message.usage=/difficulty <新難度> +commands.op.failed=無法加 %s 為op +commands.op.success=%s 晉升為管理員。 +commands.op.usage=/op <玩家> +commands.players.list=共有 %s/%s 玩家在線上: +commands.players.usage=/list +commands.playsound.playerTooFar=玩家 %s 距離太遠,聽不到音效。 +commands.playsound.success=播放 '%s' 給 %s 聽 +commands.playsound.usage=/playsound [x] [y] [z] [音量] [音調] [最小音量] +commands.publish.failed=無法將此本機遊戲公開 +commands.publish.started=已在 %s 網路埠上發佈本機遊戲 +commands.publish.usage=/publish +commands.save-off.alreadyOff=儲存功能已經關閉。 +commands.save-off.usage=/save-off +commands.save-on.alreadyOn=儲存功能已經開啟。 +commands.save-on.usage=/save-on +commands.save.disabled=關閉地圖自動存檔功能 +commands.save.enabled=開啓地圖自動存檔功能 +commands.save.failed=存檔失敗: %s +commands.save.start=儲存中... +commands.save.success=世界儲存完畢。 +commands.save.usage=/save-all +commands.say.usage=/say <訊息...> +commands.scoreboard.objectiveNotFound=無法找到名稱為「%s」的物件 +commands.scoreboard.objectiveReadOnly=物件 '%s' 是唯讀的並且無法設置 +commands.scoreboard.objectives.add.alreadyExists=名為'%s'的目標已經存在 +commands.scoreboard.objectives.add.displayTooLong='%s'作為目標顯示名稱太長了,最多只能有%s個字元 +commands.scoreboard.objectives.add.success=已成功添加新目標'%s' +commands.scoreboard.objectives.add.tooLong='%s'作為目標名稱太長了,最多只能有%s個字元 +commands.scoreboard.objectives.add.usage=/scoreboard objectives add <名稱> <準則類型> [顯示名稱...] +commands.scoreboard.objectives.add.wrongType=使用了無效的評分判據類型 '%s' +commands.scoreboard.objectives.list.count=顯示計分板上 %s 個計分項目: +commands.scoreboard.objectives.list.empty=記分板上沒有目標 +commands.scoreboard.objectives.list.entry=——%s: 顯示名為'%s',類型為'%s' +commands.scoreboard.objectives.remove.success=已成功移除目標'%s' +commands.scoreboard.objectives.remove.usage=/scoreboard objectives remove <名稱> +commands.scoreboard.objectives.setdisplay.invalidSlot=沒有名稱為'%s'的顯示位置 +commands.scoreboard.objectives.setdisplay.successCleared=已清空'%s'目標顯示區 +commands.scoreboard.objectives.setdisplay.successSet=已將目標顯示區從'%s'設定為'%s' +commands.scoreboard.objectives.setdisplay.usage=/scoreboard objectives setdisplay <位置> [目標] +commands.scoreboard.players.add.usage=/scoreboard players add <玩家> <目標> <記分> +commands.scoreboard.players.list.count=正追蹤計分板上 %s 個玩家: +commands.scoreboard.players.list.empty=記分板上沒有可追蹤的玩家 +commands.scoreboard.players.list.player.count=正追蹤計分板上 %s 個計分項目: +commands.scoreboard.players.list.player.empty=玩家 %s 沒有記錄分數 +commands.scoreboard.players.list.usage=/scoreboard players list [玩家名稱] +commands.scoreboard.players.remove.usage=/scoreboard players remove <玩家> <目標> <記分> +commands.scoreboard.players.reset.success=已重置玩家%s所有的分數 +commands.scoreboard.players.reset.usage=/scoreboard players reset <玩家> +commands.scoreboard.players.set.success=已將%s的分數在%s上設定為%s +commands.scoreboard.players.set.usage=/scoreboard players set <玩家> <目標> <分數> +commands.scoreboard.teamNotFound=無法找到名稱為「%s」的隊伍 +commands.scoreboard.teams.add.alreadyExists=名為'%s'的隊伍已經存在 +commands.scoreboard.teams.add.displayTooLong='%s'作為顯示隊名太長了,最多只能有%s個字元 +commands.scoreboard.teams.add.success=已成功添加新隊伍'%s' +commands.scoreboard.teams.add.tooLong='%s'作為隊伍名稱太長了,最多只能有%s個字元 +commands.scoreboard.teams.add.usage=/scoreboard teams add <名稱> [顯示名稱...] +commands.scoreboard.teams.empty.alreadyEmpty=隊伍%s已空,無法移除不存在的玩家 +commands.scoreboard.teams.empty.success=從隊伍 %s 中移除全部 %s 個玩家 +commands.scoreboard.teams.join.failure=無法給隊伍%s添加%s個玩家:%s +commands.scoreboard.teams.join.success=已將%s個玩家添加到隊伍%s:%s +commands.scoreboard.teams.join.usage=/scoreboard teams join <隊名> [玩家] +commands.scoreboard.teams.leave.failure=無法從他們隊伍中移除%s個玩家:%s +commands.scoreboard.teams.leave.noTeam=你不屬於任何一隊 +commands.scoreboard.teams.leave.success=已從他們的隊伍中移除%s個玩家:%s +commands.scoreboard.teams.leave.usage=/scoreboard teams leave [玩家] +commands.scoreboard.teams.list.count=顯示計分板上 %s 個隊伍: +commands.scoreboard.teams.list.empty=記分板上沒有註冊的隊伍 +commands.scoreboard.teams.list.entry=——%1$s: '%2$s'有%3$s個玩家 +commands.scoreboard.teams.list.player.count=顯示隊伍 %s 中 %s 個玩家: +commands.scoreboard.teams.list.player.empty=隊伍%s沒有玩家 +commands.scoreboard.teams.list.player.entry=- %2$s: %1$s (%3$s) +commands.scoreboard.teams.list.usage=/scoreboard teams list [名稱] +commands.scoreboard.teams.option.noValue=選項%s的有效數值為:%s +commands.scoreboard.teams.option.success=將選項%s在隊伍%s上設定為%s +commands.scoreboard.teams.option.usage=/scoreboard teams option <隊名> <值> +commands.scoreboard.teams.remove.success=已移除隊伍%s +commands.scoreboard.teams.remove.usage=/scoreboard teams remove <名稱> +commands.scoreboard.teams.usage=/scoreboard teams +commands.scoreboard.usage=/scoreboard +commands.seed.success=世界種子碼:%s +commands.seed.usage=/seed +commands.setblock.failed=無法放置方塊 +commands.setblock.noChange=無法放置此方塊 +commands.setblock.notFound=ID或名稱為 %s 的方塊並不存在 +commands.setblock.outOfWorld=無法將方塊放置在世界邊界之外 +commands.setblock.success=方塊已被放置 +commands.setblock.tagError=資料標籤解析失敗:%s +commands.setblock.usage=/setblock <方塊名稱> [方塊附加值] [原始方塊處理] [附加資料標籤] +commands.setidletimeout.success=成功設置閒置踢出時間為 %s 分鐘。 +commands.setidletimeout.usage=/setidletimeout <被踢除前閒置分鐘數量> +commands.setworldspawn.success=將世界重生點設置為 (%s, %s, %s) +commands.setworldspawn.usage=/setworldspawn 或 /setworldspawn +commands.spawnpoint.success=將%s的重生點設在(%s, %s, %s) +commands.spawnpoint.usage=/spawnpoint 或 /spawnpoint <玩家> 或 /spawnpoint <玩家> +commands.spreadplayers.failure.players=未能分散 %s 個玩家;分散中心座標為 (%s, %s) (可能原因:玩家間空間太少,請嘗試調整最大分散範圍至 %s 格) +commands.spreadplayers.failure.teams=未能分散 %s 個隊伍;分散中心座標為 (%s, %s) (可能原因:玩家間空間太少,請嘗試調整最大分散範圍至 %s 格) +commands.spreadplayers.info.players=(玩家間平均相距 %s 格;相應反覆運算次數為 %s ) +commands.spreadplayers.info.teams=(隊伍間平均相距 %s 格;相應反覆運算次數為 %s ) +commands.spreadplayers.spreading.players=正在分散 %s 個玩家;分散距離中心點 %s 格;中心座標為 (%s, %s);最少分散距離為 %s 格 +commands.spreadplayers.spreading.teams=正在分散 %s 個隊伍;分散距離中心點 %s 格;中心座標為 (%s, %s);最少分散距離為 %s 格 +commands.spreadplayers.success.players=已成功分散 %s 個玩家;分散中心座標為 (%s, %s) +commands.spreadplayers.success.teams=已成功分散 %s 個隊伍;分散中心座標為 (%s, %s) +commands.spreadplayers.usage=/spreadplayers <目標玩家間的距離> <最大分佈範圍> <隊員間進行分散 true|false> <目標玩家名單...> +commands.stop.start=正在停止伺服器。 +commands.stop.usage=/stop +commands.summon.failed=召喚失敗 +commands.summon.outOfWorld=無法召喚在世界外的物體 +commands.summon.success=召喚成功 +commands.summon.tagError=資料標籤解析失敗: %s +commands.summon.usage=/summon <實體名稱> [x] [y] [z] [資料標記] +commands.tellraw.jsonException=無效的 json: %s +commands.tellraw.usage=/tellraw <玩家> <原始 json 訊息> +commands.testfor.failed=/testfor 只可用於類比輸出的指令方塊 +commands.testfor.usage=/testfor <玩家> +commands.testforblock.failed.data=在 %s,%s,%s 的方塊ID是 %s (預期: %s )。 +commands.testforblock.failed.nbt=在 %s,%s,%s 的方塊沒有所需的 NBT 值。 +commands.testforblock.failed.tile=在 %s,%s,%s 的方塊是 %s (預期: %s )。 +commands.testforblock.failed.tileEntity=在 %s,%s,%s 的方塊不是實體,無法匹配資料標籤值。 +commands.testforblock.outOfWorld=無法測試位於世界外的方塊 +commands.testforblock.success=在 %s,%s,%s 成功找到該方塊。 +commands.testforblock.usage=/testforblock <方塊名稱> [資料數值] [資料標籤] +commands.time.added=時間增加了 %s 。 +commands.time.set=時間設定為 %s 。 +commands.time.usage=/time <數值> +commands.tp.notSameDimension=因玩家處在不同世界而無法傳送 +commands.tp.success=傳送 %s 到 %s 。 +commands.tp.success.coordinates=傳送 %s 到 %s,%s,%s +commands.tp.usage=/tp [目標玩家] <目的地玩家> 或 /tp [目標玩家] +commands.unban.failed=無法取消玩家 %s 被ban的狀態 +commands.unban.success=解禁玩家 %s +commands.unban.usage=/pardon <名稱> +commands.unbanip.invalid=你輸入了一個無效的IP位址。 +commands.unbanip.success=解除封鎖IP位址 %s +commands.unbanip.usage=/pardon-ip +commands.weather.clear=轉換天氣為放晴 +commands.weather.rain=轉換天氣為下雨 +commands.weather.thunder=轉換天氣為雷雨 +commands.weather.usage=/weather < clear(晴天) | rain(下雨) | thunder(雷雨) > [維持時間(秒)] +commands.whitelist.add.failed=無法將 %s 加到白名單 +commands.whitelist.add.success=將 %s 加入白名單 +commands.whitelist.add.usage=/whitelist add <玩家> +commands.whitelist.disabled=關閉白名單 +commands.whitelist.enabled=開啟白名單 +commands.whitelist.list=共有 %s 位白名單玩家(全體玩家共 %s 位): +commands.whitelist.reloaded=重新讀取白名單 +commands.whitelist.remove.failed=無法從白名單移除 %s +commands.whitelist.remove.success=將 %s 從白名單中移除 +commands.whitelist.remove.usage=/whitelist remove <玩家> +commands.whitelist.usage=/whitelist +commands.xp.failure.widthdrawXp=無法賦予玩家負經驗點數 +commands.xp.success=將 %s 經驗值賦予 %s +commands.xp.success.levels=將 %s 等級賦予 %s +commands.xp.success.negative.levels=從 %s 取得 %s 級經驗點數 +commands.xp.usage=/xp <經驗值> [玩家] 或 /xp <等級>L [玩家] +connect.authorizing=登入中… +connect.connecting=正在連線到伺服器… +connect.failed=與伺服器連線失敗 +container.brewing=釀造台 +container.chest=儲物箱 +container.chestDouble=大型儲物箱 +container.crafting=合成 +container.creative=選擇物品 +container.dispenser=發射器 +container.dropper=投擲器 +container.enchant=附魔 +container.enderchest=終界箱 +container.furnace=熔爐 +container.hopper=漏斗 +container.inventory=物品欄 +container.minecart=礦車 +container.repair=修復 & 命名 +container.repair.cost=所需等級:%1$s +container.repair.expensive=太貴了! +controls.reset=重置 +controls.resetAll=全部重置 +controls.title=按鍵設定 +createWorld.customize.flat.addLayer=增加地層 +createWorld.customize.flat.editLayer=編輯地層 +createWorld.customize.flat.height=高度 +createWorld.customize.flat.layer=%s +createWorld.customize.flat.layer.bottom=底層 - %s +createWorld.customize.flat.layer.top=頂層 - %s +createWorld.customize.flat.removeLayer=刪除地層 +createWorld.customize.flat.tile=地層材料 +createWorld.customize.flat.title=自訂超平坦世界 +createWorld.customize.presets=預設方案 +createWorld.customize.presets.list=您也可以選擇預先設定的地形! +createWorld.customize.presets.select=使用預設地形 +createWorld.customize.presets.share=想分享你設定的地形嗎?請使用下方文字框! +createWorld.customize.presets.title=選擇預設地形 +death.attack.anvil=%1$s 被墜落下來的鐵砧壓扁了 +death.attack.arrow=%1$s 被 %2$s 射殺了 +death.attack.arrow.item=%1$s 被 %2$s 用 %3$s 射殺 +death.attack.cactus=%1$s 被仙人掌刺死了 +death.attack.cactus.player=%1$s 避開 %2$s 時,被仙人掌刺死了 +death.attack.drown=%1$s溺死了 +death.attack.drown.player=%1$s 為了逃離 %2$s 的追殺而在水中溺斃 +death.attack.explosion=%1$s被炸飛了 +death.attack.explosion.player=%1$s 被 %2$s 炸死了 +death.attack.fall=%1$s 以為能安然無恙的著地 +death.attack.fallingBlock=%1$s 被墜落下來的方塊壓扁了 +death.attack.fireball=%1$s 被 %2$s 的火球殺死了 +death.attack.fireball.item=%1$s 被 %2$s 用 %3$s 打出的火球殺死 +death.attack.generic=%1$s 已死亡 +death.attack.inFire=%1$s在火焰中昇天 +death.attack.inFire.player=%1$s 在和 %2$s 戰鬥時踏入了火中 +death.attack.inWall=%1$s在牆壁裡窒息 +death.attack.indirectMagic=%1$s 被 %2$s 用魔法殺死 +death.attack.indirectMagic.item=%1$s 被 %2$s 用 %3$s 殺死 +death.attack.lava=%1$s 想在岩漿裡游泳 +death.attack.lava.player=%1$s 為了逃離 %2$s 的追殺而跳入岩漿 +death.attack.magic=%1$s 被魔法殺死了 +death.attack.mob=%1$s 被 %2$s 殺死了 +death.attack.onFire=%1$s 被燒死了 +death.attack.onFire.player=%1$s 在和 %2$s 戰鬥時被火焰燒成灰燼 +death.attack.outOfWorld=%1$s 掉到世界外面了 +death.attack.player=%1$s 被 %2$s 殺死了 +death.attack.player.item=%1$s 被 %2$s 用 %3$s 殺死 +death.attack.starve=%1$s 餓死了 +death.attack.thorns=%1$s 試圖襲擊 %2$s,但被反將一軍 +death.attack.thrown=%1$s 被 %2$s 活生生揍死了 +death.attack.thrown.item=%1$s 被 %2$s 用 %3$s 揍死 +death.attack.wither=%1$s 凋零至死了 +death.fell.accident.generic=%1$s 從高處跌落 +death.fell.accident.ladder=%1$s 從梯子上摔了下來 +death.fell.accident.vines=%1$s 從藤蔓上摔了下來 +death.fell.accident.water=%1$s 從水下摔出 +death.fell.assist=%1$s 被 %2$s 推下懸崖 +death.fell.assist.item=%1$s 被 %2$s 用 %3$s 打下懸崖 +death.fell.finish=%1$s 摔傷後被 %2$s 殺了 +death.fell.finish.item=%1$s 摔的太重後被 %2$s 用 %3$s 補了尾刀 +death.fell.killer=%1$s 從空中摔落地面 +deathScreen.deleteWorld=刪除世界 +deathScreen.hardcoreInfo=你無法在極限模式中重生! +deathScreen.leaveServer=離開伺服器 +deathScreen.quit.confirm=你確定要退出了嗎? +deathScreen.respawn=重生 +deathScreen.score=分數 +deathScreen.title=你死了! +deathScreen.title.hardcore=遊戲結束! +deathScreen.titleScreen=回到標題畫面 +demo.day.1=試玩版只有五天的遊戲內時間,盡你所能吧! +demo.day.2=第二天 +demo.day.3=第三天 +demo.day.4=第四天 +demo.day.5=這是你的最後一天! +demo.day.6=你度過了你的第五天,按F2來截圖留念 +demo.day.warning=你的時間即將用盡! +demo.demoExpired=試玩時間結束! +demo.help.buy=立即購買! +demo.help.fullWrapped=您可以在遊戲中試玩五天 (約為現實世界的 1 小時 40 分鐘)。在選單的「成就」選項中,可以找到一些有幫助的提示。希望您玩得開心! +demo.help.inventory=按%1$s來打開你的物品欄 +demo.help.jump=按%1$s鍵跳躍 +demo.help.later=繼續進行遊戲! +demo.help.movement=使用%1$s,%2$s,%3$s,%4$s及滑鼠移動 +demo.help.movementMouse=移動滑鼠來查看四周 +demo.help.movementShort=按下%1$s,%2$s,%3$s,%4$s來移動 +demo.help.title=Minecraft試玩模式 +demo.remainingTime=剩餘時間:%s +demo.reminder=試玩時間已經結束,請購買遊戲來繼續,或重新開始一個新的世界! +disconnect.closed=連線關閉 +disconnect.disconnected=與伺服器連線中斷 +disconnect.endOfStream=數據傳送終止 +disconnect.genericReason=%s +disconnect.kicked=已被踢出遊戲 +disconnect.loginFailed=登入失敗 +disconnect.loginFailedInfo=登入失敗:%s +disconnect.loginFailedInfo.invalidSession=無效的 session(請重開您的遊戲) +disconnect.loginFailedInfo.serversUnavailable=正版驗證系統下線維修中 +disconnect.lost=失去連線 +disconnect.overflow=緩衝區溢位 +disconnect.quitting=離開 +disconnect.spam=因垃圾訊息被踢出 +disconnect.timeout=連線逾時 +enchantment.arrowDamage=強力 +enchantment.arrowFire=火焰 +enchantment.arrowInfinite=無限 +enchantment.arrowKnockback=擊退 +enchantment.damage.all=鋒利 +enchantment.damage.arthropods=節肢剋星 +enchantment.damage.undead=不死剋星 +enchantment.digging=效率 +enchantment.durability=耐久 +enchantment.fire=燃燒 +enchantment.fishingSpeed=魚餌 +enchantment.knockback=擊退 +enchantment.level.1=I +enchantment.level.10=X +enchantment.level.2=II +enchantment.level.3=III +enchantment.level.4=IV +enchantment.level.5=V +enchantment.level.6=VI +enchantment.level.7=VII +enchantment.level.8=VIII +enchantment.level.9=IX +enchantment.lootBonus=掠奪 +enchantment.lootBonusDigger=幸運 +enchantment.lootBonusFishing=海洋的祝福 +enchantment.oxygen=水中呼吸 +enchantment.protect.all=保護 +enchantment.protect.explosion=防爆 +enchantment.protect.fall=輕盈 +enchantment.protect.fire=抗火性 +enchantment.protect.projectile=防彈 +enchantment.thorns=尖刺 +enchantment.untouching=絲綢之觸 +enchantment.waterWorker=水中挖掘 +entity.Arrow.name=箭矢 +entity.Bat.name=蝙蝠 +entity.Blaze.name=烈焰使者 +entity.Boat.name=船 +entity.Cat.name=貓 +entity.CaveSpider.name=洞穴蜘蛛 +entity.Chicken.name=雞 +entity.Cow.name=乳牛 +entity.Creeper.name=苦力怕 +entity.EnderDragon.name=終界龍 +entity.Enderman.name=終界使者 +entity.EntityHorse.name=馬 +entity.FallingSand.name=掉落的方塊 +entity.Fireball.name=火球 +entity.Ghast.name=地獄幽靈 +entity.Giant.name=巨人 +entity.Item.name=物品 +entity.LavaSlime.name=熔岩史萊姆 +entity.Minecart.name=礦車 +entity.Mob.name=生物 +entity.Monster.name=怪物 +entity.MushroomCow.name=哞菇 +entity.Ozelot.name=野貓 + +entity.Painting.name=繪畫 +entity.Pig.name=豬 +entity.PigZombie.name=殭屍豬人 +entity.PrimedTnt.name=TNT方塊 +entity.Sheep.name=綿羊 +entity.Silverfish.name=蠹魚 +entity.Skeleton.name=骷髏 +entity.Slime.name=史萊姆 +entity.SmallFireball.name=小火球 +entity.SnowMan.name=雪人 +entity.Snowball.name=雪球 +entity.Spider.name=蜘蛛 +entity.Squid.name=烏賊 +entity.Villager.name=村民 +entity.VillagerGolem.name=鐵魔像 +entity.Witch.name=女巫 +entity.WitherBoss.name=凋靈 +entity.Wolf.name=狼 +entity.XPOrb.name=經驗球 +entity.Zombie.name=殭屍 +entity.donkey.name=驢子 +entity.generic.name=未知 +entity.horse.name=馬 +entity.mule.name=騾子 +entity.skeletonhorse.name=骷髏馬 +entity.zombiehorse.name=殭屍馬 +gameMode.adventure=冒險模式 +gameMode.changed=您的遊戲模式已更新 +gameMode.creative=創造模式 +gameMode.hardcore=極限模式! +gameMode.survival=生存模式 +generator.amplified=巨大化世界 +generator.amplified.info=注意:這只是為了好玩,需要強力電腦 +generator.default=預設 +generator.flat=平地 +generator.largeBiomes=大型生態域 +gui.achievements=成就 +gui.back=返回 +gui.cancel=取消 +gui.done=完成 +gui.down=下 +gui.no=否 +gui.stats=統計 +gui.toMenu=回到標題畫面 +gui.up=上 +gui.yes=是 +inventory.binSlot=銷毀物品 +item.apple.name=蘋果 +item.appleGold.name=金蘋果 +item.arrow.name=箭矢 +item.bed.name=床 +item.beefCooked.name=牛排 +item.beefRaw.name=生牛肉 +item.blazePowder.name=烈焰粉 +item.blazeRod.name=烈焰桿 +item.boat.name=船 +item.bone.name=骨頭 +item.book.name=書 +item.bootsChain.name=鎖鏈靴子 +item.bootsCloth.name=皮革靴子 +item.bootsDiamond.name=鑽石靴子 +item.bootsGold.name=黃金靴子 +item.bootsIron.name=鐵製靴子 +item.bow.name=弓 +item.bowl.name=碗 +item.bread.name=麵包 +item.brewingStand.name=釀造台 +item.brick.name=紅磚頭 +item.bucket.name=鐵桶 +item.bucketLava.name=岩漿桶 +item.bucketWater.name=水桶 +item.cake.name=蛋糕 +item.carrotGolden.name=金胡蘿蔔 +item.carrotOnAStick.name=胡蘿蔔釣竿 +item.carrots.name=胡蘿蔔 +item.cauldron.name=鍋釜 +item.charcoal.name=木炭 +item.chestplateChain.name=鎖鏈胸甲 +item.chestplateCloth.name=皮革上衣 +item.chestplateDiamond.name=鑽石胸甲 +item.chestplateGold.name=黃金胸甲 +item.chestplateIron.name=鐵製胸甲 +item.chickenCooked.name=烤雞 +item.chickenRaw.name=生雞肉 +item.clay.name=黏土 +item.clock.name=時鐘 +item.coal.name=煤炭 +item.comparator.name=紅石比較器 +item.compass.name=羅盤 +item.cookie.name=餅乾 +item.diamond.name=鑽石 +item.diode.name=紅石中繼器 +item.doorIron.name=鐵門 +item.doorWood.name=木門 +item.dyePowder.black.name=墨囊 +item.dyePowder.blue.name=青金石 +item.dyePowder.brown.name=可可豆 +item.dyePowder.cyan.name=青色染料 +item.dyePowder.gray.name=灰色染料 +item.dyePowder.green.name=仙人掌綠 +item.dyePowder.lightBlue.name=淺藍色染料 +item.dyePowder.lime.name=淺綠染料 +item.dyePowder.magenta.name=洋紅色染料 +item.dyePowder.orange.name=橘色染料 +item.dyePowder.pink.name=粉紅色染料 +item.dyePowder.purple.name=紫色染料 +item.dyePowder.red.name=玫瑰紅 +item.dyePowder.silver.name=淺灰色染料 +item.dyePowder.white.name=骨粉 +item.dyePowder.yellow.name=蒲公英黃 +item.dyed=染色的 +item.egg.name=雞蛋 +item.emerald.name=綠寶石 +item.emptyMap.name=空白地圖 +item.emptyPotion.name=水瓶 +item.enchantedBook.name=附魔書 +item.enderPearl.name=終界珍珠 +item.expBottle.name=經驗瓶 +item.eyeOfEnder.name=終界之眼 +item.feather.name=羽毛 +item.fermentedSpiderEye.name=發酵蜘蛛眼 +item.fireball.name=火焰彈 +item.fireworks.flight=飛行時間: +item.fireworks.name=煙火 +item.fireworksCharge.black=黑色 +item.fireworksCharge.blue=藍色 +item.fireworksCharge.brown=棕色 +item.fireworksCharge.customColor=自訂 +item.fireworksCharge.cyan=青色 +item.fireworksCharge.fadeTo=淡出 +item.fireworksCharge.flicker=閃爍 +item.fireworksCharge.gray=灰色 +item.fireworksCharge.green=綠色 +item.fireworksCharge.lightBlue=淺藍色 +item.fireworksCharge.lime=黃綠色 +item.fireworksCharge.magenta=洋紅色 +item.fireworksCharge.name=火藥球 +item.fireworksCharge.orange=橘色 +item.fireworksCharge.pink=粉紅色 +item.fireworksCharge.purple=紫色 +item.fireworksCharge.red=紅色 +item.fireworksCharge.silver=淺灰色 +item.fireworksCharge.trail=軌跡 +item.fireworksCharge.type=未知形狀 +item.fireworksCharge.type.0=小型球狀 +item.fireworksCharge.type.1=大型球狀 +item.fireworksCharge.type.2=星形 +item.fireworksCharge.type.3=苦力怕形 +item.fireworksCharge.type.4=爆裂 +item.fireworksCharge.white=白色 +item.fireworksCharge.yellow=黃色 +item.fish.clownfish.raw.name=小丑魚 +item.fish.cod.cooked.name=烤魚 +item.fish.cod.raw.name=生魚 +item.fish.pufferfish.raw.name=河豚 +item.fish.salmon.cooked.name=熟鮭魚片 +item.fish.salmon.raw.name=生鮭魚 +item.fishingRod.name=釣竿 +item.flint.name=燧石 +item.flintAndSteel.name=打火石 +item.flowerPot.name=花盆 +item.frame.name=物品展示框 +item.ghastTear.name=幽靈之淚 +item.glassBottle.name=玻璃瓶 +item.goldNugget.name=金粒 +item.hatchetDiamond.name=鑽石斧 +item.hatchetGold.name=金斧 +item.hatchetIron.name=鐵斧 +item.hatchetStone.name=石斧 +item.hatchetWood.name=木斧 +item.helmetChain.name=鎖鏈頭盔 +item.helmetCloth.name=皮革帽子 +item.helmetDiamond.name=鑽石頭盔 +item.helmetGold.name=黃金頭盔 +item.helmetIron.name=鐵製頭盔 +item.hoeDiamond.name=鑽石鋤 +item.hoeGold.name=金鋤 +item.hoeIron.name=鐵製鋤頭 +item.hoeStone.name=石鋤 +item.hoeWood.name=木鋤 +item.horsearmordiamond.name=鑽石製馬鎧 +item.horsearmorgold.name=黃金製馬鎧 +item.horsearmormetal.name=鐵製馬鎧 +item.ingotGold.name=金錠 +item.ingotIron.name=鐵錠 +item.leash.name=拴繩 +item.leather.name=皮革 +item.leaves.name=樹葉 +item.leggingsChain.name=鎖鏈護腿 +item.leggingsCloth.name=皮革褲子 +item.leggingsDiamond.name=鑽石護腿 +item.leggingsGold.name=黃金護腿 +item.leggingsIron.name=鐵製護腿 +item.magmaCream.name=熔岩球 +item.map.name=地圖 +item.melon.name=西瓜 +item.milk.name=牛奶 +item.minecart.name=礦車 +item.minecartChest.name=運輸礦車 +item.minecartCommandBlock.name=指令方塊礦車 +item.minecartFurnace.name=動力礦車 +item.minecartHopper.name=漏斗礦車 +item.minecartTnt.name=TNT礦車 +item.monsterPlacer.name=生成 +item.mushroomStew.name=蘑菇湯 +item.nameTag.name=命名牌 +item.netherStalkSeeds.name=地獄疙瘩 +item.netherStar.name=地獄之星 +item.netherbrick.name=地獄磚 +item.netherquartz.name=地獄石英 +item.painting.name=繪畫 +item.paper.name=紙 +item.pickaxeDiamond.name=鑽石鎬 +item.pickaxeGold.name=金鎬 +item.pickaxeIron.name=鐵鎬 +item.pickaxeStone.name=石鎬 +item.pickaxeWood.name=木鎬 +item.porkchopCooked.name=烤豬肉 +item.porkchopRaw.name=生豬肉 +item.potato.name=馬鈴薯 +item.potatoBaked.name=烤馬鈴薯 +item.potatoPoisonous.name=毒馬鈴薯 +item.potion.name=藥水 +item.pumpkinPie.name=南瓜派 +item.record.11.desc=C418 - 11 +item.record.13.desc=C418 - 13 +item.record.blocks.desc=C418 - blocks +item.record.cat.desc=C418 - cat +item.record.chirp.desc=C418 - chirp +item.record.far.desc=C418 - far +item.record.mall.desc=C418 - mall +item.record.mellohi.desc=C418 - mellohi +item.record.name=唱片 +item.record.stal.desc=C418 - stal +item.record.strad.desc=C418 - strad +item.record.wait.desc=C418 - wait +item.record.ward.desc=C418 - ward +item.redstone.name=紅石 +item.reeds.name=甘蔗 +item.rottenFlesh.name=腐肉 +item.ruby.name=紅寶石 +item.saddle.name=鞍 +item.seeds.name=種子 +item.seeds_melon.name=西瓜種子 +item.seeds_pumpkin.name=南瓜種子 +item.shears.name=剪刀 +item.shovelDiamond.name=鑽石鏟 +item.shovelGold.name=金鏟 +item.shovelIron.name=鐵鏟 +item.shovelStone.name=石鏟 +item.shovelWood.name=木鏟 +item.sign.name=告示牌 +item.skull.char.name=頭顱 +item.skull.creeper.name=苦力怕頭顱 +item.skull.player.name=%s的頭顱 +item.skull.skeleton.name=骷髏頭顱 +item.skull.wither.name=凋靈骷髏頭顱 +item.skull.zombie.name=殭屍頭顱 +item.slimeball.name=史萊姆球 +item.snowball.name=雪球 +item.speckledMelon.name=鑲金西瓜 +item.spiderEye.name=蜘蛛眼 +item.stick.name=木棒 +item.string.name=線 +item.sugar.name=糖 +item.sulphur.name=火藥 +item.swordDiamond.name=鑽石劍 +item.swordGold.name=金劍 +item.swordIron.name=鐵劍 +item.swordStone.name=石劍 +item.swordWood.name=木劍 +item.unbreakable=無法破壞的 +item.wheat.name=小麥 +item.writingBook.name=書和羽毛筆 +item.writtenBook.name=完成的書 +item.yellowDust.name=螢石粉 +itemGroup.brewing=釀造類 +itemGroup.buildingBlocks=建材類 +itemGroup.combat=戰鬥用類 +itemGroup.decorations=裝飾類 +itemGroup.food=食品類 +itemGroup.inventory=生存模式背包 +itemGroup.materials=材料類 +itemGroup.misc=雜項類 +itemGroup.redstone=紅石 +itemGroup.search=搜尋物品 +itemGroup.tools=工具類 +itemGroup.transportation=交通工具 +key.attack=攻擊/破壞 +key.back=後退 +key.categories.gameplay=游戲機制 +key.categories.inventory=物品欄 +key.categories.misc=其他 +key.categories.movement=腳色控制 +key.categories.multiplayer=多人遊戲 +key.categories.stream=影音串流 +key.categories.ui=遊戲介面 +key.chat=開啟聊天視窗 +key.command=開啟指令視窗 +key.drop=丟棄物品 +key.forward=往前 +key.fullscreen=切換全屏幕 +key.hotbar.1=物品欄1 +key.hotbar.2=物品欄2 +key.hotbar.3=物品欄3 +key.hotbar.4=物品欄4 +key.hotbar.5=物品欄5 +key.hotbar.6=物品欄6 +key.hotbar.7=物品欄7 +key.hotbar.8=物品欄8 +key.hotbar.9=物品欄9 +key.inventory=物品欄 +key.jump=跳躍 +key.left=往左 +key.mouseButton=滑鼠 %1$s +key.pickItem=選取方塊 +key.playerlist=玩家列表 +key.right=往右 +key.screenshot=截取畫面 +key.smoothCamera=切換視角平滑移動 +key.sneak=潛行 +key.sprint=跑步 +key.streamCommercial=播放串流廣告 +key.streamPauseUnpause=暫停/繼續影音串流 +key.streamStartStop=啟動/停止影音串流 +key.streamToggleMic=按下以說話/靜音 +key.togglePerspective=切換視角 +key.use=使用物品/放置方塊 +lanServer.otherPlayers=對其他玩家的設定 +lanServer.scanning=正在您的區網內尋找可供加入的遊戲 +lanServer.start=建立局域網世界 +lanServer.title=局域網世界 +language.code=zh_TW +language.name=繁體中文 +language.region=台灣 +mcoServer.title=線上Minecraft世界 +menu.convertingLevel=轉換世界中 +menu.disconnect=中斷連線 +menu.game=遊戲目錄 +menu.generatingLevel=生成世界中 +menu.generatingTerrain=地形建構中… +menu.loadingLevel=讀取世界中 +menu.multiplayer=多人遊戲 +menu.online=威廉與境界 +menu.options=選項… +menu.playdemo=試玩 +menu.quit=離開遊戲 +menu.resetdemo=重置試玩的世界 +menu.respawning=重生中… +menu.returnToGame=繼續遊戲 +menu.returnToMenu=儲存並回到標題畫面 +menu.shareToLan=公開至局域網 +menu.simulating=正在模擬世界… +menu.singleplayer=單人遊戲 +menu.switchingLevel=切換世界中 +mount.onboard=按 %1$s 取消乘坐 +multiplayer.connect=連接 +multiplayer.downloadingStats=下載統計及成就記錄中... +multiplayer.downloadingTerrain=正在下載地形… +multiplayer.info1=Minecraft的多人遊戲模式尚未正式完成,但當中的 +multiplayer.info2=一些漏洞已在版本釋出前被測試過。 +multiplayer.ipinfo=請輸入伺服器的IP位址: +multiplayer.player.joined=%s 加入了遊戲 +multiplayer.player.joined.renamed=%s(之前是 %s)加入遊戲 +multiplayer.player.left=%s 離開了遊戲 +multiplayer.stopSleeping=起床 +multiplayer.texturePrompt.line1=此伺服器建議使用該伺服器所提供的資源包。 +multiplayer.texturePrompt.line2=你想要下載並且自動安裝嗎? +multiplayer.title=進行多人遊戲 +options.advancedButton=進階顯示設定... +options.advancedOpengl=進階環境渲染 +options.advancedOpenglDesc0=啟用 occlusion queries。 +options.advancedOpenglDesc1=在使用AMD 和Intel 的電腦,容易導致效能降低。 +options.advancedVideoTitle=進階顯示設定 +options.anaglyph=3D立體模式 +options.anisotropicFiltering=非均向性濾鏡(Anisotropic Filtering) +options.ao=柔和化照明效果 +options.ao.max=最大值 +options.ao.min=最小值 +options.ao.off=關閉 +options.aoDesc0=啟用方塊上光線會被人造物遮蔽。 +options.chat.color=顏色 +options.chat.height.focused=聚焦高度 +options.chat.height.unfocused=未聚焦高度 +options.chat.links=網址連結 +options.chat.links.prompt=連結提示 +options.chat.opacity=不透明度 +options.chat.scale=範圍 +options.chat.title=交談設定... +options.chat.visibility=聊天欄 +options.chat.visibility.full=顯示全部 +options.chat.visibility.hidden=隱藏 +options.chat.visibility.system=只顯示指令 +options.chat.width=寬度 +options.controls=按鍵設定… +options.difficulty=難易度 +options.difficulty.easy=簡單 +options.difficulty.hard=困難 +options.difficulty.hardcore=極限 +options.difficulty.normal=普通 +options.difficulty.peaceful=和平 +options.farWarning1=建議安裝64位元Java +options.farWarning2=以呈現「遠」的視野距離(目前使用的是32位元的Java) +options.fboEnable=啟用 FBOs +options.fboEnableDesc0=啟用緩衝區物件。 +options.fboEnableDesc1=這對Minecraft某些功能是必需的。 +options.forceUnicodeFont=強制使用Unicode字符 +options.fov=廣角視野 +options.fov.max=超廣角 +options.fov.min=普通 +options.framerateLimit=FPS 限制 +options.framerateLimit.max=無限 +options.framerateLimitDesc0=選擇最大FPS +options.framerateLimitDesc1=35 fps、120 fps 或 200 + fps。 +options.fullscreen=全螢幕 +options.gamma=亮度 +options.gamma.max=明亮 +options.gamma.min=昏暗 +options.graphics=畫質 +options.graphics.fancy=畫質優先 +options.graphics.fast=性能優先 +options.graphicsDesc0=「畫質優先」:啟用額外透明度。 +options.graphicsDesc1=「性能優先」:建議較低階電腦使用。 +options.guiScale=介面大小 +options.guiScale.auto=自動 +options.guiScale.large=大 +options.guiScale.normal=標準 +options.guiScale.small=小 +options.hidden=隱藏 +options.invertMouse=滑鼠反轉 +options.language=語言... +options.languageWarning=語言翻譯並不一定是100%%精確 +options.mipmapLevels=多精度材質(Mipmap)等級 +options.multiplayer.title=多人遊戲設定... +options.music=音樂 +options.off=關閉 +options.on=開啟 +options.particles=粒子密度 +options.particles.all=最高 +options.particles.decreased=少量 +options.particles.minimal=最少 +options.particlesDesc0=選擇粒子的總數。 +options.particlesDesc1=對低階電腦,值越低越好。 +options.performanceButton=顯示效能設定... +options.performanceVideoTitle=顯示效能設定 +options.postButton=後處理(post-processing)設定... +options.postProcessEnable=啟用後處理(Post-Processing) +options.postProcessEnableDesc0=啟用後處理(post-processing)。禁用將會 +options.postProcessEnableDesc1=導致畫質大幅下降。 +options.postVideoTitle=後處理(post-processing)設定 +options.qualityButton=顯示品質設定... +options.qualityVideoTitle=顯示品質設定 +options.renderClouds=雲塊 +options.renderCloudsDesc0=啟用雲的渲染。 +options.renderDistance=視野距離 +options.renderDistance.far=遠 +options.renderDistance.normal=一般 +options.renderDistance.short=近 +options.renderDistance.tiny=最近 +options.renderDistanceDesc0=最大視野距離。數值越小 +options.renderDistanceDesc1=在低階電腦上運行效果越好。 +options.resourcepack=資源包... +options.saturation=飽食度 +options.sensitivity=滑鼠靈敏度 +options.sensitivity.max=超高速!!! +options.sensitivity.min=*哈欠* +options.showCape=顯示披風 +options.snooper=允許搜集電腦資訊 +options.snooper.desc=我們希望可以透過搜集您電腦的相關資訊,協助改善 Minecraft 並修正程式錯誤。 您可以在下面查看我們所希望搜集到的資料,這些資料均為匿名傳送,並保證不會被用於不當場合。 若您覺得不妥,可以隨時將這個功能關閉。 +options.snooper.title=電腦相關資訊搜集 +options.snooper.view=電腦資訊搜集... +options.sound=音效 +options.sounds=音樂和音效 +options.sounds.title=音樂和音效選項 +options.stream=轉播設定... +options.stream.bytesPerPixel=品質 +options.stream.changes=你可能需要重新啟動影音串流,方可套用變更。 +options.stream.chat.enabled=啟用 +options.stream.chat.enabled.always=永遠 +options.stream.chat.enabled.never=永不 +options.stream.chat.enabled.streaming=僅在串流時 +options.stream.chat.title=Twitch聊天設定 +options.stream.chat.userFilter=用戶過濾 +options.stream.chat.userFilter.all=所有觀眾 +options.stream.chat.userFilter.mods=僅版主 +options.stream.chat.userFilter.subs=僅訂閱者 +options.stream.compression=壓縮率 +options.stream.compression.high=高 +options.stream.compression.low=低 +options.stream.compression.medium=中 +options.stream.estimation=預計解析度: %sx%s +options.stream.fps=畫面更新率 +options.stream.ingest.reset=重置設定 +options.stream.ingest.title=Twitch直播伺服器 +options.stream.ingestSelection=廣播伺服器列表 +options.stream.kbps=頻寬 +options.stream.micToggleBehavior=按下以 +options.stream.micVolumne=麥克風音量 +options.stream.mic_toggle.mute=靜音 +options.stream.mic_toggle.talk=說話 +options.stream.sendMetadata=傳送後設資料 +options.stream.systemVolume=系統音量 +options.stream.title=Twitch 轉播設定 +options.title=選項 +options.touchscreen=觸控模式 +options.video=顯示設定… +options.videoTitle=顯示設定 +options.viewBobbing=走路晃動 +options.viewBobbingDesc0=啟用走路晃動 +options.visible=顯示 +options.vsync=使用垂直同步 +potion.absorption=吸收 +potion.absorption.postfix=傷害吸收藥水 +potion.blindness=失明 +potion.blindness.postfix=失明藥水 +potion.confusion=噁心 +potion.confusion.postfix=噁心藥水 +potion.damageBoost=力量 +potion.damageBoost.postfix=力量藥水 +potion.digSlowDown=挖掘減速 +potion.digSlowDown.postfix=遲緩藥水 +potion.digSpeed=挖掘加速 +potion.digSpeed.postfix=速掘藥水 +potion.effects.whenDrank=使用後會得到: +potion.empty=無效果 +potion.fireResistance=抗火性 +potion.fireResistance.postfix=抗火藥水 +potion.harm=立即傷害 +potion.harm.postfix=傷害藥水 +potion.heal=立即治療 +potion.heal.postfix=治療藥水 +potion.healthBoost=生命值提升 +potion.healthBoost.postfix=生命值藥水 +potion.hunger=飢餓 +potion.hunger.postfix=飢餓藥水 +potion.invisibility=隱身 +potion.invisibility.postfix=隱形藥水 +potion.jump=跳躍提升 +potion.jump.postfix=跳躍藥水 +potion.moveSlowdown=移動減速 +potion.moveSlowdown.postfix=緩速藥水 +potion.moveSpeed=移動加速 +potion.moveSpeed.postfix=迅捷藥水 +potion.nightVision=夜視 +potion.nightVision.postfix=夜視藥水 +potion.poison=中毒 +potion.poison.postfix=劇毒藥水 +potion.potency.1=II +potion.potency.2=III +potion.potency.3=IV +potion.prefix.acrid=刺鼻的 +potion.prefix.artless=樸實的 +potion.prefix.awkward=基礎 +potion.prefix.bland=溫和的 +potion.prefix.bulky=笨重的 +potion.prefix.bungling=笨拙的 +potion.prefix.buttered=脂狀的 +potion.prefix.charming=迷人的 +potion.prefix.clear=清澈的 +potion.prefix.cordial=甜膩的 +potion.prefix.dashing=華麗的 +potion.prefix.debonair=無慮的 +potion.prefix.diffuse=瀰漫的 +potion.prefix.elegant=典雅的 +potion.prefix.fancy=花俏的 +potion.prefix.flat=單調的 +potion.prefix.foul=汙穢的 +potion.prefix.grenade=飛濺 +potion.prefix.gross=噁心的 +potion.prefix.harsh=劣質的 +potion.prefix.milky=混濁的 +potion.prefix.mundane=平凡的 +potion.prefix.odorless=無臭 +potion.prefix.potent=濃烈的 +potion.prefix.rank=惡臭的 +potion.prefix.refined=精緻的 +potion.prefix.smooth=攪勻的 +potion.prefix.sparkling=發光的 +potion.prefix.stinky=異味的 +potion.prefix.suave=嫻淑的 +potion.prefix.thick=黏稠的 +potion.prefix.thin=稀薄的 +potion.prefix.uninteresting=乏味的 +potion.regeneration=回復 +potion.regeneration.postfix=回復藥水 +potion.resistance=抗性 +potion.resistance.postfix=抗性藥水 +potion.saturation=飽食 +potion.saturation.postfix=飽食藥水 +potion.waterBreathing=水下呼吸 +potion.waterBreathing.postfix=水下呼吸藥水 +potion.weakness=虛弱 +potion.weakness.postfix=虛弱藥水 +potion.wither=凋零 +potion.wither.postfix=腐朽藥水 +record.nowPlaying=正在播放: %s +resourcePack.available.title=可用的資源包 +resourcePack.folderInfo=(請將資源包置於此處) +resourcePack.openFolder=開啓資源包資料夾 +resourcePack.selected.title=已選擇的資源包 +resourcePack.title=選擇資源包 +screenshot.failure=無法儲存截圖: %s +screenshot.success=已截圖存檔為 %s +selectServer.add=新增伺服器 +selectServer.defaultName=Minecraft 伺服器 +selectServer.delete=刪除 +selectServer.deleteButton=刪除 +selectServer.deleteQuestion=確定要刪除這個伺服器嗎? +selectServer.deleteWarning=將會永遠消失! +selectServer.direct=直接連線 +selectServer.edit=編輯 +selectServer.empty=空 +selectServer.hiddenAddress=(隱藏) +selectServer.refresh=重新整理 +selectServer.select=加入伺服器 +selectServer.title=選擇伺服器 +selectWorld.allowCommands=允許作弊: +selectWorld.allowCommands.info=允許使用 /gamemode、/xp 等指令 +selectWorld.bonusItems=新手獎勵箱: +selectWorld.cheats=允許作弊 +selectWorld.conversion=必須被轉換! +selectWorld.create=創建新世界 +selectWorld.createDemo=進入新的試玩世界 +selectWorld.customizeType=自訂 +selectWorld.delete=刪除 +selectWorld.deleteButton=刪除 +selectWorld.deleteQuestion=你確定要刪除這個世界嗎? +selectWorld.deleteWarning=將會永遠消失! +selectWorld.empty=空 +selectWorld.enterName=世界名稱 +selectWorld.enterSeed=世界種子碼 +selectWorld.gameMode=遊戲模式 +selectWorld.gameMode.adventure=冒險 +selectWorld.gameMode.adventure.line1=同生存模式,但不能放置或移除方塊 +selectWorld.gameMode.adventure.line2=被增加或移除 +selectWorld.gameMode.creative=創造 +selectWorld.gameMode.creative.line1=無限資源、自由飛行、 +selectWorld.gameMode.creative.line2=並且能夠瞬間破壞方塊 +selectWorld.gameMode.hardcore=極限 +selectWorld.gameMode.hardcore.line1=同生存模式,但鎖定在最高難度 +selectWorld.gameMode.hardcore.line2=並且只有一次生命 +selectWorld.gameMode.survival=生存 +selectWorld.gameMode.survival.line1=獲取資源、合成製造、 +selectWorld.gameMode.survival.line2=等級、生命值和飢餓值 +selectWorld.hardcoreMode=極限: +selectWorld.hardcoreMode.info=死亡後將刪除世界 +selectWorld.mapFeatures=生成建築物: +selectWorld.mapFeatures.info=村莊、地牢等 +selectWorld.mapType=世界類型: +selectWorld.mapType.normal=普通 +selectWorld.moreWorldOptions=更多世界選項… +selectWorld.newWorld=新的世界 +selectWorld.newWorld.copyOf=%s 的拷貝 +selectWorld.recreate=重新創造 +selectWorld.rename=重新命名 +selectWorld.renameButton=重新命名 +selectWorld.renameTitle=重新命名世界 +selectWorld.resultFolder=將被儲存於: +selectWorld.seedInfo=若不填則隨機生成種子碼 +selectWorld.select=進入所選的世界 +selectWorld.title=選擇世界 +selectWorld.world=世界 +sign.edit=編輯告示牌文字 +soundCategory.ambient=環境音效/環境 +soundCategory.block=方塊 +soundCategory.hostile=敵對生物 +soundCategory.master=主音量 +soundCategory.music=音樂 +soundCategory.neutral=友好生物 +soundCategory.player=玩家 +soundCategory.record=唱片機/音符盒 +soundCategory.weather=天氣 +stat.animalsBred=養殖動物次數 +stat.blocksButton=方塊 +stat.boatOneCm=坐船航行距離 +stat.breakItem=%1$s個工具被消耗 +stat.climbOneCm=攀爬高度 +stat.craftItem=%1$s個物品被合成 +stat.crafted=合成次數 +stat.createWorld=世界數量 +stat.damageDealt=傷害總量 +stat.damageTaken=總承受傷害量 +stat.deaths=死亡次數 +stat.depleted=消耗次數 +stat.diveOneCm=潛水距離 +stat.drop=丟棄物品數量 +stat.entityKilledBy=%s 殺了你 %s 次 +stat.entityKilledBy.none=你沒有被%s 殺死過 +stat.entityKills=你殺了%s %s 次 +stat.entityKills.none=你沒有殺死過%s +stat.fallOneCm=掉落高度 +stat.fishCaught=捕獲魚量 +stat.flyOneCm=飛行距離 +stat.generalButton=一般 +stat.horseOneCm=騎馬移動距離 +stat.itemsButton=物品 +stat.joinMultiplayer=加入多人遊戲次數 +stat.jump=跳躍次數 +stat.junkFished=釣中垃圾次數 +stat.leaveGame=離開遊戲次數 +stat.loadWorld=讀檔次數 +stat.mineBlock=挖掘了%1$s個方塊 +stat.minecartOneCm=乘坐礦車距離 +stat.mined=挖掘次數 +stat.mobKills=生物擊殺數 +stat.mobsButton=生物 +stat.pigOneCm=騎豬移動距離 +stat.playOneMinute=遊戲時間(分鐘) +stat.playerKills=玩家擊殺數 +stat.startGame=遊玩次數 +stat.swimOneCm=游泳距離 +stat.treasureFished=釣中寶物次數 +stat.useItem=使用了%1$s個物品 +stat.used=使用次數 +stat.walkOneCm=行走距離 +stats.tooltip.type.achievement=成就 +stats.tooltip.type.statistic=統計資訊 +stream.confirm_start=您確定要開始實況? +stream.unavailable.account_not_bound=要用Twitch直播你的Minecraft遊戲實況,你需要透過mojang.com連接你的Twitch帳戶。你要現在完成這項設定嗎? +stream.unavailable.account_not_bound.okay=連繫帳戶 +stream.unavailable.account_not_migrated=在您通過 Twitch 直播 Minecraft 之前,您需要將您的 Minecraft 帳戶遷移到 Mojang 帳戶上。你想現在這麼做嗎? +stream.unavailable.account_not_migrated.okay=遷移帳戶 +stream.unavailable.failed_auth=Twitch驗證失敗。請到mojang.com重新連繫你的Twitch帳戶。 +stream.unavailable.failed_auth.okay=重新連繫帳戶 +stream.unavailable.failed_auth_error=無法進行Twitch驗證。請稍後重試。 +stream.unavailable.initialization_failure=無法初始化Twitch SDK。 +stream.unavailable.initialization_failure.extra=(原因: %s) +stream.unavailable.library_arch_mismatch=用來啟動Minecraft的自訂Java版本所採用的系統結構,與Minecraft啟動器並不相符。請確認兩者是否同樣使用32位元或64位元的系統結構。 +stream.unavailable.library_failure=無法載入用作Twitch實況轉播整合功能的函式庫。 +stream.unavailable.no_fbo=你的顯示卡必須最少支援OpenGL 3.0版本,或透過擴展元件以支援Framebuffer Objects,方可使用綜合式Twitch直播。 +stream.unavailable.no_fbo.arb=通過 ARB 支援的框架緩存物件是: %s +stream.unavailable.no_fbo.blend=通過 EXT 支援的單獨混合是: %s +stream.unavailable.no_fbo.ext=通過 EXT 支援的框架緩存物件是: %s +stream.unavailable.no_fbo.version=當前版本: %s +stream.unavailable.not_supported.mac=很可惜,Mac上的Twitch遊戲直播整合功能需要採用較新版本的OS X。你必須使用10.7 (Mac OS X Lion) 或更高版本,方可使用本服務。你想現在瀏覽apple.com了解升級的詳請嗎? +stream.unavailable.not_supported.mac.okay=升級 +stream.unavailable.not_supported.other=很可惜,Twitch遊戲直播整合服務需要採用Windows (Vista或更高版本) 或 Mac OS X (10.7/Lion或更高版本)。 +stream.unavailable.not_supported.windows=很可惜,Twitch遊戲直播整合功能需要採用較新版本的Windows。你必須使用Windows Vista或更高版本。 +stream.unavailable.report_to_mojang=向Mojang回報 +stream.unavailable.soundflower.chat=必須安裝Soundflower以於Mac上啟動串流。%s +stream.unavailable.soundflower.chat.link=請按此安裝。 +stream.unavailable.title=Twitch轉播暫停服務 +stream.unavailable.unknown=可惜,你現在不可以用Twitch直播。而且我們並不知道它的原因。:'( +stream.unavailable.unknown.chat=無法啟動串流: %s +stream.user.mode.administrator=Twitch 管理人 +stream.user.mode.banned=已封禁 +stream.user.mode.banned.other=已在%s的頻道上封禁 +stream.user.mode.banned.self=已在您的頻道上封禁 +stream.user.mode.broadcaster=主持人 +stream.user.mode.broadcaster.other=主持人 +stream.user.mode.broadcaster.self=主持人 (你!) +stream.user.mode.moderator=版主 +stream.user.mode.moderator.other=%s頻道上的版主 +stream.user.mode.moderator.self=您的頻道版主 +stream.user.mode.staff=Twitch 員工 +stream.user.subscription.subscriber=訂閱者 +stream.user.subscription.subscriber.other=%s頻道的訂閱者 +stream.user.subscription.subscriber.self=您頻道的訂閱者 +stream.user.subscription.turbo=Twitch Turbo +stream.userinfo.ban=封禁 +stream.userinfo.chatTooltip=按此管理用戶 +stream.userinfo.mod=擢升為版主 +stream.userinfo.timeout=逾時 +stream.userinfo.unban=解除封禁 +stream.userinfo.unmod=將版主降級 +tile.activatorRail.name=觸發鐵軌 +tile.anvil.intact.name=鐵砧 +tile.anvil.name=鐵砧 +tile.anvil.slightlyDamaged.name=輕微損耗的鐵砧 +tile.anvil.veryDamaged.name=嚴重損耗的鐵砧 +tile.beacon.name=烽火台 +tile.beacon.primary=主要效果 +tile.beacon.secondary=次要效果 +tile.bed.name=床 +tile.bed.noSleep=你只能在晚上睡覺 +tile.bed.notSafe=有怪物在附近遊蕩,無法入睡 +tile.bed.notValid=你的床已遺失或被阻擋 +tile.bed.occupied=這張床已有人使用 +tile.bedrock.name=基岩 +tile.blockCoal.name=煤炭磚 +tile.blockDiamond.name=鑽石磚 +tile.blockEmerald.name=綠寶石磚 +tile.blockGold.name=金磚 +tile.blockIron.name=鐵磚 +tile.blockLapis.name=青金石磚 +tile.blockRedstone.name=紅石磚 +tile.bookshelf.name=書櫃 +tile.brick.name=紅磚 +tile.button.name=按鈕 +tile.cactus.name=仙人掌 +tile.cake.name=蛋糕 +tile.carrots.name=胡蘿蔔 +tile.cauldron.name=鍋釜 +tile.chest.name=儲物箱 +tile.chestTrap.name=陷阱儲物箱 +tile.clay.name=黏土塊 +tile.clayHardened.name=硬化黏土 +tile.clayHardenedStained.black.name=黑色黏土塊 +tile.clayHardenedStained.blue.name=藍色黏土塊 +tile.clayHardenedStained.brown.name=棕色黏土塊 +tile.clayHardenedStained.cyan.name=青色黏土塊 +tile.clayHardenedStained.gray.name=灰色黏土塊 +tile.clayHardenedStained.green.name=綠色黏土塊 +tile.clayHardenedStained.lightBlue.name=淺藍黏土塊 +tile.clayHardenedStained.lime.name=黃綠色黏土塊 +tile.clayHardenedStained.magenta.name=洋紅黏土塊 +tile.clayHardenedStained.orange.name=橘色黏土塊 +tile.clayHardenedStained.pink.name=粉紅黏土塊 +tile.clayHardenedStained.purple.name=紫色黏土塊 +tile.clayHardenedStained.red.name=紅色黏土塊 +tile.clayHardenedStained.silver.name=淺灰黏土塊 +tile.clayHardenedStained.white.name=白色黏土塊 +tile.clayHardenedStained.yellow.name=黃色黏土塊 +tile.cloth.black.name=黑色羊毛 +tile.cloth.blue.name=藍色羊毛 +tile.cloth.brown.name=棕色羊毛 +tile.cloth.cyan.name=青色羊毛 +tile.cloth.gray.name=灰色羊毛 +tile.cloth.green.name=綠色羊毛 +tile.cloth.lightBlue.name=淺藍羊毛 +tile.cloth.lime.name=淺綠羊毛 +tile.cloth.magenta.name=桃紅羊毛 +tile.cloth.name=羊毛 +tile.cloth.orange.name=橘色羊毛 +tile.cloth.pink.name=粉紅羊毛 +tile.cloth.purple.name=紫色羊毛 +tile.cloth.red.name=紅色羊毛 +tile.cloth.silver.name=淺灰色羊毛 +tile.cloth.white.name=羊毛 +tile.cloth.yellow.name=黃色羊毛 +tile.cobbleWall.mossy.name=青苔石牆 +tile.cobbleWall.normal.name=鵝卵石牆 +tile.cocoa.name=可可豆 +tile.commandBlock.name=指令方塊 +tile.crops.name=農作物 +tile.daylightDetector.name=陽光感測器 +tile.deadbush.name=枯灌木 +tile.detectorRail.name=壓力鐵軌 +tile.dirt.default.name=泥土 +tile.dirt.podzol.name=灰壤 +tile.dispenser.name=發射器 +tile.doorIron.name=鐵門 +tile.doorWood.name=木門 +tile.doublePlant.fern.name=大型蕨類 +tile.doublePlant.grass.name=芒草 +tile.doublePlant.paeonia.name=牡丹花 +tile.doublePlant.rose.name=玫瑰叢 +tile.doublePlant.sunflower.name=向日葵 +tile.doublePlant.syringa.name=紫丁香 +tile.dragonEgg.name=龍蛋 +tile.dropper.name=投擲器 +tile.enchantmentTable.name=附魔台 +tile.endPortalFrame.name=終界傳送門 +tile.enderChest.name=終界箱 +tile.farmland.name=耕地 +tile.fence.name=柵欄 +tile.fenceGate.name=柵欄門 +tile.fenceIron.name=鐵欄杆 +tile.fire.name=火 +tile.flower1.dandelion.name=蒲公英 +tile.flower2.allium.name=紫紅球花 +tile.flower2.blueOrchid.name=藍色蝴蝶蘭 +tile.flower2.houstonia.name=雛草 +tile.flower2.oxeyeDaisy.name=雛菊 +tile.flower2.poppy.name=罌粟 +tile.flower2.tulipOrange.name=橘色鬱金香 +tile.flower2.tulipPink.name=粉紅色鬱金香 +tile.flower2.tulipRed.name=紅色鬱金香 +tile.flower2.tulipWhite.name=白色鬱金香 +tile.furnace.name=熔爐 +tile.glass.name=玻璃 +tile.goldenRail.name=動力鐵軌 + +tile.grass.name=草地 +tile.gravel.name=礫石 +tile.hayBlock.name=乾草捆 +tile.hellrock.name=地獄石 +tile.hellsand.name=靈魂砂 +tile.hopper.name=漏斗 +tile.ice.name=冰 +tile.icePacked.name=冰磚 +tile.jukebox.name=唱片機 +tile.ladder.name=梯子 +tile.lava.name=岩漿 +tile.leaves.acacia.name=相思木樹葉 +tile.leaves.big_oak.name=黑橡木樹葉 +tile.leaves.birch.name=樺木樹葉 +tile.leaves.jungle.name=叢林木樹葉 +tile.leaves.name=樹葉 +tile.leaves.oak.name=橡木樹葉 +tile.leaves.spruce.name=杉木樹葉 +tile.lever.name=控制桿 +tile.lightgem.name=螢光石 +tile.litpumpkin.name=南瓜燈 +tile.lockedchest.name=上鎖的箱子 +tile.log.acacia.name=相思原木 +tile.log.big_oak.name=黑橡木原木 +tile.log.birch.name=樺木原木 +tile.log.jungle.name=叢林原木 +tile.log.name=原木 +tile.log.oak.name=橡木原木 +tile.log.spruce.name=杉木原木 +tile.melon.name=西瓜磚 +tile.mobSpawner.name=生怪磚 +tile.monsterStoneEgg.brick.name=石磚怪物蛋 +tile.monsterStoneEgg.chiseledbrick.name=浮雕石磚怪物蛋 +tile.monsterStoneEgg.cobble.name=鵝卵石怪物蛋 +tile.monsterStoneEgg.crackedbrick.name=裂石磚怪物蛋 +tile.monsterStoneEgg.mossybrick.name=青苔石磚怪物蛋 +tile.monsterStoneEgg.stone.name=石頭怪物蛋 +tile.mushroom.name=蘑菇 +tile.musicBlock.name=音階盒 +tile.mycel.name=菌絲土 +tile.netherBrick.name=地獄磚 +tile.netherFence.name=地獄柵欄 +tile.netherStalk.name=地獄疙瘩 +tile.netherquartz.name=地獄石英礦 +tile.notGate.name=紅石火把 +tile.obsidian.name=黑曜石 +tile.oreCoal.name=煤礦 +tile.oreDiamond.name=鑽石礦 +tile.oreEmerald.name=綠寶石礦 +tile.oreGold.name=金礦 +tile.oreIron.name=鐵礦 +tile.oreLapis.name=青金石礦 +tile.oreRedstone.name=紅石礦 +tile.oreRuby.name=紅寶石礦 +tile.pistonBase.name=活塞 +tile.pistonStickyBase.name=黏性活塞 +tile.portal.name=傳送門 +tile.potatoes.name=馬鈴薯 +tile.pressurePlate.name=壓力板 +tile.pumpkin.name=南瓜 +tile.quartzBlock.chiseled.name=浮雕石英磚 +tile.quartzBlock.default.name=石英磚 +tile.quartzBlock.lines.name=柱狀石英磚 +tile.rail.name=鐵軌 +tile.redstoneDust.name=紅石粉 +tile.redstoneLight.name=紅石燈 +tile.reeds.name=甘蔗 +tile.sand.default.name=沙 +tile.sand.red.name=紅沙 +tile.sandStone.chiseled.name=浮雕砂岩 +tile.sandStone.default.name=砂岩 +tile.sandStone.name=砂岩 +tile.sandStone.smooth.name=平滑砂岩 +tile.sapling.acacia.name=相思木樹苗 +tile.sapling.birch.name=樺木樹苗 +tile.sapling.jungle.name=叢林木樹苗 +tile.sapling.oak.name=橡木樹苗 +tile.sapling.roofed_oak.name=黑橡木樹苗 +tile.sapling.spruce.name=杉木樹苗 +tile.sign.name=告示牌 +tile.snow.name=雪 +tile.sponge.name=海綿 +tile.stainedGlass.black.name=黑色玻璃 +tile.stainedGlass.blue.name=藍色玻璃 +tile.stainedGlass.brown.name=棕色玻璃 +tile.stainedGlass.cyan.name=青色玻璃 +tile.stainedGlass.gray.name=灰色玻璃 +tile.stainedGlass.green.name=綠色玻璃 +tile.stainedGlass.lightBlue.name=淺藍色玻璃 +tile.stainedGlass.lime.name=淺綠色玻璃 +tile.stainedGlass.magenta.name=桃紅色玻璃 +tile.stainedGlass.name=染色玻璃 +tile.stainedGlass.orange.name=橘色玻璃 +tile.stainedGlass.pink.name=粉紅色玻璃 +tile.stainedGlass.purple.name=紫色玻璃 +tile.stainedGlass.red.name=紅色玻璃 +tile.stainedGlass.silver.name=淺灰色玻璃 +tile.stainedGlass.white.name=白色玻璃 +tile.stainedGlass.yellow.name=黃色玻璃 +tile.stairsBrick.name=紅磚階梯 +tile.stairsNetherBrick.name=地獄磚階梯 +tile.stairsQuartz.name=石英階梯 +tile.stairsSandStone.name=砂岩樓梯 +tile.stairsStone.name=石樓梯 +tile.stairsStoneBrickSmooth.name=石磚階梯 +tile.stairsWood.name=橡木樓梯 +tile.stairsWoodAcacia.name=相思木樓梯 +tile.stairsWoodBirch.name=樺木樓梯 +tile.stairsWoodDarkOak.name=黑橡木樓梯 +tile.stairsWoodJungle.name=叢林木樓梯 +tile.stairsWoodSpruce.name=杉木樓梯 +tile.stone.name=石頭 +tile.stoneMoss.name=青苔石 +tile.stoneSlab.brick.name=紅磚半磚 +tile.stoneSlab.cobble.name=鵝卵石半磚 +tile.stoneSlab.netherBrick.name=地獄磚半磚 +tile.stoneSlab.quartz.name=石英半磚 +tile.stoneSlab.sand.name=砂岩半磚 +tile.stoneSlab.smoothStoneBrick.name=石磚半磚 +tile.stoneSlab.stone.name=石半磚 +tile.stoneSlab.wood.name=木半磚 +tile.stonebrick.name=鵝卵石 +tile.stonebricksmooth.chiseled.name=浮雕石磚 +tile.stonebricksmooth.cracked.name=裂石磚 +tile.stonebricksmooth.default.name=石磚 +tile.stonebricksmooth.mossy.name=青苔石磚 +tile.stonebricksmooth.name=石磚 +tile.tallgrass.fern.name=蕨 +tile.tallgrass.grass.name=草 +tile.tallgrass.name=草 +tile.tallgrass.shrub.name=灌木 +tile.thinGlass.name=玻璃片 +tile.thinStainedGlass.black.name=黑色玻璃片 +tile.thinStainedGlass.blue.name=藍色玻璃片 +tile.thinStainedGlass.brown.name=棕色玻璃片 +tile.thinStainedGlass.cyan.name=青色玻璃片 +tile.thinStainedGlass.gray.name=灰色玻璃片 +tile.thinStainedGlass.green.name=綠色玻璃片 +tile.thinStainedGlass.lightBlue.name=淺藍色玻璃片 +tile.thinStainedGlass.lime.name=淺綠色玻璃片 +tile.thinStainedGlass.magenta.name=洋紅玻璃片 +tile.thinStainedGlass.name=染色玻璃片 +tile.thinStainedGlass.orange.name=橘色玻璃片 +tile.thinStainedGlass.pink.name=粉紅玻璃片 +tile.thinStainedGlass.purple.name=紫色玻璃片 +tile.thinStainedGlass.red.name=紅色玻璃片 +tile.thinStainedGlass.silver.name=淡灰玻璃片 +tile.thinStainedGlass.white.name=白色玻璃片 +tile.thinStainedGlass.yellow.name=黃色玻璃片 +tile.tnt.name=TNT +tile.torch.name=火把 +tile.trapdoor.name=地板門 +tile.tripWire.name=絆線 +tile.tripWireSource.name=絆線鉤 +tile.vine.name=藤蔓 +tile.water.name=水 +tile.waterlily.name=荷葉 +tile.web.name=蜘蛛網 +tile.weightedPlate_heavy.name=感重壓力板(重) +tile.weightedPlate_light.name=感重壓力板(輕) +tile.whiteStone.name=終界石 +tile.wood.acacia.name=相思木材 +tile.wood.big_oak.name=黑橡木材 +tile.wood.birch.name=樺木材 +tile.wood.jungle.name=叢林木材 +tile.wood.name=木材 +tile.wood.oak.name=橡木材 +tile.wood.spruce.name=杉木材 +tile.woodSlab.acacia.name=相思木板 +tile.woodSlab.big_oak.name=黑橡木板 +tile.woodSlab.birch.name=樺木半磚 +tile.woodSlab.jungle.name=叢林木半磚 +tile.woodSlab.oak.name=橡木半磚 +tile.woodSlab.spruce.name=杉木半磚 +tile.woolCarpet.black.name=黑色地毯 +tile.woolCarpet.blue.name=藍色地毯 +tile.woolCarpet.brown.name=棕色地毯 +tile.woolCarpet.cyan.name=青色地毯 +tile.woolCarpet.gray.name=灰色地毯 +tile.woolCarpet.green.name=綠色地毯 +tile.woolCarpet.lightBlue.name=淺藍色地毯 +tile.woolCarpet.lime.name=萊姆綠地毯 +tile.woolCarpet.magenta.name=洋紅地毯 +tile.woolCarpet.orange.name=橘色地毯 +tile.woolCarpet.pink.name=粉紅地毯 +tile.woolCarpet.purple.name=紫色地毯 +tile.woolCarpet.red.name=紅色地毯 +tile.woolCarpet.silver.name=淺灰色地毯 +tile.woolCarpet.white.name=地毯 +tile.woolCarpet.yellow.name=黃色地毯 +tile.workbench.name=工作台 +title.oldgl1=偵測到舊版顯示卡;這會妨礙你 +title.oldgl2=在未來需要使用OpenGL 2.0版本時繼續遊戲。 +translation.test.args=%s %s +translation.test.complex=前期修正 的 2$ s 再次 %s 和 %1$s 最後 %s 和 %1$s 再次 ! +translation.test.escape=%%s %%%s %%%%s %%%%%s +translation.test.invalid= % 你好 +translation.test.invalid2=你好 %s +translation.test.none=世界,你好! +translation.test.world=世界 diff --git a/src/main/resource/plugin.yml b/src/main/resource/plugin.yml new file mode 100644 index 0000000..044b42d --- /dev/null +++ b/src/main/resource/plugin.yml @@ -0,0 +1,156 @@ +name: BossShop +version: 2.2.3 +description: The BOSS Shop Plugin redone +author: Black_ixx +website: http://felix.neuby.de +main: org.black_ixx.bossshop.BossShop +softdepend: [PlaceholderAPI] +commands: + bs: + description: BossShop Command + bossshop: + description: BossShop Command + shop: + description: BossShop Command +permissions: + BossShop.createSign: + description: Allows to create BossShop Signs + default: op + + BossShop: + description: BossShop全部权限 + default: op + children: + BossShop.cmdcomplete: true + BossShop.help: true + BossShop.mail: true + BossShop.nbt: true + BossShop.open: true + BossShop.reload: true + BossShop.sale: true + BossShop.status.clear: true + BossShop.unsale: true + BossShop.cmdcomplete: + default: true + BossShop.help: + default: true + BossShop.nbt: + description: 操作BooShop NBT库存的相关权限 + default: op + children: + BossShop.nbt.add: true + BossShop.nbt.clear: true + BossShop.nbt.help: true + BossShop.nbt.add: + default: op + BossShop.nbt.clear: + default: op + BossShop.nbt.help: + default: op + BossShop.mail: + description: 邮件命令的相关权限 + default: op + children: + BossShop.mail.help: true + BossShop.mail.check: true + BossShop.mail.recive: true + BossShop.mail.send: true + BossShop.mail.check: + description: 检查邮件的权限 + default: true + BossShop.mail.help: + description: 获取邮件命令帮助的权限 + default: true + BossShop.mail.recive: + description: 接收邮件的权限 + default: true + BossShop.mail.send: + description: 发送邮件的权限 + default: op + BossShop.open: + description: 打开商店的权限 + default: op + children: + BossShop.open.sign: true + BossShop.open.command: true + BossShop.open.item: true + BossShop.open.other: true + BossShop.open.sign: + description: 允许通过牌子打开商店 + default: true + BossShop.open.command: + description: 允许通过命令打开商店 + default: true + BossShop.open.other: + description: 允许打开其他玩家的商店 + default: op + BossShop.open.item: + description: 允许通过物品来打开商店 + default: op + BossShop.reload: + description: 重载配置 + default: op + children: + BossShop.reload.all: true + BossShop.reload.help: true + BossShop.reload.lang: true + BossShop.reload.shop: true + BossShop.reload.all: + description: 重载BossShop所有配置的权限 + default: op + BossShop.reload.config: + description: 重载BossShop商店配置的权限 + default: op + BossShop.reload.help: + description: 获取/BS reload命令帮助的权限 + default: op + BossShop.reload.lang: + description: 重载BossShop语言文件 + default: op + BossShop.reload.shop: + description: 重载BossShop商店的权限 + default: op + BossShop.sale: + description: BooShop 寄售系统相关权限 + default: op + children: + BossShop.sale.forsale: true + BossShop.sale.unlimited: true + BossShop.sale.forsale: + description: 允许玩家寄售物品 + default: op + BossShop.sale.announce.other: + description: 物品上架时发送全服公告的权限 + default: op + BossShop.sale.unlimited: + description: 无视玩家寄售物品数量的上限 + default: op + BossShop.status.clear: + description: 清理插件缓存数据的权限 + default: op + BossShop.unsale: + description: 物品下架 + default: op + children: + BossShop.unsale.user: true + BossShop.unsale.admin: true + BossShop.unsale.user: + description: 用户物品下架自己权限 + default: true + BossShop.unsale.admin: + description: 管理员下架物品权限 + default: op + + + + + + + + + + + + + + \ No newline at end of file