diff --git a/.gitignore b/.gitignore index 3e757f6..6136bf5 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,4 @@ hs_err_pid* .gradle/4.3.1/ .idea -target +target \ No newline at end of file diff --git a/TabooLib.iml b/TabooLib.iml index 88212e0..e1d93e7 100644 --- a/TabooLib.iml +++ b/TabooLib.iml @@ -26,7 +26,6 @@ - diff --git a/pom.xml b/pom.xml index 7653b83..c21d414 100644 --- a/pom.xml +++ b/pom.xml @@ -72,12 +72,6 @@ activejdbc 2.0 - - javax.servlet - servlet-api - 2.5 - provided - com.h2database h2 diff --git a/src/main/java/com/ilummc/tlib/inject/TConfigWatcher.java b/src/main/java/com/ilummc/tlib/inject/TConfigWatcher.java index 34d67b0..c33e1d6 100644 --- a/src/main/java/com/ilummc/tlib/inject/TConfigWatcher.java +++ b/src/main/java/com/ilummc/tlib/inject/TConfigWatcher.java @@ -19,7 +19,8 @@ import java.util.function.Consumer; */ public class TConfigWatcher { - private final ScheduledExecutorService service = Executors.newScheduledThreadPool(1, new BasicThreadFactory.Builder().namingPattern("tconfig-watcher-schedule-pool-%d").daemon(true).build()); + private final ScheduledExecutorService service = Executors.newScheduledThreadPool(1, + new BasicThreadFactory.Builder().namingPattern("TConfigWatcherService-%d").build()); private final Map>> map = new HashMap<>(); @@ -54,7 +55,16 @@ public class TConfigWatcher { public void removeListener(File file) { synchronized (map) { - map.entrySet().removeIf(entry -> entry.getValue().getLeft().equals(file)); + map.entrySet().removeIf(entry -> { + if (entry.getValue().getLeft().equals(file)) { + try { + entry.getKey().close(); + } catch (IOException ignored) { + } + return true; + } + return false; + }); } } diff --git a/src/main/java/com/ilummc/tlib/resources/TLocaleInstance.java b/src/main/java/com/ilummc/tlib/resources/TLocaleInstance.java index 1a5371a..d1729d6 100644 --- a/src/main/java/com/ilummc/tlib/resources/TLocaleInstance.java +++ b/src/main/java/com/ilummc/tlib/resources/TLocaleInstance.java @@ -15,8 +15,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Collectors; @@ -24,9 +22,8 @@ import java.util.stream.Collectors; @SuppressWarnings("rawtypes") class TLocaleInstance { - private final Map> map = new ConcurrentHashMap<>(); + private final Map> map = new HashMap<>(); private final Plugin plugin; - private final AtomicInteger updateNodes = new AtomicInteger(); TLocaleInstance(Plugin plugin) { this.plugin = plugin; @@ -49,10 +46,6 @@ class TLocaleInstance { return plugin; } - public int getUpdateNodes() { - return updateNodes.get(); - } - public void sendTo(String path, CommandSender sender, String... args) { try { map.getOrDefault(path, ImmutableList.of(TLocaleSerialize.getEmpty(path))).forEach(tSender -> { @@ -77,34 +70,30 @@ class TLocaleInstance { return map.getOrDefault(path, ImmutableList.of(TLocaleSerialize.getEmpty(path))).get(0).asStringList(args); } + private static boolean isListString(List list) { + for (Object o : list) { + if (!(o instanceof String)) { + return false; + } + } + return true; + } + public void load(YamlConfiguration configuration) { - updateNodes.set(0); configuration.getKeys(true).forEach(s -> { - boolean isCover = false; Object object = configuration.get(s); if (object instanceof TLocaleSerialize) { - isCover = map.put(s, Collections.singletonList((TLocaleSerialize) object)) != null; + map.put(s, Collections.singletonList((TLocaleSerialize) object)); } else if (object instanceof List && !((List) object).isEmpty()) { - isCover = map.put(s, ((List) object).stream().map(TO_SERIALIZE).collect(Collectors.toList())) != null; + if (isListString((List) object)) { + map.put(s, Collections.singletonList(TLocaleText.of(object))); + } else { + map.put(s, ((List) object).stream().map(o -> o instanceof TLocaleSerialize ? (TLocaleSerialize) o : TLocaleText.of(String.valueOf(o))).collect(Collectors.toList())); + } } else if (!(object instanceof ConfigurationSection)) { String str = String.valueOf(object); - isCover = map.put(s, Collections.singletonList(str.length() == 0 ? TLocaleSerialize.getEmpty() : TLocaleText.of(str))) != null; - } - if (isCover) { - updateNodes.getAndIncrement(); + map.put(s, Collections.singletonList(str.length() == 0 ? TLocaleSerialize.getEmpty() : TLocaleText.of(str))); } }); } - - private static final Function TO_SERIALIZE = o -> { - if (o instanceof TLocaleSerialize) { - return ((TLocaleSerialize) o); - } else if (o instanceof List) { - return TLocaleText.of(((List) o)); - } else if (o instanceof String) { - return TLocaleText.of(((String) o)); - } else { - return TLocaleText.of(String.valueOf(o)); - } - }; } diff --git a/src/main/java/com/ilummc/tlib/resources/TLocaleLoader.java b/src/main/java/com/ilummc/tlib/resources/TLocaleLoader.java index c3badb6..d6d8359 100644 --- a/src/main/java/com/ilummc/tlib/resources/TLocaleLoader.java +++ b/src/main/java/com/ilummc/tlib/resources/TLocaleLoader.java @@ -1,5 +1,6 @@ package com.ilummc.tlib.resources; +import com.google.common.io.Files; import com.ilummc.tlib.TLib; import com.ilummc.tlib.annotations.TLocalePlugin; import com.ilummc.tlib.logger.TLogger; @@ -8,20 +9,18 @@ import com.ilummc.tlib.util.IO; import com.ilummc.tlib.util.Strings; import me.skymc.taboolib.Main; import me.skymc.taboolib.fileutils.ConfigUtils; -import me.skymc.taboolib.other.NumberUtils; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.serialization.ConfigurationSerialization; import org.bukkit.plugin.Plugin; +import org.yaml.snakeyaml.Yaml; +import sun.security.krb5.Config; import java.io.File; import java.io.InputStream; import java.nio.charset.Charset; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class TLocaleLoader { @@ -75,23 +74,20 @@ public class TLocaleLoader { if (isLoadLocale(plugin, isCover)) { // 获取文件 File localeFile = getLocaleFile(plugin); - if (localeFile == null) { - return; - } // 加载文件 infoLogger("TRY-LOADING-LANG", plugin.getName(), localeFile.getName()); - YamlConfiguration localeConfiguration = ConfigUtils.loadYaml(plugin, localeFile); - YamlConfiguration localeConfigurationAtStream = getLocaleAtStream(plugin, localeFile); + Map originMap = getLocaleAtStream(plugin, localeFile); + + TLib.getTLib().getConfigWatcher().removeListener(localeFile); // 载入配置 - loadPluginLocale(plugin, localeFile, localeConfiguration, localeConfigurationAtStream); + updateAndLoad(plugin, localeFile, originMap); // 注册监听 - TLib.getTLib().getConfigWatcher().removeListener(localeFile); TLib.getTLib().getConfigWatcher().addListener(localeFile, null, obj -> { infoLogger("RELOADING-LANG", plugin.getName()); - loadPluginLocale(plugin, localeFile, ConfigUtils.loadYaml(plugin, localeFile), getLocaleAtStream(plugin, localeFile)); + updateAndLoad(plugin, localeFile, getLocaleAtStream(plugin, localeFile)); }); } } catch (Exception e) { @@ -117,22 +113,22 @@ public class TLocaleLoader { private static File getLocaleFile(Plugin plugin) { releaseLocales(plugin); - return getLocalePriority().stream().map(localeName -> new File(plugin.getDataFolder(), "lang/" + localeName + ".yml")).filter(File::exists).findFirst().orElse(null); + return getLocalePriority().stream().map(localeName -> new File(plugin.getDataFolder(), "lang/" + localeName + ".yml")).filter(File::exists).findFirst().orElseThrow(NullPointerException::new); } private static void releaseLocales(Plugin plugin) { getLocalePriority().stream().filter(localeName -> !new File(plugin.getDataFolder(), "lang/" + localeName + ".yml").exists() && plugin.getResource("lang/" + localeName + ".yml") != null).forEach(localeName -> plugin.saveResource("lang/" + localeName + ".yml", true)); } - public static boolean isLocaleLoaded(Plugin plugin) { + private static boolean isLocaleLoaded(Plugin plugin) { return map.containsKey(plugin.getName()); } - public static boolean isDependWithTabooLib(Plugin plugin) { + private static boolean isDependWithTabooLib(Plugin plugin) { return plugin.getClass().getAnnotation(TLocalePlugin.class) != null || plugin.getDescription().getDepend().contains(Main.getInst().getName()) || plugin.getDescription().getSoftDepend().contains(Main.getInst().getName()); } - public static List getLocalePriority() { + private static List getLocalePriority() { return Main.getInst().getConfig().contains("LOCALE.PRIORITY") ? Main.getInst().getConfig().getStringList("LOCALE.PRIORITY") : Collections.singletonList("zh_CN"); } @@ -142,29 +138,65 @@ public class TLocaleLoader { return instance; } - private static YamlConfiguration getLocaleAtStream(Plugin plugin, File localeFile) { + private static Map getLocaleAtStream(Plugin plugin, File localeFile) { InputStream localeInputSteam = plugin.getClass().getResourceAsStream("/lang/" + localeFile.getName()); try { String yamlText = new String(IO.readFully(localeInputSteam), Charset.forName("utf-8")); - YamlConfiguration yaml = new YamlConfiguration(); - yaml.loadFromString(yamlText); - return yaml; - } catch (Exception ignored) { - return null; + Object load = new Yaml().load(yamlText); + return load instanceof Map ? (Map) load : new HashMap<>(0); + } catch (Exception e) { + return new HashMap<>(0); } } - private static void loadPluginLocale(Plugin plugin, File localeFile, YamlConfiguration localeConfiguration, YamlConfiguration localeConfigurationAtStream) { - TLocaleInstance localeInstance = getLocaleInstance(plugin); - boolean versionOutOfDate = isVersionOutOfDate(localeConfiguration, localeConfigurationAtStream); - if (versionOutOfDate) { - localeInstance.load(localeConfigurationAtStream); + private static Map currentLocaleMap(File localeFile) { + try { + Object load = new Yaml().load(Files.toString(localeFile, Charset.forName("utf-8"))); + return load instanceof Map ? (Map) load : new HashMap<>(0); + } catch (Exception e) { + return new HashMap<>(0); } + } + + private static int compareAndSet(Map origin, Map current, File file) { + int i = compareMaps(origin, current); +// DumperOptions options = new DumperOptions(); +// options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); +// options.setAllowUnicode(false); +// Yaml yaml = new Yaml(options); +// String dump = yaml.dump(current); +// try { +// Files.write(dump.getBytes(Charset.forName("utf-8")), file); +// } catch (IOException ignored) { +// } + return i; + } + + @SuppressWarnings("unchecked") + private static int compareMaps(Map origin, Map current) { + int res = 0; + for (Map.Entry entry : origin.entrySet()) { + if (current.putIfAbsent(entry.getKey(), entry.getValue()) != null) { + if (entry.getValue() instanceof Map && !((Map) entry.getValue()).containsKey("==") && current.get(entry.getKey()) instanceof Map) { + res += compareMaps((Map) entry.getValue(), (Map) current.get(entry.getKey())); + } + } else { + ++res; + } + } + return res; + } + + private static void updateAndLoad(Plugin plugin, File localeFile, Map originMap) { + Map currentMap = currentLocaleMap(localeFile); + int update = compareAndSet(originMap, currentMap, localeFile); + TLocaleInstance localeInstance = getLocaleInstance(plugin); + YamlConfiguration localeConfiguration = (YamlConfiguration) ConfigUtils.mapToConf(currentMap); localeInstance.load(localeConfiguration); - if (!versionOutOfDate || localeInstance.size() - localeInstance.getUpdateNodes() == 0) { + if (update == 0) { infoLogger("SUCCESS-LOADING-LANG-NORMAL", plugin.getName(), localeFile.getName().split("\\.")[0], String.valueOf(localeInstance.size())); } else { - infoLogger("SUCCESS-LOADING-LANG-UPDATE", plugin.getName(), localeFile.getName().split("\\.")[0], String.valueOf(localeInstance.size()), String.valueOf(localeInstance.size() - localeInstance.getUpdateNodes())); + infoLogger("SUCCESS-LOADING-LANG-UPDATE", plugin.getName(), localeFile.getName().split("\\.")[0], String.valueOf(localeInstance.size()), String.valueOf(update)); } } } diff --git a/src/main/java/com/ilummc/tlib/resources/type/TLocaleJson.java b/src/main/java/com/ilummc/tlib/resources/type/TLocaleJson.java index 28fa06f..4cc689b 100644 --- a/src/main/java/com/ilummc/tlib/resources/type/TLocaleJson.java +++ b/src/main/java/com/ilummc/tlib/resources/type/TLocaleJson.java @@ -38,27 +38,38 @@ public class TLocaleJson extends TLocaleSerialize { public static TLocaleJson valueOf(Map map) { List textList = getTextList(map.getOrDefault("text", "Empty Node")); + + // 分析 args 并替换 Object argsObj = map.get("args"); if (argsObj instanceof Map) { Map section = new HashMap<>(((Map) argsObj).size()); + + // valueOf(k) 是因为这个键可能加载为一个 Integer 导致 contains(String) 返回 false ((Map) argsObj).forEach((k, v) -> section.put(String.valueOf(k), v)); List collect = textList.stream().map(s -> { int index = 0; String[] template = pattern.split(s); Matcher matcher = pattern.matcher(s); + // 有可能开头和结尾是替换文本,所以做个特判 List builder = template.length > index ? new ArrayList<>(Arrays.asList(TextComponent.fromLegacyText(template[index++]))) : new ArrayList<>(); while (matcher.find()) { String replace = matcher.group(); + // 假的 <@> if (replace.length() <= 2) { continue; } + // 真的 <@xxx> replace = replace.substring(1, replace.length() - 1); String[] split = replace.split("@"); + // @ 前面的字符串 String text = split.length > 1 ? split[0] : ""; + // @ 后面的节点名 String node = split.length > 1 ? split[1] : split[0]; + // 如果 args 有这个 xxx if (section.containsKey(node)) { Map arg = (Map) section.get(node); text = TLocale.Translate.setColored(String.valueOf(arg.getOrDefault("text", text))); + // 可能有很多个 BaseComponent,于是为每个 component 单独设置各种事件 BaseComponent[] component = TextComponent.fromLegacyText(text); arg.forEach((key, value) -> { if ("suggest".equalsIgnoreCase(key)) { @@ -69,11 +80,14 @@ public class TLocaleJson extends TLocaleSerialize { Arrays.stream(component).forEach(baseComponent -> baseComponent.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(TLocale.Translate.setColored(String.valueOf(value))).create()))); } }); + // 添加到原来的 list 里面 builder.addAll(Arrays.asList(component)); } else { + // 这个参数节点并没有找到,于是随便放点字符串进去 builder.addAll(Arrays.asList(TextComponent.fromLegacyText(text))); TLib.getTLib().getLogger().warn(Strings.replaceWithOrder(TLib.getInternalLanguage().getString("MISSING-ARGUMENT"), node)); } + // 有可能一开头就是 <@xxx>,然后 split 出来就少了一些,于是直接加上 if (index < template.length) { builder.addAll(Arrays.asList(TextComponent.fromLegacyText(template[index++]))); } diff --git a/src/main/java/com/ilummc/tlib/resources/type/TLocaleText.java b/src/main/java/com/ilummc/tlib/resources/type/TLocaleText.java index d9943f1..fc5e587 100644 --- a/src/main/java/com/ilummc/tlib/resources/type/TLocaleText.java +++ b/src/main/java/com/ilummc/tlib/resources/type/TLocaleText.java @@ -38,8 +38,8 @@ public class TLocaleText extends TLocaleSerialize { return new TLocaleText(TLocale.Translate.setColored(s), TLocale.Translate.isPlaceholderUseDefault()); } - public static TLocaleText of(List s) { - return new TLocaleText(TLocale.Translate.setColored(s), TLocale.Translate.isPlaceholderUseDefault()); + public static TLocaleText of(Object o) { + return o instanceof String ? of(((String) o)) : new TLocaleText(o, false); } public static TLocaleText valueOf(Map map) { @@ -71,7 +71,7 @@ public class TLocaleText extends TLocaleSerialize { @Override public List asStringList(String... args) { if (text instanceof List) { - return ((List) text).stream().map(x -> "list: " + Strings.replaceWithOrder(TLocale.Translate.setColored(x), args)).collect(Collectors.toList()); + return ((List) text).stream().map(x -> Strings.replaceWithOrder(TLocale.Translate.setColored(x), args)).collect(Collectors.toList()); } else { return Collections.singletonList(asString(args)); } diff --git a/src/main/java/me/skymc/taboolib/commands/taboolib/listener/ListenerItemListCommand.java b/src/main/java/me/skymc/taboolib/commands/taboolib/listener/ListenerItemListCommand.java index d7cc2e0..da01e8c 100644 --- a/src/main/java/me/skymc/taboolib/commands/taboolib/listener/ListenerItemListCommand.java +++ b/src/main/java/me/skymc/taboolib/commands/taboolib/listener/ListenerItemListCommand.java @@ -44,7 +44,18 @@ public class ListenerItemListCommand implements Listener { if (loop >= (page - 1) * 28) { if (loop < page * 28) { int slot = InventoryUtil.SLOT_OF_CENTENTS.get(loop - ((page - 1) * 28)); - inventory.setItem(slot, getItem(map, name)); + ItemStack item = map.get(name).clone(); + ItemMeta meta = item.getItemMeta(); + List lore = meta.hasLore() ? meta.getLore() : new ArrayList<>(); + lore.addAll(TLocale.asStringList("COMMANDS.TABOOLIB.ITEMLIST.MENU.LORE", name)); + meta.setLore(lore); + item.setItemMeta(meta); + inventory.setItem(slot, item); + + System.out.println("lore: " + TLocale.asString("COMMANDS.TABOOLIB.ITEMLIST.MENU.LORE", name)); + System.out.println("lore: " + TLocale.asStringList("COMMANDS.TABOOLIB.ITEMLIST.MENU.LORE", name)); + + TLocale.sendTo(player, "COMMANDS.TABOOLIB.ITEMLIST.MENU.LORE", name); holder.ITEMS_DATA.put(slot, name); } else { break; @@ -62,16 +73,6 @@ public class ListenerItemListCommand implements Listener { player.openInventory(inventory); } - private static ItemStack getItem(HashMap map, String name) { - ItemStack item = map.get(name).clone(); - ItemMeta meta = item.getItemMeta(); - List lore = meta.hasLore() ? meta.getLore() : new ArrayList<>(); - lore.addAll(TLocale.asStringList("COMMANDS.TABOOLIB.ITEMLIST.MENU.LORE", name)); - meta.setLore(lore); - item.setItemMeta(meta); - return item; - } - @EventHandler public void inventoryClick(InventoryClickEvent e) { if (e.getInventory().getHolder() instanceof ItemLibraryHolder) { diff --git a/src/main/resources/lang/zh_CN.yml b/src/main/resources/lang/zh_CN.yml index d772c1a..13f3a41 100644 --- a/src/main/resources/lang/zh_CN.yml +++ b/src/main/resources/lang/zh_CN.yml @@ -463,6 +463,4 @@ COMMANDS: DATABASE: CONNECTION-ESTABLISHED: '成功连接到 {0} 数据库,连接池大小 {1}' - CONNECTION-ERROR: '连接到数据库错误:{0}' - -VERSION: 4.0 \ No newline at end of file + CONNECTION-ERROR: '连接到数据库错误:{0}' \ No newline at end of file