diff --git a/.gitignore b/.gitignore index 3e7ef6d..2823d02 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ /release .idea/ *.iml +/bin/ +dependency-reduced-pom.xml diff --git a/.travis.yml b/.travis.yml index 93c9606..edea56c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,9 @@ language: java jdk: - - oraclejdk7 - - oraclejdk8 + - oraclejdk11 + - openjdk8 + - openjdk10 + - openjdk11 branches: only: - master diff --git a/lib/PermissionsEx-1.23.1.jar b/lib/PermissionsEx-1.23.1.jar new file mode 100644 index 0000000..0e9a753 Binary files /dev/null and b/lib/PermissionsEx-1.23.1.jar differ diff --git a/pom.xml b/pom.xml index 7c9ae1e..7ac74a9 100644 --- a/pom.xml +++ b/pom.xml @@ -1,135 +1,215 @@ - - 4.0.0 - org.bitbucket.ucchy - UndineMailer - 2.0.0 - A mail system plugin for bukkit server - - - - GNU Lesser General Public License version 3 - http://www.gnu.org/licenses/lgpl-3.0.en.html - repo - - - - - UTF-8 - - - - - internal.repo - Temporary Staging Repository - file://${basedir}/../mvn-repo - - - - - ${project.artifactId} - clean javadoc:jar source:jar deploy - - - src/main/resources - true - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.5.1 - - 1.6 - 1.6 - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.4 - - 1.6 - UTF-8 - UTF-8 - UTF-8 - public - true - - http://docs.oracle.com/javase/6/docs/api/ - https://hub.spigotmc.org/javadocs/bukkit/ - - - - - org.apache.maven.plugins - maven-assembly-plugin - 2.6 - - ${project.artifactId}-${project.version} - - distribution.xml - - - - - deploy - - assembly - - - - - - - - - - spigot-repo - Spigot Maven Repository - https://hub.spigotmc.org/nexus/content/groups/public - - - vault-repo - Vault Repository - http://nexus.hc.to/content/repositories/pub_releases - - - pex-repo - PermissionsEx Repository - http://pex-repo.aoeu.xyz - - - - - - org.bukkit - bukkit - 1.10.2-R0.1-SNAPSHOT - - - net.milkbowl.vault - VaultAPI - 1.6 - true - - - ru.tehkode - PermissionsEx - 1.23.1 - true - - - net.gravitydevelopment.updater - updater - - - - - junit - junit - 4.12 - test - - - + + 4.0.0 + org.bitbucket.ucchy + UndineMailer + 2.0.0 + A mail system plugin for bukkit server + + + + GNU Lesser General Public License version 3 + http://www.gnu.org/licenses/lgpl-3.0.en.html + repo + + + + + UTF-8 + + + + + internal.repo + Temporary Staging Repository + file://${basedir}/../mvn-repo + + + + + ${project.artifactId} + clean javadoc:jar source:jar deploy + + + src/main/resources + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.0 + + 1.8 + UTF-8 + UTF-8 + UTF-8 + public + true + + https://docs.oracle.com/javase/jp/8/docs/api/ + https://hub.spigotmc.org/javadocs/bukkit/ + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + false + + + + package + + shade + + + false + true + + + com.github.ucchyocean:MessagingUtility + com.github.ucchyocean:ItemConfigUtility + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.1.1 + + ${project.artifactId}-${project.version} + + distribution.xml + + + + + deploy + + + + + org.codehaus.mojo + versions-maven-plugin + 1.3.1 + + + + + + + spigot-repo + Spigot Maven Repository + https://hub.spigotmc.org/nexus/content/groups/public + + + jitpack.io + Vault Repository + https://jitpack.io/ + + + pex-repo + PermissionsEx Repository + http://pex-repo.aoeu.xyz + + + pcgf-repo + PCGF PluginLib Repository + https://repo.pcgamingfreaks.at/repository/everything + + + ucchy-github + ucchy github repository + https://raw.github.com/ucchyocean/mvn-repo/master + + + + + + org.spigotmc + spigot-api + 1.16.2-R0.1-SNAPSHOT + true + provided + + + com.github.MilkBowl + VaultAPI + 1.7 + true + provided + + + org.bukkit + bukkit + + + + + ru.tehkode + PermissionsEx + 1.23.1 + true + system + ${project.basedir}/lib/PermissionsEx-1.23.1.jar + + + net.gravitydevelopment.updater + updater + + + + + com.google.code.gson + gson + + 2.8.0 + provided + + + org.xerial + sqlite-jdbc + 3.32.3 + runtime + + + mysql + mysql-connector-java + 5.1.49 + runtime + + + com.github.ucchyocean + MessagingUtility + 0.0.3 + true + + + com.github.ucchyocean + ItemConfigUtility + 0.0.5 + true + + + junit + junit + 4.13.1 + test + + + diff --git a/src/main/java/org/bitbucket/ucchy/undine/AttachmentBoxManager.java b/src/main/java/org/bitbucket/ucchy/undine/AttachmentBoxManager.java index a0bf6fa..e94d453 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/AttachmentBoxManager.java +++ b/src/main/java/org/bitbucket/ucchy/undine/AttachmentBoxManager.java @@ -25,7 +25,7 @@ public class AttachmentBoxManager { private HashMap editmodeBoxes; private HashMap attachmentBoxes; - private HashMap indexCache; + private HashMap indexCache; /** * コンストラクタ @@ -35,7 +35,7 @@ public AttachmentBoxManager(UndineMailer parent) { this.parent = parent; editmodeBoxes = new HashMap(); attachmentBoxes = new HashMap(); - indexCache = new HashMap(); + indexCache = new HashMap(); } /** @@ -43,12 +43,13 @@ public AttachmentBoxManager(UndineMailer parent) { * @param player プレイヤー * @param インベントリ名 */ - private String displayEditmodeBox(Player player) { + private void displayEditmodeBox(Player player) { // 既に、該当プレイヤーの編集中ボックスインベントリがある場合は、そちらを表示する if ( editmodeBoxes.containsKey(player) ) { player.openInventory(editmodeBoxes.get(player)); - return editmodeBoxes.get(player).getName(); + return; + //return editmodeBoxes.get(player).getName(); } // 添付ボックスの作成 @@ -71,7 +72,7 @@ private String displayEditmodeBox(Player player) { editmodeBoxes.put(player, box); player.openInventory(box); - return box.getName(); + //return box.getName(); } /** @@ -105,12 +106,13 @@ public void clearEditmodeBox(Player player) { * @param mail メール * @param インベントリ名 */ - private String displayAttachmentBox(Player player, MailData mail) { + private void displayAttachmentBox(Player player, MailData mail) { // 既に、該当メールの添付ボックスインベントリがある場合は、そちらを表示する if ( attachmentBoxes.containsKey(mail.getIndex()) ) { player.openInventory(attachmentBoxes.get(mail.getIndex())); - return attachmentBoxes.get(mail.getIndex()).getName(); + return; + //return attachmentBoxes.get(mail.getIndex()).getName(); } // 添付ボックスの作成 @@ -134,7 +136,7 @@ private String displayAttachmentBox(Player player, MailData mail) { // 指定されたplayerの画面に添付ボックスを表示する player.openInventory(box); - return box.getName(); + //return box.getName(); } /** @@ -144,7 +146,7 @@ private String displayAttachmentBox(Player player, MailData mail) { */ public void displayAttachBox(Player player, MailData mail) { - if ( mail.isEditmode() ) { + if ( !mail.isSent() ) { displayEditmodeBox(player); } else { displayAttachmentBox(player, mail); @@ -152,7 +154,7 @@ public void displayAttachBox(Player player, MailData mail) { } // メールのインデクスを記録しておく - indexCache.put(player, mail.getIndex()); + indexCache.put(player, mail); } /** @@ -163,7 +165,7 @@ public void displayAttachBox(Player player, MailData mail) { */ protected boolean isOpeningAttachBox(Player player) { return indexCache.containsKey(player) - && indexCache.get(player) > 0; + && indexCache.get(player).isSent(); } /** @@ -174,7 +176,7 @@ protected boolean isOpeningAttachBox(Player player) { */ protected boolean isOpeningEditmodeBox(Player player) { return indexCache.containsKey(player) - && indexCache.get(player) == 0; + && !indexCache.get(player).isSent(); } /** @@ -186,19 +188,17 @@ protected void syncAttachBox(Player player) { // 開いていたボックスのインデクスが記録されていないなら、何もしない if ( !indexCache.containsKey(player) ) return; + // 同期するボックスとメールを取得する + MailData mail = indexCache.get(player); + // インデクスを削除する - int index = indexCache.get(player); indexCache.remove(player); - // 同期するボックスとメールを取得する - MailData mail; Inventory inv; - if ( index == 0 ) { - mail = parent.getMailManager().getEditmodeMail(MailSender.getMailSender(player)); + if ( !mail.isSent() ) { inv = editmodeBoxes.get(player); } else { - mail = parent.getMailManager().getMail(index); - inv = attachmentBoxes.get(index); + inv = attachmentBoxes.get(mail.getIndex()); } // 一旦取り出して再度挿入することで、アイテムをスタックして整理する @@ -227,7 +227,7 @@ protected void syncAttachBox(Player player) { // メール詳細を開く if ( player.isOnline() ) { - if ( mail.isEditmode() ) { + if ( !mail.isSent() ) { parent.getMailManager().displayEditmode( MailSender.getMailSender(player)); } else { diff --git a/src/main/java/org/bitbucket/ucchy/undine/MailData.java b/src/main/java/org/bitbucket/ucchy/undine/MailData.java index 80eb63f..da53583 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/MailData.java +++ b/src/main/java/org/bitbucket/ucchy/undine/MailData.java @@ -5,380 +5,41 @@ */ package org.bitbucket.ucchy.undine; -import java.io.File; -import java.io.IOException; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Objects; import org.bitbucket.ucchy.undine.group.GroupData; -import org.bitbucket.ucchy.undine.group.GroupManager; import org.bitbucket.ucchy.undine.group.SpecialGroupAll; -import org.bitbucket.ucchy.undine.item.ItemConfigParseException; -import org.bitbucket.ucchy.undine.item.ItemConfigParser; import org.bitbucket.ucchy.undine.sender.MailSender; -import org.bitbucket.ucchy.undine.sender.MailSenderPlayer; -import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.inventory.ItemStack; /** * メールのデータ * @author ucchy */ -public class MailData implements Comparable, Cloneable { +public abstract class MailData implements Comparable, Cloneable { public static final int MESSAGE_MAX_SIZE = 15; private static final int SUMMARY_MAX_SIZE = 45; - // 編集中に設定される属性 - private List to; - private List toGroups; - private MailSender from; - private List message; - private List attachments; - private double costMoney; - private ItemStack costItem; - - // 送信後に設定される属性 - private int index; - private List toTotal; - private List readFlags; - private List trashFlags; - private List attachmentsOriginal; - private boolean isAttachmentsOpened; - private boolean isAttachmentsCancelled; - private boolean isAttachmentsRefused; - private String attachmentsRefusedReason; - private Date date; - private Location location; - - /** - * コンストラクタ - */ - public MailData() { - this(new ArrayList(), null, new ArrayList()); - } - - /** - * コンストラクタ - * @param to 宛先 - * @param from 送り主 - * @param message メッセージ - */ - public MailData(List to, MailSender from, String message) { - this(to, from, new ArrayList()); - this.message.add(message); - } - - /** - * コンストラクタ - * @param to 宛先 - * @param from 送り主 - * @param message メッセージ - */ - public MailData(List to, MailSender from, List message) { - this(to, from, message, new ArrayList()); - } - - /** - * コンストラクタ - * @param to 宛先 - * @param from 送り主 - * @param message メッセージ - * @param attachments 添付アイテム - */ - public MailData(List to, MailSender from, List message, - List attachments) { - this(to, from, message, attachments, 0, null); - } - - /** - * コンストラクタ - * @param to 宛先 - * @param from 送り主 - * @param message メッセージ - * @param attachments 添付アイテム - * @param costMoney 受け取りにかかる金額 - * @param costItem 受け取りにかかる取引アイテム - */ - public MailData(List to, MailSender from, List message, - List attachments, double costMoney, ItemStack costItem) { - this(to, from, message, attachments, costMoney, costItem, new ArrayList()); - } - - /** - * コンストラクタ - * @param to 宛先 - * @param from 送り主 - * @param message メッセージ - * @param attachments 添付アイテム - * @param costMoney 受け取りにかかる金額 - * @param costItem 受け取りにかかる取引アイテム - * @param toGroup 宛先グループ - */ - public MailData(List to, MailSender from, List message, - List attachments, double costMoney, ItemStack costItem, - List toGroup) { - this.index = 0; - this.to = to; - this.toGroups = toGroup; - this.from = from; - this.message = message; - this.attachments = attachments; - this.costMoney = costMoney; - this.costItem = costItem; - this.readFlags = new ArrayList(); - this.trashFlags = new ArrayList(); - this.isAttachmentsOpened = false; - this.isAttachmentsCancelled = false; - this.isAttachmentsRefused = false; - } + protected int index = 0; + + /** メールが送信されたかどうか。Database モード用の変数であり、フラットファイルモードでは index == 0 のとき isSent == true と判定する */ + private boolean isSent; /** * 指定されたファイルへ保存する * @param file 保存先 */ - protected void save(File file) { - - YamlConfiguration config = new YamlConfiguration(); - saveToConfigSection(config); - try { - config.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * 指定されたコンフィグセクションへ保存する - * @param section コンフィグセクション - */ - protected void saveToConfigSection(ConfigurationSection section) { - - ArrayList toList = new ArrayList(); - for ( MailSender t : to ) { - toList.add(t.toString()); - } - section.set("to", toList); - - section.set("toGroups", toGroups); - - if ( toTotal != null ) { - ArrayList toTotalList = new ArrayList(); - for ( MailSender t : toTotal ) { - toTotalList.add(t.toString()); - } - section.set("toTotal", toTotalList); - } - - section.set("from", from.toString()); - section.set("message", message); - - if ( attachments != null ) { - ConfigurationSection sub = section.createSection("attachments"); - int i = 1; - for ( ItemStack item : attachments ) { - ConfigurationSection subsub = sub.createSection("attachment" + i++); - ItemConfigParser.setItemToSection(subsub, item); - } - } - - section.set("costMoney", costMoney); - - if ( costItem != null ) { - ConfigurationSection sub = section.createSection("costItem"); - ItemConfigParser.setItemToSection(sub, costItem); - } - - section.set("index", index); - - ArrayList flagList = new ArrayList(); - for ( MailSender t : readFlags ) { - flagList.add(t.toString()); - } - section.set("readFlags", flagList); - - ArrayList trashList = new ArrayList(); - for ( MailSender t : trashFlags ) { - trashList.add(t.toString()); - } - section.set("trashFlags", trashList); - - if ( attachmentsOriginal != null ) { - ConfigurationSection sub = section.createSection("attachmentsOriginal"); - int i = 1; - for ( ItemStack item : attachmentsOriginal ) { - ConfigurationSection subsub = sub.createSection("attachment" + i++); - ItemConfigParser.setItemToSection(subsub, item); - } - } - - if ( date != null ) { - section.set("date", date.getTime()); - } - - if ( location != null ) { - ConfigurationSection locSec = section.createSection("location"); - locSec.set("world", location.getWorld().getName()); - locSec.set("x", location.getX()); - locSec.set("y", location.getY()); - locSec.set("z", location.getZ()); - locSec.set("yaw", location.getYaw()); - locSec.set("pitch", location.getPitch()); - } - - section.set("isAttachmentsCancelled", isAttachmentsCancelled); - section.set("isAttachmentsRefused", isAttachmentsRefused); - section.set("attachmentsRefusedReason", attachmentsRefusedReason); - } - - /** - * 指定されたファイルからロードする - * @param file ファイル - * @return ロードされたMailData - */ - protected static MailData load(File file) { - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - return loadFromConfigSection(config); - } - - /** - * 指定されたコンフィグセクションからロードする - * @param section コンフィグセクション - * @return ロードされたMailData - */ - protected static MailData loadFromConfigSection(ConfigurationSection section) { - - MailData data = new MailData(); - - data.to = new ArrayList(); - for ( String t : section.getStringList("to") ) { - MailSender sender = MailSender.getMailSenderFromString(t); - if ( sender != null ) { - data.to.add(sender); - } - } - - data.toGroups = section.getStringList("toGroups"); - - if ( section.contains("toTotal") ) { - data.toTotal = new ArrayList(); - for ( String t : section.getStringList("toTotal") ) { - MailSender sender = MailSender.getMailSenderFromString(t); - if ( sender != null ) { - data.toTotal.add(sender); - } - } - } - - data.from = MailSender.getMailSenderFromString(section.getString("from")); - data.message = section.getStringList("message"); - - if ( section.contains("attachments") ) { - data.attachments = new ArrayList(); - - for ( String name : section.getConfigurationSection("attachments").getKeys(false) ) { - ConfigurationSection sub = section.getConfigurationSection("attachments." + name); - try { - ItemStack item = ItemConfigParser.getItemFromSection(sub); - data.attachments.add(item); - } catch (ItemConfigParseException e) { - e.printStackTrace(); - } - } - } - - data.costMoney = section.getInt("costMoney", 0); - if ( section.contains("costItem") ) { - try { - data.costItem = ItemConfigParser.getItemFromSection( - section.getConfigurationSection("costItem")); - } catch (ItemConfigParseException e) { - e.printStackTrace(); - } - } - - data.index = section.getInt("index"); - - data.readFlags = new ArrayList(); - for ( String t : section.getStringList("readFlags") ) { - MailSender sender = MailSender.getMailSenderFromString(t); - if ( sender != null ) { - data.readFlags.add(sender); - } - } - - data.trashFlags = new ArrayList(); - for ( String t : section.getStringList("trashFlags") ) { - MailSender sender = MailSender.getMailSenderFromString(t); - if ( sender != null ) { - data.trashFlags.add(sender); - } - } - - if ( section.contains("attachmentsOriginal") ) { - data.attachmentsOriginal = new ArrayList(); - - for ( String name : - section.getConfigurationSection("attachmentsOriginal").getKeys(false) ) { - ConfigurationSection sub = - section.getConfigurationSection("attachmentsOriginal." + name); - try { - ItemStack item = ItemConfigParser.getItemFromSection(sub); - data.attachmentsOriginal.add(item); - } catch (ItemConfigParseException e) { - e.printStackTrace(); - } - } - } - - if ( section.contains("date") ) { - data.date = new Date(section.getLong("date")); - } - - if ( section.contains("location") && section.contains("location.world") ) { - ConfigurationSection locSec = section.getConfigurationSection("location"); - World world = Bukkit.getWorld(locSec.getString("world")); - if ( world != null ) { - double x = locSec.getDouble("x"); - double y = locSec.getDouble("y"); - double z = locSec.getDouble("z"); - double yaw = locSec.getDouble("yaw"); - double pitch = locSec.getDouble("pitch"); - data.location = new Location(world, x, y, z, (float)yaw, (float)pitch); - } - } - - data.isAttachmentsCancelled = section.getBoolean("isAttachmentsCancelled", false); - data.isAttachmentsRefused = section.getBoolean("isAttachmentsRefused", false); - data.attachmentsRefusedReason = section.getString("attachmentsRefusedReason"); - - return data; - } - - /** - * このオブジェクトの複製を作成して返す。 - * @see java.lang.Object#clone() - */ - public MailData clone() { - return new MailData( - new ArrayList(to), from, message, - new ArrayList(attachments), costMoney, costItem, - toGroups); - } + public abstract void save(); /** * 設定されている宛先を全て消去する */ - public void deleteAllTo() { - to.clear(); - toGroups.clear(); - } + public abstract void deleteAllTo(); /** * このメールのインデクス番号を取得します。 @@ -392,7 +53,7 @@ public int getIndex() { * このメールのインデクス番号を設定します(メール送信時に自動で割り当てられます)。 * @param index インデクス番号 */ - protected void setIndex(int index) { + public void setIndex(int index) { this.index = index; } @@ -400,133 +61,88 @@ protected void setIndex(int index) { * このメールの宛先を取得します。 * @return 宛先 */ - public List getTo() { - return to; - } + public abstract List getTo(); /** * このメールの宛先を設定します。 * @param line 宛先番号(0から始まることに注意) * @param to 宛先 */ - public void setTo(int line, MailSender to) { - if ( this.to.size() <= line ) { - this.to.add(to); - } else if ( this.to.size() > line ) { - this.to.set(line, to); - } + public abstract void setTo(int line, MailSender to); - // 全体メールだった場合は、全体グループを除去しておく。 - if ( isAllMail() ) { - toGroups.remove(SpecialGroupAll.NAME); - } - } + /** + * このメールの宛先を追加します。 + * @param to 宛先 + */ + public abstract void addTo(MailSender to); /** * このメールの宛先を追加します。 * @param to 宛先 */ - public void addTo(MailSender to) { - setTo(Integer.MAX_VALUE, to); - } + public abstract void addTo(List to); /** * このメールの指定された宛先を削除します。 * @param line 宛先番号(0から始まることに注意) */ - public void deleteTo(int line) { - if ( this.to.size() > line ) { - this.to.remove(line); - } - } + public abstract void deleteTo(int line); /** * このメールの発信元を取得します。 * @return 発信元 */ - public MailSender getFrom() { - return from; - } + public abstract MailSender getFrom(); /** * このメールの発信元を設定します。 * @param from 発信元 */ - public void setFrom(MailSender from) { - this.from = from; - } + public abstract void setFrom(MailSender from); /** * このメールのメッセージを取得します。 * @return メッセージ */ - public List getMessage() { - return message; - } + public abstract List getMessage(); /** * このメールのメッセージを設定します。 * @param message メッセージ */ - public void setMessage(List message) { - this.message = message; - } + public abstract void setMessage(List message); /** * このメールのメッセージを設定します。 * @param line 行番号(0から始まることに注意) * @param message メッセージ */ - public void setMessage(int line, String message) { - while ( this.message.size() <= line ) { - this.message.add(""); - } - this.message.set(line, message); - } + public abstract void setMessage(int line, String message); /** * このメールのメッセージに、指定した内容を追加します。 * @param message メッセージ */ - public void addMessage(String message) { - String[] lines = message.split("\n"); - for ( String line : lines ) { - this.message.add(line); - } - } + public abstract void addMessage(String message); /** * このメールのメッセージの、指定された行番号を削除します。 * @param line 宛先番号(0から始まることに注意) */ - public void deleteMessage(int line) { - if ( this.message.size() > line && line >= 0 ) { - this.message.remove(line); - } - } + public abstract void deleteMessage(int line); /** * 宛先グループを取得します。 * @return 宛先グループ */ - public List getToGroups() { - return toGroups; - } + public abstract List getToGroups(); /** * 宛先グループを取得します。 * @return 宛先グループ */ public List getToGroupsConv() { - GroupManager manager = UndineMailer.getInstance().getGroupManager(); - List result = new ArrayList(); - for ( String name : toGroups ) { - GroupData group = manager.getGroup(name); - if ( group != null ) { - result.add(group); - } - } - return result; + return UndineMailer.getInstance().getGroupManager().getGroups(getToGroups()); } /** @@ -534,31 +150,11 @@ public List getToGroupsConv() { * @param line 宛先番号(0から始まることに注意) * @param group グループ */ - public void setToGroup(int line, String group) { - - // 追加するグループが全体グループなら、 - // 他の宛先を全て削除する - if ( SpecialGroupAll.NAME.equals(group) ) { - this.to.clear(); - this.toGroups.clear(); - this.toGroups.add(group); - return; - } - - if ( this.toGroups.size() <= line ) { - this.toGroups.add(group); - } else if ( this.toGroups.size() > line ) { - this.toGroups.set(line, group); - } - - // 全体グループが含まれていたなら、全体グループを除去する - if ( toGroups.contains(SpecialGroupAll.NAME) ) { - toGroups.remove(SpecialGroupAll.NAME); - } - } + public abstract void setToGroup(int line, String group); /** * このメールの宛先グループに、新しいグループを追加します。 + * * @param group グループ名 */ public void addToGroup(String group) { @@ -569,278 +165,208 @@ public void addToGroup(String group) { * このメールの宛先グループの、指定された行番号を削除します。 * @param line 宛先番号(0から始まることに注意) */ - public void deleteToGroup(int line) { - if ( this.toGroups.size() > line && line >= 0 ) { - this.toGroups.remove(line); - } - } + public abstract void deleteToGroup(int line); /** * 統合宛先を設定する * @param total 統合宛先 */ - protected void setToTotal(List total) { - this.toTotal = total; - } + protected abstract void setToTotal(List total); /** * 統合宛先(宛先+宛先グループの和集合)を取得する。未送信メールの場合はnullになる。 * @return 統合宛先 */ - public List getToTotal() { - return toTotal; - } + public abstract List getToTotal(); /** * このメールに添付されたアイテムを取得します。 * @return 添付アイテム */ - public List getAttachments() { - return attachments; - } + public abstract List getAttachments(); /** * このメールの添付アイテムを設定します。 * @param attachments 添付アイテム */ - public void setAttachments(List attachments) { - this.attachments = attachments; - } + public abstract void setAttachments(List attachments); /** * 指定されたアイテムを添付アイテムに追加します。 * @param item アイテム */ - public void addAttachment(ItemStack item) { - this.attachments.add(item); - } + public abstract void addAttachment(ItemStack item); /** * このメールを読んだ人のリストを取得します。 * @return 読んだ人のリスト */ - public List getReadFlags() { - return readFlags; - } + public abstract List getReadFlags(); /** * このメールに削除フラグを付けた人のリストを取得します。 * @return 削除フラグをつけている人のリスト */ - public List getTrashFlags() { - return trashFlags; - } + public abstract List getTrashFlags(); /** * このメールの添付アイテムを受け取るのに必要な金額を取得します。 * @return 受け取り金額 */ - public double getCostMoney() { - return costMoney; - } + public abstract double getCostMoney(); /** * このメールの添付アイテムを受け取るのに必要な金額を設定します。 * @param feeMoney 受け取り金額 */ - public void setCostMoney(double feeMoney) { - this.costMoney = feeMoney; - } + public abstract void setCostMoney(double feeMoney); /** * このメールの添付アイテムを受け取るのに必要な引き換えアイテムを取得します。 * @return 引き換えアイテム */ - public ItemStack getCostItem() { - return costItem; - } + public abstract ItemStack getCostItem(); /** * このメールの添付アイテムを受け取るのに必要な引き換えアイテムを設定します。 * @param feeItem 引き換えアイテム */ - public void setCostItem(ItemStack feeItem) { - this.costItem = feeItem; - } + public abstract void setCostItem(ItemStack feeItem); /** * このメールの送信時間を取得します。 * @return 送信時間 */ - public Date getDate() { - return date; - } + public abstract Date getDate(); /** * このメールの送信時間を設定します(メール送信時に自動で割り当てられます)。 * @param date 送信時間 */ - protected void setDate(Date date) { - this.date = date; - } + protected abstract void setDate(Date date); /** * このメールが送信された地点を取得します。 * @return 送信地点 */ - public Location getLocation() { - return location; - } + public abstract Location getLocation(); /** * このメールの送信地点を設定します(メール送信時に自動で割り当てられます)。 * @param location 送信地点 */ - public void setLocation(Location location) { - this.location = location; - } + public abstract void setLocation(Location location); /** * メール送信時の添付アイテムを取得します。 * @return メール送信時の添付アイテム */ - public List getAttachmentsOriginal() { - return attachmentsOriginal; - } + public abstract List getAttachmentsOriginal(); /** * attachmentOriginalに、添付ファイルのコピーを行います * (メール送信時に自動で行われます)。 */ - protected void makeAttachmentsOriginal() { - attachmentsOriginal = new ArrayList(attachments); - } + protected abstract void makeAttachmentsOriginal(); /** * 指定したプレイヤーが、このメールを読んだかどうかを返します。 * @param player プレイヤー * @return 読んだかどうか */ - public boolean isRead(MailSender player) { - return readFlags.contains(player); - } + public abstract boolean isRead(MailSender player); /** * 指定した名前のsenderの既読マークを付ける * @param sender sender */ - public void setReadFlag(MailSender sender) { - if ( !readFlags.contains(sender) ) { - readFlags.add(sender); - } - } + public abstract void setReadFlag(MailSender sender); /** * 指定した人が、このメールに削除マークをつけているかどうかを返します。 * @param sender * @return 削除マークをつけているかどうか */ - public boolean isSetTrash(MailSender sender) { - return trashFlags.contains(sender); - } + public abstract boolean isSetTrash(MailSender sender); /** * 指定した人の削除マークを付ける * @param sender */ - public void setTrashFlag(MailSender sender) { - if ( !trashFlags.contains(sender) ) { - trashFlags.add(sender); - } - } + public abstract void setTrashFlag(MailSender sender); /** * 指定した人の削除マークを消す * @param sender */ - public void removeTrashFlag(MailSender sender) { - if ( trashFlags.contains(sender) ) { - trashFlags.remove(sender); - } - } + public abstract void removeTrashFlag(MailSender sender); /** * 指定された名前のプレイヤーは、このメールの関係者かどうかを返す。 * @param sender sender * @return 指定された名前がtoまたはfromに含まれるかどうか */ - public boolean isRelatedWith(MailSender sender) { - if ( isAllMail() ) return true; - if ( from.equals(sender) ) return true; - if ( toTotal != null ) return toTotal.contains(sender); - return to.contains(sender); - } + public abstract boolean isRelatedWith(MailSender sender); /** * 指定された名前のプレイヤーは、このメールの受信者かどうかを返す。 * @param sender sender * @return 指定された名前がtoに含まれるかどうか */ - public boolean isRecipient(MailSender sender) { - if ( isAllMail() ) return true; - if ( toTotal != null ) return toTotal.contains(sender); - return to.contains(sender); - } + public abstract boolean isRecipient(MailSender sender); /** * このメールは編集中モードなのかどうかを返す * @return 編集中かどうか */ - public boolean isEditmode() { - return (index == 0); + public boolean isSent() { + return isSent; + } + + /** + * このメールを送信済みとしてマークする。 + */ + public void setSent() { + this.isSent = true; } /** * このメールの添付アイテムがオープンされたのかどうかを返す * @return 添付アイテムがオープンされたのかどうか */ - public boolean isAttachmentsOpened() { - return isAttachmentsOpened; - } + public abstract boolean isAttachmentsOpened(); /** * このメールの添付アイテムをオープンされたとして記録する。 * 受信者が添付ボックスを一度でも開いた事がある状態なら、 * 送信者は添付アイテムをキャンセルすることができなくなる。 */ - public void setOpenAttachments() { - this.isAttachmentsOpened = true; - } + public abstract void setOpenAttachments(); /** * このメールの添付アイテムがキャンセルされたのかどうかを返す * @return 添付アイテムがキャンセルされたのかどうか */ - public boolean isAttachmentsCancelled() { - return isAttachmentsCancelled; - } + public abstract boolean isAttachmentsCancelled(); /** * このメールの添付アイテムをキャンセルする。 * 添付アイテムがキャンセルされると、受信者はボックスを開けなくなり、 * 逆に送信者がボックスを開くことができるようになる。 */ - public void cancelAttachments() { - this.isAttachmentsCancelled = true; - this.costItem = null; - this.costMoney = 0; - } + public abstract void cancelAttachments(); /** * このメールの添付アイテムが拒否されたのかどうかを返す * @return 添付アイテムが拒否されたのかどうか */ - public boolean isAttachmentsRefused() { - return isAttachmentsRefused; - } + public abstract boolean isAttachmentsRefused(); /** * 受取拒否の理由を取得します。 * @return 受取拒否の理由(設定されていない場合はnullになることに注意すること) */ - public String getAttachmentsRefusedReason() { - return attachmentsRefusedReason; - } + public abstract String getAttachmentsRefusedReason(); /** * このメールの添付アイテムを拒否する。 @@ -848,23 +374,23 @@ public String getAttachmentsRefusedReason() { * 逆に送信者がボックスを開くことができるようになる。 * @param attachmentsRefusedReason 拒否理由 */ - public void refuseAttachments(String attachmentsRefusedReason) { - this.isAttachmentsCancelled = true; // キャンセルフラグも立てる - this.isAttachmentsRefused = true; - if ( attachmentsRefusedReason != null - && attachmentsRefusedReason.length() > 0 ) { - this.attachmentsRefusedReason = attachmentsRefusedReason; - } - this.costItem = null; - this.costMoney = 0; - } + public abstract void refuseAttachments(String attachmentsRefusedReason); + + + /** + * このオブジェクトの複製を作成して返す。 + * + * @see java.lang.Object#clone() + */ + @Override + public abstract MailData clone(); /** * このメールが全体メールなのかどうかを返します。 * @return 全体メールかどうか */ public boolean isAllMail() { - return toGroups.contains(SpecialGroupAll.NAME); + return getToGroups().contains(SpecialGroupAll.NAME); } /** @@ -886,11 +412,11 @@ public int compareTo(MailData other) { * "送信者 (送信日時) 1行目の内容" * @return サマリー */ - protected String getInboxSummary() { + public String getInboxSummary() { - String fdate = getFormattedDate(date); + String fdate = getFormattedDate(getDate()); String summary = String.format("%s (%s) %s", - from.getName(), fdate, Utility.removeColorCode(message.get(0))); + getFrom().getName(), fdate, Utility.removeColorCode(getMessage().get(0))); // 長すぎる場合は切る if ( summary.length() > SUMMARY_MAX_SIZE + 2 ) { @@ -905,15 +431,15 @@ protected String getInboxSummary() { * "受信者 (送信日時) 1行目の内容" * @return サマリー */ - protected String getOutboxSummary() { + public String getOutboxSummary() { - String fdate = getFormattedDate(date); + String fdate = getFormattedDate(getDate()); String todesc = joinToAndGroup(); if ( todesc.length() > 15 ) { // 長すぎる場合は切る todesc = todesc.substring(0, 15); } String summary = String.format("%s (%s) %s", - todesc, fdate, Utility.removeColorCode(message.get(0))); + todesc, fdate, Utility.removeColorCode(getMessage().get(0))); // 長すぎる場合は切る if ( summary.length() > SUMMARY_MAX_SIZE + 2 ) { @@ -930,13 +456,13 @@ protected String getOutboxSummary() { private String joinToAndGroup() { StringBuffer buffer = new StringBuffer(); - for ( MailSender item : to ) { + for ( MailSender item : getTo() ) { if ( buffer.length() > 0 ) { buffer.append(", "); } buffer.append(item.getName()); } - for ( String group : toGroups ) { + for ( String group : getToGroups() ) { if ( buffer.length() > 0 ) { buffer.append(", "); } @@ -958,41 +484,21 @@ private String getFormattedDate(Date date) { * データのアップグレードを行う。 * @return アップグレードを実行したかどうか */ - protected boolean upgrade() { - boolean upgraded = false; - for ( MailSender ms : to ) { - if ( ms instanceof MailSenderPlayer ) { - if ( ((MailSenderPlayer) ms).upgrade() ) { - upgraded = true; - } - } - } - if ( from instanceof MailSenderPlayer ) { - if ( ((MailSenderPlayer) from).upgrade() ) { - upgraded = true; - } - } - for ( MailSender ms : toTotal ) { - if ( ms instanceof MailSenderPlayer ) { - if ( ((MailSenderPlayer) ms).upgrade() ) { - upgraded = true; - } - } - } - for ( MailSender ms : readFlags ) { - if ( ms instanceof MailSenderPlayer ) { - if ( ((MailSenderPlayer) ms).upgrade() ) { - upgraded = true; - } - } - } - for ( MailSender ms : trashFlags ) { - if ( ms instanceof MailSenderPlayer ) { - if ( ((MailSenderPlayer) ms).upgrade() ) { - upgraded = true; - } - } + protected abstract boolean upgrade(); + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof MailDataFlatFile)) { + return false; } - return upgraded; + MailDataFlatFile mailDataDatabase = (MailDataFlatFile) o; + return index == mailDataDatabase.index; + } + + @Override + public int hashCode() { + return Objects.hashCode(index); } } diff --git a/src/main/java/org/bitbucket/ucchy/undine/MailDataFlatFile.java b/src/main/java/org/bitbucket/ucchy/undine/MailDataFlatFile.java new file mode 100644 index 0000000..7e16f35 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/MailDataFlatFile.java @@ -0,0 +1,978 @@ +/* + * @author ucchy + * @license LGPLv3 + * @copyright Copyright ucchy 2015 + */ +package org.bitbucket.ucchy.undine; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.bitbucket.ucchy.undine.group.SpecialGroupAll; +import org.bitbucket.ucchy.undine.sender.MailSender; +import org.bitbucket.ucchy.undine.sender.MailSenderPlayer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; + +import com.github.ucchyocean.itemconfig.ItemConfigParseException; +import com.github.ucchyocean.itemconfig.ItemConfigParser; + +/** + * メールのデータ + * @author ucchy + */ +public class MailDataFlatFile extends MailData { + + // 編集中に設定される属性 + private List to; + private List toGroups; + private MailSender from; + private List message; + private List attachments; + private double costMoney; + private ItemStack costItem; + + // 送信後に設定される属性 + private List toTotal; + private List readFlags; + private List trashFlags; + private List attachmentsOriginal; + private boolean isAttachmentsOpened; + private boolean isAttachmentsCancelled; + private boolean isAttachmentsRefused; + private String attachmentsRefusedReason; + private Date date; + private Location location; + + /** + * コンストラクタ + */ + public MailDataFlatFile() { + this(new ArrayList(), null, new ArrayList()); + } + + /** + * コンストラクタ + * + * @param to 宛先 + * @param from 送り主 + * @param message メッセージ + */ + public MailDataFlatFile(List to, MailSender from, String message) { + this(to, from, new ArrayList()); + this.message.add(message); + } + + /** + * コンストラクタ + * + * @param to 宛先 + * @param from 送り主 + * @param message メッセージ + */ + public MailDataFlatFile(List to, MailSender from, List message) { + this(to, from, message, new ArrayList()); + } + + /** + * コンストラクタ + * + * @param to 宛先 + * @param from 送り主 + * @param message メッセージ + * @param attachments 添付アイテム + */ + public MailDataFlatFile(List to, MailSender from, List message, List attachments) { + this(to, from, message, attachments, 0, null); + } + + /** + * コンストラクタ + * + * @param to 宛先 + * @param from 送り主 + * @param message メッセージ + * @param attachments 添付アイテム + * @param costMoney 受け取りにかかる金額 + * @param costItem 受け取りにかかる取引アイテム + */ + public MailDataFlatFile(List to, MailSender from, List message, List attachments, + double costMoney, ItemStack costItem) { + this(to, from, message, attachments, costMoney, costItem, new ArrayList()); + } + + /** + * コンストラクタ + * + * @param to 宛先 + * @param from 送り主 + * @param message メッセージ + * @param attachments 添付アイテム + * @param costMoney 受け取りにかかる金額 + * @param costItem 受け取りにかかる取引アイテム + * @param toGroup 宛先グループ + */ + public MailDataFlatFile(List to, MailSender from, List message, List attachments, + double costMoney, ItemStack costItem, List toGroup) { + this.to = to; + this.toGroups = toGroup; + this.from = from; + this.message = message; + this.attachments = attachments; + this.costMoney = costMoney; + this.costItem = costItem; + this.readFlags = new ArrayList(); + this.trashFlags = new ArrayList(); + this.isAttachmentsOpened = false; + this.isAttachmentsCancelled = false; + this.isAttachmentsRefused = false; + } + + /** + * このメールが送信されたかどうか、すなわち編集中かどうかを返す。 + * @return このメールが送信されたかどうか + */ + @Override + public boolean isSent() { + return index != 0; + } + + /** + * ファイルへ保存する + */ + @Override + public void save() { + // 編集中で未送信のメールは保存できません。 + if (!isSent()) { + return; + } + String filename = String.format("%1$08d.yml", index); + File folder = UndineMailer.getInstance().getMailFolder(); + File file = new File(folder, filename); + YamlConfiguration config = new YamlConfiguration(); + saveToConfigSection(config); + try { + config.save(file); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 指定されたコンフィグセクションへ保存する + * + * @param section コンフィグセクション + */ + protected void saveToConfigSection(ConfigurationSection section) { + + ArrayList toList = new ArrayList(); + for (MailSender t : to) { + toList.add(t.toString()); + } + section.set("to", toList); + + section.set("toGroups", toGroups); + + if (toTotal != null) { + ArrayList toTotalList = new ArrayList(); + for (MailSender t : toTotal) { + toTotalList.add(t.toString()); + } + section.set("toTotal", toTotalList); + } + + section.set("from", from.toString()); + section.set("message", message); + + if (attachments != null) { + ConfigurationSection sub = section.createSection("attachments"); + int i = 1; + for (ItemStack item : attachments) { + ConfigurationSection subsub = sub.createSection("attachment" + i++); + ItemConfigParser.setItemToSection(subsub, item); + } + } + + section.set("costMoney", costMoney); + + if (costItem != null) { + ConfigurationSection sub = section.createSection("costItem"); + ItemConfigParser.setItemToSection(sub, costItem); + } + + section.set("index", index); + + ArrayList flagList = new ArrayList(); + for (MailSender t : readFlags) { + flagList.add(t.toString()); + } + section.set("readFlags", flagList); + + ArrayList trashList = new ArrayList(); + for (MailSender t : trashFlags) { + trashList.add(t.toString()); + } + section.set("trashFlags", trashList); + + if (attachmentsOriginal != null) { + ConfigurationSection sub = section.createSection("attachmentsOriginal"); + int i = 1; + for (ItemStack item : attachmentsOriginal) { + ConfigurationSection subsub = sub.createSection("attachment" + i++); + ItemConfigParser.setItemToSection(subsub, item); + } + } + + if (date != null) { + section.set("date", date.getTime()); + } + + if (location != null) { + World world = location.getWorld(); + if (world != null) { + ConfigurationSection locSec = section.createSection("location"); + locSec.set("world", world.getName()); + locSec.set("x", location.getX()); + locSec.set("y", location.getY()); + locSec.set("z", location.getZ()); + locSec.set("yaw", location.getYaw()); + locSec.set("pitch", location.getPitch()); + } + } + + section.set("isAttachmentsCancelled", isAttachmentsCancelled); + section.set("isAttachmentsRefused", isAttachmentsRefused); + section.set("attachmentsRefusedReason", attachmentsRefusedReason); + } + + /** + * 指定されたファイルからロードする + * + * @param file ファイル + * @return ロードされたMailData + */ + protected static MailDataFlatFile load(File file) { + YamlConfiguration config = YamlConfiguration.loadConfiguration(file); + return loadFromConfigSection(config); + } + + /** + * 指定されたコンフィグセクションからロードする + * + * @param section コンフィグセクション + * @return ロードされたMailData + */ + protected static MailDataFlatFile loadFromConfigSection(ConfigurationSection section) { + + MailDataFlatFile data = new MailDataFlatFile(); + + data.to = new ArrayList(); + for (String t : section.getStringList("to")) { + MailSender sender = MailSender.getMailSenderFromString(t); + if (sender != null) { + data.to.add(sender); + } + } + + data.toGroups = section.getStringList("toGroups"); + + if (section.contains("toTotal")) { + data.toTotal = new ArrayList(); + for (String t : section.getStringList("toTotal")) { + MailSender sender = MailSender.getMailSenderFromString(t); + if (sender != null) { + data.toTotal.add(sender); + } + } + } + + data.from = MailSender.getMailSenderFromString(section.getString("from")); + data.message = section.getStringList("message"); + + ConfigurationSection attachmentsSec = section.getConfigurationSection("attachments"); + if (attachmentsSec != null) { + data.attachments = new ArrayList(); + + for (String name : attachmentsSec.getKeys(false)) { + ConfigurationSection sub = section.getConfigurationSection("attachments." + name); + try { + ItemStack item = ItemConfigParser.getItemFromSection(sub); + if (item != null) + data.attachments.add(item); + } catch (ItemConfigParseException e) { + e.printStackTrace(); + } + } + } + + data.costMoney = section.getInt("costMoney", 0); + if (section.contains("costItem")) { + try { + data.costItem = ItemConfigParser.getItemFromSection(section.getConfigurationSection("costItem")); + } catch (ItemConfigParseException e) { + e.printStackTrace(); + } + } + + data.index = section.getInt("index"); + + data.readFlags = new ArrayList(); + for (String t : section.getStringList("readFlags")) { + MailSender sender = MailSender.getMailSenderFromString(t); + if (sender != null) { + data.readFlags.add(sender); + } + } + + data.trashFlags = new ArrayList(); + for (String t : section.getStringList("trashFlags")) { + MailSender sender = MailSender.getMailSenderFromString(t); + if (sender != null) { + data.trashFlags.add(sender); + } + } + + ConfigurationSection attachmentsOrgSec = section.getConfigurationSection("attachmentsOriginal"); + if (attachmentsOrgSec != null) { + data.attachmentsOriginal = new ArrayList(); + + for (String name : attachmentsOrgSec.getKeys(false)) { + ConfigurationSection sub = attachmentsOrgSec.getConfigurationSection(name); + if (sub != null) { + try { + ItemStack item = ItemConfigParser.getItemFromSection(sub); + data.attachmentsOriginal.add(item); + } catch (ItemConfigParseException e) { + e.printStackTrace(); + } + } + } + } + + if (section.contains("date")) { + data.date = new Date(section.getLong("date")); + } + + ConfigurationSection locSec = section.getConfigurationSection("location"); + if (locSec != null) { + String worldName = locSec.getString("world"); + if (worldName != null) { + World world = Bukkit.getWorld(worldName); + if (world != null) { + double x = locSec.getDouble("x"); + double y = locSec.getDouble("y"); + double z = locSec.getDouble("z"); + double yaw = locSec.getDouble("yaw"); + double pitch = locSec.getDouble("pitch"); + data.location = new Location(world, x, y, z, (float) yaw, (float) pitch); + } + } + } + + data.isAttachmentsCancelled = section.getBoolean("isAttachmentsCancelled", false); + data.isAttachmentsRefused = section.getBoolean("isAttachmentsRefused", false); + data.attachmentsRefusedReason = section.getString("attachmentsRefusedReason"); + + return data; + } + + /** + * このオブジェクトの複製を作成して返す。 + * + * @see java.lang.Object#clone() + */ + @Override + public MailDataFlatFile clone() { + return new MailDataFlatFile(new ArrayList(to), from, message, new ArrayList(attachments), + costMoney, costItem, toGroups); + } + + /** + * 設定されている宛先を全て消去する + */ + @Override + public void deleteAllTo() { + to.clear(); + toGroups.clear(); + } + + /** + * このメールの宛先を取得します。 + * + * @return 宛先 + */ + @Override + public List getTo() { + return to; + } + + /** + * このメールの宛先を設定します。 + * + * @param line 宛先番号(0から始まることに注意) + * @param to 宛先 + */ + @Override + public void setTo(int line, MailSender to) { + if (this.to.size() <= line) { + this.to.add(to); + } else if (this.to.size() > line) { + this.to.set(line, to); + } + + // 全体メールだった場合は、全体グループを除去しておく。 + if (isAllMail()) { + toGroups.remove(SpecialGroupAll.NAME); + } + } + + /** + * このメールの宛先を追加します。 + * + * @param to 宛先 + */ + @Override + public void addTo(MailSender to) { + setTo(Integer.MAX_VALUE, to); + } + + /** + * このメールの宛后を複数追加します。 + */ + @Override + public void addTo(List to) { + getTo().addAll(to); + } + + /** + * このメールの指定された宛先を削除します。 + * + * @param line 宛先番号(0から始まることに注意) + */ + @Override + public void deleteTo(int line) { + if (this.to.size() > line) { + this.to.remove(line); + } + } + + /** + * このメールの発信元を取得します。 + * + * @return 発信元 + */ + @Override + public MailSender getFrom() { + return from; + } + + /** + * このメールの発信元を設定します。 + * + * @param from 発信元 + */ + @Override + public void setFrom(MailSender from) { + this.from = from; + } + + /** + * このメールのメッセージを取得します。 + * + * @return メッセージ + */ + @Override + public List getMessage() { + return message; + } + + /** + * このメールのメッセージを設定します。 + * + * @param message メッセージ + */ + @Override + public void setMessage(List message) { + this.message = message; + } + + /** + * このメールのメッセージを設定します。 + * + * @param line 行番号(0から始まることに注意) + * @param message メッセージ + */ + @Override + public void setMessage(int line, String message) { + while (this.message.size() <= line) { + this.message.add(""); + } + this.message.set(line, message); + } + + /** + * このメールのメッセージに、指定した内容を追加します。 + * + * @param message メッセージ + */ + @Override + public void addMessage(String message) { + String[] lines = message.split("\r\n|\r|\n", -1); + for (String line : lines) { + this.message.add(line); + } + } + + /** + * このメールのメッセージの、指定された行番号を削除します。 + * + * @param line 宛先番号(0から始まることに注意) + */ + @Override + public void deleteMessage(int line) { + if (this.message.size() > line && line >= 0) { + this.message.remove(line); + } + } + + /** + * 宛先グループを取得します。 + * + * @return 宛先グループ + */ + @Override + public List getToGroups() { + return toGroups; + } + + /** + * このメールの宛先グループを設定します。 + * + * @param line 宛先番号(0から始まることに注意) + * @param group グループ + */ + @Override + public void setToGroup(int line, String group) { + + // 追加するグループが全体グループなら、 + // 他の宛先を全て削除する + if (SpecialGroupAll.NAME.equals(group)) { + this.to.clear(); + this.toGroups.clear(); + this.toGroups.add(group); + return; + } + + if (this.toGroups.size() <= line) { + this.toGroups.add(group); + } else if (this.toGroups.size() > line) { + this.toGroups.set(line, group); + } + + // 全体グループが含まれていたなら、全体グループを除去する + if (toGroups.contains(SpecialGroupAll.NAME)) { + toGroups.remove(SpecialGroupAll.NAME); + } + } + + /** + * このメールの宛先グループの、指定された行番号を削除します。 + * + * @param line 宛先番号(0から始まることに注意) + */ + @Override + public void deleteToGroup(int line) { + if (this.toGroups.size() > line && line >= 0) { + this.toGroups.remove(line); + } + } + + /** + * 統合宛先を設定する + * + * @param total 統合宛先 + */ + @Override + protected void setToTotal(List total) { + this.toTotal = total; + } + + /** + * 統合宛先(宛先+宛先グループの和集合)を取得する。未送信メールの場合はnullになる。 + * + * @return 統合宛先 + */ + @Override + public List getToTotal() { + return toTotal; + } + + /** + * このメールに添付されたアイテムを取得します。 + * + * @return 添付アイテム + */ + @Override + public List getAttachments() { + return attachments; + } + + /** + * このメールの添付アイテムを設定します。 + * + * @param attachments 添付アイテム + */ + @Override + public void setAttachments(List attachments) { + this.attachments = attachments; + } + + /** + * 指定されたアイテムを添付アイテムに追加します。 + * + * @param item アイテム + */ + @Override + public void addAttachment(ItemStack item) { + this.attachments.add(item); + } + + /** + * このメールを読んだ人のリストを取得します。 + * + * @return 読んだ人のリスト + */ + @Override + public List getReadFlags() { + return readFlags; + } + + /** + * このメールに削除フラグを付けた人のリストを取得します。 + * + * @return 削除フラグをつけている人のリスト + */ + @Override + public List getTrashFlags() { + return trashFlags; + } + + /** + * このメールの添付アイテムを受け取るのに必要な金額を取得します。 + * + * @return 受け取り金額 + */ + @Override + public double getCostMoney() { + return costMoney; + } + + /** + * このメールの添付アイテムを受け取るのに必要な金額を設定します。 + * + * @param feeMoney 受け取り金額 + */ + @Override + public void setCostMoney(double feeMoney) { + this.costMoney = feeMoney; + } + + /** + * このメールの添付アイテムを受け取るのに必要な引き換えアイテムを取得します。 + * + * @return 引き換えアイテム + */ + @Override + public ItemStack getCostItem() { + return costItem; + } + + /** + * このメールの添付アイテムを受け取るのに必要な引き換えアイテムを設定します。 + * + * @param feeItem 引き換えアイテム + */ + @Override + public void setCostItem(ItemStack feeItem) { + this.costItem = feeItem; + } + + /** + * このメールの送信時間を取得します。 + * + * @return 送信時間 + */ + @Override + public Date getDate() { + return date; + } + + /** + * このメールの送信時間を設定します(メール送信時に自動で割り当てられます)。 + * + * @param date 送信時間 + */ + @Override + public void setDate(Date date) { + this.date = date; + } + + /** + * このメールが送信された地点を取得します。 + * + * @return 送信地点 + */ + @Override + public Location getLocation() { + return location; + } + + /** + * このメールの送信地点を設定します(メール送信時に自動で割り当てられます)。 + * + * @param location 送信地点 + */ + @Override + public void setLocation(Location location) { + this.location = location; + } + + /** + * メール送信時の添付アイテムを取得します。 + * + * @return メール送信時の添付アイテム + */ + @Override + public List getAttachmentsOriginal() { + return attachmentsOriginal; + } + + /** + * attachmentOriginalに、添付ファイルのコピーを行います (メール送信時に自動で行われます)。 + */ + @Override + protected void makeAttachmentsOriginal() { + attachmentsOriginal = new ArrayList(attachments); + } + + /** + * 指定したプレイヤーが、このメールを読んだかどうかを返します。 + * + * @param player プレイヤー + * @return 読んだかどうか + */ + @Override + public boolean isRead(MailSender player) { + return readFlags.contains(player); + } + + /** + * 指定した名前のsenderの既読マークを付ける + * + * @param sender sender + */ + @Override + public void setReadFlag(MailSender sender) { + if (!readFlags.contains(sender)) { + readFlags.add(sender); + } + } + + /** + * 指定した人が、このメールに削除マークをつけているかどうかを返します。 + * + * @param sender + * @return 削除マークをつけているかどうか + */ + @Override + public boolean isSetTrash(MailSender sender) { + return trashFlags.contains(sender); + } + + /** + * 指定した人の削除マークを付ける + * + * @param sender + */ + @Override + public void setTrashFlag(MailSender sender) { + if (!trashFlags.contains(sender)) { + trashFlags.add(sender); + } + } + + /** + * 指定した人の削除マークを消す + * + * @param sender + */ + @Override + public void removeTrashFlag(MailSender sender) { + if (trashFlags.contains(sender)) { + trashFlags.remove(sender); + } + } + + /** + * 指定された名前のプレイヤーは、このメールの関係者かどうかを返す。 + * + * @param sender sender + * @return 指定された名前がtoまたはfromに含まれるかどうか + */ + @Override + public boolean isRelatedWith(MailSender sender) { + if (isAllMail()) + return true; + if (from.equals(sender)) + return true; + if (toTotal != null) + return toTotal.contains(sender); + return to.contains(sender); + } + + /** + * 指定された名前のプレイヤーは、このメールの受信者かどうかを返す。 + * + * @param sender sender + * @return 指定された名前がtoに含まれるかどうか + */ + @Override + public boolean isRecipient(MailSender sender) { + if (isAllMail()) + return true; + if (toTotal != null) + return toTotal.contains(sender); + return to.contains(sender); + } + + /** + * このメールの添付アイテムがオープンされたのかどうかを返す + * + * @return 添付アイテムがオープンされたのかどうか + */ + @Override + public boolean isAttachmentsOpened() { + return isAttachmentsOpened; + } + + /** + * このメールの添付アイテムをオープンされたとして記録する。 受信者が添付ボックスを一度でも開いた事がある状態なら、 + * 送信者は添付アイテムをキャンセルすることができなくなる。 + */ + @Override + public void setOpenAttachments() { + this.isAttachmentsOpened = true; + } + + /** + * このメールの添付アイテムがキャンセルされたのかどうかを返す + * + * @return 添付アイテムがキャンセルされたのかどうか + */ + @Override + public boolean isAttachmentsCancelled() { + return isAttachmentsCancelled; + } + + /** + * このメールの添付アイテムをキャンセルする。 添付アイテムがキャンセルされると、受信者はボックスを開けなくなり、 + * 逆に送信者がボックスを開くことができるようになる。 + */ + @Override + public void cancelAttachments() { + this.isAttachmentsCancelled = true; + this.costItem = null; + this.costMoney = 0; + } + + /** + * このメールの添付アイテムが拒否されたのかどうかを返す + * + * @return 添付アイテムが拒否されたのかどうか + */ + @Override + public boolean isAttachmentsRefused() { + return isAttachmentsRefused; + } + + /** + * 受取拒否の理由を取得します。 + * + * @return 受取拒否の理由(設定されていない場合はnullになることに注意すること) + */ + @Override + public String getAttachmentsRefusedReason() { + return attachmentsRefusedReason; + } + + /** + * このメールの添付アイテムを拒否する。 添付アイテムが拒否されると、受信者はボックスを開けなくなり、 逆に送信者がボックスを開くことができるようになる。 + * + * @param attachmentsRefusedReason 拒否理由 + */ + @Override + public void refuseAttachments(String attachmentsRefusedReason) { + this.isAttachmentsCancelled = true; // キャンセルフラグも立てる + this.isAttachmentsRefused = true; + if (attachmentsRefusedReason != null && attachmentsRefusedReason.length() > 0) { + this.attachmentsRefusedReason = attachmentsRefusedReason; + } + this.costItem = null; + this.costMoney = 0; + } + + /** + * データのアップグレードを行う。 + * @return アップグレードを実行したかどうか + */ + @Override + protected boolean upgrade() { + boolean upgraded = false; + for ( MailSender ms : to ) { + if ( ms instanceof MailSenderPlayer ) { + if ( ((MailSenderPlayer) ms).upgrade() ) { + upgraded = true; + } + } + } + if ( from instanceof MailSenderPlayer ) { + if ( ((MailSenderPlayer) from).upgrade() ) { + upgraded = true; + } + } + for ( MailSender ms : toTotal ) { + if ( ms instanceof MailSenderPlayer ) { + if ( ((MailSenderPlayer) ms).upgrade() ) { + upgraded = true; + } + } + } + for ( MailSender ms : readFlags ) { + if ( ms instanceof MailSenderPlayer ) { + if ( ((MailSenderPlayer) ms).upgrade() ) { + upgraded = true; + } + } + } + for ( MailSender ms : trashFlags ) { + if ( ms instanceof MailSenderPlayer ) { + if ( ((MailSenderPlayer) ms).upgrade() ) { + upgraded = true; + } + } + } + return upgraded; + } + + @Override + public boolean equals(Object o) { + return super.equals(o) && o instanceof MailDataFlatFile; + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/MailManager.java b/src/main/java/org/bitbucket/ucchy/undine/MailManager.java index 333d82f..4bbf67f 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/MailManager.java +++ b/src/main/java/org/bitbucket/ucchy/undine/MailManager.java @@ -1,19 +1,9 @@ -/* - * @author ucchy - * @license LGPLv3 - * @copyright Copyright ucchy 2015 - */ package org.bitbucket.ucchy.undine; -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.Date; -import java.util.HashMap; import java.util.List; import org.bitbucket.ucchy.undine.bridge.VaultEcoBridge; @@ -25,23 +15,20 @@ import org.bitbucket.ucchy.undine.sender.MailSenderBlock; import org.bitbucket.ucchy.undine.sender.MailSenderConsole; import org.bitbucket.ucchy.undine.sender.MailSenderPlayer; -import org.bitbucket.ucchy.undine.tellraw.ClickEventType; -import org.bitbucket.ucchy.undine.tellraw.MessageComponent; -import org.bitbucket.ucchy.undine.tellraw.MessageParts; -import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import org.bukkit.scheduler.BukkitRunnable; + +import com.github.ucchyocean.messaging.tellraw.ClickEventType; +import com.github.ucchyocean.messaging.tellraw.MessageComponent; +import com.github.ucchyocean.messaging.tellraw.MessageParts; /** - * メールデータマネージャ - * @author ucchy + * メールデータマネージャ。 + * NOTE: Editmode mail = 送信前のメール + * @author LazyGon */ -public class MailManager { +public abstract class MailManager { protected static final String MAILLIST_METAKEY = "UndineMailList"; public static final String SENDTIME_METAKEY = "MailSendTime"; @@ -54,12 +41,7 @@ public class MailManager { private static final int PAGE_SIZE = 10; private static final int MESSAGE_ADD_SIZE = 3; - private ArrayList mails; - private HashMap editmodeMails; - private int nextIndex; - private boolean isLoaded; - - private UndineMailer parent; + protected UndineMailer parent; /** * コンストラクタ @@ -74,61 +56,7 @@ public MailManager(UndineMailer parent) { * メールデータを再読込する * @param リロードが完了した時に、通知する先。通知が不要なら、nullでよい。 */ - protected void reload(final CommandSender sender) { - - final long start = System.currentTimeMillis(); - - new BukkitRunnable() { - public void run() { - - isLoaded = false; - mails = new ArrayList(); - nextIndex = 1; - - File folder = parent.getMailFolder(); - File[] files = folder.listFiles(new FilenameFilter() { - public boolean accept(File dir, String name) { - return name.endsWith(".yml"); - } - }); - - if ( files != null ) { - for ( File file : files ) { - MailData data = MailData.load(file); - mails.add(data); - - if ( nextIndex <= data.getIndex() ) { - nextIndex = data.getIndex() + 1; - } - } - } - - UndineMailer.getInstance().getLogger().info("Async load mail data... Done. Time: " - + (System.currentTimeMillis() - start) + "ms, Data: " + mails.size() + "."); - - long upgradeStart = System.currentTimeMillis(); - - int total = 0; - for ( MailData mail : mails ) { - if ( mail.upgrade() ) { - saveMail(mail); - total++; - } - } - - if ( total > 0 ) { - UndineMailer.getInstance().getLogger().info("Async upgrade mail data... Done. Time: " - + (System.currentTimeMillis() - upgradeStart) + "ms, Data: " + total + "."); - } - - isLoaded = true; - - if ( sender != null ) { - sender.sendMessage(Messages.get("InformationReload")); - } - } - }.runTaskAsynchronously(UndineMailer.getInstance()); - } + protected abstract void reload(final CommandSender sender); /** * メールデータがロード完了したかどうか。 @@ -137,25 +65,14 @@ public boolean accept(File dir, String name) { * 注意してください。 * @return ロード完了したかどうか */ - public boolean isLoaded() { - return isLoaded; - } + public abstract boolean isLoaded(); /** * 指定されたインデクスのメールを取得する * @param index インデクス * @return メールデータ */ - public MailData getMail(int index) { - - if ( !isLoaded ) return null; - for ( MailData m : mails ) { - if ( m.getIndex() == index ) { - return m; - } - } - return null; - } + public abstract MailData getMail(int index); /** * 新しいテキストメールを送信する @@ -169,7 +86,9 @@ public void sendNewMail(MailSender from, MailSender to, String message) { toList.add(to); ArrayList messageList = new ArrayList(); messageList.add(message); - MailData mail = new MailData(toList, from, messageList); + MailData mail = makeEditmodeMail(from); + mail.addTo(to); + mail.setMessage(messageList); sendNewMail(mail); } @@ -183,7 +102,9 @@ public void sendNewMail(MailSender from, List to, String message) { ArrayList messageList = new ArrayList(); messageList.add(message); - MailData mail = new MailData(to, from, messageList); + MailData mail = makeEditmodeMail(from); + mail.addTo(to); + mail.setMessage(messageList); sendNewMail(mail); } @@ -195,7 +116,9 @@ public void sendNewMail(MailSender from, List to, String message) { */ public void sendNewMail(MailSender from, List to, List message) { - MailData mail = new MailData(to, from, message); + MailData mail = makeEditmodeMail(from); + mail.addTo(to); + mail.setMessage(message); sendNewMail(mail); } @@ -203,214 +126,42 @@ public void sendNewMail(MailSender from, List to, List messa * 新しいメールを送信する * @param mail メール */ - public void sendNewMail(MailData mail) { - - // メールデータの本文が1行も無いときは、ここで1行追加を行う。 - if ( mail.getMessage().size() == 0 ) { - mail.addMessage(""); - } - - // ロードが完了していないうちは、メールを送信できないようにする - if ( !isLoaded ) { - UndineMailer.getInstance().getLogger().warning( - "Because mailer has not yet been initialized, mailer dropped new mail."); - UndineMailer.getInstance().getLogger().warning(mail.getInboxSummary()); - return; - } - - // 統合宛先を設定する。 - ArrayList to_total = new ArrayList(); - for ( MailSender t : mail.getTo() ) { - if ( !to_total.contains(t) ) { - to_total.add(t); - } - } - for ( GroupData group : mail.getToGroupsConv() ) { - for ( MailSender t : group.getMembers() ) { - if ( !to_total.contains(t) ) { - to_total.add(t); - } - } - } - mail.setToTotal(to_total); - - // インデクスを設定する - mail.setIndex(nextIndex); - nextIndex++; - - // 送信時間を設定する - mail.setDate(new Date()); - - // 送信地点を設定する - mail.setLocation(mail.getFrom().getLocation()); - - // オリジナルの添付ファイルを記録する - mail.makeAttachmentsOriginal(); - - // 添付が無いなら、着払い設定はクリアしておく - if ( mail.getAttachments().size() == 0 ) { - mail.setCostMoney(0); - mail.setCostItem(null); - } - - // 着払いアイテムが設定されているなら、着払い料金はクリアしておく - if ( mail.getCostItem() != null ) { - mail.setCostMoney(0); - } - - // 着払い料金が無効なら、着払い料金はクリアしておく - if ( !parent.getUndineConfig().isEnableCODMoney() ) { - mail.setCostMoney(0); - } - - // 着払いアイテム無効なら、着払いアイテムはクリアしておく - if ( !parent.getUndineConfig().isEnableCODItem() ) { - mail.setCostItem(null); - } - - // 保存する - mails.add(mail); - saveMail(mail); - - // 宛先の人がログイン中なら知らせる - String msg = Messages.get("InformationYouGotMail", - "%from", mail.getFrom().getName()); - - if ( mail.isAllMail() ) { - for ( Player player : Utility.getOnlinePlayers() ) { - player.sendMessage(msg); - String pre = Messages.get("ListVerticalParts"); - sendMailLine(MailSender.getMailSender(player), - pre, ChatColor.GOLD + mail.getInboxSummary(), mail); - } - } else { - for ( MailSender to : mail.getToTotal() ) { - if ( to.isOnline() ) { - to.sendMessage(msg); - String pre = Messages.get("ListVerticalParts"); - sendMailLine(to, pre, ChatColor.GOLD + mail.getInboxSummary(), mail); - } - } - } - - // 送った時刻を、メタデータに記録する - long time = System.currentTimeMillis(); - mail.getFrom().setStringMetadata(SENDTIME_METAKEY, time + ""); - } + public abstract void sendNewMail(MailData mail); /** * 受信したメールのリストを取得する * @param sender 取得する対象 * @return メールのリスト */ - public ArrayList getInboxMails(MailSender sender) { - - if ( !isLoaded ) { - return null; - } - - ArrayList box = new ArrayList(); - for ( MailData mail : mails ) { - if ( mail.isAllMail() - || (mail.getToTotal() != null && mail.getToTotal().contains(sender)) - || mail.getTo().contains(sender) ) { - if ( !mail.isSetTrash(sender) ) { - box.add(mail); - } - } - } - sortNewer(box); - return box; - } + public abstract ArrayList getInboxMails(MailSender sender); /** * 受信したメールで未読のリストを取得する * @param sender 取得する対象 * @return メールのリスト */ - public ArrayList getUnreadMails(MailSender sender) { - - if ( !isLoaded ) { - return null; - } - - ArrayList box = new ArrayList(); - for ( MailData mail : mails ) { - if ( mail.isAllMail() - || (mail.getToTotal() != null && mail.getToTotal().contains(sender)) - || mail.getTo().contains(sender) ) { - if ( !mail.isRead(sender) && !mail.isSetTrash(sender) ) { - box.add(mail); - } - } - } - sortNewer(box); - return box; - } + public abstract ArrayList getUnreadMails(MailSender sender); /** * 送信したメールのリストを取得する * @param sender 取得する対象 * @return メールのリスト */ - public ArrayList getOutboxMails(MailSender sender) { - - if ( !isLoaded ) { - return null; - } - - ArrayList box = new ArrayList(); - for ( MailData mail : mails ) { - if ( mail.getFrom().equals(sender) && !mail.isSetTrash(sender) ) { - box.add(mail); - } - } - sortNewer(box); - return box; - } + public abstract ArrayList getOutboxMails(MailSender sender); /** * 関連メールのリストを取得する * @param sender 取得する対象 * @return メールのリスト */ - public ArrayList getRelatedMails(MailSender sender) { - - if ( !isLoaded ) { - return null; - } - - ArrayList box = new ArrayList(); - for ( MailData mail : mails ) { - if ( mail.isRelatedWith(sender) && mail.isRead(sender) - && !mail.isSetTrash(sender) ) { - box.add(mail); - } - } - sortNewer(box); - return box; - } + public abstract ArrayList getRelatedMails(MailSender sender); /** * ゴミ箱フォルダのメールリストを取得する * @param sender 取得する対象 * @return メールのリスト */ - public ArrayList getTrashboxMails(MailSender sender) { - - if ( !isLoaded ) { - return null; - } - - ArrayList box = new ArrayList(); - for ( MailData mail : mails ) { - if ( mail.isRelatedWith(sender) && mail.isSetTrash(sender) ) { - box.add(mail); - } - } - sortNewer(box); - return box; - } + public abstract ArrayList getTrashboxMails(MailSender sender); /** * 指定されたメールを開いて確認する @@ -420,7 +171,7 @@ public ArrayList getTrashboxMails(MailSender sender) { public void displayMail(MailSender sender, MailData mail) { // ロード中の場合は、メールを表示できません - if ( !isLoaded ) { + if ( !isLoaded() ) { return; } @@ -438,101 +189,38 @@ public void displayMail(MailSender sender, MailData mail) { * 指定されたメールデータをUndineに保存する * @param mail メールデータ */ - public void saveMail(MailData mail) { - - // 編集中で未送信のメールは保存できません。 - if ( mail.getIndex() == 0 ) { - return; - } - - String filename = String.format("%1$08d.yml", mail.getIndex()); - File folder = parent.getMailFolder(); - File file = new File(folder, filename); - mail.save(file); - } + public abstract void saveMail(MailData mail); /** * 指定されたインデクスのメールを削除する * @param index インデクス */ - public void deleteMail(int index) { - - if ( isLoaded ) { - MailData mail = getMail(index); - if ( mail != null ) { - mails.remove(mail); - } - } - - String filename = String.format("%1$08d.yml", index); - File folder = parent.getMailFolder(); - File file = new File(folder, filename); - if ( file.exists() ) { - file.delete(); - } - } + public abstract void deleteMail(int index); /** * 古いメールを削除する */ - protected void cleanup() { - - if ( !isLoaded ) { - return; - } - - ArrayList queue = new ArrayList(); - int period = parent.getUndineConfig().getMailStorageTermDays(); - Date now = new Date(); - - for ( MailData mail : mails ) { - int days = (int)((now.getTime() - mail.getDate().getTime()) / (1000*60*60*24)); - if ( days > period ) { - queue.add(mail.getIndex()); - } - } - - for ( int index : queue ) { - deleteMail(index); - } - } + protected abstract void cleanup(); /** * 編集中メールを作成して返す * @param sender 取得対象のsender * @return 編集中メール */ - public MailData makeEditmodeMail(MailSender sender) { - String id = sender.toString(); - if ( editmodeMails.containsKey(id) ) { - return editmodeMails.get(id); - } - MailData mail = new MailData(); - mail.setFrom(sender); - editmodeMails.put(id, mail); - return mail; - } + public abstract MailData makeEditmodeMail(MailSender sender); /** * 編集中メールを取得する * @param sender 取得対象のsender * @return 編集中メール(編集中でないならnull) */ - public MailData getEditmodeMail(MailSender sender) { - String id = sender.toString(); - if ( editmodeMails.containsKey(id) ) { - return editmodeMails.get(id); - } - return null; - } + public abstract MailData getEditmodeMail(MailSender sender); /** * 編集中メールを削除する * @param sender 削除対象のsender */ - public void clearEditmodeMail(MailSender sender) { - editmodeMails.remove(sender.toString()); - } + public abstract void clearEditmodeMail(MailSender sender); /** * 指定されたsenderに、Inboxリストを表示する。 @@ -542,7 +230,7 @@ public void clearEditmodeMail(MailSender sender) { public void displayInboxList(MailSender sender, int page) { // ロード中の場合は、リストを表示しないようにする - if ( !isLoaded ) { + if ( !isLoaded() ) { return; } @@ -593,7 +281,7 @@ public void displayInboxList(MailSender sender, int page) { public void displayOutboxList(MailSender sender, int page) { // ロード中の場合は、リストを表示しないようにする - if ( !isLoaded ) { + if ( !isLoaded() ) { return; } @@ -637,7 +325,7 @@ public void displayOutboxList(MailSender sender, int page) { protected void displayUnreadOnJoin(MailSender sender) { // ロード中の場合は、リストを表示しないようにする - if ( !isLoaded ) { + if ( !isLoaded() ) { return; } @@ -673,7 +361,7 @@ protected void displayUnreadOnJoin(MailSender sender) { public void displayTrashboxList(MailSender sender, int page) { // ロード中の場合は、リストを表示しないようにする - if ( !isLoaded ) { + if ( !isLoaded() ) { return; } @@ -713,63 +401,19 @@ public void displayTrashboxList(MailSender sender, int page) { /** * 編集中メールをeditmails.ymlへ保存する */ - protected void storeEditmodeMail() { - - YamlConfiguration config = new YamlConfiguration(); - for ( String name : editmodeMails.keySet() ) { - ConfigurationSection section = config.createSection(name); - editmodeMails.get(name).saveToConfigSection(section); - } - - try { - File file = new File(parent.getDataFolder(), "editmails.yml"); - config.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } + protected abstract void storeEditmodeMail(); /** * editmails.ymlから編集中メールを復帰する */ - protected void restoreEditmodeMail() { - - editmodeMails = new HashMap(); - - File file = new File(parent.getDataFolder(), "editmails.yml"); - if ( !file.exists() ) return; - - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - for ( String name : config.getKeys(false) ) { - MailData mail = MailData.loadFromConfigSection( - config.getConfigurationSection(name)); - editmodeMails.put(name, mail); - } - - // 復帰元ファイルを削除しておく - file.delete(); - } + protected abstract void restoreEditmodeMail(); /** * 指定したsenderが使用中の添付ボックスの個数を返す * @param sender * @return 使用中添付ボックスの個数 */ - public int getAttachBoxUsageCount(MailSender sender) { - - // ロード中の場合は、Integer最大値を返す - if ( !isLoaded ) { - return Integer.MAX_VALUE; - } - - int count = 0; - for ( MailData mail : mails ) { - if ( mail.getFrom().equals(sender) && mail.getAttachments().size() > 0 ) { - count++; - } - } - return count; - } + public abstract int getAttachBoxUsageCount(MailSender sender); /** * メールの詳細情報を表示する @@ -795,8 +439,8 @@ public void displayMailDescription(MailSender sender, MailData mail) { sender.sendMessage(""); } - String num = mail.isEditmode() ? Messages.get("Editmode") : mail.getIndex() + ""; - String fdate = mail.isEditmode() ? null : getFormattedDate(mail.getDate()); + String num = !mail.isSent() ? Messages.get("Editmode") : mail.getIndex() + ""; + String fdate = !mail.isSent() ? null : getFormattedDate(mail.getDate()); String parts = Messages.get("DetailHorizontalParts"); String pre = Messages.get("DetailVerticalParts"); @@ -831,7 +475,7 @@ public void displayMailDescription(MailSender sender, MailData mail) { msg.addText(pre + Messages.get("MailDetailAttachmentsLine")); msg.addText(" "); - if ( !mail.isEditmode() ) { + if ( mail.isSent() ) { if ( (!mail.isAttachmentsCancelled() && mail.isRecipient(sender)) || (mail.isAttachmentsCancelled() && mail.getFrom().equals(sender)) ) { @@ -860,7 +504,7 @@ public void displayMailDescription(MailSender sender, MailData mail) { ChatColor.AQUA); button.setClickEvent(ClickEventType.RUN_COMMAND, COMMAND + " attach " + mail.getIndex() + " cancel"); - button.addHoverText(Messages.get("MailDetailAttachmentBoxCancelToolTip")); + button.setHoverText(Messages.get("MailDetailAttachmentBoxCancelToolTip")); msg.addParts(button); } @@ -879,27 +523,29 @@ public void displayMailDescription(MailSender sender, MailData mail) { } } - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); for ( ItemStack i : mail.getAttachments() ) { sender.sendMessage(pre + " " + ChatColor.WHITE + getItemDesc(i, true)); } - if ( mail.getCostMoney() > 0 || mail.getCostItem() != null ) { - String costDesc = mail.getCostMoney() + ""; + double costMoney = mail.getCostMoney(); + ItemStack costItem = mail.getCostItem(); + if ( costMoney > 0 || costItem != null ) { + String costDesc = costMoney + ""; VaultEcoBridge eco = UndineMailer.getInstance().getVaultEco(); if ( eco != null ) { - costDesc = eco.format(mail.getCostMoney()); + costDesc = eco.format(costMoney); } msg = new MessageComponent(); - if ( mail.getCostMoney() > 0 ) { + if ( costMoney > 0 ) { msg.addText(pre + Messages.get( "MailDetailAttachCostMoneyLine", "%fee", costDesc)); } else { msg.addText(pre + Messages.get( "MailDetailAttachCostItemLine", "%item", - getItemDesc(mail.getCostItem(), true))); + getItemDesc(costItem, true))); } if ( mail.getTo().contains(sender) ) { msg.addText(" "); @@ -909,11 +555,11 @@ public void displayMailDescription(MailSender sender, MailData mail) { refuseButton.setClickEvent( ClickEventType.SUGGEST_COMMAND, UndineCommand.COMMAND + " attach " + mail.getIndex() + " refuse "); - refuseButton.addHoverText( + refuseButton.setHoverText( Messages.get("MailDetailAttachmentBoxRefuseToolTip")); msg.addParts(refuseButton); } - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); } } else if ( mail.isAttachmentsCancelled() ) { @@ -932,7 +578,7 @@ public void displayMailDescription(MailSender sender, MailData mail) { } } - if ( !mail.isEditmode() && mail.getAttachmentsOriginal() != null + if ( mail.isSent() && mail.getAttachmentsOriginal() != null && mail.getAttachmentsOriginal().size() > 0 && mail.getFrom().equals(sender) ) { // 添付アイテムオリジナルがあり、表示先が送信者なら、元の添付アイテムを表示する。 @@ -945,7 +591,7 @@ public void displayMailDescription(MailSender sender, MailData mail) { } if ( sender instanceof MailSenderPlayer - && mail.isRelatedWith(sender) && !mail.isEditmode() ) { + && mail.isRelatedWith(sender) && mail.isSent() ) { if ( mail.isSetTrash(sender) ) { // ゴミ箱に入っているメールなら、Restoreボタンを表示する @@ -959,7 +605,7 @@ public void displayMailDescription(MailSender sender, MailData mail) { COMMAND + " trash restore " + mail.getIndex()); msg.addParts(button); - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); } else { // 既に添付が1つもないメールなら、Deleteボタンを表示する @@ -994,12 +640,12 @@ public void displayMailDescription(MailSender sender, MailData mail) { msg.addParts(button); } - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); } } } - if ( !mail.isEditmode() && mail.getLocation() != null + if ( mail.isSent() && mail.getLocation() != null && sender instanceof MailSenderPlayer && sender.hasPermission(PERMISSION_TELEPORT) ) { @@ -1012,7 +658,7 @@ public void displayMailDescription(MailSender sender, MailData mail) { COMMAND + " teleport " + mail.getIndex()); msg.addParts(button); - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); } sendMailDescriptionPager(sender, mail.getIndex()); @@ -1039,9 +685,11 @@ public void displayEditmode(MailSender sender) { } // メッセージが3行に満たない場合は、この時点で空行を足しておく - while ( mail.getMessage().size() < MESSAGE_ADD_SIZE ) { - mail.addMessage(""); + List message = mail.getMessage(); + while ( message.size() < MESSAGE_ADD_SIZE ) { + message.add(""); } + mail.setMessage(message); // 空行を挿入する int lines = UndineMailer.getInstance().getUndineConfig().getUiEmptyLines(); @@ -1055,7 +703,8 @@ public void displayEditmode(MailSender sender) { String title = Messages.get("EditmodeTitle"); sender.sendMessage(parts + parts + " " + title + " " + parts + parts); - for ( int i=0; i to = mail.getTo(); + for ( int i=0; i toGroups = mail.getToGroups(); + for ( int i=0; i MailData.MESSAGE_MAX_SIZE ) { num = MailData.MESSAGE_MAX_SIZE; } @@ -1179,7 +829,7 @@ public void displayEditmode(MailSender sender) { ClickEventType.RUN_COMMAND, COMMAND + " message " + num); msg.addParts(button); - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); } boolean senderHasPermissionOfOpenAttachBox = @@ -1187,6 +837,10 @@ public void displayEditmode(MailSender sender) { sender.hasPermission(PERMISSION_ATTACH_SENDMAIL); if ( config.isEnableAttachment() && senderHasPermissionOfOpenAttachBox ) { + List attachments = mail.getAttachments(); + double costMoney = mail.getCostMoney(); + ItemStack costItem = mail.getCostItem(); + MessageComponent msg = new MessageComponent(); msg.addText(pre); MessageParts button = new MessageParts( @@ -1196,26 +850,26 @@ public void displayEditmode(MailSender sender) { COMMAND + " attach"); msg.addParts(button); msg.addText(" "); - msg.addText(Messages.get("EditmodeAttachNum", "%num", mail.getAttachments().size())); - sendMessageComponent(msg, sender); + msg.addText(Messages.get("EditmodeAttachNum", "%num", attachments.size())); + sender.sendMessageComponent(msg); boolean isEnableCODMoney = (UndineMailer.getInstance().getVaultEco() != null) && config.isEnableCODMoney(); boolean isEnableCODItem = config.isEnableCODItem(); - if ( mail.getAttachments().size() > 0 && (isEnableCODMoney || isEnableCODItem) ) { + if ( attachments.size() > 0 && (isEnableCODMoney || isEnableCODItem) ) { MessageComponent msgfee = new MessageComponent(); msgfee.addText(pre); - if ( mail.getCostMoney() == 0 && mail.getCostItem() == null ) { + if ( costMoney == 0 && costItem == null ) { if ( isEnableCODMoney ) { MessageParts buttonFee = new MessageParts( Messages.get("EditmodeCostMoney"), ChatColor.AQUA); buttonFee.setClickEvent( ClickEventType.SUGGEST_COMMAND, COMMAND + " costmoney "); - buttonFee.addHoverText(Messages.get("EditmodeCostMoneyToolTip")); + buttonFee.setHoverText(Messages.get("EditmodeCostMoneyToolTip")); msgfee.addParts(buttonFee); } @@ -1228,16 +882,16 @@ public void displayEditmode(MailSender sender) { Messages.get("EditmodeCostItem"), ChatColor.AQUA); buttonItem.setClickEvent( ClickEventType.SUGGEST_COMMAND, COMMAND + " costitem "); - buttonItem.addHoverText(Messages.get("EditmodeCostItemToolTip")); + buttonItem.setHoverText(Messages.get("EditmodeCostItemToolTip")); msgfee.addParts(buttonItem); } - } else if ( mail.getCostMoney() > 0 ) { + } else if ( costMoney > 0 ) { - String costDesc = mail.getCostMoney() + ""; + String costDesc = String.valueOf(costMoney); VaultEcoBridge eco = UndineMailer.getInstance().getVaultEco(); if ( eco != null ) { - costDesc = eco.format(mail.getCostMoney()); + costDesc = eco.format(costMoney); } MessageParts buttonDelete = new MessageParts( @@ -1245,38 +899,38 @@ public void displayEditmode(MailSender sender) { buttonDelete.setClickEvent( ClickEventType.RUN_COMMAND, COMMAND + " costmoney 0"); - buttonDelete.addHoverText(Messages.get("EditmodeCostMoneyRemoveToolTip")); + buttonDelete.setHoverText(Messages.get("EditmodeCostMoneyRemoveToolTip")); msgfee.addParts(buttonDelete); MessageParts buttonFee = new MessageParts( Messages.get("EditmodeCostMoneyData", "%fee", costDesc), ChatColor.AQUA); buttonFee.setClickEvent( ClickEventType.SUGGEST_COMMAND, - COMMAND + " costmoney " + mail.getCostMoney()); + COMMAND + " costmoney " + costMoney); msgfee.addParts(buttonFee); } else { - String desc = getItemDesc(mail.getCostItem(), true); + String desc = getItemDesc(costItem, true); MessageParts buttonDelete = new MessageParts( Messages.get("EditmodeCostItemRemove"), ChatColor.AQUA); buttonDelete.setClickEvent( ClickEventType.RUN_COMMAND, COMMAND + " costitem remove"); - buttonDelete.addHoverText(Messages.get("EditmodeCostItemRemoveToolTip")); + buttonDelete.setHoverText(Messages.get("EditmodeCostItemRemoveToolTip")); msgfee.addParts(buttonDelete); MessageParts buttonItem = new MessageParts( Messages.get("EditmodeCostItemData", "%item", desc), ChatColor.AQUA); buttonItem.setClickEvent( ClickEventType.SUGGEST_COMMAND, - COMMAND + " costitem " + getItemDesc(mail.getCostItem(), false)); + COMMAND + " costitem " + getItemDesc(costItem, false)); msgfee.addParts(buttonItem); } - sendMessageComponent(msgfee, sender); + sender.sendMessageComponent(msgfee); } } @@ -1292,7 +946,12 @@ public void displayEditmode(MailSender sender) { cancelButton.setClickEvent(ClickEventType.RUN_COMMAND, COMMAND + " cancel"); last.addParts(cancelButton); last.addText(parts); - sendMessageComponent(last, sender); + sender.sendMessageComponent(last); + + // メッセージがすべて空行の場合は、TIPSを表示する(see issue #90) + if ( areMessagesEmpty(mail) ) { + sender.sendMessage(Messages.get("EditmodeTipsMessage")); + } } /** @@ -1314,13 +973,13 @@ private void sendMailDescriptionPager(MailSender sender, int index) { // リストの取得 ArrayList list; if ( meta.equals("inbox") ) { - list = UndineMailer.getInstance().getMailManager().getInboxMails(sender); + list = getInboxMails(sender); } else if ( meta.equals("outbox") ) { - list = UndineMailer.getInstance().getMailManager().getOutboxMails(sender); + list = getOutboxMails(sender); } else if ( meta.equals("trash") ) { - list = UndineMailer.getInstance().getMailManager().getTrashboxMails(sender); + list = getTrashboxMails(sender); } else { - list = UndineMailer.getInstance().getMailManager().getUnreadMails(sender); + list = getUnreadMails(sender); } // ページ番号の取得 @@ -1358,7 +1017,7 @@ private void sendMailDescriptionPager(MailSender sender, int index) { MessageParts returnButton = new MessageParts( Messages.get("Return"), ChatColor.AQUA); returnButton.setClickEvent(ClickEventType.RUN_COMMAND, returnCommand); - returnButton.addHoverText(Messages.get("ReturnListToolTip")); + returnButton.setHoverText(Messages.get("ReturnListToolTip")); msg.addParts(returnButton); msg.addText(" "); @@ -1372,7 +1031,7 @@ private void sendMailDescriptionPager(MailSender sender, int index) { firstLabel, ChatColor.AQUA); firstButton.setClickEvent(ClickEventType.RUN_COMMAND, COMMAND + " read " + first); - firstButton.addHoverText(firstToolTip); + firstButton.setHoverText(firstToolTip); msg.addParts(firstButton); msg.addText(" "); @@ -1381,7 +1040,7 @@ private void sendMailDescriptionPager(MailSender sender, int index) { prevLabel, ChatColor.AQUA); prevButton.setClickEvent(ClickEventType.RUN_COMMAND, COMMAND + " read " + prev); - prevButton.addHoverText(prevToolTip); + prevButton.setHoverText(prevToolTip); msg.addParts(prevButton); } else { @@ -1399,7 +1058,7 @@ private void sendMailDescriptionPager(MailSender sender, int index) { nextLabel, ChatColor.AQUA); nextButton.setClickEvent(ClickEventType.RUN_COMMAND, COMMAND + " read " + next); - nextButton.addHoverText(nextToolTip); + nextButton.setHoverText(nextToolTip); msg.addParts(nextButton); msg.addText(" "); @@ -1408,7 +1067,7 @@ private void sendMailDescriptionPager(MailSender sender, int index) { lastLabel, ChatColor.AQUA); lastButton.setClickEvent(ClickEventType.RUN_COMMAND, COMMAND + " read " + last); - lastButton.addHoverText(lastToolTip); + lastButton.setHoverText(lastToolTip); msg.addParts(lastButton); } else { @@ -1417,7 +1076,7 @@ private void sendMailDescriptionPager(MailSender sender, int index) { msg.addText(" " + parts); - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); } /** @@ -1442,6 +1101,8 @@ private int getIndexOfMailList(int index, ArrayList list) { * @return 文字列表現 */ private String getItemDesc(ItemStack item, boolean forDescription) { + if (item == null) return "null"; + @SuppressWarnings("deprecation") String desc = item.getDurability() == 0 ? item.getType().toString() : item.getType().toString() + ":" + item.getDurability(); if ( item.getAmount() == 1 ) return desc; @@ -1453,12 +1114,8 @@ private String getItemDesc(ItemStack item, boolean forDescription) { * メールデータのリストを、新しいメール順に並び替えする * @param list リスト */ - private static void sortNewer(List list) { - Collections.sort(list, new Comparator() { - public int compare(MailData o1, MailData o2) { - return o2.getDate().compareTo(o1.getDate()); - } - }); + protected static void sortNewer(List list) { + Collections.sort(list, (o1, o2) -> o2.getDate().compareTo(o1.getDate())); } /** @@ -1468,7 +1125,7 @@ public int compare(MailData o1, MailData o2) { * @param summary サマリーの文字列 * @param mail メールデータ */ - private void sendMailLine( + public void sendMailLine( MailSender sender, String pre, String summary, MailData mail) { MessageComponent msg = new MessageComponent(); @@ -1480,14 +1137,14 @@ private void sendMailLine( button.setClickEvent( ClickEventType.RUN_COMMAND, UndineCommand.COMMAND + " read " + mail.getIndex()); - button.addHoverText(Messages.get("SummaryOpenThisMailToolTip")); + button.setHoverText(Messages.get("SummaryOpenThisMailToolTip")); msg.addParts(button); msg.addText((mail.getAttachments().size() > 0) ? "*" : " "); msg.addText(summary); - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); } /** @@ -1517,7 +1174,7 @@ private void sendPager(MailSender sender, String commandPre, int page, int max) MessageParts firstButton = new MessageParts( firstLabel, ChatColor.AQUA); firstButton.setClickEvent(ClickEventType.RUN_COMMAND, commandPre + " 1"); - firstButton.addHoverText(firstToolTip); + firstButton.setHoverText(firstToolTip); msg.addParts(firstButton); msg.addText(" "); @@ -1525,7 +1182,7 @@ private void sendPager(MailSender sender, String commandPre, int page, int max) MessageParts prevButton = new MessageParts( prevLabel, ChatColor.AQUA); prevButton.setClickEvent(ClickEventType.RUN_COMMAND, commandPre + " " + (page - 1)); - prevButton.addHoverText(prevToolTip); + prevButton.setHoverText(prevToolTip); msg.addParts(prevButton); } else { @@ -1539,7 +1196,7 @@ private void sendPager(MailSender sender, String commandPre, int page, int max) MessageParts nextButton = new MessageParts( nextLabel, ChatColor.AQUA); nextButton.setClickEvent(ClickEventType.RUN_COMMAND, commandPre + " " + (page + 1)); - nextButton.addHoverText(nextToolTip); + nextButton.setHoverText(nextToolTip); msg.addParts(nextButton); msg.addText(" "); @@ -1547,7 +1204,7 @@ private void sendPager(MailSender sender, String commandPre, int page, int max) MessageParts lastButton = new MessageParts( lastLabel, ChatColor.AQUA); lastButton.setClickEvent(ClickEventType.RUN_COMMAND, commandPre + " " + max); - lastButton.addHoverText(lastToolTip); + lastButton.setHoverText(lastToolTip); msg.addParts(lastButton); } else { @@ -1556,15 +1213,16 @@ private void sendPager(MailSender sender, String commandPre, int page, int max) msg.addText(" " + parts); - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); } /** * 言語リソース設定に従ってフォーマットされた日時の文字列を取得します。 * @param date フォーマットする日時 * @return フォーマットされた文字列 + * TODO: privateに戻す。 */ - private String getFormattedDate(Date date) { + public String getFormattedDate(Date date) { return new SimpleDateFormat(Messages.get("DateFormat")).format(date); } @@ -1592,15 +1250,16 @@ private String joinToAndGroup(MailData mail) { } /** - * 指定されたメッセージコンポーネントを、指定されたMailSenderに送信する。 - * @param msg メッセージコンポーネント - * @param sender 送信先 + * 指定されたメールのメッセージがすべて空行かどうかを判定する。 + * @param mail メール + * @return すべてのメッセージが空行かどうか */ - private void sendMessageComponent(MessageComponent msg, MailSender sender) { - if ( sender instanceof MailSenderPlayer && sender.isOnline() ) { - msg.send(sender.getPlayer()); - } else if ( sender instanceof MailSenderConsole ) { - msg.send(Bukkit.getConsoleSender()); + private boolean areMessagesEmpty(MailData mail) { + for ( String line : mail.getMessage() ) { + if ( line != null && !line.trim().equals("") ) { + return false; + } } + return true; } } diff --git a/src/main/java/org/bitbucket/ucchy/undine/MailManagerFlatFile.java b/src/main/java/org/bitbucket/ucchy/undine/MailManagerFlatFile.java new file mode 100644 index 0000000..223f5cf --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/MailManagerFlatFile.java @@ -0,0 +1,529 @@ +/* + * @author ucchy + * @license LGPLv3 + * @copyright Copyright ucchy 2015 + */ +package org.bitbucket.ucchy.undine; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; + +import org.bitbucket.ucchy.undine.group.GroupData; +import org.bitbucket.ucchy.undine.sender.MailSender; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +/** + * フラットファイルを用いたメールデータマネージャ + * @author ucchy + */ +public class MailManagerFlatFile extends MailManager { + + private ArrayList mails; + private HashMap editmodeMails; + private int nextIndex; + private boolean isLoaded; + + /** + * コンストラクタ + */ + public MailManagerFlatFile(UndineMailer parent) { + super(parent); + } + + /** + * メールデータを再読込する + * + * @param リロードが完了した時に、通知する先。通知が不要なら、nullでよい。 + */ + @Override + protected void reload(final CommandSender sender) { + + final long start = System.currentTimeMillis(); + + new BukkitRunnable() { + public void run() { + + isLoaded = false; + mails = new ArrayList(); + nextIndex = 1; + + File folder = parent.getMailFolder(); + File[] files = folder.listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.endsWith(".yml"); + } + }); + + if (files != null) { + for (File file : files) { + MailDataFlatFile data = MailDataFlatFile.load(file); + mails.add(data); + + if (nextIndex <= data.getIndex()) { + nextIndex = data.getIndex() + 1; + } + } + } + + UndineMailer.getInstance().getLogger().info("Async load mail data... Done. Time: " + + (System.currentTimeMillis() - start) + "ms, Data: " + mails.size() + "."); + + long upgradeStart = System.currentTimeMillis(); + + int total = 0; + for (MailData mail : mails) { + if (mail.upgrade()) { + saveMail(mail); + total++; + } + } + + if (total > 0) { + UndineMailer.getInstance().getLogger().info("Async upgrade mail data... Done. Time: " + + (System.currentTimeMillis() - upgradeStart) + "ms, Data: " + total + "."); + } + + isLoaded = true; + + if (sender != null) { + sender.sendMessage(Messages.get("InformationReload")); + } + } + }.runTaskAsynchronously(UndineMailer.getInstance()); + } + + /** + * メールデータがロード完了したかどうか。 UndineMailerは、保存されているメールデータをバックグラウンドで読み取ってロードするため、 + * ロードが完了していないうちは、メールリストの参照、メールの送信、リロードができないので 注意してください。 + * + * @return ロード完了したかどうか + */ + @Override + public boolean isLoaded() { + return isLoaded; + } + + /** + * 指定されたインデクスのメールを取得する + * + * @param index インデクス + * @return メールデータ + */ + @Override + public MailData getMail(int index) { + + if (!isLoaded) + return null; + for (MailData m : mails) { + if (m.getIndex() == index) { + return m; + } + } + return null; + } + + /** + * 新しいメールを送信する + * + * @param mail メール + */ + @Override + public void sendNewMail(MailData mail) { + if (!(mail instanceof MailDataFlatFile)) { + return; + } + MailDataFlatFile fileMail = (MailDataFlatFile) mail; + + // メールデータの本文が1行も無いときは、ここで1行追加を行う。 + if (fileMail.getMessage().size() == 0) { + fileMail.addMessage(""); + } + + // ロードが完了していないうちは、メールを送信できないようにする + if (!isLoaded) { + UndineMailer.getInstance().getLogger() + .warning("Because mailer has not yet been initialized, mailer dropped new mail."); + UndineMailer.getInstance().getLogger().warning(fileMail.getInboxSummary()); + return; + } + + // 統合宛先を設定する。 + ArrayList to_total = new ArrayList(); + for (MailSender t : fileMail.getTo()) { + if (!to_total.contains(t)) { + to_total.add(t); + } + } + for (GroupData group : fileMail.getToGroupsConv()) { + for (MailSender t : group.getMembers()) { + if (!to_total.contains(t)) { + to_total.add(t); + } + } + } + fileMail.setToTotal(to_total); + + // インデクスを設定する + fileMail.setIndex(nextIndex); + nextIndex++; + + // 送信時間を設定する + fileMail.setDate(new Date()); + + // 送信地点を設定する + fileMail.setLocation(fileMail.getFrom().getLocation()); + + // オリジナルの添付ファイルを記録する + fileMail.makeAttachmentsOriginal(); + + // 添付が無いなら、着払い設定はクリアしておく + if (fileMail.getAttachments().size() == 0) { + fileMail.setCostMoney(0); + fileMail.setCostItem(null); + } + + // 着払いアイテムが設定されているなら、着払い料金はクリアしておく + if (fileMail.getCostItem() != null) { + fileMail.setCostMoney(0); + } + + // 着払い料金が無効なら、着払い料金はクリアしておく + if (!parent.getUndineConfig().isEnableCODMoney()) { + fileMail.setCostMoney(0); + } + + // 着払いアイテム無効なら、着払いアイテムはクリアしておく + if (!parent.getUndineConfig().isEnableCODItem()) { + fileMail.setCostItem(null); + } + + // 編集中メールだったなら編集中ではなくしておく + editmodeMails.values().remove(fileMail); + + // 保存する + mails.add(fileMail); + saveMail(fileMail); + + // 宛先の人がログイン中なら知らせる + String msg = Messages.get("InformationYouGotMail", "%from", fileMail.getFrom().getName()); + + if (fileMail.isAllMail()) { + for (Player player : Utility.getOnlinePlayers()) { + player.sendMessage(msg); + String pre = Messages.get("ListVerticalParts"); + sendMailLine(MailSender.getMailSender(player), pre, ChatColor.GOLD + fileMail.getInboxSummary(), fileMail); + } + } else { + for (MailSender to : fileMail.getToTotal()) { + if (to.isOnline()) { + to.sendMessage(msg); + String pre = Messages.get("ListVerticalParts"); + sendMailLine(to, pre, ChatColor.GOLD + fileMail.getInboxSummary(), fileMail); + } + } + } + + // 送った時刻を、メタデータに記録する + long time = System.currentTimeMillis(); + fileMail.getFrom().setStringMetadata(SENDTIME_METAKEY, time + ""); + } + + /** + * 受信したメールのリストを取得する + * + * @param sender 取得する対象 + * @return メールのリスト + */ + @Override + public ArrayList getInboxMails(MailSender sender) { + + if (!isLoaded) { + return null; + } + + ArrayList box = new ArrayList(); + for (MailData mail : mails) { + if (mail.isAllMail() || (mail.getToTotal() != null && mail.getToTotal().contains(sender)) + || mail.getTo().contains(sender)) { + if (!mail.isSetTrash(sender)) { + box.add(mail); + } + } + } + sortNewer(box); + return box; + } + + /** + * 受信したメールで未読のリストを取得する + * + * @param sender 取得する対象 + * @return メールのリスト + */ + @Override + public ArrayList getUnreadMails(MailSender sender) { + + if (!isLoaded) { + return null; + } + + ArrayList box = new ArrayList(); + for (MailData mail : mails) { + if (mail.isAllMail() || (mail.getToTotal() != null && mail.getToTotal().contains(sender)) + || mail.getTo().contains(sender)) { + if (!mail.isRead(sender) && !mail.isSetTrash(sender)) { + box.add(mail); + } + } + } + sortNewer(box); + return box; + } + + /** + * 送信したメールのリストを取得する + * + * @param sender 取得する対象 + * @return メールのリスト + */ + @Override + public ArrayList getOutboxMails(MailSender sender) { + + if (!isLoaded) { + return null; + } + + ArrayList box = new ArrayList(); + for (MailData mail : mails) { + if (mail.getFrom().equals(sender) && !mail.isSetTrash(sender)) { + box.add(mail); + } + } + sortNewer(box); + return box; + } + + /** + * 関連メールのリストを取得する + * + * @param sender 取得する対象 + * @return メールのリスト + */ + @Override + public ArrayList getRelatedMails(MailSender sender) { + + if (!isLoaded) { + return null; + } + + ArrayList box = new ArrayList(); + for (MailData mail : mails) { + if (mail.isRelatedWith(sender) && mail.isRead(sender) && !mail.isSetTrash(sender)) { + box.add(mail); + } + } + sortNewer(box); + return box; + } + + /** + * ゴミ箱フォルダのメールリストを取得する + * + * @param sender 取得する対象 + * @return メールのリスト + */ + @Override + public ArrayList getTrashboxMails(MailSender sender) { + + if (!isLoaded) { + return null; + } + + ArrayList box = new ArrayList(); + for (MailData mail : mails) { + if (mail.isRelatedWith(sender) && mail.isSetTrash(sender)) { + box.add(mail); + } + } + sortNewer(box); + return box; + } + + /** + * 指定されたメールデータをUndineに保存する + * + * @param mail メールデータ + */ + @Override + public void saveMail(MailData mail) { + mail.save(); + } + + /** + * 指定されたインデクスのメールを削除する + * + * @param index インデクス + */ + @Override + public void deleteMail(int index) { + + if (isLoaded) { + MailData mail = getMail(index); + if (mail != null) { + mails.remove(mail); + } + } + + String filename = String.format("%1$08d.yml", index); + File folder = parent.getMailFolder(); + File file = new File(folder, filename); + if (file.exists()) { + file.delete(); + } + } + + /** + * 古いメールを削除する + */ + @Override + protected void cleanup() { + + if (!isLoaded) { + return; + } + + ArrayList queue = new ArrayList(); + int period = parent.getUndineConfig().getMailStorageTermDays(); + Date now = new Date(); + + for (MailData mail : mails) { + int days = (int) ((now.getTime() - mail.getDate().getTime()) / (1000 * 60 * 60 * 24)); + if (days > period) { + queue.add(mail.getIndex()); + } + } + + for (int index : queue) { + deleteMail(index); + } + } + + /** + * 編集中メールを作成して返す + * + * @param sender 取得対象のsender + * @return 編集中メール + */ + @Override + public MailData makeEditmodeMail(MailSender sender) { + String id = sender.toString(); + if (editmodeMails.containsKey(id)) { + return editmodeMails.get(id); + } + MailDataFlatFile mail = new MailDataFlatFile(); + mail.setFrom(sender); + editmodeMails.put(id, mail); + return mail; + } + + /** + * 編集中メールを取得する + * + * @param sender 取得対象のsender + * @return 編集中メール(編集中でないならnull) + */ + @Override + public MailData getEditmodeMail(MailSender sender) { + String id = sender.toString(); + if (editmodeMails.containsKey(id)) { + return editmodeMails.get(id); + } + return null; + } + + /** + * 編集中メールを削除する + * + * @param sender 削除対象のsender + */ + @Override + public void clearEditmodeMail(MailSender sender) { + editmodeMails.remove(sender.toString()); + } + + /** + * 編集中メールをeditmails.ymlへ保存する + */ + @Override + protected void storeEditmodeMail() { + + YamlConfiguration config = new YamlConfiguration(); + for (String name : editmodeMails.keySet()) { + ConfigurationSection section = config.createSection(name); + editmodeMails.get(name).saveToConfigSection(section); + } + + try { + File file = new File(parent.getDataFolder(), "editmails.yml"); + config.save(file); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * editmails.ymlから編集中メールを復帰する + */ + @Override + protected void restoreEditmodeMail() { + + editmodeMails = new HashMap(); + + File file = new File(parent.getDataFolder(), "editmails.yml"); + if (!file.exists()) + return; + + YamlConfiguration config = YamlConfiguration.loadConfiguration(file); + for (String name : config.getKeys(false)) { + ConfigurationSection section = config.getConfigurationSection(name); + if (section != null) { + MailDataFlatFile mail = MailDataFlatFile.loadFromConfigSection(section); + editmodeMails.put(name, mail); + } + } + + // 復帰元ファイルを削除しておく + file.delete(); + } + + /** + * 指定したsenderが使用中の添付ボックスの個数を返す + * @param sender + * @return 使用中添付ボックスの個数 + */ + @Override + public int getAttachBoxUsageCount(MailSender sender) { + + // ロード中の場合は、Integer最大値を返す + if ( !isLoaded ) { + return Integer.MAX_VALUE; + } + + int count = 0; + for ( MailData mail : mails ) { + if ( mail.getFrom().equals(sender) && mail.getAttachments().size() > 0 ) { + count++; + } + } + return count; + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/Messages.java b/src/main/java/org/bitbucket/ucchy/undine/Messages.java index a42de2b..0a90d16 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/Messages.java +++ b/src/main/java/org/bitbucket/ucchy/undine/Messages.java @@ -137,6 +137,7 @@ protected static void initialize(File _jar, File _configFolder, String lang) { // デフォルトメッセージを、jarファイル内からロードする defaultMessages = new YamlConfiguration(); JarFile jarFile = null; + BufferedReader reader = null; try { jarFile = new JarFile(jar); ZipEntry zipEntry = jarFile.getEntry(String.format("messages_%s.yml", lang)); @@ -144,8 +145,7 @@ protected static void initialize(File _jar, File _configFolder, String lang) { zipEntry = jarFile.getEntry("messages_en.yml"); } InputStream inputStream = jarFile.getInputStream(zipEntry); - BufferedReader reader = - new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); + reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); String line; while ( (line = reader.readLine()) != null ) { if ( line.startsWith("#") || !line.contains(":") ) { @@ -160,6 +160,13 @@ protected static void initialize(File _jar, File _configFolder, String lang) { } catch (IOException e) { e.printStackTrace(); } finally { + if ( reader != null ) { + try { + reader.close(); + } catch (IOException e) { + // do nothing. + } + } if ( jarFile != null ) { try { jarFile.close(); diff --git a/src/main/java/org/bitbucket/ucchy/undine/PlayerNameCache.java b/src/main/java/org/bitbucket/ucchy/undine/PlayerNameCache.java deleted file mode 100644 index f20d372..0000000 --- a/src/main/java/org/bitbucket/ucchy/undine/PlayerNameCache.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * @author ucchy - * @license LGPLv3 - * @copyright Copyright ucchy 2015 - */ -package org.bitbucket.ucchy.undine; - -import java.io.File; -import java.io.IOException; -import java.util.HashMap; - -import org.bitbucket.ucchy.undine.sender.MailSender; -import org.bitbucket.ucchy.undine.sender.MailSenderPlayer; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.scheduler.BukkitRunnable; - -/** - * プレイヤー名のキャッシュ管理クラス - * @author ucchy - */ -public class PlayerNameCache { - - private static final String FILENAME = "playercache.yml"; - private HashMap playerCache; - private boolean isPlayerCacheLoaded; - - private PlayerNameCache() { - playerCache = new HashMap(); - isPlayerCacheLoaded = false; - } - - protected static PlayerNameCache load() { - - long start = System.currentTimeMillis(); - - File file = new File(UndineMailer.getInstance().getDataFolder(), FILENAME); - if ( !file.exists() ) { - // ファイルがまだ存在しないなら、ここで空ファイルを作成する。 - YamlConfiguration temp = new YamlConfiguration(); - try { - temp.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - - PlayerNameCache cache = new PlayerNameCache(); - YamlConfiguration conf = YamlConfiguration.loadConfiguration(file); - - for ( String name : conf.getKeys(false) ) { - MailSender sender = MailSender.getMailSenderFromString(conf.getString(name)); - cache.playerCache.put(name, sender); - } - - UndineMailer.getInstance().getLogger().info("Load offline player data from cache... Done. Time: " - + (System.currentTimeMillis() - start) + "ms, Data: " + cache.playerCache.size() + "."); - - return cache; - } - - protected void save() { - - File file = new File(UndineMailer.getInstance().getDataFolder(), FILENAME); - - YamlConfiguration conf = new YamlConfiguration(); - for ( String name : playerCache.keySet() ) { - conf.set(name, playerCache.get(name).toString()); - } - - try { - conf.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected void refresh() { - - final long start = System.currentTimeMillis(); - isPlayerCacheLoaded = false; - - new BukkitRunnable() { - public void run() { - HashMap temp = new HashMap(); - for ( OfflinePlayer player : Bukkit.getOfflinePlayers() ) { - MailSender ms = MailSenderPlayer.getMailSenderFromString(player.getName()); - if ( ms != null && ms.isValidDestination() ) { - temp.put(player.getName(), ms); - } - } - UndineMailer.getInstance().getLogger().info("Async refresh offline player data... Done. Time: " - + (System.currentTimeMillis() - start) + "ms, Data: " + temp.size() + "."); - playerCache = temp; - isPlayerCacheLoaded = true; - - save(); - } - }.runTaskAsynchronously(UndineMailer.getInstance()); - } - - protected HashMap getCache() { - return playerCache; - } - - /** - * プレイヤーキャッシュがロードされているかどうかを返す - * @return プレイヤーキャッシュがロードされているかどうか - */ - protected boolean isPlayerCacheLoaded() { - return isPlayerCacheLoaded; - } -} diff --git a/src/main/java/org/bitbucket/ucchy/undine/PlayerUuidCache.java b/src/main/java/org/bitbucket/ucchy/undine/PlayerUuidCache.java new file mode 100644 index 0000000..fca7ebc --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/PlayerUuidCache.java @@ -0,0 +1,224 @@ +package org.bitbucket.ucchy.undine; + +import java.io.File; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.scheduler.BukkitRunnable; + +/** + * プレイヤーのUUIDのキャッシュを管理するクラス + * @author ucchy + */ +public class PlayerUuidCache { + + private HashMap caches; + private boolean isPlayerCacheLoaded; + private UUIDResolver resolver; + + // コンストラクタ + private PlayerUuidCache() { + caches = new HashMap(); + isPlayerCacheLoaded = false; + resolver = new UUIDResolver( + UndineMailer.getInstance().getUndineConfig().isUuidOnlineMode()); + } + + /** + * PlayerUuidCacheを、キャッシュフォルダ内のファイルからロードする。 + * @return PlayerUuidCache + */ + protected static PlayerUuidCache load() { + + PlayerUuidCache puc = new PlayerUuidCache(); + long start = System.currentTimeMillis(); + + File folder = UndineMailer.getInstance().getCacheFolder(); + File[] children = folder.listFiles(); + if ( children != null ) { + for ( File file : children ) { + if ( !file.getName().endsWith(".yml") ) { + continue; + } + PlayerUuidCacheData cache = PlayerUuidCacheData.load(file); + puc.caches.put(cache.getName(), cache); + } + } + + UndineMailer.getInstance().getLogger().info("Load offline player data from cache... Done. Time: " + + (System.currentTimeMillis() - start) + "ms, Data: " + puc.caches.size() + "."); + + return puc; + } + + /** + * サーバーのプレイヤー一覧を取得し、UUIDをキャッシュする。 + */ + protected void refresh() { + + final long start = System.currentTimeMillis(); + isPlayerCacheLoaded = false; + + new BukkitRunnable() { + public void run() { + + // UUIDがキャッシュされているかどうか確認し、キャッシュされていないプレイヤー名をリストして、 + // 10プレイヤーずつ、10秒ごとに、UUIDの確認と更新を行う。 + ArrayList namesToCheck = new ArrayList<>(); + HashMap temp = new HashMap(); + + for ( OfflinePlayer player : Bukkit.getOfflinePlayers() ) { + + String name = player.getName(); + if ( name == null ) continue; + + if ( caches.containsKey(name) ) { + if ( isBefore30Days(caches.get(name).getLastKnownDate()) ) { + namesToCheck.add(name); + } else { + temp.put(name, caches.get(name)); + } + } else { + namesToCheck.add(name); + } + } + + int pageSize = 10; // 10 names per a request. + long waitTime = 1000L * 10; // 10 seconds. + + for ( int index = 0; index < namesToCheck.size(); index += pageSize ) { + int endIndex = (index + pageSize > namesToCheck.size()) ? namesToCheck.size() : index + pageSize; + List next = namesToCheck.subList(index, endIndex); + + Map results = resolver.getUUIDsFromNames(next); + for ( String name : results.keySet() ) { + String uuid = results.get(name); + PlayerUuidCacheData data = new PlayerUuidCacheData(name, uuid, new Date()); + data.save(); + temp.put(name, data); + } + + if ( index + pageSize < namesToCheck.size() ) { + try { + Thread.sleep(waitTime); + } catch (InterruptedException e) { + // do nothing. + } + } + } + + UndineMailer.getInstance().getLogger().info("Async refresh offline player data... Done. Time: " + + (System.currentTimeMillis() - start) + "ms, Data: " + temp.size() + "."); + caches = temp; + isPlayerCacheLoaded = true; + } + }.runTaskAsynchronously(UndineMailer.getInstance()); + } + + /** + * プレイヤーキャッシュがロードされているかどうかを返す + * @return プレイヤーキャッシュがロードされているかどうか + */ + protected boolean isPlayerCacheLoaded() { + return isPlayerCacheLoaded; + } + + /** + * キャッシュしているプレイヤー名の一覧を返す + * @return プレイヤー名一覧 + */ + protected Set getPlayerNames() { + return caches.keySet(); + } + + /** + * キャッシュされているすべてのUUIDを取得する + * @return すべてのUUID + */ + protected HashSet getPlayerUuids() { + HashSet uuids = new HashSet<>(); + for ( PlayerUuidCacheData d : caches.values() ) { + uuids.add(d.getUuid()); + } + return uuids; + } + + /** + * 指定されたプレイヤー名のUUIDをキャッシュから取得する + * @param name プレイヤー名 + * @return UUID + */ + protected String getUUID(String name) { + if ( caches.containsKey(name) ) { + return caches.get(name).getUuid(); + } + return refreshPlayerUuid(name); + } + + /** + * 指定されたUUIDのプレイヤー名をキャッシュから取得する + * @param uuid UUID + * @return プレイヤー名 + */ + protected String getName(String uuid) { + for ( PlayerUuidCacheData d : caches.values() ) { + if ( d.getUuid().equals(uuid) ) { + return d.getName(); + } + } + return resolver.getNameFromUUID(uuid); + } + + /** + * 指定されたプレイヤー名のUUIDを更新する + * @param name プレイヤー名 + */ + private String refreshPlayerUuid(String name) { + + String uuid = null; + PlayerUuidCacheData data = null; + if ( caches.containsKey(name) ) { + data = caches.get(name); + if ( isBefore30Days(data.getLastKnownDate()) ) { + uuid = resolver.getUUIDFromName(name, new Date()); + if ( uuid == null ) return null; + data = new PlayerUuidCacheData(name, uuid, new Date()); + caches.put(name, data); + data.save(); + } + } else { + uuid = resolver.getUUIDFromName(name, new Date()); + if ( uuid == null ) return null; + data = new PlayerUuidCacheData(name, uuid, new Date()); + caches.put(name, data); + data.save(); + } + + return uuid; + } + + /** + * 指定されたプレイヤー名のUUIDを、非同期スレッドで更新する + * @param name プレイヤー名 + */ + protected void asyncRefreshPlayerUuid(String name) { + + new BukkitRunnable() { + public void run() { + refreshPlayerUuid(name); + } + }.runTaskAsynchronously(UndineMailer.getInstance()); + } + + // 指定されたDateが、30日以前かどうかを判定する。 + private static boolean isBefore30Days(Date date) { + return date.before(new Date(System.currentTimeMillis() - 1000L*24*3600* 30)); + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/PlayerUuidCacheData.java b/src/main/java/org/bitbucket/ucchy/undine/PlayerUuidCacheData.java new file mode 100644 index 0000000..a0dc7da --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/PlayerUuidCacheData.java @@ -0,0 +1,97 @@ +package org.bitbucket.ucchy.undine; + +import java.io.File; +import java.io.IOException; +import java.util.Date; + +import org.bukkit.configuration.file.YamlConfiguration; + +/** + * プレイヤーのUUIDキャッシュデータ + * @author ucchy + */ +public class PlayerUuidCacheData { + + private String name; + private String uuid; + private Date lastKnownDate; + + /** + * コンストラクタ + * @param name + * @param uuid + * @param lastKnownDate + */ + protected PlayerUuidCacheData(String name, String uuid, Date lastKnownDate) { + this.name = name; + this.uuid = uuid; + this.lastKnownDate = lastKnownDate; + } + + protected static PlayerUuidCacheData load(File file) { + + YamlConfiguration conf = YamlConfiguration.loadConfiguration(file); + String name = conf.getString("name"); + String uuid = conf.getString("uuid"); + Date date = new Date(conf.getLong("lastKnownDate")); + + return new PlayerUuidCacheData(name, uuid, date); + } + + protected void save() { + + File folder = UndineMailer.getInstance().getCacheFolder(); + YamlConfiguration conf = new YamlConfiguration(); + conf.set("name", name); + conf.set("uuid", uuid); + conf.set("lastKnownDate", lastKnownDate.getTime()); + + try { + conf.save(new File(folder, uuid + ".yml")); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * @return name + */ + public String getName() { + return name; + } + + /** + * @param name name + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return uuid + */ + public String getUuid() { + return uuid; + } + + /** + * @param uuid uuid + */ + public void setUuid(String uuid) { + this.uuid = uuid; + } + + /** + * @return lastKnownDate + */ + public Date getLastKnownDate() { + return lastKnownDate; + } + + /** + * @param lastKnownDate lastKnownDate + */ + public void setLastKnownDate(Date lastKnownDate) { + this.lastKnownDate = lastKnownDate; + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/UUIDResolver.java b/src/main/java/org/bitbucket/ucchy/undine/UUIDResolver.java new file mode 100644 index 0000000..6a45744 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/UUIDResolver.java @@ -0,0 +1,353 @@ +/* + * @author ucchy + * @license LGPLv3 + * @copyright Copyright ucchy 2020 + * This file was copied from at.pcgamingfreaks.UUIDConverter class. + */ +package org.bitbucket.ucchy.undine; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.TreeMap; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; + +import com.google.common.base.Charsets; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.stream.JsonReader; + +/** + * UUIDとプレイヤー名の相互変換を行うためのクラス + * @author ucchy + */ +public class UUIDResolver { + + private static final Pattern API_MAX_PROFILE_BATCH_SIZE_PATTERN = Pattern.compile(".*Not more that (?\\d+) profile name per call is allowed.*"); + private static final String UUID_FORMAT_REGEX = "(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})"; + private static final String UUID_FORMAT_REPLACE_TO = "$1-$2-$3-$4-$5"; + private static final long MOJANG_QUERY_RETRY_TIME = 600000L; + + private static final Gson GSON = new Gson(); + private static final Map UUID_CACHE = new HashMap(); + + private boolean onlineMode = false; + + /** + * コンストラクタ + */ + public UUIDResolver() { + this(false); + } + + /** + * コンストラクタ + * @param useUserCacheJson usercache.jsonからキャッシュの初期値を取得するかどうか + */ + public UUIDResolver(boolean onlineMode) { + this.onlineMode = onlineMode; + if ( !onlineMode ) loadUserCache(); + } + + // usercache.jsonを、uuidCacheの初期値としてロードする + private void loadUserCache() { + + // 既にロード済みなら何もしない + if ( UUID_CACHE.size() > 0 ) return; + + int loaded = 0; + File uuidCache = new File("usercache.json"); + if ( !uuidCache.exists() ) return; + + try (JsonReader reader = new JsonReader(new FileReader(uuidCache))) { + CacheData[] dat = new Gson().fromJson(reader, CacheData[].class); + Date now = new Date(); + for (CacheData d : dat) { + if (now.before(d.getExpiresDate())) { + loaded++; + UUID_CACHE.put(d.name, d.uuid); + } + } + } catch(Exception e) { + e.printStackTrace(); + } + + System.out.println("Loaded " + loaded + " UUIDs from local cache."); + } + + /** + * UUIDから現在のプレイヤー名を取得する + * @param uuid UUID + * @return プレイヤー名(存在しないUUIDが指定された場合はnullになる) + */ + protected String getNameFromUUID(String uuid) { + if ( uuid == null ) return null; + if ( onlineMode ) { + NameChange[] names = getOnlineNamesFromUUID(uuid); + if ( names == null ) return null; + return names[names.length - 1].name; + } else { + // Bukkit pass-throgh mode. + OfflinePlayer p = Bukkit.getOfflinePlayer(UUID.fromString(uuid)); + if ( p == null ) return null; + return p.getName(); + } + } + + /** + * UUIDからプレイヤー名履歴を取得する + * @param uuid UUID + * @return プレイヤー名の履歴(存在しないUUIDが指定された場合はnullになる) + */ + private NameChange[] getOnlineNamesFromUUID(String uuid) { + NameChange[] names = null; + try { + Scanner jsonScanner = new Scanner((new URL("https://api.mojang.com/user/profiles/" + uuid.replaceAll("-", "") + "/names")).openConnection().getInputStream(), "UTF-8"); + names = GSON.fromJson(jsonScanner.next(), NameChange[].class); + jsonScanner.close(); + } catch(IOException e) { + if (e.getMessage().contains("HTTP response code: 429")) { + System.out.println("You have reached the request limit of the Mojang api! Please retry later!"); + } else { + System.out.println("Looks like there is a problem with the connection with Mojang. Please retry later."); + } + } catch(Exception e) { + System.out.println("Looks like there is no player with this uuid!\n UUID: \"" + uuid + "\""); + } + return names; + } + + /** + * プレイヤー名からUUIDを取得する。 + * 例えば、name=ucchy、lastKnownDate=2020/04/28を指定した場合は、2020/04/28の時点でucchyというプレイヤー名を使っていた人のUUIDを取得する。 + * @param name プレイヤー名 + * @param lastKnownDate 時点 + * @return UUID + */ + protected String getUUIDFromName(String name, Date lastKnownDate) { + if ( name == null ) return null; + if ( onlineMode ) { + String uuid = getOnlineUUID(name, lastKnownDate); + if (uuid == null) return null; + if (!uuid.contains("-")) { + uuid = uuid.replaceAll(UUID_FORMAT_REGEX, UUID_FORMAT_REPLACE_TO); + } + return uuid; + } else { + // Bukkit pass-throgh mode. + @SuppressWarnings("deprecation") + OfflinePlayer p = Bukkit.getOfflinePlayer(name); + if ( p == null ) return null; + return p.getUniqueId().toString(); + } + } + + // ネットワーク経由でUUIDを取得する + private String getOnlineUUID(String name, Date at) { + if ((at == null || at.after(new Date(System.currentTimeMillis() - 1000L*24*3600* 30))) && UUID_CACHE.containsKey(name)) { + return UUID_CACHE.get(name); + } + + String uuid = null; + try (BufferedReader in = new BufferedReader(new InputStreamReader( + new URL("https://api.mojang.com/users/profiles/minecraft/" + name + ((at != null) ? "?at=" + (at.getTime()/1000L) : "")).openStream(), StandardCharsets.UTF_8))) { + uuid = (((JsonObject) new JsonParser().parse(in)).get("id")).getAsString(); + if (uuid != null && (at == null || at.after(new Date(System.currentTimeMillis() - 1000L*24*3600* 30)))) { + UUID_CACHE.put(name, uuid); + } + } catch(MalformedURLException e) { + System.out.println("Failed to get uuid cause of a malformed url!\n Name: \"" + name + "\" Date: " + ((at != null) ? "?at=" + at.getTime()/1000L : "null")); + } catch(IOException e) { + if (e.getMessage().contains("HTTP response code: 429")) { + System.out.println("You have reached the request limit of the mojang api! Please retry later!"); + } else { + System.out.println("Looks like there is a problem with the connection with mojang. Please retry later."); + } + } catch(Exception e) { + if(at == null) { + // We can't resolve the uuid for the player + System.out.println("Unable to get UUID for: " + name + "!"); + } else if(at.getTime() == 0) { + // If it's not his first name maybe it's his current name + System.out.println("Unable to get UUID for: " + name + " at 0! Trying without date!"); + uuid = getOnlineUUID(name, null); + } else { + // If we cant get the player with the date he was here last time it's likely that it is his first name + System.out.println("Unable to get UUID for: " + name + " at " + at.getTime()/1000L + "! Trying at=0!"); + uuid = getOnlineUUID(name, new Date(0)); + } + } + return uuid; + } + + private static int BATCH_SIZE = 10; // Limit from Mojang + + /** + * 複数のプレイヤー名からUUIDをまとめて取得する + * @param names プレイヤー名 + * @return UUID + */ + protected Map getUUIDsFromNames(Collection names) { + Map result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + for (Map.Entry entry : getUUIDsFromNamesAsUUIDs(names).entrySet()) { + result.put(entry.getKey(), entry.getValue().toString()); + } + return result; + } + + /** + * 複数のプレイヤー名からUUIDをまとめて取得する + * @param names プレイヤー名 + * @return UUID + */ + protected Map getUUIDsFromNamesAsUUIDs(Collection names) { + + Map result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + + if ( !onlineMode ) { + // Bukkit pass-throgh mode. + for ( String name : names ) { + @SuppressWarnings("deprecation") + OfflinePlayer p = Bukkit.getOfflinePlayer(name); + if ( p == null ) continue; + result.put(name, p.getUniqueId()); + } + return result; + } + + List batch = new ArrayList<>(BATCH_SIZE); + Iterator players = names.iterator(); + boolean success; + int fromCache = 0, fromWeb = 0; + while (players.hasNext()) { + while (players.hasNext() && batch.size() < BATCH_SIZE) { + String name = players.next(); + if (UUID_CACHE.containsKey(name)) { + result.put(name, UUID.fromString(UUID_CACHE.get(name).replaceAll(UUID_FORMAT_REGEX, UUID_FORMAT_REPLACE_TO))); + fromCache++; + } else { + batch.add(name); + } + } + + do { + HttpURLConnection connection = null; + try { + connection = (HttpURLConnection) new URL("https://api.mojang.com/profiles/minecraft").openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json; encoding=UTF-8"); + connection.setUseCaches(false); + connection.setDoInput(true); + connection.setDoOutput(true); + try (OutputStream out = connection.getOutputStream()) { + out.write(GSON.toJson(batch).getBytes(Charsets.UTF_8)); + } + Profile[] profiles; + try (Reader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) { + profiles = GSON.fromJson(in, Profile[].class); + } + for (Profile profile : profiles) { + result.put(profile.name, profile.getUUID()); + UUID_CACHE.put(profile.name, profile.getUUID().toString()); + fromWeb++; + } + } catch(IOException e) { + try { + if(connection != null) { + if(connection.getResponseCode() == 429) { + System.out.println("Reached the request limit of the mojang api!\nConverting will be paused for 10 minutes and then continue!"); + Thread.sleep(MOJANG_QUERY_RETRY_TIME); + success = false; + continue; + } else { + InputStream errorStream = connection.getErrorStream(); + StringBuilder errorBuilder = new StringBuilder(); + int c; + while ((c = errorStream.read()) != -1) { + errorBuilder.append((char) c); + } + String errorMessage = errorBuilder.toString(); + System.out.println("Mojang responded with status code: " + connection.getResponseCode() + " Message: " + errorMessage); + Matcher matcher = API_MAX_PROFILE_BATCH_SIZE_PATTERN.matcher(errorMessage); + if (connection.getResponseCode() == 400 && matcher.matches()) { + BATCH_SIZE = Integer.parseInt(matcher.group("batchSize")); + System.out.println("Reducing batch size to " + BATCH_SIZE + " and try again ..."); + return getUUIDsFromNamesAsUUIDs(names); + } else { + e.printStackTrace(); + } + } + } else { + e.printStackTrace(); + } + } + catch(InterruptedException | IOException ignore) {} + + System.out.println("Could not convert all names to uuids because of an issue. Please check the log."); + return result; + } + batch.clear(); + success = true; + } while(!success); + } + + System.out.println("Converted " + (fromCache + fromWeb) + "/" + names.size() + " UUIDs (" + fromCache + " of them from the cache and " + fromWeb + " from Mojang)."); + return result; + } + + public class NameChange { + public String name; + public long changedToAt; + + public Date getChangeDate() { + return new Date(changedToAt); + } + } + + private class Profile { + public String id; + public String name; + + public UUID getUUID() { + return UUID.fromString(id.replaceAll(UUID_FORMAT_REGEX, UUID_FORMAT_REPLACE_TO)); + } + } + + private static class CacheData { + public String name, uuid, expiresOn; + + public Date getExpiresDate() { + try { + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").parse(expiresOn); + } catch(ParseException e) { + e.printStackTrace(); + } + return new Date(); // When we failed to parse the date we return the current time stamp + } + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/UndineConfig.java b/src/main/java/org/bitbucket/ucchy/undine/UndineConfig.java index 34e58be..8855cb7 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/UndineConfig.java +++ b/src/main/java/org/bitbucket/ucchy/undine/UndineConfig.java @@ -10,14 +10,15 @@ import java.util.ArrayList; import java.util.List; +import org.bitbucket.ucchy.undine.database.Database.DatabaseType; import org.bitbucket.ucchy.undine.group.GroupPermissionMode; -import org.bitbucket.ucchy.undine.item.ItemConfigParseException; -import org.bitbucket.ucchy.undine.item.ItemConfigParser; -import org.bitbucket.ucchy.undine.item.TradableMaterial; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.inventory.ItemStack; +import com.github.ucchyocean.itemconfig.ItemConfigParseException; +import com.github.ucchyocean.itemconfig.ItemConfigParser; + /** * Undineコンフィグ管理クラス * @author ucchy @@ -27,6 +28,24 @@ public class UndineConfig { /** メッセージ言語 */ private String lang; + /** データベースの種類。 */ + private DatabaseType databaseType; + + /** MySQLでログインするためのユーザー */ + private String mysqlUser; + + /** MySQLでログインするためのパスワード */ + private String mysqlPass; + + /** MySQLでログインするためのホスト */ + private String mysqlHost; + + /** MySQLでログインするためのポート */ + private int mysqlPort; + + /** MySQLで使うデータベースの名前 */ + private String mysqlDBName; + /** メールにアイテムの添付を可能にするかどうか */ private boolean enableAttachment; @@ -55,7 +74,7 @@ public class UndineConfig { private List disableWorldsToOpenAttachBox; /** 添付ボックスに添付できないようにするアイテム */ - private List prohibitItemsToAttach; + private List prohibitItemsToAttach; /** 着払い料金を使用するかどうか */ private boolean enableCODMoney; @@ -129,6 +148,9 @@ public class UndineConfig { /** ウェルカムメールの添付アイテム */ private List welcomeMailAttachments; + /** UUIDのオンラインモード */ + private boolean uuidOnlineMode; + private UndineMailer parent; /** @@ -175,6 +197,12 @@ protected void reloadConfig() { // 読み込み lang = conf.getString("lang", "ja"); + databaseType = DatabaseType.getByName(conf.getString("databaseType", "sqlite")); + mysqlUser = conf.getString("mysqlUser", "root"); + mysqlPass = conf.getString("mysqlPass", "password"); + mysqlHost = conf.getString("mysqlHost", "127.0.0.1"); + mysqlPort = conf.getInt("mysqlPort", 3306); + mysqlDBName = conf.getString("mysqlDBName", "undinemailer"); enableAttachment = conf.getBoolean("enableAttachment", true); enableSendFee = conf.getBoolean("enableSendFee", false); sendFee = conf.getDouble("sendFee", 10); @@ -225,13 +253,9 @@ protected void reloadConfig() { welcomeMailAttachments = getItemStackListFromConfig( conf.getConfigurationSection("welcomeMailAttachments")); - prohibitItemsToAttach = new ArrayList(); - for ( String name : conf.getStringList("prohibitItemsToAttach") ) { - TradableMaterial material = TradableMaterial.getMaterial(name); - if ( material != null ) { - prohibitItemsToAttach.add(material); - } - } + prohibitItemsToAttach = conf.getStringList("prohibitItemsToAttach"); + + uuidOnlineMode = conf.getBoolean("uuidOnlineMode", false); // sendFeeは、マイナスが指定されていたら0に変更する if ( sendFee < 0 ) { @@ -311,6 +335,48 @@ public String getLang() { return lang; } + /** + * @return databaseType + */ + public DatabaseType getDatabaseType() { + return databaseType; + } + + /** + * @return mysqlHost + */ + public String getMysqlHost() { + return mysqlHost; + } + + /** + * @return mysqlPass + */ + public String getMysqlPass() { + return mysqlPass; + } + + /** + * @return mysqlUser + */ + public String getMysqlUser() { + return mysqlUser; + } + + /** + * @return mysqlPort + */ + public int getMysqlPort() { + return mysqlPort; + } + + /** + * @return mysqlDBName + */ + public String getMysqlDBName() { + return mysqlDBName; + } + /** * @return enableAttachment */ @@ -377,7 +443,7 @@ public List getDisableWorldsToOpenAttachBox() { /** * @return prohibitItemsToAttach */ - public List getProhibitItemsToAttach() { + public List getProhibitItemsToAttach() { return prohibitItemsToAttach; } @@ -549,4 +615,10 @@ public List getWelcomeMailAttachments() { return welcomeMailAttachments; } + /** + * @return uuidOnlineMode + */ + public boolean isUuidOnlineMode() { + return uuidOnlineMode; + } } diff --git a/src/main/java/org/bitbucket/ucchy/undine/UndineListener.java b/src/main/java/org/bitbucket/ucchy/undine/UndineListener.java index 7549c3b..0527e21 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/UndineListener.java +++ b/src/main/java/org/bitbucket/ucchy/undine/UndineListener.java @@ -8,7 +8,7 @@ import java.util.ArrayList; import java.util.List; -import org.bitbucket.ucchy.undine.item.TradableMaterial; +import org.bitbucket.ucchy.undine.database.Database.DatabaseType; import org.bitbucket.ucchy.undine.sender.MailSender; import org.bitbucket.ucchy.undine.sender.MailSenderConsole; import org.bukkit.Material; @@ -22,6 +22,8 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.scheduler.BukkitRunnable; +import com.github.ucchyocean.itemconfig.ItemConfigParserV111; + /** * Undineのリスナークラス * @author ucchy @@ -63,8 +65,8 @@ public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); UndineConfig config = parent.getUndineConfig(); - // プレイヤーキャッシュを更新する - parent.getPlayerCache().put(player.getName(), MailSender.getMailSender(player)); + // プレイヤーキャッシュを非同期更新する + parent.asyncRefreshPlayerUuid(player.getName()); // MailManagerのロードが完了していないなら、以降は何もしない if ( !parent.getMailManager().isLoaded() ) { @@ -72,6 +74,15 @@ public void onPlayerJoin(PlayerJoinEvent event) { } final MailSender sender = MailSender.getMailSender(player); + boolean hasPlayedBefore = player.hasPlayedBefore(); + + // データベースを使っている場合は、初参加のプレイヤーをデータベースに登録する。 + if (config.getDatabaseType() != DatabaseType.FLAT_FILE) { + hasPlayedBefore = parent.getDatabase().mailSenderTable.exists(MailSender.getMailSender(player)); + if (!hasPlayedBefore) { + parent.getDatabase().mailSenderTable.add(sender); + } + } // 未読のメールを遅れて表示する int delay = config.getLoginNotificationDelaySeconds(); @@ -82,7 +93,7 @@ public void run() { }.runTaskLater(parent, delay * 20); // 新規プレイヤーの場合は、ウェルカムメールを送る - if ( !player.hasPlayedBefore() && config.isUseWelcomeMail() ) { + if ( !hasPlayedBefore && config.isUseWelcomeMail() ) { MailSender from = MailSenderConsole.getMailSenderConsole(); List to = new ArrayList(); to.add(sender); @@ -91,11 +102,18 @@ public void run() { message.add(msg); } List attachments = cloneItemStackList(config.getWelcomeMailAttachments()); - final MailData mail = new MailData(to, from, message, attachments); + MailManager manager = parent.getMailManager(); int welcomeDelay = config.getWelcomeMailDelaySeconds(); new BukkitRunnable() { public void run() { - parent.getMailManager().sendNewMail(mail); + // メールの作成と送信の処理が離れすぎると + // その間にメールが送信されたときにIndexが競合して + // 最終的にNPEが発生するため、作成と送信は同時にする。 + final MailData mail = manager.makeEditmodeMail(from); + mail.addTo(to); + mail.setMessage(message); + mail.setAttachments(attachments); + manager.sendNewMail(mail); } }.runTaskLater(parent, welcomeDelay * 20); } @@ -134,7 +152,7 @@ public void onInventoryClick(InventoryClickEvent event) { case HOTBAR_SWAP: case HOTBAR_MOVE_AND_READD: ItemStack hotbar = player.getInventory().getItem(event.getHotbarButton()); - if ( hotbar.getType() != Material.AIR ) { + if ( hotbar != null && hotbar.getType() != Material.AIR ) { event.setCancelled(true); } return; @@ -145,7 +163,7 @@ public void onInventoryClick(InventoryClickEvent event) { } else if ( parent.getBoxManager().isOpeningEditmodeBox(player) ) { // 編集メールの添付ボックスのアイテム処理 - List disables + List disables = parent.getUndineConfig().getProhibitItemsToAttach(); // 禁止アイテムが設定されていないなら何もしない @@ -162,17 +180,29 @@ public void onInventoryClick(InventoryClickEvent event) { case PLACE_ONE: case PLACE_SOME: case SWAP_WITH_CURSOR: - if ( inside && isDisableItem(event.getCursor() ) ) { - event.setCancelled(true); - player.sendMessage(Messages.get("ErrorProhibitItemAttached", - "%material", event.getCursor().getType().toString())); + if ( inside ) { + ItemStack cursor = event.getCursor(); + if ( cursor != null && isDisableItem(cursor) ) { + event.setCancelled(true); + player.sendMessage(Messages.get("ErrorProhibitItemAttached", + "%material", cursor.getType().toString())); + } else if ( cursor != null && containsDisableItemInShulkerBox(cursor) ) { + event.setCancelled(true); + player.sendMessage(Messages.get("ErrorContainsProhibitItemInShulkerbox")); + } } return; case MOVE_TO_OTHER_INVENTORY: - if ( !inside && isDisableItem(event.getCurrentItem()) ) { - event.setCancelled(true); - player.sendMessage(Messages.get("ErrorProhibitItemAttached", - "%material", event.getCurrentItem().getType().toString())); + if ( !inside ) { + ItemStack current = event.getCurrentItem(); + if ( current != null && isDisableItem(current) ) { + event.setCancelled(true); + player.sendMessage(Messages.get("ErrorProhibitItemAttached", + "%material", current.getType().toString())); + } else if ( current != null && containsDisableItemInShulkerBox(current) ) { + event.setCancelled(true); + player.sendMessage(Messages.get("ErrorContainsProhibitItemInShulkerbox")); + } } return; case HOTBAR_SWAP: @@ -182,6 +212,9 @@ public void onInventoryClick(InventoryClickEvent event) { event.setCancelled(true); player.sendMessage(Messages.get("ErrorProhibitItemAttached", "%material", hotbar.getType().toString())); + } else if ( containsDisableItemInShulkerBox(hotbar) ) { + event.setCancelled(true); + player.sendMessage(Messages.get("ErrorContainsProhibitItemInShulkerbox")); } return; default: @@ -214,7 +247,7 @@ public void onInventoryDrag(InventoryDragEvent event) { } else if ( parent.getBoxManager().isOpeningEditmodeBox(player) ) { // 編集メールの添付ボックスのアイテム処理 - List disables + List disables = parent.getUndineConfig().getProhibitItemsToAttach(); // 禁止アイテムが設定されていないなら何もしない @@ -223,7 +256,8 @@ public void onInventoryDrag(InventoryDragEvent event) { } // 禁止アイテムに関する操作でなければ何もしない - if ( !isDisableItem(event.getOldCursor()) ) { + boolean isShulkerboxContainsDisableItem = containsDisableItemInShulkerBox(event.getOldCursor()); + if ( !isDisableItem(event.getOldCursor()) && !isShulkerboxContainsDisableItem ) { return; } @@ -232,8 +266,12 @@ public void onInventoryDrag(InventoryDragEvent event) { for ( int i : event.getRawSlots() ) { if ( i < size ) { event.setCancelled(true); - player.sendMessage(Messages.get("ErrorProhibitItemAttached", - "%material", event.getOldCursor().getType().toString())); + if ( !isShulkerboxContainsDisableItem ) { + player.sendMessage(Messages.get("ErrorProhibitItemAttached", + "%material", event.getOldCursor().getType().toString())); + } else { + player.sendMessage(Messages.get("ErrorContainsProhibitItemInShulkerbox")); + } return; } } @@ -260,11 +298,33 @@ private static List cloneItemStackList(List org) { */ private boolean isDisableItem(ItemStack item) { if ( item == null || item.getType() == Material.AIR ) return false; - for ( TradableMaterial mat : parent.getUndineConfig().getProhibitItemsToAttach() ) { - if ( mat.isSameMaterial(item.getType()) ) { + for ( String mat : parent.getUndineConfig().getProhibitItemsToAttach() ) { + if ( mat.equals(item.getType().toString()) ) { return true; } } return false; } + + /** + * シャルカーボックスの中に、添付禁止のアイテムを含んでいるかどうかを判断する + * @param item シャルカーボックス + * @return 指定されたアイテムがシャルカーボックスであり、添付禁止のアイテムを含んでいる場合に、trueを返す + */ + private boolean containsDisableItemInShulkerBox(ItemStack item) { + + // シャルカーボックスでないならfalseを返す + if ( !Utility.isCB111orLater() || !ItemConfigParserV111.isShulkerBox(item) ) return false; + + // シャルカーボックスの内容をチェックする see issue #96 + if ( Utility.isCB111orLater() && ItemConfigParserV111.isShulkerBox(item) ) { + + if ( ItemConfigParserV111.containsMaterialStringInShulkerBox( + parent.getUndineConfig().getProhibitItemsToAttach(), item) ) { + return true; + } + } + + return false; + } } diff --git a/src/main/java/org/bitbucket/ucchy/undine/UndineMailer.java b/src/main/java/org/bitbucket/ucchy/undine/UndineMailer.java index 01cd502..83fbc3e 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/UndineMailer.java +++ b/src/main/java/org/bitbucket/ucchy/undine/UndineMailer.java @@ -6,17 +6,24 @@ package org.bitbucket.ucchy.undine; import java.io.File; -import java.util.HashMap; +import java.io.IOException; +import java.sql.SQLException; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Set; import org.bitbucket.ucchy.undine.bridge.PermissionsExBridge; import org.bitbucket.ucchy.undine.bridge.VaultEcoBridge; import org.bitbucket.ucchy.undine.command.GroupCommand; import org.bitbucket.ucchy.undine.command.ListCommand; import org.bitbucket.ucchy.undine.command.UndineCommand; +import org.bitbucket.ucchy.undine.database.Database; +import org.bitbucket.ucchy.undine.database.GroupManagerDatabase; +import org.bitbucket.ucchy.undine.database.MailManagerDatabase; +import org.bitbucket.ucchy.undine.database.Database.DatabaseType; import org.bitbucket.ucchy.undine.group.GroupManager; -import org.bitbucket.ucchy.undine.sender.MailSender; +import org.bitbucket.ucchy.undine.group.GroupManagerFlatFile; import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -30,12 +37,13 @@ public class UndineMailer extends JavaPlugin { private static final String MAIL_FOLDER = "mail"; private static final String GROUP_FOLDER = "group"; + private static final String CACHE_FOLDER = "cache"; private MailManager mailManager; private AttachmentBoxManager boxManager; private GroupManager groupManager; private MailCleanupTask cleanupTask; - private PlayerNameCache playerNameCache; + private PlayerUuidCache playerUuidCache; private UndineCommand undineCommand; private ListCommand listCommand; @@ -46,6 +54,8 @@ public class UndineMailer extends JavaPlugin { private VaultEcoBridge vaulteco; private PermissionsExBridge pex; + private Database database; + /** * プラグインが有効化されたときに呼び出されるメソッド * @see org.bukkit.plugin.java.JavaPlugin#onEnable() @@ -68,9 +78,21 @@ public void onEnable() { getServer().getPluginManager().getPlugin("PermissionsEx")); } - // マネージャを生成し、データをロードする - groupManager = new GroupManager(this); - mailManager = new MailManager(this); + // マネージャを生成する + try { + if (config.getDatabaseType() == DatabaseType.FLAT_FILE) { + database = null; + groupManager = new GroupManagerFlatFile(this); + mailManager = new MailManagerFlatFile(this); + } else { + database = new Database(this, config.getDatabaseType()); + groupManager = new GroupManagerDatabase(this); + mailManager = new MailManagerDatabase(this); + } + } catch (SQLException | IOException e) { + new IllegalStateException("Could not initialize database.", e); + } + boxManager = new AttachmentBoxManager(this); // メッセージをロードする @@ -91,10 +113,10 @@ public void onEnable() { getServer().getPluginManager().registerEvents(new UndineListener(this), this); // プレイヤーキャッシュの作成 - playerNameCache = PlayerNameCache.load(); + playerUuidCache = PlayerUuidCache.load(); // プレイヤーキャッシュのリロード - playerNameCache.refresh(); + playerUuidCache.refresh(); } /** @@ -107,6 +129,11 @@ public void onDisable() { // タスクを停止する cleanupTask.cancel(); + // データベースの終了 + if (database != null) { + database.dispose(); + } + // 添付ボックスを開いたままにしているプレイヤーの // インベントリを強制的に閉じる boxManager.closeAllBox(); @@ -169,6 +196,26 @@ public File getGroupFolder() { return folder; } + /** + * キャッシュデータを格納するフォルダを返す + * @return キャッシュデータ格納フォルダ + */ + public File getCacheFolder() { + File folder = new File(getDataFolder(), CACHE_FOLDER); + if ( !folder.exists() ) { + folder.mkdirs(); + } + return folder; + } + + /** + * データベースを取得する。フラットファイルモードの場合はnullを返す。 + * @return データベース + */ + public Database getDatabase() { + return database; + } + /** * メールマネージャを取得する * @return メールマネージャ @@ -236,11 +283,45 @@ protected static String getDefaultLocaleLanguage() { } /** - * プレイヤーキャッシュを取得する - * @return プレイヤーキャッシュ + * 指定されたプレイヤー名のUUIDをキャッシュから取得する + * @param name プレイヤー名 + * @return UUID + */ + public String getUUID(String name) { + return playerUuidCache.getUUID(name); + } + + /** + * 指定されたUUIDのプレイヤー名をキャッシュから取得する + * @param uuid UUID + * @return プレイヤー名 + */ + public String getName(String uuid) { + return playerUuidCache.getName(uuid); + } + + /** + * 指定されたプレイヤー名のUUIDを、非同期スレッドで更新する + * @param name プレイヤー名 + */ + public void asyncRefreshPlayerUuid(String name) { + playerUuidCache.asyncRefreshPlayerUuid(name); + } + + /** + * キャッシュしているプレイヤー名の一覧を返す + * @return プレイヤー名一覧 + */ + public Set getPlayerNames() { + return playerUuidCache.getPlayerNames(); + } + + /** + * キャッシュされているすべてのUUIDを取得する + * @return すべてのUUID */ - public HashMap getPlayerCache() { - return playerNameCache.getCache(); + public HashSet getPlayerUuids() { + return playerUuidCache.getPlayerUuids(); } /** @@ -255,7 +336,7 @@ public void reloadAll(CommandSender sender) { config.reloadConfig(); Messages.reload(config.getLang()); - playerNameCache.refresh(); + playerUuidCache.refresh(); } /** @@ -263,7 +344,7 @@ public void reloadAll(CommandSender sender) { * @return プレイヤーキャッシュがロードされているかどうか */ public boolean isPlayerCacheLoaded() { - return playerNameCache.isPlayerCacheLoaded(); + return playerUuidCache.isPlayerCacheLoaded(); } /** diff --git a/src/main/java/org/bitbucket/ucchy/undine/Utility.java b/src/main/java/org/bitbucket/ucchy/undine/Utility.java index f65828a..3aaf68e 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/Utility.java +++ b/src/main/java/org/bitbucket/ucchy/undine/Utility.java @@ -31,9 +31,6 @@ */ public class Utility { - private static Boolean isCB178orLaterCache; - private static Boolean isCB19orLaterCache; - /** * jarファイルの中に格納されているファイルを、jarファイルの外にコピーするメソッド * @param jarFile jarファイル @@ -48,6 +45,7 @@ public static void copyFileFromJar( FileOutputStream fos = null; BufferedReader reader = null; BufferedWriter writer = null; + JarFile jar = null; File parent = targetFile.getParentFile(); if ( !parent.exists() ) { @@ -55,7 +53,7 @@ public static void copyFileFromJar( } try { - JarFile jar = new JarFile(jarFile); + jar = new JarFile(jarFile); ZipEntry zipEntry = jar.getEntry(sourceFilePath); is = jar.getInputStream(zipEntry); @@ -124,6 +122,13 @@ public static void copyFileFromJar( // do nothing. } } + if ( jar != null ) { + try { + jar.close(); + } catch (IOException e) { + // do nothing. + } + } } } @@ -225,10 +230,7 @@ public static boolean isValidColorCode(String code) { * @return v1.7.8以上ならtrue、そうでないならfalse */ public static boolean isCB178orLater() { - if ( isCB178orLaterCache == null ) { - isCB178orLaterCache = isUpperVersion(Bukkit.getBukkitVersion(), "1.7.8"); - } - return isCB178orLaterCache; + return isUpperVersion(Bukkit.getBukkitVersion(), "1.7.8"); } /** @@ -236,10 +238,15 @@ public static boolean isCB178orLater() { * @return v1.9以上ならtrue、そうでないならfalse */ public static boolean isCB19orLater() { - if ( isCB19orLaterCache == null ) { - isCB19orLaterCache = isUpperVersion(Bukkit.getBukkitVersion(), "1.9"); - } - return isCB19orLaterCache; + return isUpperVersion(Bukkit.getBukkitVersion(), "1.9"); + } + + /** + * 現在動作中のCraftBukkitが、v1.11 以上かどうかを確認する + * @return v1.11以上ならtrue、そうでないならfalse + */ + public static boolean isCB111orLater() { + return isUpperVersion(Bukkit.getBukkitVersion(), "1.11"); } /** diff --git a/src/main/java/org/bitbucket/ucchy/undine/command/GroupCommand.java b/src/main/java/org/bitbucket/ucchy/undine/command/GroupCommand.java index 610d8d7..0716027 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/command/GroupCommand.java +++ b/src/main/java/org/bitbucket/ucchy/undine/command/GroupCommand.java @@ -16,18 +16,16 @@ import org.bitbucket.ucchy.undine.group.GroupPermissionMode; import org.bitbucket.ucchy.undine.group.SpecialGroupPex; import org.bitbucket.ucchy.undine.sender.MailSender; -import org.bitbucket.ucchy.undine.sender.MailSenderConsole; -import org.bitbucket.ucchy.undine.sender.MailSenderPlayer; -import org.bitbucket.ucchy.undine.tellraw.ClickEventType; -import org.bitbucket.ucchy.undine.tellraw.MessageComponent; -import org.bitbucket.ucchy.undine.tellraw.MessageParts; -import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; +import com.github.ucchyocean.messaging.tellraw.ClickEventType; +import com.github.ucchyocean.messaging.tellraw.MessageComponent; +import com.github.ucchyocean.messaging.tellraw.MessageParts; + /** * groupコマンド * @author ucchy @@ -203,13 +201,12 @@ private boolean doCreateCommand(CommandSender sender, Command command2, String l sender.sendMessage(Messages.get("ErrorGroupIsAlreadyExist", "%name", name)); return true; } - - // グループ作成 - GroupData group = new GroupData(name, ms); - manager.addGroup(group); + + // グループ作成 保存もこのメソッドが行ってくれる + GroupData.create(parent, name, ms); // グループリスト表示 - parent.getGroupManager().displayGroupList(ms, 1); + manager.displayGroupList(ms, 1); sender.sendMessage(Messages.get("InformationMakeGroup", "%name", name)); @@ -253,7 +250,7 @@ private boolean doDeleteCommand(CommandSender sender, Command command2, String l manager.removeGroup(name); // グループリスト表示 - parent.getGroupManager().displayGroupList(MailSender.getMailSender(sender), 1); + manager.displayGroupList(MailSender.getMailSender(sender), 1); sender.sendMessage(Messages.get("InformationDeleteGroup", "%name", name)); @@ -687,19 +684,6 @@ private void showOKCancelButton( buttonCancel.setClickEvent(ClickEventType.RUN_COMMAND, cancelCommand); msg.addParts(buttonCancel); - sendMessageComponent(msg, ms); - } - - /** - * 指定されたメッセージコンポーネントを、指定されたMailSenderに送信する。 - * @param msg メッセージコンポーネント - * @param sender 送信先 - */ - private void sendMessageComponent(MessageComponent msg, MailSender sender) { - if ( sender instanceof MailSenderPlayer && sender.isOnline() ) { - msg.send(sender.getPlayer()); - } else if ( sender instanceof MailSenderConsole ) { - msg.send(Bukkit.getConsoleSender()); - } + ms.sendMessageComponent(msg); } } diff --git a/src/main/java/org/bitbucket/ucchy/undine/command/ListCommand.java b/src/main/java/org/bitbucket/ucchy/undine/command/ListCommand.java index e0d7691..1ef7c95 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/command/ListCommand.java +++ b/src/main/java/org/bitbucket/ucchy/undine/command/ListCommand.java @@ -7,19 +7,19 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.List; import org.bitbucket.ucchy.undine.Messages; import org.bitbucket.ucchy.undine.UndineMailer; -import org.bitbucket.ucchy.undine.tellraw.ClickEventType; -import org.bitbucket.ucchy.undine.tellraw.MessageComponent; -import org.bitbucket.ucchy.undine.tellraw.MessageParts; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; +import com.github.ucchyocean.messaging.tellraw.ClickEventType; +import com.github.ucchyocean.messaging.tellraw.MessageComponent; +import com.github.ucchyocean.messaging.tellraw.MessageParts; + /** * listコマンド * @author ucchy @@ -66,12 +66,8 @@ public boolean onCommand(final CommandSender sender, final Command command, fina } // 以下、プレイヤーリスト表示処理 - ArrayList names = new ArrayList(parent.getPlayerCache().keySet()); - Collections.sort(names, new Comparator() { - public int compare(String o1, String o2) { - return o1.compareToIgnoreCase(o2); - } - }); + ArrayList names = new ArrayList(parent.getPlayerNames()); + Collections.sort(names, String::compareToIgnoreCase); String parts = Messages.get("ListHorizontalParts"); String pre = Messages.get("ListVerticalParts"); @@ -216,7 +212,7 @@ private void sendPager(CommandSender sender, String commandPre, int page, int ma MessageParts returnButton = new MessageParts(returnLabel, ChatColor.AQUA); returnButton.setClickEvent(ClickEventType.RUN_COMMAND, COMMAND_INDEX + next); - returnButton.addHoverText(returnToolTip); + returnButton.setHoverText(returnToolTip); msg.addParts(returnButton); msg.addText(" "); @@ -225,7 +221,7 @@ private void sendPager(CommandSender sender, String commandPre, int page, int ma MessageParts firstButton = new MessageParts( firstLabel, ChatColor.AQUA); firstButton.setClickEvent(ClickEventType.RUN_COMMAND, commandPre + " 1" + next); - firstButton.addHoverText(firstToolTip); + firstButton.setHoverText(firstToolTip); msg.addParts(firstButton); msg.addText(" "); @@ -233,7 +229,7 @@ private void sendPager(CommandSender sender, String commandPre, int page, int ma MessageParts prevButton = new MessageParts( prevLabel, ChatColor.AQUA); prevButton.setClickEvent(ClickEventType.RUN_COMMAND, commandPre + " " + (page - 1) + next); - prevButton.addHoverText(prevToolTip); + prevButton.setHoverText(prevToolTip); msg.addParts(prevButton); } else { @@ -246,7 +242,7 @@ private void sendPager(CommandSender sender, String commandPre, int page, int ma MessageParts nextButton = new MessageParts( nextLabel, ChatColor.AQUA); nextButton.setClickEvent(ClickEventType.RUN_COMMAND, commandPre + " " + (page + 1) + next); - nextButton.addHoverText(nextToolTip); + nextButton.setHoverText(nextToolTip); msg.addParts(nextButton); msg.addText(" "); @@ -254,7 +250,7 @@ private void sendPager(CommandSender sender, String commandPre, int page, int ma MessageParts lastButton = new MessageParts( lastLabel, ChatColor.AQUA); lastButton.setClickEvent(ClickEventType.RUN_COMMAND, commandPre + " " + max + next); - lastButton.addHoverText(lastToolTip); + lastButton.setHoverText(lastToolTip); msg.addParts(lastButton); } else { diff --git a/src/main/java/org/bitbucket/ucchy/undine/command/UndineAttachCommand.java b/src/main/java/org/bitbucket/ucchy/undine/command/UndineAttachCommand.java index d67064a..27b0146 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/command/UndineAttachCommand.java +++ b/src/main/java/org/bitbucket/ucchy/undine/command/UndineAttachCommand.java @@ -14,7 +14,6 @@ import org.bitbucket.ucchy.undine.UndineConfig; import org.bitbucket.ucchy.undine.UndineMailer; import org.bitbucket.ucchy.undine.bridge.VaultEcoBridge; -import org.bitbucket.ucchy.undine.item.TradableMaterial; import org.bitbucket.ucchy.undine.sender.MailSender; import org.bitbucket.ucchy.undine.sender.MailSenderConsole; import org.bukkit.OfflinePlayer; @@ -92,7 +91,9 @@ public void runCommand(CommandSender sender, String label, String[] args) { } // パーミッション確認 - if ( !sender.hasPermission(NODE_ATTACH_SENDMAIL) ) { + // 設定で添付不可にしている場合も拒否する(see issue #93) + if ( !sender.hasPermission(NODE_ATTACH_SENDMAIL) + || !config.isEnableAttachment() ) { sender.sendMessage(Messages.get("PermissionDeniedCommand")); return; } @@ -142,10 +143,10 @@ public void runCommand(CommandSender sender, String label, String[] args) { } // 取引可能な種類のアイテムでないなら、エラーを表示して終了 - if ( !TradableMaterial.isTradable(item.getType()) ) { - sender.sendMessage(Messages.get("ErrorInvalidItem", "%item", args[2])); - return; - } +// if ( !TradableMaterial.isTradable(item.getType()) ) { +// sender.sendMessage(Messages.get("ErrorInvalidItem", "%item", args[2])); +// return; +// } // 個数も指定されている場合は、個数を反映する if ( args.length >= 4 && args[3].matches("[0-9]{1,9}") ) { @@ -195,7 +196,9 @@ public void runCommand(CommandSender sender, String label, String[] args) { } // パーミッション確認 - if ( !sender.hasPermission(NODE_ATTACH_INBOXMAIL) ) { + // 設定で添付不可にしている場合も拒否する(see issue #93) + if ( !sender.hasPermission(NODE_ATTACH_INBOXMAIL) + || !config.isEnableAttachment() ) { sender.sendMessage(Messages.get("PermissionDeniedCommand")); return; } @@ -320,9 +323,8 @@ public void runCommandForSentMail(Player player, MailSender ms, manager.saveMail(mail); // 送信者側に新規メールで、アイテムを差し戻す - MailData reply = new MailData(); + MailData reply = manager.makeEditmodeMail(MailSenderConsole.getMailSenderConsole()); reply.setTo(0, mail.getFrom()); - reply.setFrom(MailSenderConsole.getMailSenderConsole()); reply.addMessage(Messages.get( "BoxRefuseSenderResult", new String[]{"%to", "%num"}, @@ -497,14 +499,22 @@ private boolean checkForCostItem(Player player, MailSender ms, new String[]{fee.getType().toString(), fee.getAmount() + ""})); // メールの送信元に送金 - MailData reply = new MailData(); + MailData reply = manager.makeEditmodeMail(ms); reply.setTo(0, mail.getFrom()); - reply.setFrom(ms); reply.setMessage(0, Messages.get( "BoxOpenCostItemSenderResult", new String[]{"%to", "%material", "%amount"}, new String[]{ms.getName(), fee.getType().toString(), fee.getAmount() + ""})); + + // アイテムをのスタック状態を規定の個数に整理する。 + // see issue #99. + int stackSize = fee.getType().getMaxStackSize(); + while ( fee.getAmount() > stackSize ) { + reply.addAttachment(new ItemStack(fee.getType(), stackSize)); + fee.setAmount(fee.getAmount() - stackSize); + } reply.addAttachment(fee); + parent.getMailManager().sendNewMail(reply); // 着払いをnullに設定して保存 @@ -543,6 +553,7 @@ private boolean checkForCostItem(Player player, MailSender ms, * @param item アイテム * @return 持っているかどうか */ + @SuppressWarnings("deprecation") private boolean hasItem(Player player, ItemStack item) { //return player.getInventory().contains(item.getType(), item.getAmount()); // ↑のコードは、アイテムのデータ値を検査しないのでNG diff --git a/src/main/java/org/bitbucket/ucchy/undine/command/UndineCommandUtil.java b/src/main/java/org/bitbucket/ucchy/undine/command/UndineCommandUtil.java index b3ac5ba..913a085 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/command/UndineCommandUtil.java +++ b/src/main/java/org/bitbucket/ucchy/undine/command/UndineCommandUtil.java @@ -7,16 +7,14 @@ import org.bitbucket.ucchy.undine.Messages; import org.bitbucket.ucchy.undine.sender.MailSender; -import org.bitbucket.ucchy.undine.sender.MailSenderConsole; -import org.bitbucket.ucchy.undine.sender.MailSenderPlayer; -import org.bitbucket.ucchy.undine.tellraw.ClickEventType; -import org.bitbucket.ucchy.undine.tellraw.MessageComponent; -import org.bitbucket.ucchy.undine.tellraw.MessageParts; -import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import com.github.ucchyocean.messaging.tellraw.ClickEventType; +import com.github.ucchyocean.messaging.tellraw.MessageComponent; +import com.github.ucchyocean.messaging.tellraw.MessageParts; + /** * コマンド実行関連のユーティリティクラス * @author ucchy @@ -26,19 +24,15 @@ public class UndineCommandUtil { /** * アイテム表記から、ItemStackを作成して返す * @param desc アイテム表記 - * (マテリアル名、または、アイテムID。コロンを付けた後にデータ値を指定することも可能。 - * 例:WOOL, WOOL:3, 35, 35:6 ) + * (マテリアル名。コロンを付けた後にデータ値を指定することも可能。 + * 例:WOOL, WOOL:3) * @return ItemStack */ + @SuppressWarnings("deprecation") protected static ItemStack getItemStackFromDescription(String desc) { String[] descs = desc.split(":"); if ( descs.length <= 0 ) return null; Material material = Material.getMaterial(descs[0].toUpperCase()); - if ( material == null && descs[0].matches("[0-9]{1,5}") ) { - @SuppressWarnings("deprecation") - Material m = Material.getMaterial(Integer.parseInt(descs[0])); - material = m; - } if ( material == null ) return null; ItemStack item = new ItemStack(material); if ( descs.length >= 2 && descs[1].matches("[0-9]{1,5}") ) { @@ -70,7 +64,7 @@ protected static void showOKCancelButton( buttonCancel.setClickEvent(ClickEventType.RUN_COMMAND, cancelCommand); msg.addParts(buttonCancel); - sendMessageComponent(msg, ms); + ms.sendMessageComponent(msg); } /** @@ -79,8 +73,10 @@ protected static void showOKCancelButton( * @return 文字列表現 */ protected static String getItemDesc(ItemStack item) { - return item.getDurability() == 0 ? item.getType().toString() : - item.getType().toString() + ":" + item.getDurability(); +// return item.getDurability() == 0 ? item.getType().toString() : +// item.getType().toString() + ":" + item.getDurability(); +// return item.serialize().toString(); + return item != null ? item.getType().toString() : "null"; } /** @@ -95,17 +91,4 @@ protected static double tryParseDouble(String value) { return -1; } } - - /** - * 指定されたメッセージコンポーネントを、指定されたMailSenderに送信する。 - * @param msg メッセージコンポーネント - * @param sender 送信先 - */ - private static void sendMessageComponent(MessageComponent msg, MailSender sender) { - if ( sender instanceof MailSenderPlayer && sender.isOnline() ) { - msg.send(sender.getPlayer()); - } else if ( sender instanceof MailSenderConsole ) { - msg.send(Bukkit.getConsoleSender()); - } - } } diff --git a/src/main/java/org/bitbucket/ucchy/undine/command/UndineCostItemCommand.java b/src/main/java/org/bitbucket/ucchy/undine/command/UndineCostItemCommand.java index 3432aaa..c70340c 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/command/UndineCostItemCommand.java +++ b/src/main/java/org/bitbucket/ucchy/undine/command/UndineCostItemCommand.java @@ -13,8 +13,8 @@ import org.bitbucket.ucchy.undine.Messages; import org.bitbucket.ucchy.undine.UndineConfig; import org.bitbucket.ucchy.undine.UndineMailer; -import org.bitbucket.ucchy.undine.item.TradableMaterial; import org.bitbucket.ucchy.undine.sender.MailSender; +import org.bukkit.Material; import org.bukkit.command.CommandSender; import org.bukkit.inventory.ItemStack; @@ -106,10 +106,10 @@ public void runCommand(CommandSender sender, String label, String[] args) { } // 取引可能な種類のアイテムでないなら、エラーを表示して終了 - if ( !TradableMaterial.isTradable(item.getType()) ) { - sender.sendMessage(Messages.get("ErrorInvalidCostItem", "%item", args[1])); - return; - } +// if ( !TradableMaterial.isTradable(item.getType()) ) { +// sender.sendMessage(Messages.get("ErrorInvalidCostItem", "%item", args[1])); +// return; +// } // アイテムと料金を両方設定しようとしたら、エラーを表示して終了 if ( mail.getCostMoney() > 0 ) { @@ -143,7 +143,7 @@ public List tabComplete(CommandSender sender, String[] args) { // costitemコマンドの2つ目は、マテリアル名で補完する String arg = args[1].toUpperCase(); ArrayList candidates = new ArrayList(); - for ( TradableMaterial material : TradableMaterial.values() ) { + for ( Material material : Material.values() ) { if ( material.toString().startsWith(arg) ) { candidates.add(material.toString()); } diff --git a/src/main/java/org/bitbucket/ucchy/undine/command/UndineHelpCommand.java b/src/main/java/org/bitbucket/ucchy/undine/command/UndineHelpCommand.java index 557ac53..b1ae9d4 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/command/UndineHelpCommand.java +++ b/src/main/java/org/bitbucket/ucchy/undine/command/UndineHelpCommand.java @@ -8,12 +8,13 @@ import java.util.List; import org.bitbucket.ucchy.undine.Messages; -import org.bitbucket.ucchy.undine.tellraw.ClickEventType; -import org.bitbucket.ucchy.undine.tellraw.MessageComponent; -import org.bitbucket.ucchy.undine.tellraw.MessageParts; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import com.github.ucchyocean.messaging.tellraw.ClickEventType; +import com.github.ucchyocean.messaging.tellraw.MessageComponent; +import com.github.ucchyocean.messaging.tellraw.MessageParts; + /** * undine help コマンド * @author ucchy diff --git a/src/main/java/org/bitbucket/ucchy/undine/command/UndineItemCommand.java b/src/main/java/org/bitbucket/ucchy/undine/command/UndineItemCommand.java index 2b839be..549b6ee 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/command/UndineItemCommand.java +++ b/src/main/java/org/bitbucket/ucchy/undine/command/UndineItemCommand.java @@ -9,7 +9,6 @@ import org.bitbucket.ucchy.undine.Messages; import org.bitbucket.ucchy.undine.Utility; -import org.bitbucket.ucchy.undine.item.TradableMaterial; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -64,8 +63,7 @@ public void runCommand(CommandSender sender, String label, String[] args) { // 情報表示 String description = UndineCommandUtil.getItemDesc(hand); - String isTradable = TradableMaterial.isTradable(hand.getType()) - ? Messages.get("Yes") : Messages.get("No"); + String isTradable = Messages.get("Yes"); // すべてのアイテムが取引可能 sender.sendMessage(Messages.get("InformationItemDetail", new String[]{"%desc", "%tradable"}, new String[]{description, isTradable})); diff --git a/src/main/java/org/bitbucket/ucchy/undine/command/UndineSendCommand.java b/src/main/java/org/bitbucket/ucchy/undine/command/UndineSendCommand.java index 7b5805b..79ecad0 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/command/UndineSendCommand.java +++ b/src/main/java/org/bitbucket/ucchy/undine/command/UndineSendCommand.java @@ -148,10 +148,8 @@ public void runCommand(CommandSender sender, String label, String[] args) { return; } - for ( MailSender t : parent.getPlayerCache().values() ) { - if ( !to_total.contains(t) ) { - to_total.add(t); - } + for ( String uuid : parent.getPlayerUuids() ) { + to_total.add(new MailSenderPlayer("$" + uuid)); } } else { diff --git a/src/main/java/org/bitbucket/ucchy/undine/command/UndineTextCommand.java b/src/main/java/org/bitbucket/ucchy/undine/command/UndineTextCommand.java index 0c54a53..afa477a 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/command/UndineTextCommand.java +++ b/src/main/java/org/bitbucket/ucchy/undine/command/UndineTextCommand.java @@ -150,8 +150,9 @@ public void runCommand(CommandSender sender, String label, String[] args) { } // メール生成 - MailData mail = new MailData( - targets, ms, message.toString()); + MailData mail = manager.makeEditmodeMail(ms); + mail.addTo(targets); + mail.addMessage(message.toString()); for ( GroupData g : targetGroups ) { mail.setToGroup(mail.getToGroups().size(), g.getName()); } @@ -211,7 +212,7 @@ public List tabComplete(CommandSender sender, String[] args) { // オフラインプレイヤー名で補完する String arg = args[1].toLowerCase(); ArrayList candidates = new ArrayList(); - for ( String name : parent.getPlayerCache().keySet() ) { + for ( String name : parent.getPlayerNames() ) { if ( name.toLowerCase().startsWith(arg) && !name.equals(sender.getName())) { candidates.add(name); diff --git a/src/main/java/org/bitbucket/ucchy/undine/command/UndineToCommand.java b/src/main/java/org/bitbucket/ucchy/undine/command/UndineToCommand.java index e77844e..10a94a0 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/command/UndineToCommand.java +++ b/src/main/java/org/bitbucket/ucchy/undine/command/UndineToCommand.java @@ -230,7 +230,7 @@ public List tabComplete(CommandSender sender, String[] args) { // オフラインプレイヤー名で補完する String arg = args[2].toLowerCase(); ArrayList candidates = new ArrayList(); - for ( String name : parent.getPlayerCache().keySet() ) { + for ( String name : parent.getPlayerNames() ) { if ( name.toLowerCase().startsWith(arg) && !name.equals(sender.getName())) { candidates.add(name); diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/Database.java b/src/main/java/org/bitbucket/ucchy/undine/database/Database.java new file mode 100644 index 0000000..7d439a8 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/Database.java @@ -0,0 +1,323 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Properties; +import java.util.function.Function; +import java.util.logging.Level; + +import org.bitbucket.ucchy.undine.UndineConfig; +import org.bitbucket.ucchy.undine.UndineMailer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; + +/** + * データベースの接続を保持するクラス。 + * @author LazyGon + */ +public class Database { + + + private final DatabaseType databaseType; + + private String mySQLHost; + private String mySQLUser; + private String mySQLPass; + private int mySQLPort; + private String mySQLDBName; + + private Path sqliteDBFile; + + private Connection connection; + + /** データベースへの接続。 */ + final UndineMailer parent; + + public final MailSenderTable mailSenderTable; + public final GroupDataTable groupDataTable; + public final GroupMembersTable groupMembersTable; + public final MailDataTable mailDataTable; + public final MailRecipientsTable mailRecipientsTable; + public final MailRecipientGroupsTable mailRecipientGroupsTable; + public final MailAttachmentBoxTable mailAttachmentBoxTable; + public final MailAttachmentBoxSnapshotTable mailAttachmentBoxSnapshotTable; + public final DraftMailDataTable draftMailDataTable; + public final DraftMailRecipientsTable draftMailRecipientsTable; + public final DraftMailRecipientGroupsTable draftMailRecipientGroupsTable; + public final DraftMailAttachmentBoxTable draftMailAttachmentBoxTable; + + /** + * コンストラクタ。データベースに接続する。 + * + * @param parent プラグイン + * @param type データベースのタイプ。FLAT_FILEを指定するとIllegalArgumentExceptionをスローする。 + * @throws IOException SQLiteデータベースのファイルが作成できなかったとき + * @throws SQLException JDBCドライバーが読み込めなかったとき + * @throws SQLException コネクションを作れなかったとき + */ + public Database(UndineMailer parent, DatabaseType type) throws IOException, SQLException { + this.parent = parent; + this.databaseType = type; + + UndineConfig config = parent.getUndineConfig(); + + if (databaseType == DatabaseType.SQLITE) { + this.sqliteDBFile = UndineMailer.getInstance().getDataFolder().toPath().resolve("maildata.db"); + if (!Files.exists(sqliteDBFile)) { + Files.createFile(sqliteDBFile); + } + + try { + Class.forName("org.sqlite.JDBC"); + } catch (ClassNotFoundException | LinkageError e) { + throw new SQLException("Error occurred on loading SQLite JDBC driver.", e); + } + } else if (databaseType == DatabaseType.MYSQL) { + this.mySQLHost = config.getMysqlHost(); + this.mySQLUser = config.getMysqlUser(); + this.mySQLDBName = config.getMysqlDBName(); + this.mySQLPass = config.getMysqlPass(); + this.mySQLPort = config.getMysqlPort(); + try { + Class.forName("com.mysql.jdbc.Driver"); + } catch (ClassNotFoundException | LinkageError e) { + throw new SQLException("Error occurred on loading MySQL connector.", e); + } + } else { + throw new IllegalStateException("DatabaseType must be sqlite or mysql"); + } + + reloadConnection(); + if (databaseType == DatabaseType.SQLITE) { + execute("PRAGMA foreign_keys = ON"); + } + + mailSenderTable = new MailSenderTable(this); + groupDataTable = new GroupDataTable(this, mailSenderTable); + groupMembersTable = new GroupMembersTable(this, mailSenderTable, groupDataTable); + mailDataTable = new MailDataTable(this, mailSenderTable); + mailRecipientsTable = new MailRecipientsTable(this, mailSenderTable, mailDataTable); + mailRecipientGroupsTable = new MailRecipientGroupsTable(this, mailDataTable, groupDataTable); + mailAttachmentBoxTable = new MailAttachmentBoxTable(this, mailDataTable); + mailAttachmentBoxSnapshotTable = new MailAttachmentBoxSnapshotTable(this, mailDataTable); + draftMailDataTable = new DraftMailDataTable(this, mailSenderTable); + draftMailRecipientsTable = new DraftMailRecipientsTable(this, mailSenderTable, draftMailDataTable); + draftMailRecipientGroupsTable = new DraftMailRecipientGroupsTable(this, draftMailDataTable, groupDataTable); + draftMailAttachmentBoxTable = new DraftMailAttachmentBoxTable(this, draftMailDataTable); + + } + + /** + * コネクションがクローズしていた場合、再作成する。クローズしていない場合は今あるコネクションを返す。 + * 再作成されたコネクションはconnectionのフィールドに格納される。 + * + * @return 今あるコネクションか、それが利用不能のときは新しいコネクション。 + */ + private Connection reloadConnection() throws SQLException { + if (connection != null && !connection.isClosed()) { + return connection; + } + if (databaseType == DatabaseType.MYSQL) { + Properties prop = new Properties(); + prop.put("user", mySQLUser); + prop.put("password", mySQLPass); + connection = DriverManager.getConnection( + "jdbc:mysql://" + mySQLHost + ":" + mySQLPort + "/" + mySQLDBName + "?autoReconnect=true&useSSL=false", + prop + ); + } else if (databaseType == DatabaseType.SQLITE) { + connection = DriverManager.getConnection("jdbc:sqlite:" + sqliteDBFile); + } + + return connection; + } + + /** + * データベース接続を切る。 + */ + public void dispose() { + try { + if (connection != null && !connection.isClosed()) { + connection.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * このインスタンスがどのデータベースの種類を使っているか返す。 + * + * @return MySQL か SQLITE + */ + public DatabaseType getDatabaseType() { + return databaseType; + } + + /** + * データベースに保存される形式の文字列からLocationを生成して返す。 + * + * @param dbLocationStr データベースに保存される形式のLocation文字列 + * @return Location + */ + public static Location fromDBLocationString(String dbLocationStr) { + String[] part = dbLocationStr.split(","); + World world = Bukkit.getWorld(part[0]); + if (world == null) { + world = Bukkit.getWorlds().get(0); + } + try { + return new Location( + world, + Integer.parseInt(part[1]), + Integer.parseInt(part[2]), + Integer.parseInt(part[3]), + (float) Double.parseDouble(part[4]), + (float) Double.parseDouble(part[5]) + ); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + e.printStackTrace(); + return new Location(world, 0, 0, 0, 0, 0); + } + } + + /** + * Locationからデータベースに保存される形式の文字列を生成する。 + * @param location ロケーション + * @return Location文字列 + */ + public static String createDBLocationString(Location location) { + if (location == null || location.getWorld() == null) return null; + return location.getWorld().getName() + "," + + location.getBlockX() + "," + + location.getBlockY() + "," + + location.getBlockZ() + "," + + (Math.floor(location.getYaw() * 10) / 10) + "," + + (Math.floor(location.getPitch()) * 10) / 10; + } + + /** + * 指定した {@code SQL}を実行する。 + * + * @param SQL 実行するSQL文。メソッド内でPreparedStatementに変換される。 + * @return SQL文の実行に成功したかどうか + */ + boolean execute(String SQL) { + try (PreparedStatement preparedStatement = reloadConnection().prepareStatement(SQL)) { + preparedStatement.execute(); + return true; + } catch (SQLException e) { + System.err.println("Error occurred on executing SQL: " + SQL); + e.printStackTrace(); + return false; + } + } + + /** + * 指定したINSERT文を実行する。AUTOINCREMENTによって新たに生成された数値を返す。 + * 値は挿入されたレコードの数だけ生成される。主キーがAUTOINCREMENTでない場合は値が生成されない。 + * INSERTではないSQLを実行する場合や、生成されたキーが必要ない場合は {@link org.bitbucket.ucchy.undine.database.Database#execute(String)} を使うこと。 + * + * @param insert 実行するSQL文。メソッド内でPreparedStatementに変換される。 + * @return AUTOINCREMENTで生成された数値のリスト + */ + List insert(String insert) { + try (PreparedStatement preparedStatement = reloadConnection().prepareStatement(insert, Statement.RETURN_GENERATED_KEYS)) { + if (preparedStatement.executeUpdate() == 0) { + return new ArrayList<>(); + } + ResultSet rs = preparedStatement.getGeneratedKeys(); + List newIds = new ArrayList<>(); + while (rs.next()) { + newIds.add(rs.getInt(1)); + } + return newIds; + } catch (SQLException e) { + System.err.println("Error occurred on executing SQL: " + insert); + e.printStackTrace(); + return new ArrayList<>(); + } + } + + /** + * 指定した {@code SQL}を実行し、結果を第二引数で処理する。第二引数の処理が終わった後に、ResultSetはクローズされる。 + * + * @param SQL 実行するSQL文。メソッド内でPreparedStatementに変換される。 + * @param function 実行結果を処理する関数。 + * @return fuctionの処理結果 + */ + T query(String SQL, Function function) { + try (PreparedStatement preparedStatement = reloadConnection().prepareStatement(SQL)) { + return function.apply(preparedStatement.executeQuery()); + } catch (SQLException e) { + UndineMailer.getInstance().getLogger().log(Level.SEVERE, "Error occurred on executing SQL: " + SQL, e); + return null; + } + } + + /** + * 渡されたコレクションから、WHERE句の中で使えるIN(element, element, ...)という文字列を生成する。 + * @param コレクションの型 + * @param collection コレクション + * @return IN(element, element, ...) という文字列 + */ + static String createIn(Collection collection) { + if (collection == null || collection.isEmpty()) { + return "IN()"; + } + StringBuilder inBuilder = new StringBuilder("IN("); + for (T element : collection) { + if (element instanceof Number) { + inBuilder.append(element.toString()).append(", "); + } else { + inBuilder.append("'").append(element.toString()).append("'").append(", "); + } + } + inBuilder.delete(inBuilder.length() - 2, inBuilder.length()).append(")"); + return inBuilder.toString(); + } + + public enum DatabaseType { + FLAT_FILE(""), + SQLITE("AUTOINCREMENT"), + MYSQL("AUTO_INCREMENT"); + + public final String autoIncrement; + + private DatabaseType(String autoIncrement) { + this.autoIncrement = autoIncrement; + } + + /** + * 名前からデータベースタイプを取得する。 + * @param name 名前 + * @return データベースタイプ + */ + public static DatabaseType getByName(String name) { + for (DatabaseType type : values()) { + if (type.name().toLowerCase(Locale.ROOT).replace("_", "").equals(name)) { + return type; + } + } + + return FLAT_FILE; + } + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/DraftMailAttachmentBoxTable.java b/src/main/java/org/bitbucket/ucchy/undine/database/DraftMailAttachmentBoxTable.java new file mode 100644 index 0000000..1dbb3dd --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/DraftMailAttachmentBoxTable.java @@ -0,0 +1,100 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import com.github.ucchyocean.itemconfig.ItemConfigParseException; +import com.github.ucchyocean.itemconfig.ItemConfigParser; + +import org.bitbucket.ucchy.undine.database.Database.DatabaseType; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; + +/** + * 編集中メールの添付アイテムボックスを保持するテーブルにアクセスするクラス。。 + * @author LazyGon + */ +public class DraftMailAttachmentBoxTable { + public static final String NAME = "undine_draftmailattachmentbox"; + + private final Database database; + @SuppressWarnings("unused") + private final DraftMailDataTable draftMailDataTable; + + DraftMailAttachmentBoxTable(Database database, DraftMailDataTable draftMailDataTable) { + this.database = database; + this.draftMailDataTable = draftMailDataTable; + createTable(); + } + + void createTable() { + database.execute( + "CREATE TABLE IF NOT EXISTS " + NAME + " (" + + "id INTEGER PRIMARY KEY " + database.getDatabaseType().autoIncrement + ", " + + "mailId INTEGER NOT NULL, " + + "item TEXT(8192) NOT NULL, " + + "FOREIGN KEY (mailId) REFERENCES " + DraftMailDataTable.NAME + "(id) ON DELETE CASCADE ON UPDATE CASCADE" + + (database.getDatabaseType() == DatabaseType.MYSQL ? ", INDEX attachmentmailid (id, mailId)" : "") + + ")" + ); + if (database.getDatabaseType() == DatabaseType.SQLITE) { + database.execute("CREATE INDEX IF NOT EXISTS attachmentmailid ON " + NAME + "(id, mailId)"); + } + } + + /** + * 指定されたメールIDの添付アイテムのリストを取得する。 + * @param mailId メールID + * @return 添付アイテムボックス + */ + public ArrayList getAttachmentBoxOf(int mailId) { + return database.query("SELECT item FROM " + NAME + " WHERE mailId = " + mailId, rs -> { + try { + ArrayList items = new ArrayList<>(); + YamlConfiguration itemSection = new YamlConfiguration(); + while (rs.next()) { + itemSection.loadFromString(rs.getString("item").replace("\\'", "'")); + items.add(ItemConfigParser.getItemFromSection(itemSection)); + } + return items; + } catch (SQLException | InvalidConfigurationException | ItemConfigParseException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * このメールIDの添付アイテムボックスを設定する。 + * @param mailId メールID + * @param items 添付アイテムボックス + */ + public void setAttachmentBox(int mailId, List items) { + database.execute("DELETE FROM " + NAME + " WHERE mailId = " + mailId); + if (items.isEmpty()) { + return; + } + + StringBuilder valuesBuilder = new StringBuilder(); + YamlConfiguration itemSection = new YamlConfiguration(); + for (ItemStack item : items) { + ItemConfigParser.setItemToSection(itemSection, item); + valuesBuilder.append("(").append(mailId).append(", '").append(itemSection.saveToString().replace("'", "\\'")).append("'), "); + } + valuesBuilder.delete(valuesBuilder.length() - 2, valuesBuilder.length()); + + String insert = "INSERT INTO " + NAME + " (mailId, item) VALUES " + valuesBuilder.toString(); + if (database.getDatabaseType() == DatabaseType.MYSQL) { + database.execute(insert + " ON DUPLICATE KEY UPDATE item = VALUES(item)"); + } else if (database.getDatabaseType() == DatabaseType.SQLITE) { + database.execute(insert + " ON CONFLICT(id) DO UPDATE SET item = execluded.item"); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/DraftMailDataTable.java b/src/main/java/org/bitbucket/ucchy/undine/database/DraftMailDataTable.java new file mode 100644 index 0000000..befc5b7 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/DraftMailDataTable.java @@ -0,0 +1,228 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import com.github.ucchyocean.itemconfig.ItemConfigParseException; +import com.github.ucchyocean.itemconfig.ItemConfigParser; + +import org.bitbucket.ucchy.undine.sender.MailSender; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; + +/** + * 編集中メールのメインテーブルにアクセスするクラス。 + * @author LazyGon + */ +public class DraftMailDataTable { + + public static final String NAME = "undine_draftmaildata"; + + private final Database database; + private final MailSenderTable mailSenderTable; + + DraftMailDataTable(Database database, MailSenderTable mailSenderTable) { + this.database = database; + this.mailSenderTable = mailSenderTable; + createTable(); + } + + void createTable() { + database.execute( + "CREATE TABLE IF NOT EXISTS " + NAME + " (" + + "id INTEGER PRIMARY KEY " + database.getDatabaseType().autoIncrement + ", " + + "sender INTEGER NOT NULL, " + + "message TEXT(4096), " + + "costMoney DOUBLE NOT NULL DEFAULT 0, " + + "costItem TEXT(8192), " + + "UNIQUE (id, sender), " + + "FOREIGN KEY (sender) REFERENCES " + MailSenderTable.NAME + "(id) ON DELETE CASCADE ON UPDATE CASCADE" + + ")" + ); + } + + /** + * 新しい編集中メールを作成し、そのメールのIDを返す。 + * @param senderId 新しいメールの送信者 + * @return メールID + */ + public int newMail(int senderId) { + return database.insert("INSERT INTO " + NAME + " (sender) VALUES (" + senderId + ") ").get(0); + } + + /** + * 指定されたIDの送信者が送信したメールIDのリストを返す。 + * @param senderId 送信者 + * @return メールIDのリスト + */ + public ArrayList getIdsBySenderId(int senderId) { + ArrayList ids = new ArrayList<>(); + if (senderId == -1) { + return ids; + } + database.query("SELECT id FROM " + NAME + " WHERE sender = " + senderId, rs -> { + try { + while (rs.next()) { + ids.add(rs.getInt("id")); + } + return null; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + }); + + return ids; + } + + /** + * 指定したメールIDのメールの送信者を設定する。 + * @param id メールのID + * @param senderId 送信者 + */ + public void setSender(int id, int senderId) { + database.execute("UPDATE " + NAME + " SET sender = " + senderId + " WHERE id = " + id); + } + + /** + * メールIDから送信者を取得する。 + * @param id メールのID + * @return 送信者 + */ + public MailSender getSenderById(int id) { + int senderId = database.query("SELECT sender FROM " + NAME + " WHERE id = " + id, rs -> { + try { + if (rs.next()) { + return rs.getInt("sender"); + } + return -1; + } catch (SQLException e) { + e.printStackTrace(); + return -1; + } + }); + + return mailSenderTable.getById(senderId); + } + + /** + * メールIDから本文を取得する。 + * @param id メールのID + * @return 本文。改行を含む。 + */ + public String getMessage(int id) { + return database.query("SELECT message FROM " + NAME + " WHERE id = " + id, rs -> { + try { + String message; + if (rs.next() && (message = rs.getString("message")) != null) { + return message.replace("\\'", "'"); + } + return null; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + }); + } + + /** + * 指定したIDのメールの本文を設定する。 + * @param id メールのID + * @param message 本文 + */ + public void setMessage(int id, String message) { + database.execute("UPDATE " + NAME + " SET message = " + (message == null ? "NULL" : "'" + message.replace("'", "\\'") + "'") + " WHERE id = " + id); + } + + /** + * メールのIDから添付アイテムボックスを開くのに必要な金額を取得する。 + * @param id メールのID + * @return 添付アイテムボックスを開くのに必要な金額 + */ + public double getCostMoney(int id) { + return database.query("SELECT costMoney FROM " + NAME + " WHERE id = " + id, rs -> { + try { + if (rs.next()) { + return rs.getDouble("costMoney"); + } + return 0D; + } catch (SQLException e) { + e.printStackTrace(); + return 0D; + } + }); + } + + /** + * 指定したIDのメールの添付アイテムボックスを開くのに必要な金額を設定する。 + * @param id メールのID + * @param money 金額 + */ + public void setCostMoney(int id, double money) { + database.execute("UPDATE " + NAME + " SET = " + (Math.floor(money * 10) / 10) + " WHERE id = " + id); + } + + /** + * 指定したIDのメールの添付アイテムボックスを開くのに必要なアイテムを取得する。 + * @param id メールのID + * @return アイテム + */ + public ItemStack getCostItem(int id) { + return database.query("SELECT costItem FROM " + NAME + " WHERE id = " + id, rs -> { + try { + String itemStr; + if (!rs.next() || (itemStr = rs.getString("costItem")) == null) { + return null; + } + itemStr = itemStr.replace("\\'", "'"); + YamlConfiguration itemSection = new YamlConfiguration(); + itemSection.loadFromString(itemStr); + return ItemConfigParser.getItemFromSection(itemSection); + } catch (SQLException | InvalidConfigurationException | ItemConfigParseException e) { + e.printStackTrace(); + return null; + } + }); + } + + /** + * 指定したIDのメールの添付アイテムボックスを開くのに必要なアイテムを設定する。 + * @param id メールのID + * @param item アイテム + * @return 設定されたかどうか。 + */ + public boolean setCostItem(int id, ItemStack item) { + if (item == null) { + return database.execute("UPDATE " + NAME + " SET costItem = NULL WHERE id = " + id); + } + YamlConfiguration itemSection = new YamlConfiguration(); + ItemConfigParser.setItemToSection(itemSection, item); + String itemStr = itemSection.saveToString(); + return database.execute("UPDATE " + NAME + " SET costItem = '" + itemStr.replace("'", "\\'") + "' WHERE id = " + id); + } + + /** + * 指定したIDのメールを削除する。 + * @param id メールのID + */ + public void removeMail(int id) { + database.execute("DELETE FROM " + NAME + " WHERE id = " + id); + } + + /** + * 複数指定したIDのメールをすべて削除する。 + * @param ids IDのリスト + */ + public void removeMails(List ids) { + if (!ids.isEmpty()) { + database.execute("DELETE FROM " + NAME + " WHERE id " + Database.createIn(ids)); + } + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/DraftMailRecipientGroupsTable.java b/src/main/java/org/bitbucket/ucchy/undine/database/DraftMailRecipientGroupsTable.java new file mode 100644 index 0000000..33d8cab --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/DraftMailRecipientGroupsTable.java @@ -0,0 +1,141 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.bitbucket.ucchy.undine.database.Database.DatabaseType; + +/** + * 編集中テーブルの宛先グループを保持するテーブルにアクセスするクラス。 + * @author LazyGon + */ +public class DraftMailRecipientGroupsTable { + + public static final String NAME = "undine_draftmailrecipientgroups"; + + private final Database database; + @SuppressWarnings("unused") + private final DraftMailDataTable draftMailDataTable; + @SuppressWarnings("unused") + private final GroupDataTable groupDataTable; + + DraftMailRecipientGroupsTable(Database database, DraftMailDataTable draftMailDataTable, GroupDataTable groupDataTable) { + this.database = database; + this.draftMailDataTable = draftMailDataTable; + this.groupDataTable = groupDataTable; + createTable(); + } + + void createTable() { + database.execute( + "CREATE TABLE IF NOT EXISTS " + NAME + " (" + + "mailId INTEGER NOT NULL, " + + "recipientGroup VARCHAR(64) NOT NULL" + (database.getDatabaseType() == DatabaseType.SQLITE ? " COLLATE NOCASE" : "") + ", " + + "PRIMARY KEY (mailId, recipientGroup), " + + "FOREIGN KEY (mailId) REFERENCES " + DraftMailDataTable.NAME + "(id) ON DELETE CASCADE ON UPDATE CASCADE, " + + "FOREIGN KEY (recipientGroup) REFERENCES " + GroupDataTable.NAME + "(name) ON DELETE CASCADE ON UPDATE CASCADE" + + ")" + ); + } + + /** + * toGroupに指定したgroupを含むメールのidをすべて取得する。 + * @param group グループ + * @return メールIDのリスト + */ + public ArrayList getMailIdsByGroup(String groupName) { + return database.query("SELECT mailId FROM " + NAME + " WHERE recipientGroup = '" + groupName + "'", rs -> { + try { + ArrayList ids = new ArrayList<>(); + while (rs.next()) { + ids.add(rs.getInt("mailId")); + } + return ids; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定したgroupをどれか一つでも送信先に含んでいるメールのidをすべて取得する。 + * @param groups グループIDのリスト + * @return メールIDのリスト + */ + public ArrayList getMailIdsByGroups(List groupNames) { + if (groupNames.isEmpty()) { + return new ArrayList<>(); + } + return database.query( + "SELECT mailId FROM " + NAME + " WHERE recipientGroup " + Database.createIn(groupNames), + rs -> { + try { + ArrayList ids = new ArrayList<>(); + while (rs.next()) { + ids.add(rs.getInt("mailId")); + } + return ids; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定されたIDのメールが宛先としているグループをすべて取得する。 + * @param mailId メールのID + * @return グループのリスト + */ + public ArrayList getGroups(int mailId) { + return database.query("SELECT recipientGroup FROM " + NAME + " WHERE mailId = " + mailId, rs -> { + try { + ArrayList groupNames = new ArrayList<>(); + while (rs.next()) { + groupNames.add(rs.getString("recipientGroup")); + } + return groupNames; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定されたグループを指定されたIDのメールの宛先に追加する。 + * @param mailId メールのID + * @param groupName グループ + */ + public void addGroup(int mailId, String groupName) { + if (database.getDatabaseType() == DatabaseType.MYSQL) { + database.execute("INSERT INTO " + NAME + " (mailId, recipientGroup) VALUES (" + mailId + ", '" + groupName + "') ON DUPLICATE KEY UPDATE mailId = mailId"); + } else if (database.getDatabaseType() == DatabaseType.SQLITE) { + database.execute("INSERT INTO " + NAME + " (mailId, recipientGroup) VALUES (" + mailId + ", '" + groupName + "') ON CONFLICT(mailId, recipientGroup) DO NOTHING"); + } + } + + /** + * 指定されたグループを指定されたメールの宛先から削除する。 + * @param mailId メールのID + * @param groupName グループ + */ + public void removeGroup(int mailId, String groupName) { + database.execute("DELETE FROM " + NAME + " WHERE mailId = " + mailId + " AND recipientGroup = '" + groupName + "'"); + } + + /** + * 指定されたIDのメールの宛先グループをすべて削除する。 + * @param mailId メールのID + */ + public void clearGroup(int mailId) { + database.execute("DELETE FROM " + NAME + " WHERE mailId = " + mailId); + } +} \ No newline at end of file diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/DraftMailRecipientsTable.java b/src/main/java/org/bitbucket/ucchy/undine/database/DraftMailRecipientsTable.java new file mode 100644 index 0000000..6fa0ae0 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/DraftMailRecipientsTable.java @@ -0,0 +1,140 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.bitbucket.ucchy.undine.database.Database.DatabaseType; + +/** + * 編集中メールの宛先を保持するテーブルにアクセスするクラス。 + * @author LazyGon + */ +public class DraftMailRecipientsTable { + + public static final String NAME = "undine_draftmailrecipients"; + + private final Database database; + @SuppressWarnings("unused") + private final MailSenderTable mailSenderTable; + @SuppressWarnings("unused") + private final DraftMailDataTable draftMailDataTable; + + DraftMailRecipientsTable(Database database, MailSenderTable mailSenderTable, DraftMailDataTable draftMailDataTable) { + this.database = database; + this.mailSenderTable = mailSenderTable; + this.draftMailDataTable = draftMailDataTable; + createTable(); + } + + void createTable() { + database.execute( + "CREATE TABLE IF NOT EXISTS " + NAME + " (" + + "mailId INTEGER NOT NULL, " + + "recipient INTEGER NOT NULL, " + + "PRIMARY KEY (mailId, recipient), " + + "FOREIGN KEY (mailId) REFERENCES " + DraftMailDataTable.NAME + "(id) ON DELETE CASCADE ON UPDATE CASCADE, " + + "FOREIGN KEY (recipient) REFERENCES " + MailSenderTable.NAME + "(id) ON DELETE CASCADE ON UPDATE CASCADE" + + ")" + ); + } + + /** + * 指定された受信者を宛先に含むメールのIDをすべて取得する。 + * @param recipientId 受信者 + * @return メールのID + */ + public ArrayList getMailIdsByRecipient(int recipientId) { + if (recipientId == -1) { + return new ArrayList<>(); + } + return database.query("SELECT mailId FROM " + NAME + " WHERE recipient = " + recipientId, rs -> { + try { + ArrayList ids = new ArrayList<>(); + while (rs.next()) { + ids.add(rs.getInt("mailId")); + } + return ids; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定されたIDのメールの宛先の受信者のIDをすべて取得する。 + * @param mailId メールのID + * @return 受信者のIDのリスト + */ + public ArrayList getRecipients(int mailId) { + return database.query("SELECT recipient FROM " + NAME + " WHERE mailId = " + mailId, rs -> { + try { + ArrayList ids = new ArrayList<>(); + while (rs.next()) { + ids.add(rs.getInt("recipient")); + } + return ids; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定された受信者をすべて指定されたIDのメールに追加する。 + * @param mailId メールのID + * @param recipientIds 受信者のリスト + */ + public void addRecipients(int mailId, List recipientIds) { + if (recipientIds.isEmpty()) { + return; + } + StringBuilder valuesBuilder = new StringBuilder(); + for (int recipientId : recipientIds) { + valuesBuilder.append("(").append(mailId).append(", ").append(recipientId).append("), "); + } + valuesBuilder.delete(valuesBuilder.length() - 2, valuesBuilder.length()); + + if (database.getDatabaseType() == DatabaseType.MYSQL) { + database.execute("INSERT INTO " + NAME + " (mailId, recipient) VALUES " + valuesBuilder.toString() + " ON DUPLICATE KEY UPDATE recipient = recipient"); + } else if (database.getDatabaseType() == DatabaseType.SQLITE) { + database.execute("INSERT INTO " + NAME + " (mailId, recipient) VALUES " + valuesBuilder.toString() + " ON CONFLICT(mailId, recipient) DO NOTHING"); + } + } + + /** + * 指定された受信者を指定されたIDのメールの宛先に追加する。 + * @param mailId メールのID + * @param recipientId 受信者 + */ + public void addRecipient(int mailId, int recipientId) { + addRecipients(mailId, Arrays.asList(recipientId)); + } + + /** + * 指定された受信者を指定されたIDのメールの宛先から削除する。 + * @param mailId メールのID + * @param recipientId 受信者 + */ + public void removeRecipient(int mailId, int recipientId) { + if (recipientId != -1) { + database.execute("DELETE FROM " + NAME + " WHERE mailId = " + mailId + " AND recipient = " + recipientId); + } + } + + /** + * 指定されたIDのメールの宛先をすべて削除する。 + * @param mailId メールのID + */ + public void clearRecipient(int mailId) { + database.execute("DELETE FROM " + NAME + " WHERE mailId = " + mailId); + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/GroupDataDatabase.java b/src/main/java/org/bitbucket/ucchy/undine/database/GroupDataDatabase.java new file mode 100644 index 0000000..d6c4053 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/GroupDataDatabase.java @@ -0,0 +1,198 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.util.ArrayList; + +import org.bitbucket.ucchy.undine.UndineMailer; +import org.bitbucket.ucchy.undine.group.GroupData; +import org.bitbucket.ucchy.undine.group.GroupPermissionMode; +import org.bitbucket.ucchy.undine.sender.MailSender; + +/** + * データベース管理のメールグループ。 + * @author LazyGon + */ +public class GroupDataDatabase extends GroupData { + + private GroupDataTable groupDataTable; + private GroupMembersTable groupMembersTable; + private MailSenderTable mailSenderTable; + + public GroupDataDatabase(UndineMailer parent, String name) { + super(parent, name); + this.groupDataTable = parent.getDatabase().groupDataTable; + this.groupMembersTable = parent.getDatabase().groupMembersTable; + this.mailSenderTable = parent.getDatabase().mailSenderTable; + + if (!this.groupDataTable.exists(name)) { + throw new IllegalArgumentException("The group " + name + " is not registered on database yet. Specify owner for constructor to register group."); + } + this.name = this.groupDataTable.fixCase(name); + } + + /** + * コンストラクタ + */ + public GroupDataDatabase(UndineMailer parent, String name, MailSender owner) { + super(parent, name); + this.groupDataTable = parent.getDatabase().groupDataTable; + this.groupMembersTable = parent.getDatabase().groupMembersTable; + this.mailSenderTable = parent.getDatabase().mailSenderTable; + + this.groupDataTable.add(name, owner); + addMember(owner); + setSendMode(parent.getUndineConfig().getSendModeDefault()); + setModifyMode(parent.getUndineConfig().getModifyModeDefault()); + setDissolutionMode(parent.getUndineConfig().getDissolutionModeDefault()); + } + + @Override + public void save() { + // Do nothing. データは常にデータベースと同期されているため、保存は不要。 + } + + /** + * グループにメンバーを追加する + * + * @param member メンバー + */ + @Override + public void addMember(MailSender member) { + groupMembersTable.addMember(name, mailSenderTable.getId(member)); + } + + /** + * グループからメンバーを削除する + * + * @param member メンバー + */ + @Override + public void removeMember(MailSender member) { + groupMembersTable.removeMember(name, mailSenderTable.getId(member)); + } + + /** + * グループのオーナーを取得する + * + * @return オーナー + */ + @Override + public MailSender getOwner() { + return mailSenderTable.getById(groupDataTable.getOwnerId(name)); + } + + /** + * グループのオーナーを設定する + * + * @param owner + */ + @Override + public void setOwner(MailSender owner) { + groupDataTable.setOwner(name, mailSenderTable.getId(owner)); + } + + /** + * グループのメンバーを取得する + * + * @return メンバー + */ + @Override + public ArrayList getMembers() { + return new ArrayList<>(mailSenderTable.getByIds(groupMembersTable.getMemberIdsOf(name)).values()); + } + + /** + * 指定されたsenderが、グループのメンバーかどうかを返す + * + * @param sender + * @return メンバーかどうか + */ + @Override + public boolean isMember(MailSender sender) { + return groupMembersTable.isMember(name, mailSenderTable.getId(sender)); + } + + /** + * 指定されたsenderがオーナーかどうかを返す + * + * @param sender + * @return オーナーかどうか + */ + @Override + public boolean isOwner(MailSender sender) { + return groupDataTable.getOwnerId(name) == mailSenderTable.getId(sender); + } + + /** + * 送信権限モードを取得する + * + * @return sendMode + */ + @Override + public GroupPermissionMode getSendMode() { + return groupDataTable.getSendMode(name); + } + + /** + * 送信権限モードを設定する + * + * @param sendMode sendMode + */ + @Override + public void setSendMode(GroupPermissionMode sendMode) { + groupDataTable.setSendMode(name, sendMode); + } + + /** + * 変更権限モードを取得する + * + * @return modifyMode + */ + @Override + public GroupPermissionMode getModifyMode() { + return groupDataTable.getModifyMode(name); + } + + /** + * 変更権限モードを設定する + * + * @param modifyMode modifyMode + */ + @Override + public void setModifyMode(GroupPermissionMode modifyMode) { + groupDataTable.setModifyMode(name, modifyMode); + } + + /** + * 解散権限モードを取得する + * + * @return dissolutionMode + */ + @Override + public GroupPermissionMode getDissolutionMode() { + return groupDataTable.getDissolutionMode(name); + } + + /** + * 解散権限モードを設定する + * + * @param dissolutionMode dissolutionMode + */ + @Override + public void setDissolutionMode(GroupPermissionMode dissolutionMode) { + groupDataTable.setDissolutionMode(name, dissolutionMode); + } + + /** + * データのアップグレードを行う。データベースへの書き込みと読み出しのタイミングで常にチェックしているため、不要。 + * @return アップグレードを実行したかどうか + */ + @Override + protected boolean upgrade() { + return false; + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/GroupDataTable.java b/src/main/java/org/bitbucket/ucchy/undine/database/GroupDataTable.java new file mode 100644 index 0000000..fbf2860 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/GroupDataTable.java @@ -0,0 +1,315 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.bitbucket.ucchy.undine.UndineConfig; +import org.bitbucket.ucchy.undine.UndineMailer; +import org.bitbucket.ucchy.undine.database.Database.DatabaseType; +import org.bitbucket.ucchy.undine.group.GroupDataFlatFile; +import org.bitbucket.ucchy.undine.group.GroupPermissionMode; +import org.bitbucket.ucchy.undine.sender.MailSender; + +/** + * グループデータを保持するメインテーブルにアクセスするクラス。 + * @author LazyGon + */ +public class GroupDataTable { + + public static final String NAME = "undine_groupdata"; + + private final Database database; + private final MailSenderTable mailSenderTable; + + GroupDataTable(Database database, MailSenderTable mailSenderTable) { + this.database = database; + this.mailSenderTable = mailSenderTable; + createTable(); + } + + void createTable() { + UndineConfig config = database.parent.getUndineConfig(); + database.execute( + "CREATE TABLE IF NOT EXISTS " + NAME + " (" + + "name VARCHAR(64) PRIMARY KEY" + (database.getDatabaseType() == DatabaseType.SQLITE ? " COLLATE NOCASE" : "") + ", " + + "owner INTEGER NOT NULL, " + + "sendMode TINYINT NOT NULL DEFAULT " + config.getSendModeDefault().ordinal() + ", " + + "modifyMode TINYINT NOT NULL DEFAULT " + config.getModifyModeDefault().ordinal() + ", " + + "dissolutionMode TINYINT NOT NULL DEFAULT " + config.getDissolutionModeDefault().ordinal() + ", " + + "FOREIGN KEY (owner) REFERENCES " + MailSenderTable.NAME + "(id) ON DELETE CASCADE ON UPDATE CASCADE" + + ")" + ); + } + + /** + * + * @param name + * @param owner + * @return + */ + public boolean add(String name, MailSender owner) { + UndineConfig config = database.parent.getUndineConfig(); + return add(name, owner, config.getSendModeDefault(), config.getModifyModeDefault(), config.getDissolutionModeDefault()); + } + + public boolean add(String name, MailSender owner, GroupPermissionMode sendMode, GroupPermissionMode modifyMode, GroupPermissionMode dissolutionMode) { + if (database.getDatabaseType() == DatabaseType.MYSQL) { + return database.execute( + "INSERT INTO " + NAME + " (name, owner, sendMode, modifyMode, dissolutionMode) VALUES (" + + "'" + name + "', " + + mailSenderTable.getId(owner) + ", " + + sendMode.ordinal() + ", " + + modifyMode.ordinal() + ", " + + dissolutionMode.ordinal() + + ") ON DUPLICATE KEY UPDATE " + + "owner = VALUES(owner), " + + "sendMode = VALUES(sendMode), " + + "modifyMode = VALUES(modifyMode), " + + "dissolutionMode = VALUES(dissolutionMode)" + ); + } else if (database.getDatabaseType() == DatabaseType.SQLITE) { + return database.execute( + "INSERT INTO " + NAME + " (name, owner, sendMode, modifyMode, dissolutionMode) VALUES (" + + "'" + name + "', " + + mailSenderTable.getId(owner) + ", " + + sendMode.ordinal() + ", " + + modifyMode.ordinal() + ", " + + dissolutionMode.ordinal() + + ") ON CONFLICT(name) DO UPDATE SET " + + "owner = excluded.owner, " + + "sendMode = excluded.sendMode, " + + "modifyMode = excluded.modifyMode, " + + "dissolutionMode = excluded.dissolutionMode" + ); + } else { + return false; + } + } + + public String fixCase(String groupName) { + return database.query("SELECT name FROM " + NAME + " WHERE name = '" + groupName + "'", rs -> { + try { + return rs.next() ? rs.getString("name") : null; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + }); + } + + public ArrayList getNames() { + return getNamesWhere(null); + } + + public boolean exists(String groupName) { + return database.query("SELECT name FROM " + NAME + " WHERE name = '" + groupName + "'", rs -> { + try { + return rs.next(); + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + }); + } + + private ArrayList getNamesWhere(String where) { + return database.query("SELECT name FROM " + NAME + (where == null || where.isEmpty() ? "" : " " + where), rs -> { + try { + ArrayList result = new ArrayList<>(); + while (rs.next()) { + result.add(rs.getString("name")); + } + return result; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + public void retainExistsByName(List names) { + if (!names.isEmpty()) { + names.retainAll(getNamesWhere("WHERE name " + Database.createIn(names))); + } + } + + public ArrayList getNamesByOwner(int ownerId) { + if (ownerId <= 0) { + return new ArrayList<>(); + } + return getNamesWhere("WHERE owner = " + ownerId); + } + + public int getOwnerId(String groupName) { + return database.query("SELECT owner FROM " + NAME + " WHERE name = '" + groupName + "'", rs -> { + try { + if (rs.next()) { + return rs.getInt("owner"); + } + return null; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + }); + } + + public void setOwner(String groupName, int ownerId) { + if (ownerId > 0) { + database.execute("UPDATE " + NAME + " SET owner = " + ownerId + " WHERE name = '" + groupName + "'"); + } + } + + private GroupPermissionMode getMode(String groupName, String columnName) { + return database.query("SELECT " + columnName + " FROM " + NAME + " WHERE name = '" + groupName + "'", rs -> { + try { + if (rs.next()) { + return GroupPermissionMode.values()[(int)rs.getByte(columnName)]; + } + return null; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + }); + } + + public GroupPermissionMode getSendMode(String groupName) { + return getMode(groupName, "sendMode"); + } + + public GroupPermissionMode getModifyMode(String groupName) { + return getMode(groupName, "modifyMode"); + } + + public GroupPermissionMode getDissolutionMode(String groupName) { + return getMode(groupName, "dissolutionMode"); + } + + private void setMode(String groupName, String columnName, GroupPermissionMode mode) { + database.execute("UPDATE " + NAME + " SET " + columnName + " = " + mode.ordinal() + " WHERE name = '" + groupName + "'"); + } + + public void setSendMode(String groupName, GroupPermissionMode mode) { + setMode(groupName, "sendMode", mode); + } + + public void setModifyMode(String groupName, GroupPermissionMode mode) { + setMode(groupName, "modifyMode", mode); + } + + public void setDissolutionMode(String groupName, GroupPermissionMode mode) { + setMode(groupName, "dissolutionMode", mode); + } + + public boolean deleteByName(String groupName) { + return database.execute("DELETE FROM " + NAME + " WHERE name = '" + groupName + "'"); + } + + public void deleteByNames(List groupNames) { + if (!groupNames.isEmpty()) { + database.execute("DELETE FROM " + NAME + " WHERE name " + Database.createIn(groupNames)); + } + } + + public void deleteAll() { + database.execute("DELETE FROM " + NAME); + } + + /** + * グループ名のリストを用いてデータベースからデータをロードし、フラットファイルに保存可能なグループクラスを作成する。 + * + * @param groupNames 読み込むグループ名のリスト。 + * @return 読み込まれたグループデータのリスト + */ + public ArrayList getAsFlatFiles(List groupNames) { + if (groupNames.isEmpty()) { + groupNames = new ArrayList<>(); + } + + Map nameGroupMap = new HashMap<>(); + + // OwnerをIdからMailSenderに一斉変換するためにマップを作っておく。 + Map groupNameOwnerMap = new HashMap<>(); + + String inNames = Database.createIn(groupNames); + String select = "SELECT name, owner, sendMode, modifyMode, dissolutionMode FROM " + NAME + " WHERE name " + inNames; + database.query(select, rs -> { + try { + // 指定した名前を持つグループのデータを一気に作る。ただし、オーナーはnullにしておいて後で代入する。 + while (rs.next()) { + String name = rs.getString("name"); + + GroupDataFlatFile data = new GroupDataFlatFile(UndineMailer.getInstance(), name, null); + GroupPermissionMode[] groupPermissions = GroupPermissionMode.values(); + data.setSendMode(groupPermissions[(int)rs.getByte("gd.sendMode")]); + data.setModifyMode(groupPermissions[(int)rs.getByte("gd.modifyMode")]); + data.setDissolutionMode(groupPermissions[(int)rs.getByte("gd.dissolutionMode")]); + + nameGroupMap.put(name, data); + groupNameOwnerMap.put(name, rs.getInt("owner")); + } + return null; // なにも返さない。 + } catch (SQLException e) { + e.printStackTrace(); + nameGroupMap.clear(); + return null; + } + }); + + // 指定した名前のグループがデータベースにない。 + if (nameGroupMap.isEmpty()) { + return new ArrayList<>(); + } + + // 取得した各グループのオーナーを一斉に変換する。 + Map idOwnerMap = mailSenderTable.getByIds(new ArrayList<>(groupNameOwnerMap.values())); + // 変換したオーナーをセットする。 + nameGroupMap.forEach((name, group) -> group.setOwner(idOwnerMap.get(groupNameOwnerMap.get(name)))); + + Map> membersMap = new HashMap<>(); + Set mailSenderIds = new HashSet<>(); + + database.query( + "SELECT groupName, member FROM " + GroupMembersTable.NAME + "WHERE groupName " + inNames, + rs -> { + try { + while (rs.next()) { + String name = rs.getString("groupName"); + int memberId = rs.getInt("member"); + mailSenderIds.add(memberId); + + if (membersMap.containsKey(name)) { + membersMap.put(name, new ArrayList<>()); + } + List members = membersMap.get(name); + members.add(memberId); + } + return null; // 何も返さない。 + } catch (SQLException e) { + e.printStackTrace(); + return null; // 何も返さない。 + } + }); + + Map idMemberMap = mailSenderTable.getByIds(new ArrayList<>(mailSenderIds)); + membersMap.forEach((groupName, members) -> { + GroupDataFlatFile group = nameGroupMap.get(groupName); + for (int memberId : membersMap.get(groupName)) { + group.addMember(idMemberMap.get(memberId)); + } + }); + + return new ArrayList<>(nameGroupMap.values()); + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/GroupManagerDatabase.java b/src/main/java/org/bitbucket/ucchy/undine/database/GroupManagerDatabase.java new file mode 100644 index 0000000..b5aa758 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/GroupManagerDatabase.java @@ -0,0 +1,169 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.util.ArrayList; +import java.util.List; + +import org.bitbucket.ucchy.undine.UndineMailer; +import org.bitbucket.ucchy.undine.group.GroupData; +import org.bitbucket.ucchy.undine.group.GroupManager; +import org.bitbucket.ucchy.undine.group.SpecialGroupAll; +import org.bitbucket.ucchy.undine.group.SpecialGroupAllConnected; +import org.bitbucket.ucchy.undine.group.SpecialGroupAllLogin; +import org.bitbucket.ucchy.undine.group.SpecialGroupPex; +import org.bitbucket.ucchy.undine.sender.MailSender; + +/** + * メールグループをデータベースで管理するクラス。 + * @author LazyGon + */ +public class GroupManagerDatabase extends GroupManager { + + private GroupDataTable groupDataTable; + + /** + * コンストラクタ + * + * @param parent + */ + public GroupManagerDatabase(UndineMailer parent) { + super(parent); + + this.groupDataTable = parent.getDatabase().groupDataTable; + + // 特殊グループの登録 + addGroup(groupAll); + addGroup(groupAllConnected); + addGroup(groupAllLogin); + } + + /** + * 全データを再読み込みする + */ + @Override + public void reload() { + // Do nothing. 常にデータベースと同期されているため、再読込する必要はない。 + return; + } + + /** + * グループを追加する。 + * 重複するグループ名が既に追加されている場合は、 + * 古いグループが上書きされてしまうことに注意する。 + * @param group グループ + */ + @Override + public void addGroup(GroupData group) { + saveGroupData(group); + } + + /** + * 指定したグループ名のグループを取得する + * @param name グループ名 + * @return グループ + */ + @Override + public GroupData getGroup(String name) { + GroupData pexGroup = getPexGroup(name); + if (pexGroup != null) { + return pexGroup; + } + + return groupDataTable.exists(name) + ? new GroupDataDatabase(parent, name) + : null; + } + + /** + * 指定したグループ名のグループをすべて取得する。 + * @param names グループ名のリスト + * @return グループのリスト + */ + public ArrayList getGroups(List names) { + return convert(names, true); + } + + /** + * 指定したグループ名のグループを削除する + * @param name グループ名 + */ + @Override + public void removeGroup(String name) { + groupDataTable.deleteByName(name); + } + + /** + * 全てのグループ名を取得する + * @return 全てのグループ名 + */ + @Override + public ArrayList getAllGroupNames() { + return groupDataTable.getNames(); + } + + /** + * 全てのグループを取得する + * @return 全てのグループ + */ + @Override + public ArrayList getAllGroups() { + return convert(groupDataTable.getNames(), false); + } + + /** + * 指定されたグループ名は既に存在するかどうかを確認する + * @return 存在するかどうか + */ + @Override + public boolean existGroupName(String name) { + return groupDataTable.exists(name); + } + + /** + * 指定したグループを保存する + * @param group グループ + */ + @Override + public void saveGroupData(GroupData group) { + groupDataTable.add(group.getName(), group.getOwner(), group.getSendMode(), group.getModifyMode(), group.getDissolutionMode()); + } + + /** + * @return サーバーの起動している間に接続したすべてのプレイヤーを示すグループ + */ + @Override + public int getOwnerGroupCount(MailSender sender) { + return groupDataTable.getNamesByOwner(parent.getDatabase().mailSenderTable.getId(sender)).size(); + } + + /** + * 指定されたグループ名からグループを作成して返す。 + * @param groupNames グループ名のリスト + * @param filter データベースにグループが有るかチェックするかどうか + * @return グループのリスト + */ + private ArrayList convert(List groupNames, boolean filter) { + if (filter) { + groupDataTable.retainExistsByName(groupNames); + } + ArrayList groups = new ArrayList<>(); + for (String name : groupNames) { + if (name.equals(SpecialGroupAll.NAME)) { + groups.add(parent.getGroupManager().getGroupAll()); + } else if (name.equals(SpecialGroupAllConnected.NAME)) { + groups.add(parent.getGroupManager().getGroupAllConnected()); + } else if (name.equals(SpecialGroupAllLogin.NAME)) { + groups.add(parent.getGroupManager().getGroupAllLogin()); + } else if (name.toLowerCase().startsWith(SpecialGroupPex.NAME_PREFIX)) { + groups.add(parent.getGroupManager().getPexGroup(name)); + } else { + groups.add(new GroupDataDatabase(parent, name)); + } + } + return groups; + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/GroupMembersTable.java b/src/main/java/org/bitbucket/ucchy/undine/database/GroupMembersTable.java new file mode 100644 index 0000000..cdc61b5 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/GroupMembersTable.java @@ -0,0 +1,223 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.bitbucket.ucchy.undine.database.Database.DatabaseType; + +/** + * グループメンバーを保持するテーブルにアクセスするクラス。 + * @author LazyGon + */ +public class GroupMembersTable { + + public static final String NAME = "undine_groupmembers"; + + private final Database database; + @SuppressWarnings("unused") + private final MailSenderTable mailSenderTable; + @SuppressWarnings("unused") + private final GroupDataTable groupDataTable; + + GroupMembersTable(Database database, MailSenderTable mailSenderTable, GroupDataTable groupDataTable) { + this.database = database; + this.mailSenderTable = mailSenderTable; + this.groupDataTable = groupDataTable; + createTable(); + } + + void createTable() { + database.execute( + "CREATE TABLE IF NOT EXISTS " + NAME + " (" + + "groupName VARCHAR(64) NOT NULL" + (database.getDatabaseType() == DatabaseType.SQLITE ? " COLLATE NOCASE" : "") + ", " + + "member INTEGER NOT NULL, " + + "PRIMARY KEY (groupName, member), " + + "FOREIGN KEY (groupName) REFERENCES " + GroupDataTable.NAME + "(name) ON DELETE CASCADE ON UPDATE CASCADE, " + + "FOREIGN KEY (member) REFERENCES " + MailSenderTable.NAME + "(id) ON DELETE CASCADE ON UPDATE CASCADE" + + ")" + ); + } + + /** + * 指定されたすべてのグループのメンバーの和集合を取得する。 + * @param groupNames グループ名のリスト + * @return 各グループのメンバーをまとめたリスト + */ + public ArrayList getMemberIdsOf(List groupNames) { + if (groupNames.isEmpty()) { + return new ArrayList<>(); + } + return database.query( + "SELECT member FROM " + NAME + " WHERE groupName " + Database.createIn(groupNames), + rs -> { + try { + ArrayList ids = new ArrayList<>(); + while (rs.next()) { + ids.add(rs.getInt("member")); + } + return ids; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定したグループのメンバーを取得する。 + * @param groupName グループ + * @return メンバーのリスト + */ + public ArrayList getMemberIdsOf(String groupName) { + return database.query( + "SELECT member FROM " + NAME + " WHERE groupName = '" + groupName + "'", + rs -> { + try { + ArrayList ids = new ArrayList<>(); + while (rs.next()) { + ids.add(rs.getInt("member")); + } + return ids; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定したグループから指定したメンバーを削除する。 + * @param groupName グループ + * @param memberId メンバー + * @return 削除したかどうか + */ + public boolean removeMember(String groupName, int memberId) { + return database.execute( + "DELETE FROM " + NAME + " WHERE " + + "member = " + memberId + " AND " + + "groupName = '" + groupName + "'" + ); + } + + /** + * 指定したすべてのグループから、指定されたメンバーを削除する。 + * @param memberId メンバー + * @param groupNames グループのリスト + */ + public void removeMemberFromGroups(int memberId, ArrayList groupNames) { + database.execute( + "DELETE FROM " + NAME + " WHERE " + + "member = " + memberId + " AND " + + "groupName " + Database.createIn(groupNames) + ); + } + + /** + * 指定されたメンバーをすべてのグループから削除する。 + * @param memberId メンバー + */ + public void removeMemberFromAllGroups(int memberId) { + database.execute("DELETE FROM " + NAME + " WHERE member = " + memberId); + } + + /** + * 指定されたグループのメンバーをすべて削除する。 + * @param groupName グループ + */ + public void clearMembers(String groupName) { + database.execute("DELETE FROM" + NAME + " WHERE groupName = '" + groupName + "'"); + } + + /** + * 指定されたグループにメンバーを追加する。 + * @param groupName グループ + * @param memberId メンバー + * @return 追加したか + */ + public boolean addMember(String groupName, int memberId) { + String insert = "INSERT INTO " + NAME + " (groupName, member) VALUES ('" + groupName + "', " + memberId + ")"; + if (database.getDatabaseType() == DatabaseType.MYSQL) { + database.execute(insert + " ON DUPLICATE KEY UPDATE groupName = groupName"); + } else if (database.getDatabaseType() == DatabaseType.SQLITE) { + database.execute(insert + " ON CONFLICT(groupName, member) DO NOTHING"); + } + return true; + } + + /** + * 指定されたグループにはいっているメンバーかどうか調べる。 + * @param groupName グループ + * @param memberId メンバー + * @return メンバーだったらtrue + */ + public boolean isMember(String groupName, int memberId) { + return database.query("SELECT member FROM " + NAME + " WHERE groupName = '" + groupName + "'", rs -> { + try { + return rs.next(); + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + }); + } + + /** + * 指定されたすべてのメンバーを指定されたグループに追加する。 + * @param groupName グループ + * @param memberIds メンバーのリスト + */ + public void addAllToGroup(String groupName, ArrayList memberIds) { + StringBuilder valueBuilder = new StringBuilder(); + for (Integer memberId : memberIds) { + if (memberId != null && memberId > 0) { + valueBuilder.append("('").append(groupName).append("', ").append(memberId).append("), "); + } + } + if (valueBuilder.length() == 0) { + return; + } + valueBuilder.delete(valueBuilder.length() - 2, valueBuilder.length()); + + String insert = "INSERT INTO " + NAME + " (groupName, member) VALUES " + valueBuilder.toString(); + if (database.getDatabaseType() == DatabaseType.MYSQL) { + database.execute(insert + " ON DUPLICATE KEY UPDATE groupName = groupName"); + } else if (database.getDatabaseType() == DatabaseType.SQLITE) { + database.execute(insert + " ON CONFLICT(groupName, member) DO NOTHING"); + } + } + + /** + * メンバーからが所属しているすべてのグループを取得する。 + * @param memberId メンバー + * @return グループのリスト + */ + public ArrayList getBelongingGroups(int memberId) { + return getGroupsWhere("WHERE member = " + memberId); + } + + /** + * WHERE句を指定してグループを取得する。 + * @param where SQL文のWHERE句部分 + * @return 条件に当てはまったグループのリスト + */ + public ArrayList getGroupsWhere(String where) { + return database.query("SELECT groupName FROM " + NAME + (where == null || where.isBlank() ? "" : " " + where), rs -> { + try { + ArrayList groupNames = new ArrayList<>(); + while (rs.next()) { + groupNames.add(rs.getString("groupName")); + } + return groupNames; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/MailAttachmentBoxSnapshotTable.java b/src/main/java/org/bitbucket/ucchy/undine/database/MailAttachmentBoxSnapshotTable.java new file mode 100644 index 0000000..134d615 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/MailAttachmentBoxSnapshotTable.java @@ -0,0 +1,100 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import com.github.ucchyocean.itemconfig.ItemConfigParseException; +import com.github.ucchyocean.itemconfig.ItemConfigParser; + +import org.bitbucket.ucchy.undine.database.Database.DatabaseType; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; + +/** + * メールのオリジナル添付アイテムボックスを保持するテーブルにアクセスするクラス。 + * @author LazyGon + */ +public class MailAttachmentBoxSnapshotTable { + public static final String NAME = "undine_mailattachmentboxsnapshot"; + + private final Database database; + @SuppressWarnings("unused") + private final MailDataTable mailDataTable; + + MailAttachmentBoxSnapshotTable(Database database, MailDataTable mailDataTable) { + this.database = database; + this.mailDataTable = mailDataTable; + createTable(); + } + + void createTable() { + database.execute( + "CREATE TABLE IF NOT EXISTS " + NAME + " (" + + "id INTEGER PRIMARY KEY " + database.getDatabaseType().autoIncrement + ", " + + "mailId INTEGER NOT NULL, " + + "item TEXT(8192) NOT NULL, " + + "FOREIGN KEY (mailId) REFERENCES " + MailDataTable.NAME + "(id) ON DELETE CASCADE ON UPDATE CASCADE" + + (database.getDatabaseType() == DatabaseType.MYSQL ? ", INDEX snapshotattachmentmailid (id, mailId)" : "") + + ")" + ); + if (database.getDatabaseType() == DatabaseType.SQLITE) { + database.execute("CREATE INDEX IF NOT EXISTS snapshotattachmentmailid ON " + NAME + "(id, mailId)"); + } + } + + /** + * 指定されたIDのメールの添付アイテムボックスのオリジナルを取得する。 + * @param mailId メールのID + * @return 添付アイテムボックス + */ + public ArrayList getAttachmentBoxOf(int mailId) { + return database.query("SELECT item FROM " + NAME + " WHERE mailId = " + mailId, rs -> { + try { + ArrayList items = new ArrayList<>(); + YamlConfiguration itemSection = new YamlConfiguration(); + while (rs.next()) { + itemSection.loadFromString(rs.getString("item").replace("\\'", "'")); + items.add(ItemConfigParser.getItemFromSection(itemSection)); + } + return items; + } catch (SQLException | InvalidConfigurationException | ItemConfigParseException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定されたIDのメールの添付アイテムボックスをオリジナルを設定する。 + * @param mailId メールのID + * @param items 添付アイテムボックス + */ + public void setAttachmentBox(int mailId, List items) { + database.execute("DELETE FROM " + NAME + " WHERE mailId = " + mailId); + if (items.isEmpty()) { + return; + } + + StringBuilder valuesBuilder = new StringBuilder(); + YamlConfiguration itemSection = new YamlConfiguration(); + for (ItemStack item : items) { + ItemConfigParser.setItemToSection(itemSection, item); + valuesBuilder.append("(").append(mailId).append(", '").append(itemSection.saveToString().replace("'", "\\'")).append("'), "); + } + valuesBuilder.delete(valuesBuilder.length() - 2, valuesBuilder.length()); + + String insert = "INSERT INTO " + NAME + " (mailId, item) VALUES " + valuesBuilder.toString(); + if (database.getDatabaseType() == DatabaseType.MYSQL) { + database.execute(insert + " ON DUPLICATE KEY UPDATE item = VALUES(item)"); + } else if (database.getDatabaseType() == DatabaseType.SQLITE) { + database.execute(insert + " ON CONFLICT(id) DO UPDATE SET item = execluded.item"); + } + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/MailAttachmentBoxTable.java b/src/main/java/org/bitbucket/ucchy/undine/database/MailAttachmentBoxTable.java new file mode 100644 index 0000000..d8f75a7 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/MailAttachmentBoxTable.java @@ -0,0 +1,124 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.github.ucchyocean.itemconfig.ItemConfigParseException; +import com.github.ucchyocean.itemconfig.ItemConfigParser; + +import org.bitbucket.ucchy.undine.database.Database.DatabaseType; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; + +/** + * メールの添付アイテムボックスを保持するテーブルにアクセスするクラス。 + * @author LazyGon + */ +public class MailAttachmentBoxTable { + public static final String NAME = "undine_mailattachmentbox"; + + private final Database database; + @SuppressWarnings("unused") + private final MailDataTable mailDataTable; + + MailAttachmentBoxTable(Database database, MailDataTable mailDataTable) { + this.database = database; + this.mailDataTable = mailDataTable; + createTable(); + } + + void createTable() { + database.execute( + "CREATE TABLE IF NOT EXISTS " + NAME + " (" + + "id INTEGER PRIMARY KEY " + database.getDatabaseType().autoIncrement + ", " + + "mailId INTEGER NOT NULL, " + + "item TEXT(8192) NOT NULL, " + + "FOREIGN KEY (mailId) REFERENCES " + MailDataTable.NAME + "(id) ON DELETE CASCADE ON UPDATE CASCADE" + + (database.getDatabaseType() == DatabaseType.MYSQL ? ", INDEX attachmentmailid (id, mailId)" : "") + + + ")" + ); + if (database.getDatabaseType() == DatabaseType.SQLITE) { + database.execute("CREATE INDEX IF NOT EXISTS attachmentmailid ON " + NAME + "(id, mailId)"); + } + } + + /** + * 指定された送信者がいくつの添付アイテムボックスを使っているか調べる。メール一通につき一つとカウントする。 + * @param senderId + * @return 送信者が使っている添付アイテムボックスの数 + */ + public int getAttachBoxUsageCount(int senderId) { + + return database.query("SELECT mailId FROM " + NAME + " WHERE mailId " + Database.createIn(database.mailDataTable.getIdsBySenderId(senderId)), rs -> { + try { + Set mailIdsWithAttachmentBox = new HashSet<>(); + while (rs.next()) { + mailIdsWithAttachmentBox.add(rs.getInt("mailId")); + } + return mailIdsWithAttachmentBox.size(); + } catch (SQLException e) { + e.printStackTrace(); + return 0; + } + }); + } + + /** + * 指定されたIDのメールの保持する添付アイテムボックスのオリジナルを取得する。 + * @param mailId メールのID + * @return 添付アイテムボックス + */ + public ArrayList getAttachmentBoxOf(int mailId) { + return database.query("SELECT item FROM " + NAME + " WHERE mailId = " + mailId, rs -> { + try { + ArrayList items = new ArrayList<>(); + YamlConfiguration itemSection = new YamlConfiguration(); + while (rs.next()) { + itemSection.loadFromString(rs.getString("item").replace("\\'", "'")); + items.add(ItemConfigParser.getItemFromSection(itemSection)); + } + return items; + } catch (SQLException | InvalidConfigurationException | ItemConfigParseException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定したIDのメールの添付アイテムボックスのオリジナルを設定する。 + * @param mailId メールのID + * @param items 添付アイテムボックス + */ + public void setAttachmentBox(int mailId, List items) { + database.execute("DELETE FROM " + NAME + " WHERE mailId = " + mailId); + if (items.isEmpty()) { + return; + } + + StringBuilder valuesBuilder = new StringBuilder(); + YamlConfiguration itemSection = new YamlConfiguration(); + for (ItemStack item : items) { + ItemConfigParser.setItemToSection(itemSection, item); + valuesBuilder.append("(").append(mailId).append(", '").append(itemSection.saveToString()).append("'), "); + } + valuesBuilder.delete(valuesBuilder.length() - 2, valuesBuilder.length()); + + String insert = "INSERT INTO " + NAME + " (mailId, item) VALUES " + valuesBuilder.toString(); + if (database.getDatabaseType() == DatabaseType.MYSQL) { + database.execute(insert + " ON DUPLICATE KEY UPDATE item = VALUES(item)"); + } else if (database.getDatabaseType() == DatabaseType.SQLITE) { + database.execute(insert + " ON CONFLICT(id) DO UPDATE SET item = execluded.item"); + } + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/MailDataDatabase.java b/src/main/java/org/bitbucket/ucchy/undine/database/MailDataDatabase.java new file mode 100644 index 0000000..6a22ba7 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/MailDataDatabase.java @@ -0,0 +1,833 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +import org.bitbucket.ucchy.undine.MailData; +import org.bitbucket.ucchy.undine.UndineMailer; +import org.bitbucket.ucchy.undine.group.SpecialGroupAll; +import org.bitbucket.ucchy.undine.sender.MailSender; +import org.bukkit.Location; +import org.bukkit.inventory.ItemStack; + +/** + * データベース管理のメールのデータ。 + * + * @author LazyGon + */ +public class MailDataDatabase extends MailData { + + private final Database database; + + private boolean isSent = false; + + /** + * コンストラクタ + * + * @param database + */ + MailDataDatabase(Database database, int index, boolean isSent) { + this.database = database; + if (isSent) { + markAsSentMail(index); + } else { + setIndex(index); + } + } + + /** + * データベースへ保存する + */ + @Override + public void save() { + // DO nothing. 常に同期されているため、不要。 + } + + @Override + public boolean isSent() { + return isSent; + } + + public void markAsSentMail(int newIndex) { + this.isSent = true; + setIndex(newIndex); + } + + /** + * このオブジェクトの複製を作成して返す。 + * + * @see java.lang.Object#clone() + */ + @Override + public MailDataDatabase clone() { + return new MailDataDatabase(database, index, isSent); + } + + /** + * 設定されている宛先を全て消去する + */ + @Override + public void deleteAllTo() { + if (isSent()) { + database.mailRecipientsTable.clearRecipient(index); + database.mailRecipientGroupsTable.clearGroup(index); + } else { + database.draftMailRecipientsTable.clearRecipient(index); + database.draftMailRecipientGroupsTable.clearGroup(index); + } + } + + /** + * このメールの宛先を取得します。 + * + * @return 宛先 + */ + @Override + public List getTo() { + List recipientIds; + if (isSent()) { + recipientIds = database.mailRecipientsTable.getRecipients(index); + } else { + recipientIds = database.draftMailRecipientsTable.getRecipients(index); + } + return new ArrayList<>(database.mailSenderTable.getByIds(recipientIds).values()); + } + + /** + * このメールの宛先を設定します。宛先番号は常に名前の並び順に整理されます。 + * + * @param line 宛先番号(0から始まることに注意) + * @param to 宛先 + */ + @Override + public void setTo(int line, MailSender to) { + if (isSent()) { + ArrayList recipients = new ArrayList<>( + database.mailSenderTable.getByIds(database.mailRecipientsTable.getRecipients(index)).values()); + recipients.sort((o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName())); + if (recipients.size() > line) { + database.mailRecipientsTable.removeRecipient(index, + database.mailSenderTable.getId(recipients.get(line))); + } + database.mailRecipientsTable.addRecipient(index, database.mailSenderTable.getId(to)); + + // 全体グループを除去しておく。 + database.mailRecipientGroupsTable.removeGroup(index, SpecialGroupAll.NAME); + } else { + ArrayList recipients = new ArrayList<>(database.mailSenderTable + .getByIds(database.draftMailRecipientsTable.getRecipients(index)).values()); + recipients.sort((o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName())); + if (recipients.size() > line) { + database.draftMailRecipientsTable.removeRecipient(index, + database.mailSenderTable.getId(recipients.get(line))); + } + database.draftMailRecipientsTable.addRecipient(index, database.mailSenderTable.getId(to)); + + // 全体グループを除去しておく。 + database.draftMailRecipientGroupsTable.removeGroup(index, SpecialGroupAll.NAME); + } + } + + /** + * このメールの宛先を追加します。 + * + * @param to 宛先 + */ + @Override + public void addTo(MailSender to) { + if (isSent()) { + database.mailRecipientsTable.addRecipient(index, database.mailSenderTable.getId(to)); + + // 全体グループは除去しておく。 + database.mailRecipientGroupsTable.removeGroup(index, + UndineMailer.getInstance().getGroupManager().getGroupAll().getName()); + } else { + database.draftMailRecipientsTable.addRecipient(index, database.mailSenderTable.getId(to)); + + // 全体グループは除去しておく。 + database.draftMailRecipientGroupsTable.removeGroup(index, + UndineMailer.getInstance().getGroupManager().getGroupAll().getName()); + } + } + + /** + * このメールの宛先を複数追加します。 + * + * @param to 宛先 + */ + @Override + public void addTo(List to) { + if (to.isEmpty()) { + return; + } + if (isSent()) { + database.mailRecipientsTable.addRecipients(index, database.mailSenderTable.getIds(to)); + + // 全体グループは除去しておく。 + database.mailRecipientGroupsTable.removeGroup(index, + UndineMailer.getInstance().getGroupManager().getGroupAll().getName()); + } else { + database.draftMailRecipientsTable.addRecipients(index, database.mailSenderTable.getIds(to)); + + // 全体グループは除去しておく。 + database.draftMailRecipientGroupsTable.removeGroup(index, + UndineMailer.getInstance().getGroupManager().getGroupAll().getName()); + } + } + + /** + * このメールの指定された宛先を削除します。 + * + * @param line 宛先番号(0から始まることに注意) + */ + @Override + public void deleteTo(int line) { + if (isSent()) { + ArrayList recipients = new ArrayList<>( + database.mailSenderTable.getByIds(database.mailRecipientsTable.getRecipients(index)).values()); + recipients.sort((o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName())); + if (recipients.size() > line) { + database.mailRecipientsTable.removeRecipient(index, + database.mailSenderTable.getId(recipients.get(line))); + } + } else { + ArrayList recipients = new ArrayList<>(database.mailSenderTable + .getByIds(database.draftMailRecipientsTable.getRecipients(index)).values()); + recipients.sort((o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName())); + if (recipients.size() > line) { + database.draftMailRecipientsTable.removeRecipient(index, + database.mailSenderTable.getId(recipients.get(line))); + } + } + } + + /** + * このメールの発信元を取得します。 + * + * @return 発信元 + */ + @Override + public MailSender getFrom() { + if (isSent()) { + return database.mailDataTable.getSenderById(index); + } else { + return database.draftMailDataTable.getSenderById(index); + } + } + + /** + * このメールの発信元を設定します。 + * + * @param from 発信元 + */ + @Override + public void setFrom(MailSender from) { + if (isSent()) { + database.mailDataTable.setSender(index, database.mailSenderTable.getId(from)); + } else { + database.draftMailDataTable.setSender(index, database.mailSenderTable.getId(from)); + } + } + + /** + * このメールのメッセージを取得します。 + * + * @return メッセージ + */ + @Override + public List getMessage() { + if (isSent()) { + String message = database.mailDataTable.getMessage(index); + return message == null ? new ArrayList<>() : new ArrayList<>(Arrays.asList(message.split("\r\n|\r|\n", -1))); + } else { + String message = database.draftMailDataTable.getMessage(index); + return message == null ? new ArrayList<>() : new ArrayList<>(Arrays.asList(message.split("\r\n|\r|\n", -1))); + } + } + + /** + * このメールのメッセージを設定します。 + * + * @param message メッセージ + */ + @Override + public void setMessage(List message) { + if (message.isEmpty()) { + if (isSent()) { + database.mailDataTable.setMessage(index, null); + } else { + database.draftMailDataTable.setMessage(index, null); + } + return; + } + + StringBuilder messageBuilder = new StringBuilder(); + for (int i = 0; i < message.size(); i++) { + messageBuilder.append(message.get(i)); + if (i == message.size() - 1) { + break; + } + messageBuilder.append("\n"); + } + if (isSent()) { + database.mailDataTable.setMessage(index, messageBuilder.toString()); + } else { + database.draftMailDataTable.setMessage(index, messageBuilder.toString()); + } + } + + /** + * このメールのメッセージを設定します。 + * + * @param line 行番号(0から始まることに注意) + * @param message メッセージ + */ + @Override + public void setMessage(int line, String message) { + List lines = getMessage(); + + while (lines.size() <= line) { + lines.add(""); + } + lines.set(line, message); + setMessage(lines); + } + + /** + * このメールのメッセージに、指定した内容を追加します。 + * + * @param message メッセージ + */ + @Override + public void addMessage(String message) { + List messages = getMessage(); + String[] lines = message.split("\r\n|\r|\n", -1); + for (String line : lines) { + messages.add(line); + } + setMessage(messages); + } + + /** + * このメールのメッセージの、指定された行番号を削除します。 + * + * @param line 宛先番号(0から始まることに注意) + */ + @Override + public void deleteMessage(int line) { + List message = getMessage(); + if (message.size() > line && line >= 0) { + message.remove(line); + } + setMessage(message); + } + + /** + * 宛先グループを取得します。 + * + * @return 宛先グループ + */ + @Override + public List getToGroups() { + if (isSent()) { + return database.mailRecipientGroupsTable.getGroups(index); + } else { + return database.draftMailRecipientGroupsTable.getGroups(index); + } + } + + /** + * このメールの宛先グループを設定します。 + * + * @param line 宛先番号(0から始まることに注意) + * @param group グループ + */ + @Override + public void setToGroup(int line, String group) { + if (isSent()) { + // 追加するグループが全体グループなら、 + // 他の宛先を全て削除する + if (SpecialGroupAll.NAME.equals(group)) { + database.mailRecipientsTable.clearRecipient(index); + database.mailRecipientGroupsTable.clearGroup(index); + database.mailRecipientGroupsTable.addGroup(index, group); + return; + } + + List groups = database.mailRecipientGroupsTable.getGroups(index); + groups.sort(String::compareToIgnoreCase); + if (groups.size() <= line) { + database.mailRecipientGroupsTable.addGroup(index, group); + } else if (groups.size() > line) { + database.mailRecipientGroupsTable.removeGroup(index, groups.get(line)); + database.mailRecipientGroupsTable.addGroup(index, group); + } + + // 全体グループが含まれていたなら、全体グループを除去する + if (groups.contains(SpecialGroupAll.NAME)) { + database.mailRecipientGroupsTable.removeGroup(index, SpecialGroupAll.NAME); + } + } else { + // 追加するグループが全体グループなら、 + // 他の宛先を全て削除する + if (SpecialGroupAll.NAME.equals(group)) { + database.draftMailRecipientsTable.clearRecipient(index); + database.draftMailRecipientGroupsTable.clearGroup(index); + database.draftMailRecipientGroupsTable.addGroup(index, group); + return; + } + + List groups = database.draftMailRecipientGroupsTable.getGroups(index); + groups.sort(String::compareToIgnoreCase); + if (groups.size() <= line) { + database.draftMailRecipientGroupsTable.addGroup(index, group); + } else if (groups.size() > line) { + database.draftMailRecipientGroupsTable.removeGroup(index, groups.get(line)); + database.draftMailRecipientGroupsTable.addGroup(index, group); + } + + // 全体グループが含まれていたなら、全体グループを除去する + if (groups.contains(SpecialGroupAll.NAME)) { + database.draftMailRecipientGroupsTable.removeGroup(index, SpecialGroupAll.NAME); + } + + } + } + + /** + * このメールの宛先グループの、指定された行番号を削除します。 + * + * @param line 宛先番号(0から始まることに注意) + */ + @Override + public void deleteToGroup(int line) { + if (isSent()) { + List groups = database.mailRecipientGroupsTable.getGroups(index); + groups.sort(String::compareToIgnoreCase); + if (groups.size() > line && line >= 0) { + String removal = groups.get(line); + database.mailRecipientGroupsTable.removeGroup(index, removal); + } + } else { + List groups = database.draftMailRecipientGroupsTable.getGroups(index); + groups.sort(String::compareToIgnoreCase); + if (groups.size() > line && line >= 0) { + String removal = groups.get(line); + database.draftMailRecipientGroupsTable.removeGroup(index, removal); + } + } + } + + /** + * 統合宛先を設定する + * + * @param total 統合宛先 + */ + @Override + protected void setToTotal(List total) { + // DO noting. 統合宛先は常にtoGroupとtoから計算される。 + } + + /** + * 統合宛先(宛先+宛先グループの和集合)を取得する。 + * + * @return 統合宛先 + */ + @Override + public List getToTotal() { + List toTotal = new ArrayList<>( + database.mailSenderTable.getByIds(database.groupMembersTable.getMemberIdsOf(getToGroups())).values()); + toTotal.addAll(getTo()); + return toTotal; + } + + /** + * このメールに添付されたアイテムを取得します。 + * + * @return 添付アイテム + */ + @Override + public List getAttachments() { + if (isSent()) { + return database.mailAttachmentBoxTable.getAttachmentBoxOf(index); + } else { + return database.draftMailAttachmentBoxTable.getAttachmentBoxOf(index); + } + } + + /** + * このメールの添付アイテムを設定します。 + * + * @param attachments 添付アイテム + */ + @Override + public void setAttachments(List attachments) { + if (isSent()) { + database.mailAttachmentBoxTable.setAttachmentBox(index, attachments); + } else { + database.draftMailAttachmentBoxTable.setAttachmentBox(index, attachments); + } + } + + /** + * 指定されたアイテムを添付アイテムに追加します。 + * + * @param item アイテム + */ + @Override + public void addAttachment(ItemStack item) { + List attachments = getAttachments(); + attachments.add(item); + setAttachments(attachments); + } + + /** + * このメールを読んだ人のリストを取得します。 + * + * @return 読んだ人のリスト + */ + @Override + public List getReadFlags() { + if (!isSent()) { + return new ArrayList<>(); + } + return new ArrayList<>( + database.mailSenderTable.getByIds(database.mailRecipientsTable.getWhoRead(index)).values()); + } + + /** + * このメールに削除フラグを付けた人のリストを取得します。 + * + * @return 削除フラグをつけている人のリスト + */ + @Override + public List getTrashFlags() { + if (!isSent()) { + return new ArrayList<>(); + } + return new ArrayList<>( + database.mailSenderTable.getByIds(database.mailRecipientsTable.getWhoTrash(index)).values()); + } + + /** + * このメールの添付アイテムを受け取るのに必要な金額を取得します。 + * + * @return 受け取り金額 + */ + @Override + public double getCostMoney() { + if (isSent()) { + return database.mailDataTable.getCostMoney(index); + } else { + return database.draftMailDataTable.getCostMoney(index); + } + } + + /** + * このメールの添付アイテムを受け取るのに必要な金額を設定します。 + * + * @param feeMoney 受け取り金額 + */ + @Override + public void setCostMoney(double feeMoney) { + if (isSent()) { + database.mailDataTable.setCostMoney(index, feeMoney); + } else { + database.draftMailDataTable.setCostMoney(index, feeMoney); + } + } + + /** + * このメールの添付アイテムを受け取るのに必要な引き換えアイテムを取得します。 + * + * @return 引き換えアイテム + */ + @Override + public ItemStack getCostItem() { + if (!isSent()) { + return database.mailDataTable.getCostItem(index); + } else { + return database.draftMailDataTable.getCostItem(index); + } + } + + /** + * このメールの添付アイテムを受け取るのに必要な引き換えアイテムを設定します。 + * + * @param feeItem 引き換えアイテム + */ + @Override + public void setCostItem(ItemStack feeItem) { + if (!isSent()) { + database.mailDataTable.setCostItem(index, feeItem); + } else { + database.draftMailDataTable.setCostItem(index, feeItem); + } + } + + /** + * このメールの送信時間を取得します。 + * + * @return 送信時間 + */ + @Override + public Date getDate() { + if (isSent()) { + return database.mailDataTable.getDate(index); + } + return null; + } + + /** + * このメールの送信時間を設定します(メール送信時に自動で割り当てられます)。 + * + * @param date 送信時間 + */ + @Override + protected void setDate(Date date) { + if (isSent()) { + database.mailDataTable.setDate(index, date); + } + } + + /** + * このメールが送信された地点を取得します。 + * + * @return 送信地点 + */ + @Override + public Location getLocation() { + if (isSent()) { + return database.mailDataTable.getLocation(index); + } + return null; + } + + /** + * このメールの送信地点を設定します(メール送信時に自動で割り当てられます)。 + * + * @param location 送信地点 + */ + @Override + public void setLocation(Location location) { + if (isSent()) { + database.mailDataTable.setLocation(index, location); + } + } + + /** + * メール送信時の添付アイテムを取得します。 + * + * @return メール送信時の添付アイテム + */ + @Override + public List getAttachmentsOriginal() { + if (isSent()) { + return database.mailAttachmentBoxSnapshotTable.getAttachmentBoxOf(index); + } else { + return new ArrayList<>(); + } + } + + /** + * attachmentOriginalに、添付ファイルのコピーを行います (メール送信時に自動で行われます)。 + */ + @Override + protected void makeAttachmentsOriginal() { + database.mailAttachmentBoxSnapshotTable.setAttachmentBox(index, getAttachments()); + } + + /** + * 指定したプレイヤーが、このメールを読んだかどうかを返します。 + * + * @param sender プレイヤー + * @return 読んだかどうか + */ + @Override + public boolean isRead(MailSender sender) { + if (!isSent()) { + return false; + } + return database.mailRecipientsTable.isRead(index, database.mailSenderTable.getId(sender)); + } + + /** + * 指定した名前のsenderの既読マークを付ける + * + * @param sender sender + */ + @Override + public void setReadFlag(MailSender sender) { + if (isSent()) { + database.mailRecipientsTable.setRead(index, database.mailSenderTable.getId(sender), true); + } + } + + /** + * 指定した人が、このメールに削除マークをつけているかどうかを返します。 + * + * @param sender + * @return 削除マークをつけているかどうか + */ + @Override + public boolean isSetTrash(MailSender sender) { + if (isSent()) { + return database.mailRecipientsTable.isTrash(index, database.mailSenderTable.getId(sender)); + } + return false; + } + + /** + * 指定した人の削除マークを付ける + * + * @param sender + */ + @Override + public void setTrashFlag(MailSender sender) { + if (isSent()) { + database.mailRecipientsTable.setTrash(index, database.mailSenderTable.getId(sender), true); + } + } + + /** + * 指定した人の削除マークを消す + * + * @param sender + */ + @Override + public void removeTrashFlag(MailSender sender) { + if (isSent()) { + database.mailRecipientsTable.setTrash(index, database.mailSenderTable.getId(sender), false); + } + } + + /** + * 指定された名前のプレイヤーは、このメールの関係者かどうかを返す。 + * + * @param sender sender + * @return 指定された名前がtoまたはfromに含まれるかどうか + */ + @Override + public boolean isRelatedWith(MailSender sender) { + return getFrom().equals(sender) || isAllMail() || getToTotal().contains(sender); + } + + /** + * 指定された名前のプレイヤーは、このメールの受信者かどうかを返す。 + * + * @param sender sender + * @return 指定された名前がtoに含まれるかどうか + */ + @Override + public boolean isRecipient(MailSender sender) { + return isAllMail() || getToTotal().contains(sender); + } + + /** + * このメールの添付アイテムがオープンされたのかどうかを返す + * + * @return 添付アイテムがオープンされたのかどうか + */ + @Override + public boolean isAttachmentsOpened() { + return database.mailDataTable.isAttachmentOpened(index); + } + + /** + * このメールの添付アイテムをオープンされたとして記録する。 受信者が添付ボックスを一度でも開いた事がある状態なら、 + * 送信者は添付アイテムをキャンセルすることができなくなる。 + */ + @Override + public void setOpenAttachments() { + database.mailDataTable.setAttachmentOpened(index, true); + } + + /** + * このメールの添付アイテムがキャンセルされたのかどうかを返す + * + * @return 添付アイテムがキャンセルされたのかどうか + */ + @Override + public boolean isAttachmentsCancelled() { + return database.mailDataTable.isAttachmentCancelled(index); + } + + /** + * このメールの添付アイテムをキャンセルする。 添付アイテムがキャンセルされると、受信者はボックスを開けなくなり、 + * 逆に送信者がボックスを開くことができるようになる。 + */ + @Override + public void cancelAttachments() { + database.mailDataTable.setAttachmentCancelled(index, true); + setCostItem(null); + setCostMoney(0); + } + + /** + * このメールの添付アイテムが拒否されたのかどうかを返す + * + * @return 添付アイテムが拒否されたのかどうか + */ + @Override + public boolean isAttachmentsRefused() { + return database.mailDataTable.isAttachmentRefused(index); + } + + /** + * 受取拒否の理由を取得します。 + * + * @return 受取拒否の理由(設定されていない場合はnullになることに注意すること) + */ + @Override + public String getAttachmentsRefusedReason() { + return database.mailDataTable.getAttachmentsRefusedReason(index); + } + + /** + * このメールの添付アイテムを拒否する。 添付アイテムが拒否されると、受信者はボックスを開けなくなり、 逆に送信者がボックスを開くことができるようになる。 + * + * @param attachmentsRefusedReason 拒否理由 + */ + @Override + public void refuseAttachments(String attachmentsRefusedReason) { + cancelAttachments(); + database.mailDataTable.setAttachmentRefused(index, true); + if (attachmentsRefusedReason != null && attachmentsRefusedReason.length() > 0) { + database.mailDataTable.setAttachmentsRefusedReason(index, attachmentsRefusedReason); + } + } + + /** + * データのアップグレードを行う。 + * + * @return アップグレードを実行したかどうか + */ + @Override + protected boolean upgrade() { + return false; + } + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof MailDataDatabase)) { + return false; + } + MailDataDatabase mailDataDatabase = (MailDataDatabase) o; + return super.equals(o) && isSent == mailDataDatabase.isSent; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), isSent); + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/MailDataTable.java b/src/main/java/org/bitbucket/ucchy/undine/database/MailDataTable.java new file mode 100644 index 0000000..ed57b87 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/MailDataTable.java @@ -0,0 +1,765 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import com.github.ucchyocean.itemconfig.ItemConfigParseException; +import com.github.ucchyocean.itemconfig.ItemConfigParser; + +import org.bitbucket.ucchy.undine.MailData; +import org.bitbucket.ucchy.undine.MailDataFlatFile; +import org.bitbucket.ucchy.undine.MailManager; +import org.bitbucket.ucchy.undine.Messages; +import org.bitbucket.ucchy.undine.Utility; +import org.bitbucket.ucchy.undine.sender.MailSender; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitRunnable; + +/** + * メールのデータを保持するメインテーブルにアクセスするクラス。 + * @author LazyGon + */ +public class MailDataTable { + + public static final String NAME = "undine_maildata"; + + private final Database database; + private final MailSenderTable mailSenderTable; + + private int checkedLatestId = 0; + + MailDataTable(Database database, MailSenderTable mailSenderTable) { + this.database = database; + this.mailSenderTable = mailSenderTable; + createTable(); + + // 他のサーバーから送信されたメールを確認して、存在する場合はチャットで通知する + checkedLatestId = getLastInsertedId(); + new BukkitRunnable(){ + @Override + public void run() { + int currentId = getLastInsertedId(); + while (checkedLatestId < currentId) { + checkedLatestId++; + notifyMail(checkedLatestId); + } + }; + }.runTaskTimer(database.parent, 100L, 200L); + } + + void createTable() { + database.execute( + "CREATE TABLE IF NOT EXISTS " + NAME + " (" + + "id INTEGER PRIMARY KEY " + database.getDatabaseType().autoIncrement + ", " + + "sender INTEGER NOT NULL, " + + "message TEXT(4096), " + + "costMoney DOUBLE NOT NULL DEFAULT 0, " + + "costItem TEXT(8192), " + + "isBulk TINYINT NOT NULL DEFAULT 0, " + + "isAttachmentsOpened TINYINT NOT NULL DEFAULT 0, " + + "isAttachmentsCancelled TINYINT NOT NULL DEFAULT 0, " + + "isAttachmentsRefused TINYINT NOT NULL DEFAULT 0, " + + "attachmentsRefusedReason TEXT(512), " + + "dateAndTime BIGINT NOT NULL, " + + "location VARCHAR(128), " + + "UNIQUE (id, sender), " + + "FOREIGN KEY (sender) REFERENCES " + MailSenderTable.NAME + "(id) ON DELETE CASCADE ON UPDATE CASCADE" + + ")" + ); + } + + /** + * 存在するメールのIDの中で最も大きいIDを取得する。事実上最新の送信されたメールのIDと等しい。 + * @return 最近送信されたメールのID + */ + public int getLastInsertedId() { + return database.query("SELECT MAX(id) AS maxId FROM " + NAME + "", rs -> { + try { + return rs.next() ? rs.getInt("maxId") : -1; + } catch (SQLException e) { + e.printStackTrace(); + return -1; + } + }); + } + + /** + * 編集中メールの内容を実際に送信する。送信された後編集中メールは削除される。 + * @param senderId メールの送信者 + * @return 新たにつけられたメールID + */ + public int newMail(int editId) { + List generatedKeys = database.insert("INSERT INTO " + NAME + " (sender, message, costMoney, costItem, dateAndTime) " + + "SELECT sender, message, costMoney, costItem, " + System.currentTimeMillis() + " FROM " + DraftMailDataTable.NAME + " WHERE id = " + editId); + int mailId = generatedKeys.isEmpty() ? -1 : generatedKeys.get(0); + if (mailId == -1) { + return -1; + } + database.execute("INSERT INTO " + MailAttachmentBoxTable.NAME + " (mailId, item) SELECT " + mailId + ", item FROM " + DraftMailAttachmentBoxTable.NAME + " WHERE mailId = " + editId); + database.execute("INSERT INTO " + MailRecipientsTable.NAME + " (mailId, recipient) SELECT " + mailId + ", recipient FROM " + DraftMailRecipientsTable.NAME + " WHERE mailId = " + editId); + database.execute("INSERT INTO " + MailRecipientGroupsTable.NAME + " (mailId, recipientGroup) SELECT " + mailId + ", recipientGroup FROM " + DraftMailRecipientGroupsTable.NAME + " WHERE mailId = " + editId); + + database.draftMailDataTable.removeMail(editId); + + // 他のサーバーから送信されたメールを確認して、存在する場合は現在のIDを更新する前にチャットで通知する + for (int mailIdFromOtherServer = checkedLatestId + 1; mailId > mailIdFromOtherServer; mailIdFromOtherServer++) { + notifyMail(mailIdFromOtherServer); + } + checkedLatestId = mailId; + + return mailId; + } + + /** + * メールが送信されたことを送信先に通知する。 + * @param mailId メールID + */ + private void notifyMail(int mailId) { + MailManager manager = database.parent.getMailManager(); + MailData mail = manager.getMail(mailId); + + // 宛先の人がログイン中なら知らせる + String msg = Messages.get("InformationYouGotMail", "%from", mail.getFrom().getName()); + + if (mail.isAllMail()) { + for (Player player : Utility.getOnlinePlayers()) { + player.sendMessage(msg); + String pre = Messages.get("ListVerticalParts"); + manager.sendMailLine(MailSender.getMailSender(player), pre, ChatColor.GOLD + mail.getInboxSummary(), mail); + } + } else { + for (MailSender to : mail.getToTotal()) { + if (to.isOnline()) { + to.sendMessage(msg); + String pre = Messages.get("ListVerticalParts"); + manager.sendMailLine(to, pre, ChatColor.GOLD + mail.getInboxSummary(), mail); + } + } + } + } + + /** + * 指定したIDのメールを削除する。 + * @param id メールのID + */ + public void removeMail(int id) { + database.execute("DELETE FROM " + NAME + " WHERE id = " + id); + } + + /** + * 複数指定したIDのメールをすべて削除する。 + * @param ids IDのリスト + */ + public void removeMails(List ids) { + if (!ids.isEmpty()) { + database.execute("DELETE FROM " + NAME + " WHERE id " + Database.createIn(ids)); + } + } + + /** + * すべてのメールのIDのリストを取得する。 + * @return すべてのメールのIDのリスト + */ + public ArrayList getIds() { + return database.query("SELECT id FROM " + NAME, rs -> { + try { + ArrayList ids = new ArrayList<>(); + while (rs.next()) { + ids.add(rs.getInt("id")); + } + return ids; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定した日数より前に送信されたメールをすべて削除する。 + * @param daysAgo + */ + public void removeMailsOlderThan(int daysAgo) { + database.execute("DELETE FROM " + NAME + " WHERE dateAndTime < " + (System.currentTimeMillis() - daysAgo * 24L * 60L * 60L * 1000L)); + } + + public ArrayList getIdsOlderThan(int daysAgo) { + return database.query("SELECT id FROM " + NAME + " WHERE dateAndTime < " + (System.currentTimeMillis() - daysAgo * 24L * 60L * 60L * 1000L), rs -> { + try { + ArrayList ids = new ArrayList<>(); + while (rs.next()) { + ids.add(rs.getInt("id")); + } + return ids; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定された送信先が閲覧できるメールをすべて取得する。 + * @param recipientId 送信先 + * @return メールのIDのリスト + */ + public ArrayList getIdsByRecipient(int recipientId) { + Set result = new HashSet<>(database.mailRecipientsTable.getMailIdsByRecipient(recipientId)); + List groupNames = database.groupMembersTable.getBelongingGroups(recipientId); + result.addAll(database.mailRecipientGroupsTable.getMailIdsByGroups(groupNames)); + return new ArrayList<>(result); + } + + /** + * 指定した送信者の送信したメールをすべて取得する。 + * @param senderId 送信者 + * @return メールのIDのリスト + */ + public ArrayList getIdsBySenderId(int senderId) { + ArrayList ids = new ArrayList<>(); + if (senderId == -1) { + return ids; + } + database.query("SELECT id FROM " + NAME + " WHERE sender = " + senderId, rs -> { + try { + while (rs.next()) { + ids.add(rs.getInt("id")); + } + return null; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + }); + + return ids; + } + + /** + * 指定したメールの送信者を設定する。 + * @param mailId メールのID + * @param senderId 送信者のID + */ + public void setSender(int mailId, int senderId) { + database.execute("UPDATE " + NAME + " SET sender = " + senderId + " WHERE id = " + mailId); + } + + public MailSender getSenderById(int id) { + int senderId = database.query("SELECT sender FROM " + NAME + " WHERE id = " + id, rs -> { + try { + if (rs.next()) { + return rs.getInt("sender"); + } + return -1; + } catch (SQLException e) { + e.printStackTrace(); + return -1; + } + }); + + return mailSenderTable.getById(senderId); + } + + /** + * 指定したメールの本文を取得する。 + * @param id メールのID + * @return 本文 + */ + public String getMessage(int id) { + return getString(id, "message"); + } + + /** + * 指定したメールの本文を設定する。 + * @param id メールのID + * @param message 本文 + */ + public void setMessage(int id, String message) { + setString(id, "message", message); + } + + /** + * 添付アイテムボックスが拒否された理由を取得する。値がない場合は空文字列を返す。 + * @param id メールのID + * @return 添付アイテムボックスが拒否された理由 + */ + public String getAttachmentsRefusedReason(int id) { + String refusedReason = getString(id, "attachmentsRefusedReason"); + return refusedReason == null ? "" : refusedReason; + } + + /** + * 添付アイテムボックスが拒否された理由を設定する。 + * @param id メールのID + * @param attachmentsRefusedReason 添付アイテムボックスが拒否された理由 + */ + public void setAttachmentsRefusedReason(int id, String attachmentsRefusedReason) { + setString(id, "attachmentsRefusedReason", attachmentsRefusedReason == null ? "" : attachmentsRefusedReason); + } + + /** + * 指定したカラムから文字列を取得する。 + * @param id メールのID + * @param column 取得するカラムの名前 + * @return カラムに収められていた文字列データ + */ + public String getString(int id, String column) { + return database.query("SELECT " + column + " FROM " + NAME + " WHERE id = " + id, rs -> { + try { + String value; + if (rs.next() && (value = rs.getString(column)) != null) { + return value.replace("\\'", "'"); + } + return null; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + }); + } + + /** + * 指定したカラムの文字列データを設定する。 + * @param id メールのID + * @param column カラム + * @param value 値 + */ + public void setString(int id, String column, String value) { + database.execute("UPDATE " + NAME + " SET " + column + " = " + (value == null ? "NULL" : "'" + value.replace("'", "\\'") + "'") + " WHERE id = " + id); + } + + /** + * 指定されたメールの添付アイテムボックスを開くのに必要な金額を取得する。 + * @param id メールのID + * @return 金額 + */ + public double getCostMoney(int id) { + return database.query("SELECT costMoney FROM " + NAME + " WHERE id = " + id, rs -> { + try { + if (rs.next()) { + return rs.getDouble("costMoney"); + } + return 0D; + } catch (SQLException e) { + e.printStackTrace(); + return 0D; + } + }); + } + + /** + * 指定されたメールの添付アイテムボックスを開くのに必要な金額を設定する。 + * @param id メールのID + * @param money 金額 + */ + public void setCostMoney(int id, double money) { + database.execute("UPDATE " + NAME + " SET costMoney = " + (Math.floor(money * 10) / 10) + " WHERE id = " + id); + } + + /** + * 指定されたメールの添付アイテムボックスを開くのに必要なアイテムを取得する。 + * @param id メールのID + * @return アイテム + */ + public ItemStack getCostItem(int id) { + return database.query("SELECT costItem FROM " + NAME + " WHERE id = " + id, rs -> { + try { + String itemStr; + if (!rs.next() || (itemStr = rs.getString("costItem")) == null) { + return null; + } + itemStr = itemStr.replace("\\'", "'"); + YamlConfiguration itemSection = new YamlConfiguration(); + itemSection.loadFromString(itemStr); + return ItemConfigParser.getItemFromSection(itemSection); + } catch (SQLException | InvalidConfigurationException | ItemConfigParseException e) { + e.printStackTrace(); + return null; + } + }); + } + + /** + * 指定されたメールの添付アイテムボックスを開くのに必要なアイテムを設定する。 + * @param id メールのID + * @param item アイテム + * @return 設定されたか + */ + public boolean setCostItem(int id, ItemStack item) { + if (item == null) { + return database.execute("UPDATE " + NAME + " SET costItem = NULL WHERE id = " + id); + } + YamlConfiguration itemSection = new YamlConfiguration(); + ItemConfigParser.setItemToSection(itemSection, item); + String itemStr = itemSection.saveToString(); + return database.execute("UPDATE " + NAME + " SET costItem = '" + itemStr.replace("'", "\\'") + "' WHERE id = " + id); + } + + /** + * 指定したIDのメールがスパムかどうか取得する。TODO: 実装 + * @deprecated まだスパム機能は実装されておらず、このメソッドはどこにも使われていない。 + * @param id メールのID + * @return 指定したIDのメールがスパムかどうか + */ + @Deprecated + public boolean isBulk(int id) { + return getBool(id, "isBulk"); + } + + /** + * 指定したIDのメールがスパムかどうかを設定する。 + * @deprecated まだスパム機能は実装されておらず、このメソッドはどこにも使われていない。 + * @param id メールのID + * @param isBulk 指定したIDのメールがスパムかどうか + */ + public void setBulk(int id, boolean isBulk) { + setBool(id, "isBulk", isBulk); + } + + /** + * 指定したIDのメールの添付アイテムボックスが開かれたかどうかを取得する。 + * @param id メールのID + * @return 添付アイテムボックスが開かれたかどうか + */ + public boolean isAttachmentOpened(int id) { + return getBool(id, "isAttachmentsOpened"); + } + + /** + * 指定したIDのメールの添付アイテムボックスが開かれたかどうかを設定する。 + * @param id メールのID + * @param isAttachmentOpened 添付アイテムボックスが開かれたかどうか + */ + public void setAttachmentOpened(int id, boolean isAttachmentOpened) { + setBool(id, "isAttachmentsOpened", isAttachmentOpened); + } + + /** + * 指定したIDのメールの添付アイテムボックスがキャンセルされたかどうかを取得する。 + * @param id メールのID + * @return 添付アイテムボックスがキャンセルされたかどうか + */ + public boolean isAttachmentCancelled(int id) { + return getBool(id, "isAttachmentsCancelled"); + } + + /** + * 指定したIDのメールの添付アイテムボックスがキャンセルされたかどうかを設定する。 + * @param id メールのID + * @param isAttachmentCancelled 添付アイテムボックスがキャンセルされたか + */ + public void setAttachmentCancelled(int id, boolean isAttachmentCancelled) { + setBool(id, "isAttachmentCancelled", isAttachmentCancelled); + } + + /** + * 指定したIDのメールの添付アイテムボックスが拒否されたかどうかを取得する。 + * @param id メールのID + * @return 添付アイテムボックスが拒否されたかどうか + */ + public boolean isAttachmentRefused(int id) { + return getBool(id, "isAttachmentsRefused"); + } + + /** + * 指定したIDのメールの添付アイテムボックスが拒否されたかどうかを設定する。 + * @param id メールのID + * @param isAttachmentRefused 添付アイテムボックスが拒否されたかどうか + */ + public void setAttachmentRefused(int id, boolean isAttachmentRefused) { + setBool(id, "isAttachmentsRefused", isAttachmentRefused); + } + + /** + * 指定したメールの指定したカラムの真偽値データを取得する。実際のデータベースにはByte値が格納されている。 + * @param id メールのID + * @param column カラムの名前 + * @return 真偽値データ + */ + private boolean getBool(int id, String column) { + return database.query("SELECT " + column + " FROM " + NAME + " WHERE id = " + id, rs -> { + try { + if (rs.next()) { + return rs.getByte(column) == (byte)1; + } + return false; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + }); + } + + /** + * 指定したメールの指定したカラムの真偽値データを設定する。実際のデータベースにはByte値が格納されている。 + * @param id メールのID + * @param column カラムの名前 + * @param value 値 + */ + private void setBool(int id, String column, boolean value) { + database.execute("UPDATE " + NAME + " SET " + column + " = " + (value ? 1 : 0) + " WHERE id = " + id); + } + + /** + * 指定したIDのメールが送信された日時を取得する。 + * @param id メールのID + * @return 日時 + */ + public Date getDate(int id) { + long unix = database.query("SELECT dateAndTime FROM " + NAME + " WHERE id = " + id, rs -> { + try { + if (rs.next()) { + return rs.getLong("dateAndTime"); + } + return 0L; + } catch (SQLException e) { + e.printStackTrace(); + return 0L; + } + }); + + return new Date(unix); + } + + /** + * 指定したIDのメールが送信された日時を設定する。 + * @param id メールのID + * @param date 日時 + */ + public void setDate(int id, Date date) { + database.execute("UPDATE " + NAME + " SET dateAndTime = " + date.getTime() + " WHERE id = " + id); + } + + /** + * 指定したIDのメールの送信された場所を取得する。コンソールが送信したメールの場合はnullを返す。 + * @param id メールのID + * @return 送信場所 + */ + public Location getLocation(int id) { + String loc = database.query("SELECT location FROM " + NAME + " WHERE id = " + id, rs -> { + try { + if (rs.next()) { + return rs.getString("location"); + } + return null; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + }); + + return loc == null || loc.isBlank() ? null : Database.fromDBLocationString(loc); + } + + /** + * 指定されたIDのメールが送信された場所を設定する。 + * @param id メールのID + * @param location 送信場所 + */ + public void setLocation(int id, Location location) { + if (location == null) { + database.execute("UPDATE " + NAME + " SET location = NULL WHERE id = " + id); + } else { + database.execute("UPDATE " + NAME + " SET location = '" + Database.createDBLocationString(location) + "' WHERE id = " + id); + } + } + + /** + * 指定されたIDのメールがデータベースに存在するかを調べる。 + * @param id メールのID + * @return 存在する場合はtrue + */ + public boolean exists(int id) { + return database.query("SELECT id FROM " + NAME + " WHERE id = " + id, rs -> { + try { + return rs.next(); + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + }); + } + + /** + * 指定されたメールIDのリストから、存在しないメールをすべて削除する。 + * @param ids メールIDのリスト + */ + public void retainExists(List ids) { + if (ids.isEmpty()) { + return; + } + database.query("SELECT id FROM " + NAME + " WHERE id " + Database.createIn(ids), rs -> { + try { + List found = new ArrayList<>(); + while (rs.next()) { + found.add(rs.getInt("id")); + } + ids.retainAll(found); + return null; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + }); + } + + /** + * 指定したidを持つメールをデータベースから取得し、フラットファイル管理のクラスに変換して返す。 + * @param ids 取得するメールのidのリスト + * @return メールデータ + */ + public List getMailDataFlatFileById(List ids) { + if (ids.isEmpty()) { + return new ArrayList<>(); + } + List mailIds = new ArrayList<>(ids); + retainExists(mailIds); + + String inIds = Database.createIn(mailIds); + + Map mailData = new HashMap<>(); + Map sender = new HashMap<>(); + Map> to = new HashMap<>(); + Map> whoRead = new HashMap<>(); + Map> whoTrash = new HashMap<>(); + for (int id : mailIds) { + to.put(id, new ArrayList<>()); + whoRead.put(id, new ArrayList<>()); + whoTrash.put(id, new ArrayList<>()); + } + + database.query( + "SELECT " + + "id, " + + "sender, " + + "message, " + + "costMoney, " + + "costItem, " + + "isAttachmentsOpened, " + + "isAttachmentsCancelled, " + + "isAttachmentsRefused, " + + "attachmentsRefusedReason, " + + "dateAndTime, " + + "location " + + "FROM " + NAME + " WHERE id " + inIds, + rs -> { + try { + while (rs.next()) { + int mailId = rs.getInt("id"); + MailDataFlatFile data = new MailDataFlatFile(); + mailData.put(mailId, data); + data.setIndex(mailId); + data.addMessage(rs.getString("message")); + data.setCostMoney(rs.getDouble("costMoney")); + if (rs.getByte("isAttachmentsOpened") == (byte)1) { + data.setOpenAttachments(); + } + if (rs.getByte("isAttachmentsCancelled") == (byte)1) { + data.cancelAttachments(); + } + if (rs.getByte("isAttachmentsRefused") == (byte)1) { + String refuseReason = rs.getString("attachmentsRefusedReason"); + data.refuseAttachments(refuseReason == null ? "" : refuseReason); + } + data.setLocation(Database.fromDBLocationString(rs.getString("location"))); + data.setDate(new Date(rs.getLong("dateAndTime"))); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + }); + + database.query("SELECT mailId, item FROM " + MailAttachmentBoxTable.NAME + " WHERE mailId " + inIds, rs -> { + try { + YamlConfiguration itemSection = new YamlConfiguration(); + while (rs.next()) { + itemSection.loadFromString(rs.getString("item").replace("\\'", "'")); + mailData.get(rs.getInt("mailId")).getAttachments().add(ItemConfigParser.getItemFromSection(itemSection)); + } + } catch (SQLException | InvalidConfigurationException | ItemConfigParseException e) { + e.printStackTrace(); + } + return null; + }); + + database.query("SELECT mailId, item FROM " + MailAttachmentBoxSnapshotTable.NAME + " WHERE mailId " + inIds, rs -> { + try { + YamlConfiguration itemSection = new YamlConfiguration(); + while (rs.next()) { + itemSection.loadFromString(rs.getString("item").replace("\\'", "'")); + mailData.get(rs.getInt("mailId")).getAttachmentsOriginal().add(ItemConfigParser.getItemFromSection(itemSection)); + } + } catch (SQLException | InvalidConfigurationException | ItemConfigParseException e) { + e.printStackTrace(); + } + return null; + }); + + database.query("SELECT mailId, recipientGroup FROM " + MailRecipientGroupsTable.NAME + " WHERE mailId " + inIds, rs -> { + try { + while (rs.next()) { + mailData.get(rs.getInt("mailId")).addToGroup(rs.getString("recipientGroup")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + }); + + database.query("SELECT mailId, recipient, isRead, isTrash FROM " + MailRecipientsTable.NAME + " WHERE mailId " + inIds, rs -> { + try { + while (rs.next()) { + int mailId = rs.getInt("mailId"); + int recipientId = rs.getInt("recipient"); + to.get(mailId).add(recipientId); + if (rs.getByte("isRead") == (byte)1) { + whoRead.get(mailId).add(recipientId); + } + if (rs.getByte("isTrash") == (byte)1) { + whoTrash.get(mailId).add(recipientId); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + }); + + Set mailSenderIds = to.values().stream().flatMap(List::stream).collect(Collectors.toSet()); + mailSenderIds.addAll(sender.values()); + Map converted = mailSenderTable.getByIds(mailSenderIds); + + mailData.forEach((id, data) -> { + data.setFrom(converted.get(sender.get(id))); + for (int recipientId : to.get(id)) { + data.addTo(converted.get(recipientId)); + } + for (int idWhoRead : whoRead.get(id)) { + data.setReadFlag(converted.get(idWhoRead)); + } + for (int idWhoTrash : whoTrash.get(id)) { + data.setTrashFlag(converted.get(idWhoTrash)); + } + }); + + return new ArrayList<>(mailData.values()); + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/MailManagerDatabase.java b/src/main/java/org/bitbucket/ucchy/undine/database/MailManagerDatabase.java new file mode 100644 index 0000000..050c420 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/MailManagerDatabase.java @@ -0,0 +1,364 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.bitbucket.ucchy.undine.MailData; +import org.bitbucket.ucchy.undine.MailManager; +import org.bitbucket.ucchy.undine.Messages; +import org.bitbucket.ucchy.undine.UndineMailer; +import org.bitbucket.ucchy.undine.Utility; +import org.bitbucket.ucchy.undine.sender.MailSender; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +/** + * データベース管理のメール管理クラス。このパッケージ外からデータベースにアクセスする場合はこのクラスを通す。 + * @author LazyGon + */ +public class MailManagerDatabase extends MailManager { + + private final Database database; + + /** + * コンストラクタ + */ + public MailManagerDatabase(UndineMailer parent) { + super(parent); + this.database = parent.getDatabase(); + } + + /** + * 逐一データベースからメールデータを取得するため、リロードしない。 + * + * @param sender 通知先。実際は使われない。 + */ + @Override + protected void reload(final CommandSender sender) { + } + + /** + * メールデータがロード完了したかどうか。 + * @return 常にtrue + */ + @Override + public boolean isLoaded() { + return true; + } + + /** + * 指定されたインデクスのメールを取得する + * + * @param index インデクス + * @return メールデータ + */ + @Override + public MailDataDatabase getMail(int index) { + if (database.mailDataTable.exists(index)) { + return new MailDataDatabase(database, index, true); + } + return null; + } + + /** + * 新しいメールを送信する + * + * @param mail メール + */ + @Override + public void sendNewMail(MailData mail) { + if (!(mail instanceof MailDataDatabase) || mail.isSent()) { + return; + } + MailDataDatabase dbMail = (MailDataDatabase) mail; + + // メールデータの本文が1行も無いときは、ここで1行追加を行う。 + if (dbMail.getMessage().size() == 0) { + dbMail.addMessage(""); + } + + // メールを送信する。 + dbMail.markAsSentMail(database.mailDataTable.newMail(dbMail.getIndex())); + + // 送信時間を設定する + dbMail.setDate(new Date()); + + // 送信地点を設定する + dbMail.setLocation(dbMail.getFrom().getLocation()); + + // オリジナルの添付ファイルを記録する + dbMail.makeAttachmentsOriginal(); + + // 添付が無いなら、着払い設定はクリアしておく + if (dbMail.getAttachments().size() == 0) { + dbMail.setCostMoney(0); + dbMail.setCostItem(null); + } + + // 着払いアイテムが設定されているなら、着払い料金はクリアしておく + if (dbMail.getCostItem() != null) { + dbMail.setCostMoney(0); + } + + // 着払い料金が無効なら、着払い料金はクリアしておく + if (!parent.getUndineConfig().isEnableCODMoney()) { + dbMail.setCostMoney(0); + } + + // 着払いアイテム無効なら、着払いアイテムはクリアしておく + if (!parent.getUndineConfig().isEnableCODItem()) { + dbMail.setCostItem(null); + } + + // 宛先の人がログイン中なら知らせる + String msg = Messages.get("InformationYouGotMail", "%from", dbMail.getFrom().getName()); + + if (dbMail.isAllMail()) { + for (Player player : Utility.getOnlinePlayers()) { + player.sendMessage(msg); + String pre = Messages.get("ListVerticalParts"); + sendMailLine(MailSender.getMailSender(player), pre, ChatColor.GOLD + dbMail.getInboxSummary(), dbMail); + } + } else { + for (MailSender to : dbMail.getToTotal()) { + if (to.isOnline()) { + to.sendMessage(msg); + String pre = Messages.get("ListVerticalParts"); + sendMailLine(to, pre, ChatColor.GOLD + dbMail.getInboxSummary(), dbMail); + } + } + } + + // 送った時刻を、メタデータに記録する + long time = System.currentTimeMillis(); + dbMail.getFrom().setStringMetadata(SENDTIME_METAKEY, time + ""); + } + + /** + * 受信したメールのリストを取得する + * + * @param sender 取得する対象 + * @return メールのリスト + */ + @Override + public ArrayList getInboxMails(MailSender sender) { + ArrayList box = new ArrayList(); + int senderId = database.mailSenderTable.getId(sender); + List mails = database.mailDataTable.getIdsByRecipient(senderId); + database.mailRecipientsTable.isTrashAll(mails, senderId).forEach((id, isTrash) -> { + if (isTrash) { + mails.remove(id); + } + }); + + for (int id : mails) { + box.add(new MailDataDatabase(database, id, true)); + } + sortNewer(box); + return box; + } + + /** + * 受信したメールで未読のリストを取得する + * + * @param sender 取得する対象 + * @return メールのリスト + */ + @Override + public ArrayList getUnreadMails(MailSender sender) { + ArrayList box = new ArrayList(); + int senderId = database.mailSenderTable.getId(sender); + List mails = database.mailDataTable.getIdsByRecipient(senderId); + database.mailRecipientsTable.isReadAll(mails, senderId).forEach((id, isRead) -> { + if (isRead) { + mails.remove(id); + } + }); + + for (int id : mails) { + box.add(new MailDataDatabase(database, id, true)); + } + sortNewer(box); + return box; + } + + /** + * 送信したメールのリストを取得する + * + * @param sender 取得する対象 + * @return メールのリスト + */ + @Override + public ArrayList getOutboxMails(MailSender sender) { + ArrayList box = new ArrayList(); + int senderId = database.mailSenderTable.getId(sender); + List mails = database.mailDataTable.getIdsBySenderId(senderId); + database.mailRecipientsTable.isTrashAll(mails, senderId).forEach((id, isTrash) -> { + if (isTrash) { + mails.remove(id); + } + }); + for (int id : mails) { + box.add(new MailDataDatabase(database, id, true)); + } + sortNewer(box); + return box; + } + + /** + * 関連メールのリストを取得する + * + * @param sender 取得する対象 + * @return メールのリスト + */ + @Override + public ArrayList getRelatedMails(MailSender sender) { + ArrayList box = new ArrayList(); + int senderId = database.mailSenderTable.getId(sender); + List mails = database.mailDataTable.getIdsByRecipient(senderId); + mails.addAll(database.mailDataTable.getIdsBySenderId(senderId)); + database.mailRecipientsTable.isTrashAll(mails, senderId).forEach((id, isTrash) -> { + if (isTrash) { + mails.remove(id); + } + }); + database.mailRecipientsTable.isReadAll(mails, senderId).forEach((id, isRead) -> { + if (!isRead) { + mails.remove(id); + } + }); + + for (int id : mails) { + box.add(new MailDataDatabase(database, id, true)); + } + sortNewer(box); + return box; + } + + /** + * ゴミ箱フォルダのメールリストを取得する + * + * @param sender 取得する対象 + * @return メールのリスト + */ + @Override + public ArrayList getTrashboxMails(MailSender sender) { + ArrayList box = new ArrayList(); + int senderId = database.mailSenderTable.getId(sender); + List mails = database.mailDataTable.getIdsByRecipient(senderId); + mails.addAll(database.mailDataTable.getIdsBySenderId(senderId)); + database.mailRecipientsTable.isTrashAll(mails, senderId).forEach((id, isTrash) -> { + if (!isTrash) { + mails.remove(id); + } + }); + + for (int id : mails) { + box.add(new MailDataDatabase(database, id, true)); + } + sortNewer(box); + return box; + } + + /** + * 指定されたメールデータをUndineに保存する + * + * @param mail メールデータ + */ + @Override + public void saveMail(MailData mail) { + // Do nothing. 常に同期されているため、不要。 + } + + /** + * 指定されたインデクスのメールを削除する + * + * @param index インデクス + */ + @Override + public void deleteMail(int index) { + database.mailDataTable.removeMail(index); + } + + /** + * 古いメールを削除する + */ + @Override + protected void cleanup() { + database.mailDataTable.removeMailsOlderThan(parent.getUndineConfig().getMailStorageTermDays()); + } + + /** + * 編集中メールを作成して返す + * + * @param sender 取得対象のsender + * @return 編集中メール + */ + @Override + public MailData makeEditmodeMail(MailSender sender) { + MailData mail = getEditmodeMail(sender); + if (mail == null) { + mail = new MailDataDatabase( + database, + database.draftMailDataTable.newMail(database.mailSenderTable.getId(sender)), + false + ); + } + return mail; + } + + /** + * 編集中メールを取得する + * + * @param sender 取得対象のsender + * @return 編集中メール(編集中でないならnull) + */ + @Override + public MailData getEditmodeMail(MailSender sender) { + List editmodeMails = database.draftMailDataTable.getIdsBySenderId(database.mailSenderTable.getId(sender)); + return editmodeMails.isEmpty() ? null : new MailDataDatabase(database, editmodeMails.get(0), false); + } + + /** + * 編集中メールを削除する + * + * @param sender 削除対象のsender + */ + @Override + public void clearEditmodeMail(MailSender sender) { + database.draftMailDataTable.removeMails(database.draftMailDataTable.getIdsBySenderId(database.mailSenderTable.getId(sender))); + } + + /** + * 編集中メールを保存する + */ + @Override + protected void storeEditmodeMail() { + // Do nothing. 常時同期されているため、不要。 + } + + /** + * editmails.ymlから編集中メールを復帰する + */ + @Override + protected void restoreEditmodeMail() { + // Do nothing. 常時同期されているため、不要。 + } + + /** + * 指定したsenderが使用中の添付ボックスの個数を返す + * @param sender + * @return 使用中添付ボックスの個数 + */ + @Override + public int getAttachBoxUsageCount(MailSender sender) { + return database.mailAttachmentBoxTable.getAttachBoxUsageCount(database.mailSenderTable.getId(sender)); + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/MailRecipientGroupsTable.java b/src/main/java/org/bitbucket/ucchy/undine/database/MailRecipientGroupsTable.java new file mode 100644 index 0000000..f9ab8d9 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/MailRecipientGroupsTable.java @@ -0,0 +1,140 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.bitbucket.ucchy.undine.database.Database.DatabaseType; + +/** + * メールの宛先グループを保持するテーブルにアクセスするクラス。 + * @author LazyGon + */ +public class MailRecipientGroupsTable { + + public static final String NAME = "undine_mailrecipientgroups"; + + private final Database database; + @SuppressWarnings("unused") + private final MailDataTable mailDataTable; + @SuppressWarnings("unused") + private final GroupDataTable groupDataTable; + + MailRecipientGroupsTable(Database database, MailDataTable mailDataTable, GroupDataTable groupDataTable) { + this.database = database; + this.mailDataTable = mailDataTable; + this.groupDataTable = groupDataTable; + createTable(); + } + + void createTable() { + database.execute( + "CREATE TABLE IF NOT EXISTS " + NAME + " (" + + "mailId INTEGER NOT NULL, " + + "recipientGroup VARCHAR(64) NOT NULL" + (database.getDatabaseType() == DatabaseType.SQLITE ? " COLLATE NOCASE" : "") + ", " + + "PRIMARY KEY (mailId, recipientGroup), " + + "FOREIGN KEY (mailId) REFERENCES " + MailDataTable.NAME + "(id) ON DELETE CASCADE ON UPDATE CASCADE, " + + "FOREIGN KEY (recipientGroup) REFERENCES " + GroupDataTable.NAME + "(name) ON DELETE CASCADE ON UPDATE CASCADE" + + ")" + ); + } + + /** + * 指定したグループを含むメールのidをすべて取得する。 + * @param groupName グループ + * @return メールIDのリスト + */ + public ArrayList getMailIdsByGroup(String groupName) { + return database.query("SELECT mailId FROM " + NAME + " WHERE recipientGroup = '" + groupName + "'", rs -> { + try { + ArrayList ids = new ArrayList<>(); + while (rs.next()) { + ids.add(rs.getInt("mailId")); + } + return ids; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定したgroupのどれか一つでも含むメールのidをすべて取得する。 + * @param groupNames グループIDのリスト + * @return メールIDのリスト + */ + public ArrayList getMailIdsByGroups(List groupNames) { + if (groupNames.isEmpty()) { + return new ArrayList<>(); + } + return database.query( + "SELECT mailId FROM " + NAME + " WHERE recipientGroup " + Database.createIn(groupNames), rs -> { + try { + ArrayList ids = new ArrayList<>(); + while (rs.next()) { + ids.add(rs.getInt("mailId")); + } + return ids; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定されたIDのメールが宛先としているグループをすべて取得する。 + * @param mailId メールのID + * @return グループのリスト + */ + public ArrayList getGroups(int mailId) { + return database.query("SELECT recipientGroup FROM " + NAME + " WHERE mailId = " + mailId, rs -> { + try { + ArrayList groupNames = new ArrayList<>(); + while (rs.next()) { + groupNames.add(rs.getString("recipientGroup")); + } + return groupNames; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定されたグループを指定されたIDのメールの宛先に追加する。 + * @param mailId メールのID + * @param groupName グループ + */ + public void addGroup(int mailId, String groupName) { + if (database.getDatabaseType() == DatabaseType.MYSQL) { + database.execute("INSERT INTO " + NAME + " (mailId, recipientGroup) VALUES (" + mailId + ", '" + groupName + "') ON DUPLICATE KEY UPDATE recipientGroup = recipientGroup"); + } else if (database.getDatabaseType() == DatabaseType.SQLITE) { + database.execute("INSERT INTO " + NAME + " (mailId, recipientGroup) VALUES (" + mailId + ", '" + groupName + "') ON CONFLICT(mailId, recipientGroup) DO NOTHING"); + } + } + + /** + * 指定されたグループを指定されたIDのメールの宛先から削除する。 + * @param mailId メールのID + * @param groupName グループ + */ + public void removeGroup(int mailId, String groupName) { + database.execute("DELETE FROM " + NAME + " WHERE mailId = " + mailId + " AND recipientGroup = '" + groupName + "'"); + } + + /** + * 指定されたIDのメールの宛先グループをすべて削除する。 + * @param mailId メールのID + */ + public void clearGroup(int mailId) { + database.execute("DELETE FROM " + NAME + " WHERE mailId = " + mailId); + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/MailRecipientsTable.java b/src/main/java/org/bitbucket/ucchy/undine/database/MailRecipientsTable.java new file mode 100644 index 0000000..2e71f9d --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/MailRecipientsTable.java @@ -0,0 +1,396 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.bitbucket.ucchy.undine.database.Database.DatabaseType; + +/** + * メールの宛先を保持するテーブルにアクセスするクラス。 + * @author LazyGon + */ +public class MailRecipientsTable { + + public static final String NAME = "undine_mailrecipients"; + + private final Database database; + private final MailDataTable mailDataTable; + @SuppressWarnings("unused") + private final MailSenderTable mailSenderTable; + + MailRecipientsTable(Database database, MailSenderTable mailSenderTable, MailDataTable mailDataTable) { + this.database = database; + this.mailSenderTable = mailSenderTable; + this.mailDataTable = mailDataTable; + createTable(); + } + + void createTable() { + database.execute( + "CREATE TABLE IF NOT EXISTS " + NAME + " (" + + "mailId INTEGER NOT NULL, " + + "recipient INTEGER NOT NULL, " + + "isRead TINYINT NOT NULL DEFAULT 0, " + + "isTrash TINYINT NOT NULL DEFAULT 0, " + + "PRIMARY KEY (mailId, recipient), " + + "FOREIGN KEY (mailId) REFERENCES " + MailDataTable.NAME + "(id) ON DELETE CASCADE ON UPDATE CASCADE, " + + "FOREIGN KEY (recipient) REFERENCES " + MailSenderTable.NAME + "(id) ON DELETE CASCADE ON UPDATE CASCADE" + + ")" + ); + } + + /** + * 指定された受信者を宛先に含むメールのIDを全て取得する。 + * @param recipientId 受信者 + * @return メールのIDのリスト + */ + public ArrayList getMailIdsByRecipient(int recipientId) { + if (recipientId == -1) { + return new ArrayList<>(); + } + return database.query("SELECT mailId FROM " + NAME + " WHERE recipient = " + recipientId, rs -> { + try { + ArrayList mailIds = new ArrayList<>(); + while (rs.next()) { + mailIds.add(rs.getInt("mailId")); + } + return mailIds; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定されたIDのメールの受信者をすべて取得する。 + * @param mailId メールのID + * @return 受信者のリスト + */ + public ArrayList getRecipients(int mailId) { + return getRecipientsWhere("WHERE mailId = " + mailId); + } + + /** + * 指定されたWHERE句で受信者を取得する。 + * @param where SQL文のWHERE句部分 + * @return 条件に当てはまった受信者 + */ + public ArrayList getRecipientsWhere(String where) { + return database.query("SELECT recipient FROM " + NAME + (where == null || where.isEmpty() ? "" : " " + where), rs -> { + try { + ArrayList mailIds = new ArrayList<>(); + while (rs.next()) { + mailIds.add(rs.getInt("recipient")); + } + return mailIds; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * 指定されたすべての受信者を、指定されたIDのメールの宛先に追加する。 + * @param mailId メールのID + * @param recipientIds 受信者のリスト + */ + public void addRecipients(int mailId, List recipientIds) { + if (recipientIds.isEmpty()) { + return; + } + StringBuilder valuesBuilder = new StringBuilder(); + for (int recipientId : recipientIds) { + valuesBuilder.append("(").append(mailId).append(", ").append(recipientId).append("), "); + } + valuesBuilder.delete(valuesBuilder.length() - 2, valuesBuilder.length()); + + if (database.getDatabaseType() == DatabaseType.MYSQL) { + database.execute("INSERT INTO " + NAME + " (mailId, recipient) VALUES " + valuesBuilder.toString() + " ON DUPLICATE KEY UPDATE recipient = recipient"); + } else if (database.getDatabaseType() == DatabaseType.SQLITE) { + database.execute("INSERT INTO " + NAME + " (mailId, recipient) VALUES " + valuesBuilder.toString() + " ON CONFLICT(mailId, recipient) DO NOTHING"); + } + } + + /** + * 指定された受信者を指定されたIDのメールの宛先に追加する。 + * @param mailId メールのID + * @param recipientId 受信者 + */ + public void addRecipient(int mailId, int recipientId) { + addRecipients(mailId, Arrays.asList(recipientId)); + } + + /** + * 指定された受信者を指定されたIDのメールの宛先から削除する。 + * @param mailId メールのID + * @param recipientId 受信者 + */ + public void removeRecipient(int mailId, int recipientId) { + if (recipientId != -1) { + database.execute("DELETE FROM " + NAME + " WHERE mailId = " + mailId + " AND recipient = " + recipientId); + } + } + + /** + * 指定されたメールの宛先をすべて削除する。 + * @param mailId メールのID + */ + public void clearRecipient(int mailId) { + database.execute("DELETE FROM " + NAME + " WHERE mailId = " + mailId); + } + + /** + * 指定されたメールに既読をつけた受信者をすべて取得する。 + * @param mailId メールのID + * @return 受信者のリスト + */ + public ArrayList getWhoRead(int mailId) { + return getRecipientsWhere("WHERE mailId = " + mailId + " AND isRead = 1"); + } + + /** + * 指定されたメールをゴミ箱に移動した受信者をすべて取得する。 + * @param mailId メールのID + * @return 受信者のリスト + */ + public ArrayList getWhoTrash(int mailId) { + return getRecipientsWhere("WHERE mailId = " + mailId + " AND isTrash = 1"); + } + + /** + * 指定されたIDのメールに受信者が既読をつけたかどうかを取得する。 + * @param mailId メールのID + * @param recipientId 受信者 + * @return 既読したかどうか + */ + public boolean isRead(int mailId, int recipientId) { + return getBool(mailId, "isRead", recipientId); + } + + /** + * 指定されたメールのIDそれぞれについて、指定された受信者が既読をつけているかどうかを取得する。 + * @param mailIds メールのIDのリスト + * @param recipientId 受信者 + * @return メールのIDと、既読されたかどうかのMap + */ + public Map isReadAll(List mailIds, int recipientId) { + return getBoolAll(mailIds, "isRead", recipientId); + } + + /** + * すべてのメールについて、指定された受信者が既読をつけているかどうかを取得する。 + * @param recipientId 受信者 + * @return すべてのメールのIDと、既読されたかどうかのMap + */ + public Map isReadAll(int recipientId) { + return getBoolAll("isRead", recipientId); + } + + /** + * 指定されたIDのメールに、受信者が既読をつけたかどうかを設定する。 + * @param mailId メールのID + * @param recipientId 受信者 + * @param isRead 既読をつけたか + */ + public void setRead(int mailId, int recipientId, boolean isRead) { + setBool(mailId, "isRead", recipientId, isRead); + } + + /** + * 指定されたすべてのメールに、受信者が既読をつけたかどうかを設定する。選択したメールをすべて既読にする機能を実装するときなどに利用できる。 + * @param mailIds メールのIDのリスト + * @param recipientId 受信者 + * @param isRead 既読をつけたか + */ + public void setReadAll(List mailIds, int recipientId, boolean isRead) { + setBoolAll(mailIds, "isRead", recipientId, isRead); + } + + /** + * すべてのメールに、受信者が既読をつけたかどうかを設定する。すべてのメールを既読にする機能を実装するときなどに利用できる。 + * @param recipientId 受信者 + * @param isRead 既読をつけたか + */ + public void setReadAll(int recipientId, boolean isRead) { + setBoolAll("isRead", recipientId, isRead); + } + + /** + * 指定されたIDのメールを受信者がゴミ箱に移動したかどうかを取得する。 + * @param mailId メールのID + * @param recipientId 受信者 + * @return メールがゴミ箱に移動されているかどうか。 + */ + public boolean isTrash(int mailId, int recipientId) { + return getBool(mailId, "isTrash", recipientId); + } + + /** + * 指定されたすべてのIDのメールを受信者がゴミ箱に移動したかどうかを取得する。 + * @param mailIds メールのIDのリスト + * @param recipientId 受信者 + * @return メールがゴミ箱に移動されているかどうか。 + */ + public Map isTrashAll(List mailIds, int recipientId) { + return getBoolAll(mailIds, "isTrash", recipientId); + } + + /** + * 指定されたすべてのIDのメールを受信者がゴミ箱に移動したかどうかを取得する。 + * @param recipientId 受信者 + * @return メールがゴミ箱に移動されているかどうか + */ + public Map isTrashAll(int recipientId) { + return getBoolAll("isTrash", recipientId); + } + + /** + * 指定されたIDのメールを受信者がゴミ箱に移動したかどうかを設定する。 + * @param mailId メールのID + * @param recipientId 受信者 + * @param isTrash ゴミ箱に移動したかどうか + */ + public void setTrash(int mailId, int recipientId, boolean isTrash) { + setBool(mailId, "isTrash", recipientId, isTrash); + } + + /** + * 指定されたすべてのメールを受信者がゴミ箱に移動したかどうかを設定する。選択したすべてのメールをゴミ箱に移動する機能を実装するときなどに利用できる。 + * @param mailIds メールのIDのリスト + * @param recipientId 受信者 + * @param isTrash ゴミ箱に移動したかどうか + */ + public void setTrashAll(List mailIds, int recipientId, boolean isTrash) { + setBoolAll(mailIds, "isTrash", recipientId, isTrash); + } + + /** + * すべてのメールを受信者がゴミ箱に移動したかどうかを設定する。すべてのメールをゴミ箱に移動する機能を実装するときなどに利用できる。 + * @param recipientId 受信者 + * @param isTrash ゴミ箱に移動したかどうか + */ + public void setTrashAll(int recipientId, boolean isTrash) { + setBoolAll("isTrash", recipientId, isTrash); + } + + /** + * 指定された受信者を宛先に持っているかつ指定されたIDであるメールについて、カラムの値を取得する。 + * @param mailIds メールのIDのリスト + * @param column カラム + * @param recipientId 受信者 + * @return 値 + */ + private Map getBoolAll(List mailIds, String column, int recipientId) { + if (recipientId != -1 && !mailIds.isEmpty()) { + return getBoolWhere(column, "WHERE mailId " + Database.createIn(mailIds) + " AND recipient = " + recipientId); + } + return new HashMap<>(); + } + + /** + * 指定された受信者を宛先に持つ全てのメールについて、カラムの値を取得する。 + * @param column カラム + * @param recipientId 受信者 + * @return メールIDと値のMap + */ + private Map getBoolAll(String column, int recipientId) { + if (recipientId != -1) { + return getBoolWhere(column, "WHERE recipient = " + recipientId); + } + return new HashMap<>(); + } + + + /** + * 指定された受信者を宛先に持つすべてのメールについて、カラムの値を設定する。 + * @param column カラム + * @param recipientId 受信者 + * @param value 値 + */ + private void setBoolAll(String column, int recipientId, boolean value) { + setBoolAll(mailDataTable.getIdsByRecipient(recipientId), column, recipientId, value); + } + + /** + * 指定された受信者を宛先にもっているかつ、指定されたIDのメールについて、カラムの値を取得する。 + * @param mailId メールのID + * @param column カラム + * @param recipientId 受信者 + * @return 値 + */ + private boolean getBool(int mailId, String column, int recipientId) { + if (recipientId != -1) { + return getBoolWhere(column, "WHERE mailId = " + mailId + " AND recipient = " + recipientId).getOrDefault(mailId, false); + } + return false; + } + + /** + * 指定された受信者を宛先にもつかつ、指定されたIDのメールについて、カラムの値を設定する。 + * @param mailId メールのID + * @param column カラム + * @param recipientId 受信者 + * @param value 値 + */ + private void setBool(int mailId, String column, int recipientId, boolean value) { + setBoolAll(Arrays.asList(mailId), column, recipientId, value); + } + + /** + * 指定された受信者をもつかつ、指定されたすべてのIDのメールについて、カラムの値を設定する。 + * @param mailIds メールのIDのリスト + * @param column カラム + * @param recipientId 受信者 + * @param value 値 + */ + private void setBoolAll(List mailIds, String column, int recipientId, boolean value) { + if (recipientId == -1 || mailIds.isEmpty()) { + return; + } + + StringBuilder valuesBuilder = new StringBuilder(); + for (Integer mailId : mailIds) { + valuesBuilder.append("(").append(mailId).append(", ").append(recipientId).append(", ").append(value ? 1 : 0).append("), "); + } + valuesBuilder.delete(valuesBuilder.length() - 2, valuesBuilder.length()); + + String insert = "INSERT INTO " + NAME + " (mailId, recipient, " + column + ") VALUES " + valuesBuilder.toString() + " "; + if (database.getDatabaseType() == DatabaseType.MYSQL) { + database.execute(insert + " ON DUPLICATE KEY UPDATE " + column + " = " + (value ? 1 : 0)); + } else if (database.getDatabaseType() == DatabaseType.SQLITE) { + database.execute(insert + " ON CONFLICT(mailId, recipient) DO UPDATE SET " + column + " = " + (value ? 1 : 0)); + } + } + + /** + * 指定されたWHERE句でカラムの値を取得する。 + * @param column カラム + * @param where SQL文うち、WHERE句の部分 + * @return メールのIDと値のMap + */ + private Map getBoolWhere(String column, String where) { + Map result = new HashMap<>(); + return database.query("SELECT mailId, " + column + " FROM " + NAME + (where.isEmpty() ? "" : " ") + where, rs -> { + try { + while (rs.next()) { + result.put(rs.getInt("mailId"), rs.getByte(column) == (byte)1); + } + return result; + } catch (SQLException e) { + e.printStackTrace(); + return result; + } + }); + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/database/MailSenderTable.java b/src/main/java/org/bitbucket/ucchy/undine/database/MailSenderTable.java new file mode 100644 index 0000000..ce3b72d --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/database/MailSenderTable.java @@ -0,0 +1,448 @@ +/* + * @author LazyGon + * @license LGPLv3 + * @copyright Copyright OKOCRAFT 2020 + */ +package org.bitbucket.ucchy.undine.database; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; + +import org.bitbucket.ucchy.undine.database.Database.DatabaseType; +import org.bitbucket.ucchy.undine.sender.MailSender; +import org.bitbucket.ucchy.undine.sender.MailSenderBlock; +import org.bitbucket.ucchy.undine.sender.MailSenderConsole; +import org.bitbucket.ucchy.undine.sender.MailSenderDummy; +import org.bitbucket.ucchy.undine.sender.MailSenderPlayer; +import org.bukkit.Location; + +/** + * MailSenderを保存するテーブルにアクセスするクラス。。Bukkitからのみだと他のサーバーからのメール送信に対応できないため、このテーブルを実装している。 + * @author LazyGon + */ +public class MailSenderTable { + + public static final String NAME = "undine_mailsenders"; + + private final Database database; + + private final BiMap mailSenderCache = HashBiMap.create(); + + MailSenderTable(Database database) { + this.database = database; + createTable(); + } + + void createTable() { + database.execute( + "CREATE TABLE IF NOT EXISTS " + NAME + " (" + + "id INTEGER PRIMARY KEY " + database.getDatabaseType().autoIncrement + ", " + + "uuidMost BIGINT NOT NULL, " + + "uuidLeast BIGINT NOT NULL, " + + "type TINYINT NOT NULL, " + + "name VARCHAR(128) NOT NULL DEFAULT '', " + + "location VARCHAR(128) UNIQUE, " + + "UNIQUE(uuidMost, uuidLeast)" + + ")" + ); + } + + /** + * idからMailSenderを取得する。 + * @param id MailSenderのデータベース内でのid + * @return MailSender + */ + public MailSender getById(int id) { + if (id <= 0) { + return null; + } + + if (mailSenderCache.containsKey(id)) { + return mailSenderCache.get(id); + } + + MailSender result = database.query("SELECT type, name, uuidMost, uuidLeast FROM " + NAME + " WHERE id = " + id, rs -> { + try { + if (rs.next()) { + switch ((int)rs.getByte("type")) { + case 0: return new MailSenderBlock(rs.getString("name"), Database.fromDBLocationString(rs.getString("location"))); + case 1: return MailSenderConsole.getMailSenderConsole(); + case 2: return MailSender.getMailSenderFromString("$" + new UUID(rs.getLong("uuidMost"), rs.getLong("uuidLeast"))); + case 3: + case 4: return new MailSenderDummy(rs.getString("name")); + default: return null; + } + } + return null; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + }); + + mailSenderCache.put(id, result); + + return result; + } + + /** + * 指定されたMailSenderのデータベース内でのidを取得する。MailSenderConsoleについては見つからなかったときはinsertしてそのとき生成されたidを返す。 + * @param mailSender idを取得するMailSender + * @return MailSenderのid + */ + public int getId(MailSender mailSender) { + if (mailSenderCache.containsValue(mailSender)) { + return mailSenderCache.inverse().get(mailSender); + } + + if (mailSender instanceof MailSenderBlock) { + return getIdWhere( + "WHERE " + + "type = 0 AND " + + "name = '" + mailSender.getName() + "' AND " + + "location = '" + Database.createDBLocationString(((MailSenderBlock) mailSender).getLocation()) + "'" + ); + } else if (mailSender instanceof MailSenderConsole) { + int id = getIdWhere("WHERE type = 1"); + if (id != -1) { + return id; + } + return database.insert("INSERT INTO " + NAME + " (uuidMost, uuidLeast, type, name) VALUES (0, 0, 1, 'CONSOLE')").get(0); + } else if (mailSender instanceof MailSenderPlayer) { + UUID uuid = UUID.fromString(mailSender.toString().replace("$", "")); + return getIdWhere("WHERE type = 2 AND (uuidMost = " + uuid.getMostSignificantBits() + " AND uuidLeast = " + uuid.getLeastSignificantBits() + ")"); + } else if (mailSender instanceof MailSenderDummy) { + return getIdWhere("WHERE (type = 3 OR type = 4) AND name = '" + mailSender.getName() + "'"); + } + return -1; + } + + /** + * 指定されたすべてのMailSenderのidを取得する。 + * そのidとMailSenderのマッピングが欲しい場合はここから更にgetByIdsを使う必要がある。(複雑すぎて1メソッドでは実装できなかったため) + * @param mailSenders idを取得するMailSender + * @return 取得されたidのリスト + */ + public List getIds(Collection mailSenders) { + mailSenders = new ArrayList<>(mailSenders); + List result = new ArrayList<>(); + for (MailSender mailSender : mailSenders) { + if (mailSenderCache.containsValue(mailSender)) { + result.add(mailSenderCache.inverse().get(mailSender)); + } + } + mailSenders.removeAll(mailSenderCache.values()); + + if (mailSenders.isEmpty()) { + return result; + } + + addAll(mailSenders); + + List blocks = new ArrayList<>(); + MailSender console = null; + List players = new ArrayList<>(); + List dummies = new ArrayList<>(); + for (MailSender sender : mailSenders) { + if (sender instanceof MailSenderBlock) { + blocks.add(sender); + } else if (sender instanceof MailSenderConsole && console == null) { + console = sender; + } else if (sender instanceof MailSenderPlayer) { + players.add(sender); + } else if (sender instanceof MailSenderDummy) { + dummies.add(sender); + } + } + + + if (!blocks.isEmpty()) { + StringBuilder whereBuilder = new StringBuilder(); + for (MailSender block : blocks) { + whereBuilder.append("(").append("name = '").append(block.getName()).append("' AND location = '") + .append(Database.createDBLocationString(block.getLocation())).append("') OR"); + } + whereBuilder.delete(whereBuilder.length() - 3, whereBuilder.length()); + result.addAll(getIdsWhere("WHERE type = 0 AND " + whereBuilder.toString())); + } + if (console != null) { + int id = getIdWhere("WHERE type = 1"); + if (id == -1) { + id = database.insert("INSERT INTO " + NAME + " (uuidMost, uuidLeast, type, name) VALUES (0, 0, 1, 'CONSOLE')").get(0); + } + result.add(id); + } + if (!players.isEmpty()) { + StringBuilder whereBuilder = new StringBuilder(); + for (MailSender player : players) { + UUID uuid = UUID.fromString(player.toString().replace("$", "")); + whereBuilder.append("(").append("uuidMost = ").append(uuid.getMostSignificantBits()).append(" AND uuidLeast = ") + .append(uuid.getLeastSignificantBits()).append(") OR"); + } + whereBuilder.delete(whereBuilder.length() - 3, whereBuilder.length()); + result.addAll(getIdsWhere("WHERE type = 2 AND " + whereBuilder.toString())); + } + if (!dummies.isEmpty()) { + List names = new ArrayList<>(); + for (MailSender dummy : dummies) { + names.add(dummy.getName()); + } + result.addAll(getIdsWhere("WHERE (type = 3 OR type = 4) AND name " + Database.createIn(names))); + } + + getByIds(result).forEach((id, sender) -> { + if (!mailSenderCache.containsKey(id)) { + mailSenderCache.put(id, sender); + } + }); + + return result; + } + + /** + * WHERE句を指定してMailSenderのidを取得する。 + * @param where SQL文のWHERE句部分 + * @return 条件に当てはまったMailSenderのリスト + */ + private List getIdsWhere(String where) { + return database.query("SELECT id FROM " + NAME + (where == null || where.isBlank() ? "" : " " + where), rs -> { + try { + List ids = new ArrayList<>(); + if (rs.next()) { + ids.add(rs.getInt("id")); + } + return ids; + } catch (SQLException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + }); + } + + /** + * WHERE句を指定してMailSenderを取得する。結果が一つになるようなWHERE句を書くことが推奨される。 + * @param where SQL文のWHERE句部分 + * @return 取得されたMailSender + */ + private int getIdWhere(String where) { + List ids = getIdsWhere(where); + return ids.isEmpty() ? -1 : ids.get(0); + } + + /** + * idのリストからMailSenderをすべて取得する。 + * @param ids idのリスト + * @return 指定されたidと取得されたMailSenderのMap + */ + public Map getByIds(Collection ids) { + ids = new ArrayList<>(ids); + Map result = new HashMap<>(); + for (int id : ids) { + if (mailSenderCache.containsKey(id)) { + result.put(id, mailSenderCache.get(id)); + } + } + ids.removeAll(result.keySet()); + if (ids.isEmpty()) { + return result; + } + database.query("SELECT id, type, name, uuidMost, uuidLeast FROM " + NAME + " WHERE id " + Database.createIn(ids), rs -> { + try { + while (rs.next()) { + int type = (int)rs.getByte("type"); + if (type == 0) { + result.put(rs.getInt("id"), new MailSenderBlock(rs.getString("name"), Database.fromDBLocationString(rs.getString("location")))); + } else if (type == 1) { + result.put(rs.getInt("id"), MailSenderConsole.getMailSenderConsole()); + } else if (type == 2) { + result.put(rs.getInt("id"), MailSender.getMailSenderFromString("$" + new UUID(rs.getLong("uuidMost"), rs.getLong("uuidLeast")))); + } else if (type == 3 || type == 4) { + result.put(rs.getInt("id"), new MailSenderDummy(rs.getString("name"))); + } + } + return null; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + }); + return result; + } + + /** + * 指定されたMailSenderをデータベースに追加する。 + * @param mailSender + */ + public void add(MailSender mailSender) { + addAll(Arrays.asList(mailSender)); + } + + /** + * 指定されたすべてのMailSenderをデータベースに追加する。 + * @param mailSenders MailSenderのリスト + */ + public void addAll(Collection mailSenders) { + if (mailSenders.isEmpty()) { + return; + } + List blocks = new ArrayList<>(); + MailSender console = null; + List players = new ArrayList<>(); + List dummies = new ArrayList<>(); + List others = new ArrayList<>(); + for (MailSender sender : mailSenders) { + if (sender instanceof MailSenderBlock) { + blocks.add(sender); + } else if (sender instanceof MailSenderConsole && console == null) { + console = sender; + } else if (sender instanceof MailSenderPlayer) { + players.add(sender); + } else if (sender instanceof MailSenderDummy) { + dummies.add(sender); + } else { + others.add(sender); + } + } + + String insert = "INSERT INTO " + NAME + " (uuidMost, uuidLeast, type, name, location) VALUES "; + + if (!blocks.isEmpty()) { + StringBuilder valuesBuilder = new StringBuilder(); + for (MailSender block : blocks) { + UUID uid = UUID.randomUUID(); + String loc = Database.createDBLocationString(block.getLocation()); + valuesBuilder.append("(") + .append(uid.getMostSignificantBits()).append(", ") + .append(uid.getLeastSignificantBits()).append(", ") + .append(0).append(", ") + .append("'").append(block.getName()).append("'").append(", ") + .append(loc != null ? loc : "NULL") + .append("), "); + } + valuesBuilder.delete(valuesBuilder.length() - 2, valuesBuilder.length()); + String onDuplicateKey = database.getDatabaseType() == DatabaseType.MYSQL + ? " ON DUPLICATE KEY UPDATE name = VALUES(name), location = VALUES(location)" + : " ON CONFLICT(uuidMost, uuidLeast) DO UPDATE SET name = excluded.name, location = excluded.location" + // UUIDはほぼ衝突しない。 + " ON CONFLICT(location) DO UPDATE SET name = excluded.name, uuidMost = excluded.uuidMost, uuidLeast = excluded.uuidLeast"; + database.execute(insert + valuesBuilder.toString() + onDuplicateKey); + } + if (console != null) { + String onDuplicateKey = database.getDatabaseType() == DatabaseType.MYSQL + ? " ON DUPLICATE KEY UPDATE type = type" + : " ON CONFLICT(uuidMost, uuidLeast) DO NOTHING"; + database.execute(insert + "(" + 0L + ", " + 0L + ", 1, 'CONSOLE', NULL)" + onDuplicateKey); + } + if (!players.isEmpty()) { + StringBuilder valuesBuilder = new StringBuilder(); + for (MailSender player : players) { + UUID uid = player.getOfflinePlayer().getUniqueId(); + valuesBuilder.append("(") + .append(uid.getMostSignificantBits()).append(", ") + .append(uid.getLeastSignificantBits()).append(", ") + .append(2).append(", ") + .append("'").append(player.getName()).append("'").append(", ") + .append("NULL") + .append("), "); + } + valuesBuilder.delete(valuesBuilder.length() - 2, valuesBuilder.length()); + String onDuplicateKey = database.getDatabaseType() == DatabaseType.MYSQL + ? " ON DUPLICATE KEY UPDATE name = VALUES(name)" + : " ON CONFLICT(uuidMost, uuidLeast) DO UPDATE SET name = excluded.name"; + database.execute(insert + valuesBuilder.toString() + onDuplicateKey); + } + if (!dummies.isEmpty()) { + StringBuilder valuesBuilder = new StringBuilder(); + for (MailSender dummy : dummies) { + UUID uid = UUID.randomUUID(); + valuesBuilder.append("(") + .append(uid.getMostSignificantBits()).append(", ") + .append(uid.getLeastSignificantBits()).append(", ") + .append(3).append(", ") + .append("'").append(dummy.getName()).append("'").append(", ") + .append("NULL") + .append("), "); + } + valuesBuilder.delete(valuesBuilder.length() - 2, valuesBuilder.length()); + String onDuplicateKey = database.getDatabaseType() == DatabaseType.MYSQL + ? " ON DUPLICATE KEY UPDATE name = VALUES(name)" + : " ON CONFLICT(uuidMost, uuidLeast) DO UPDATE SET name = excluded.name"; + database.execute(insert + valuesBuilder.toString() + onDuplicateKey); + } + if (!others.isEmpty()) { + // CommandSenderを実装するEntityをここに入れたいが + // UndineMailderでは残念ながら実装されていないため使われることはないと思われる。 + StringBuilder valuesBuilder = new StringBuilder(); + for (MailSender other : others) { + UUID uid = UUID.randomUUID(); + valuesBuilder.append("(") + .append(uid.getMostSignificantBits()).append(", ") + .append(uid.getLeastSignificantBits()).append(", ") + .append(4).append(", ") + .append("'").append(other.getName()).append("'").append(", ") + .append("NULL") + .append("), "); + } + valuesBuilder.delete(valuesBuilder.length() - 2, valuesBuilder.length()); + String onDuplicateKey = database.getDatabaseType() == DatabaseType.MYSQL + ? " ON DUPLICATE KEY UPDATE name = VALUES(name)" + : " ON CONFLICT(uuidMost, uuidLeast) DO UPDATE SET name = excluded.name"; + database.execute(insert + valuesBuilder.toString() + onDuplicateKey); + } + } + + /** + * データベースに登録されている名前を変更する。 + * @param mailSender 名前を変更するMailSender + * @param newName 新しい名前 + */ + public void updateName(MailSender mailSender, String newName) { + database.execute("UPDATE " + NAME + " SET name = '" + newName + "' WHERE id = " + getId(mailSender)); + } + + /** + * かつて指定された位置にあったBlockのMailSenderをデータベースから取得する。過去にそうだったとしても現在はそのブロックはMailSenderにはなりえない可能性がある。 + * @param location ブロックの位置 + * @return MailSenderBlock + */ + public MailSenderBlock getMailSenderBlockAt(Location location) { + return database.query("SELECT name, location FROM " + NAME + " " + + "WHERE type = 0 AND location = '" + Database.createDBLocationString(location) + "'", rs -> { + try { + if (rs.next()) { + return new MailSenderBlock(rs.getString("name"), Database.fromDBLocationString(rs.getString("location"))); + } + return null; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + }); + } + + /** + * 指定されたMailSenderがデータベースに存在するかを調べる。 + * @param mailSender 調べるMailSender + * @return データベースに存在したらtrue + */ + public boolean exists(MailSender mailSender) { + return getId(mailSender) != -1; + } + + /** + * データベースから指定されたMailSenderを削除する。 + * @param mailSender 削除するMailSender + * @return 削除したかどうか + */ + public boolean delete(MailSender mailSender) { + return database.execute("DELETE FROM " + NAME + " WHERE id = " + getId(mailSender)); + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/group/GroupData.java b/src/main/java/org/bitbucket/ucchy/undine/group/GroupData.java index c032433..d9e65ac 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/group/GroupData.java +++ b/src/main/java/org/bitbucket/ucchy/undine/group/GroupData.java @@ -5,81 +5,43 @@ */ package org.bitbucket.ucchy.undine.group; -import java.io.File; -import java.io.IOException; import java.util.ArrayList; -import java.util.List; import org.bitbucket.ucchy.undine.UndineMailer; +import org.bitbucket.ucchy.undine.database.GroupDataDatabase; +import org.bitbucket.ucchy.undine.database.Database.DatabaseType; import org.bitbucket.ucchy.undine.sender.MailSender; import org.bitbucket.ucchy.undine.sender.MailSenderPlayer; import org.bukkit.ChatColor; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; /** * メールグループ * @author ucchy */ -public class GroupData { +public abstract class GroupData { - private String name; - private MailSender owner; - private ArrayList members; - private GroupPermissionMode sendMode; - private GroupPermissionMode modifyMode; - private GroupPermissionMode dissolutionMode; + protected final UndineMailer parent; - /** - * コンストラクタ(データロード用) - */ - private GroupData() { - members = new ArrayList(); - } + protected String name; - /** - * コンストラクタ(継承クラス用) - * @param name グループ名 - */ - protected GroupData(String name) { + public GroupData(UndineMailer parent, String name) { + this.parent = parent; this.name = name; - members = new ArrayList(); - sendMode = UndineMailer.getInstance().getUndineConfig().getSendModeDefault(); - modifyMode = UndineMailer.getInstance().getUndineConfig().getModifyModeDefault(); - dissolutionMode = - UndineMailer.getInstance().getUndineConfig().getDissolutionModeDefault(); } - /** - * コンストラクタ(新規作成用) - * @param name グループ名 - * @param owner オーナー - */ - public GroupData(String name, MailSender owner) { - this(name); - setOwner(owner); - addMember(owner); - } + public abstract void save(); /** * グループにメンバーを追加する * @param member メンバー */ - public void addMember(MailSender member) { - if ( !members.contains(member) ) { - members.add(member); - } - } + public abstract void addMember(MailSender member); /** * グループからメンバーを削除する * @param member メンバー */ - public void removeMember(MailSender member) { - if ( members.contains(member) ) { - members.remove(member); - } - } + public abstract void removeMember(MailSender member); /** * グループ名を取得する @@ -93,59 +55,45 @@ public String getName() { * グループのオーナーを取得する * @return オーナー */ - public MailSender getOwner() { - return owner; - } + public abstract MailSender getOwner(); /** * グループのオーナーを設定する * @param owner */ - public void setOwner(MailSender owner) { - this.owner = owner; - } + public abstract void setOwner(MailSender owner); /** * グループのメンバーを取得する * @return メンバー */ - public ArrayList getMembers() { - return members; - } + public abstract ArrayList getMembers(); /** * 指定されたsenderが、グループのメンバーかどうかを返す * @param sender * @return メンバーかどうか */ - public boolean isMember(MailSender sender) { - return members.contains(sender); - } + public abstract boolean isMember(MailSender sender); /** * 指定されたsenderがオーナーかどうかを返す * @param sender * @return オーナーかどうか */ - public boolean isOwner(MailSender sender) { - return owner.equals(sender); - } + public abstract boolean isOwner(MailSender sender); /** * 送信権限モードを取得する * @return sendMode */ - public GroupPermissionMode getSendMode() { - return sendMode; - } + public abstract GroupPermissionMode getSendMode(); /** * 送信権限モードを設定する * @param sendMode sendMode */ - public void setSendMode(GroupPermissionMode sendMode) { - this.sendMode = sendMode; - } + public abstract void setSendMode(GroupPermissionMode sendMode); /** * 指定されたsenderは、送信権限を持っているかどうかを調べる @@ -154,24 +102,20 @@ public void setSendMode(GroupPermissionMode sendMode) { */ public boolean canSend(MailSender sender) { return permissionCheck(sender, - sendMode, "undine.group.send-all"); + getSendMode(), "undine.group.send-all"); } /** * 変更権限モードを取得する * @return modifyMode */ - public GroupPermissionMode getModifyMode() { - return modifyMode; - } + public abstract GroupPermissionMode getModifyMode(); /** * 変更権限モードを設定する * @param modifyMode modifyMode */ - public void setModifyMode(GroupPermissionMode modifyMode) { - this.modifyMode = modifyMode; - } + public abstract void setModifyMode(GroupPermissionMode modifyMode); /** * 指定されたsenderは、変更権限を持っているかどうかを調べる @@ -180,24 +124,20 @@ public void setModifyMode(GroupPermissionMode modifyMode) { */ public boolean canModify(MailSender sender) { return permissionCheck(sender, - modifyMode, "undine.group.modify-all"); + getModifyMode(), "undine.group.modify-all"); } /** * 解散権限モードを取得する * @return dissolutionMode */ - public GroupPermissionMode getDissolutionMode() { - return dissolutionMode; - } + public abstract GroupPermissionMode getDissolutionMode(); /** * 解散権限モードを設定する * @param dissolutionMode dissolutionMode */ - public void setDissolutionMode(GroupPermissionMode dissolutionMode) { - this.dissolutionMode = dissolutionMode; - } + public abstract void setDissolutionMode(GroupPermissionMode dissolutionMode); /** * 指定されたsenderは、解散権限を持っているかどうかを調べる @@ -206,7 +146,7 @@ public void setDissolutionMode(GroupPermissionMode dissolutionMode) { */ public boolean canBreakup(MailSender sender) { return permissionCheck(sender, - dissolutionMode, "undine.group.dissolution-all"); + getDissolutionMode(), "undine.group.dissolution-all"); } /** @@ -214,9 +154,10 @@ public boolean canBreakup(MailSender sender) { * @return ホバー用のテキスト */ public String getHoverText() { + MailSender owner = getOwner(); StringBuffer hover = new StringBuffer(); hover.append(ChatColor.GOLD + owner.getName() + ChatColor.WHITE); - ArrayList members = new ArrayList(this.members); + ArrayList members = new ArrayList(getMembers()); members.remove(owner); for ( int j=0; j<5; j++ ) { if ( members.size() <= j ) { @@ -230,70 +171,6 @@ public String getHoverText() { return hover.toString(); } - /** - * コンフィグセクションにグループを保存する - * @param section コンフィグセクション - */ - private void saveToSection(ConfigurationSection section) { - section.set("name", name); - section.set("owner", owner.toString()); - - List array = new ArrayList(); - for ( MailSender mem : members ) { - array.add(mem.toString()); - } - section.set("members", array); - - section.set("sendMode", sendMode.toString()); - section.set("modifyMode", modifyMode.toString()); - section.set("dissolutionMode", dissolutionMode.toString()); - } - - /** - * ファイルにグループを保存する - * @param file ファイル - */ - protected void saveToFile(File file) { - YamlConfiguration config = new YamlConfiguration(); - saveToSection(config); - try { - config.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * コンフィグセクションからグループをロードする - * @param section コンフィグセクション - * @return グループ - */ - private static GroupData loadFromSection(ConfigurationSection section) { - GroupData data = new GroupData(); - data.name = section.getString("name"); - data.owner = MailSender.getMailSenderFromString(section.getString("owner")); - for ( String mem : section.getStringList("members") ) { - data.members.add(MailSender.getMailSenderFromString(mem)); - } - data.sendMode = GroupPermissionMode.getFromString( - section.getString("sendMode"), GroupPermissionMode.MEMBER); - data.modifyMode = GroupPermissionMode.getFromString( - section.getString("modifyMode"), GroupPermissionMode.OWNER); - data.dissolutionMode = GroupPermissionMode.getFromString( - section.getString("dissolutionMode"), GroupPermissionMode.OWNER); - return data; - } - - /** - * ファイルからグループをロードする - * @param file ファイル - * @return グループ - */ - protected static GroupData loadFromFile(File file) { - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - return loadFromSection(config); - } - /** * パーミッションのチェックを行う * @param sender @@ -307,13 +184,14 @@ private boolean permissionCheck( if ( sender.hasPermission(specialNode) ) { return true; } + // FIXME: MEMBER権限のときOwnerやOPは権限なし判定されそうに見えるが大丈夫だろうか...? switch ( mode ) { case EVERYONE: return true; case MEMBER: return isMember(sender); case OWNER: - return owner.equals(sender); + return getOwner().equals(sender); case OP: return sender.isOp(); default: @@ -327,11 +205,13 @@ private boolean permissionCheck( */ protected boolean upgrade() { boolean upgraded = false; + MailSender owner = getOwner(); if ( owner instanceof MailSenderPlayer ) { if ( ((MailSenderPlayer) owner).upgrade() ) { upgraded = true; } } + ArrayList members = getMembers(); for ( MailSender ms : members ) { if ( ms instanceof MailSenderPlayer ) { if ( ((MailSenderPlayer) ms).upgrade() ) { @@ -341,4 +221,15 @@ protected boolean upgrade() { } return upgraded; } + + public static GroupData create(UndineMailer parent, String name, MailSender owner) { + GroupData groupData; + if (parent.getUndineConfig().getDatabaseType() == DatabaseType.FLAT_FILE) { + groupData = new GroupDataFlatFile(parent, name, owner); + parent.getGroupManager().addGroup(groupData); + } else { + groupData = new GroupDataDatabase(parent, name, owner); + } + return groupData; + } } diff --git a/src/main/java/org/bitbucket/ucchy/undine/group/GroupDataFlatFile.java b/src/main/java/org/bitbucket/ucchy/undine/group/GroupDataFlatFile.java new file mode 100644 index 0000000..7f1c853 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/group/GroupDataFlatFile.java @@ -0,0 +1,286 @@ +/* + * @author ucchy + * @license LGPLv3 + * @copyright Copyright ucchy 2015 + */ +package org.bitbucket.ucchy.undine.group; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.bitbucket.ucchy.undine.UndineMailer; +import org.bitbucket.ucchy.undine.sender.MailSender; +import org.bitbucket.ucchy.undine.sender.MailSenderPlayer; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + +/** + * メールグループ + * @author ucchy + */ +public class GroupDataFlatFile extends GroupData { + + private MailSender owner; + private ArrayList members; + private GroupPermissionMode sendMode; + private GroupPermissionMode modifyMode; + private GroupPermissionMode dissolutionMode; + + /** + * コンストラクタ(データロード用) + */ + private GroupDataFlatFile(UndineMailer parent) { + super(parent, null); + members = new ArrayList(); + } + + /** + * コンストラクタ(継承クラス用) + * + * @param name グループ名 + */ + protected GroupDataFlatFile(UndineMailer parent, String name) { + this(parent); + this.name = name; + sendMode = UndineMailer.getInstance().getUndineConfig().getSendModeDefault(); + modifyMode = UndineMailer.getInstance().getUndineConfig().getModifyModeDefault(); + dissolutionMode = UndineMailer.getInstance().getUndineConfig().getDissolutionModeDefault(); + } + + /** + * コンストラクタ(新規作成用) + * + * @param name グループ名 + * @param owner オーナー + */ + public GroupDataFlatFile(UndineMailer parent, String name, MailSender owner) { + this(parent, name); + setOwner(owner); + addMember(owner); + } + + /** + * グループにメンバーを追加する + * + * @param member メンバー + */ + @Override + public void addMember(MailSender member) { + if (!members.contains(member)) { + members.add(member); + } + } + + /** + * グループからメンバーを削除する + * + * @param member メンバー + */ + @Override + public void removeMember(MailSender member) { + if (members.contains(member)) { + members.remove(member); + } + } + + /** + * グループのオーナーを取得する + * + * @return オーナー + */ + @Override + public MailSender getOwner() { + return owner; + } + + /** + * グループのオーナーを設定する + * + * @param owner + */ + @Override + public void setOwner(MailSender owner) { + this.owner = owner; + } + + /** + * グループのメンバーを取得する + * + * @return メンバー + */ + @Override + public ArrayList getMembers() { + return members; + } + + /** + * 指定されたsenderが、グループのメンバーかどうかを返す + * + * @param sender + * @return メンバーかどうか + */ + @Override + public boolean isMember(MailSender sender) { + return members.contains(sender); + } + + /** + * 指定されたsenderがオーナーかどうかを返す + * + * @param sender + * @return オーナーかどうか + */ + @Override + public boolean isOwner(MailSender sender) { + return owner.equals(sender); + } + + /** + * 送信権限モードを取得する + * + * @return sendMode + */ + @Override + public GroupPermissionMode getSendMode() { + return sendMode; + } + + /** + * 送信権限モードを設定する + * + * @param sendMode sendMode + */ + @Override + public void setSendMode(GroupPermissionMode sendMode) { + this.sendMode = sendMode; + } + + /** + * 変更権限モードを取得する + * + * @return modifyMode + */ + @Override + public GroupPermissionMode getModifyMode() { + return modifyMode; + } + + /** + * 変更権限モードを設定する + * + * @param modifyMode modifyMode + */ + @Override + public void setModifyMode(GroupPermissionMode modifyMode) { + this.modifyMode = modifyMode; + } + + /** + * 解散権限モードを取得する + * + * @return dissolutionMode + */ + @Override + public GroupPermissionMode getDissolutionMode() { + return dissolutionMode; + } + + /** + * 解散権限モードを設定する + * + * @param dissolutionMode dissolutionMode + */ + @Override + public void setDissolutionMode(GroupPermissionMode dissolutionMode) { + this.dissolutionMode = dissolutionMode; + } + + /** + * コンフィグセクションにグループを保存する + * + * @param section コンフィグセクション + */ + private void saveToSection(ConfigurationSection section) { + section.set("name", name); + section.set("owner", owner.toString()); + + List array = new ArrayList(); + for (MailSender mem : members) { + array.add(mem.toString()); + } + section.set("members", array); + + section.set("sendMode", sendMode.toString()); + section.set("modifyMode", modifyMode.toString()); + section.set("dissolutionMode", dissolutionMode.toString()); + } + + /** + * ファイルにグループを保存する + */ + public void save() { + File folder = parent.getGroupFolder(); + File file = new File(folder, name.toLowerCase() + ".yml"); + YamlConfiguration config = new YamlConfiguration(); + saveToSection(config); + try { + config.save(file); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * コンフィグセクションからグループをロードする + * + * @param section コンフィグセクション + * @return グループ + */ + private static GroupDataFlatFile loadFromSection(UndineMailer parent, ConfigurationSection section) { + GroupDataFlatFile data = new GroupDataFlatFile(parent); + data.name = section.getString("name"); + data.owner = MailSender.getMailSenderFromString(section.getString("owner")); + for (String mem : section.getStringList("members")) { + data.members.add(MailSender.getMailSenderFromString(mem)); + } + data.sendMode = GroupPermissionMode.getFromString(section.getString("sendMode"), GroupPermissionMode.MEMBER); + data.modifyMode = GroupPermissionMode.getFromString(section.getString("modifyMode"), GroupPermissionMode.OWNER); + data.dissolutionMode = GroupPermissionMode.getFromString(section.getString("dissolutionMode"), + GroupPermissionMode.OWNER); + return data; + } + + /** + * ファイルからグループをロードする + * + * @param file ファイル + * @return グループ + */ + protected static GroupDataFlatFile loadFromFile(UndineMailer parent, File file) { + YamlConfiguration config = YamlConfiguration.loadConfiguration(file); + return loadFromSection(parent, config); + } + + /** + * データのアップグレードを行う。 + * @return アップグレードを実行したかどうか + */ + protected boolean upgrade() { + boolean upgraded = false; + if ( owner instanceof MailSenderPlayer ) { + if ( ((MailSenderPlayer) owner).upgrade() ) { + upgraded = true; + } + } + for ( MailSender ms : members ) { + if ( ms instanceof MailSenderPlayer ) { + if ( ((MailSenderPlayer) ms).upgrade() ) { + upgraded = true; + } + } + } + return upgraded; + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/group/GroupManager.java b/src/main/java/org/bitbucket/ucchy/undine/group/GroupManager.java index eb1570f..98dc8c2 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/group/GroupManager.java +++ b/src/main/java/org/bitbucket/ucchy/undine/group/GroupManager.java @@ -5,12 +5,10 @@ */ package org.bitbucket.ucchy.undine.group; -import java.io.File; -import java.io.FilenameFilter; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; +import java.util.List; import org.bitbucket.ucchy.undine.Messages; import org.bitbucket.ucchy.undine.UndineMailer; @@ -19,21 +17,19 @@ import org.bitbucket.ucchy.undine.command.ListCommand; import org.bitbucket.ucchy.undine.command.UndineCommand; import org.bitbucket.ucchy.undine.sender.MailSender; -import org.bitbucket.ucchy.undine.sender.MailSenderConsole; -import org.bitbucket.ucchy.undine.sender.MailSenderPlayer; -import org.bitbucket.ucchy.undine.tellraw.ClickEventType; -import org.bitbucket.ucchy.undine.tellraw.MessageComponent; -import org.bitbucket.ucchy.undine.tellraw.MessageParts; -import org.bukkit.Bukkit; import org.bukkit.ChatColor; +import com.github.ucchyocean.messaging.tellraw.ClickEventType; +import com.github.ucchyocean.messaging.tellraw.MessageComponent; +import com.github.ucchyocean.messaging.tellraw.MessageParts; + /** * メールグループ管理クラス * @author ucchy */ -public class GroupManager { +public abstract class GroupManager { - private static final ArrayList PEX_OPTION_FLAGS; + protected static final ArrayList PEX_OPTION_FLAGS; static { PEX_OPTION_FLAGS = new ArrayList(); PEX_OPTION_FLAGS.add("recieve-mail"); @@ -48,9 +44,13 @@ public class GroupManager { private static final String PERMISSION_INFINITE_CREATE = GroupCommand.PERMISSION_INFINITE_CREATE; private static final String PERMISSION_INFINITE_ADD_MEMBER = GroupCommand.PERMISSION_INFINITE_ADD_MEMBER; - private UndineMailer parent; - private HashMap groups; - private HashMap pexGroupsCache; + protected UndineMailer parent; + + protected SpecialGroupAll groupAll = new SpecialGroupAll(); + protected SpecialGroupAllConnected groupAllConnected = new SpecialGroupAllConnected(); + protected SpecialGroupAllLogin groupAllLogin = new SpecialGroupAllLogin(); + + protected HashMap pexGroupsCache; /** * コンストラクタ @@ -64,50 +64,7 @@ public GroupManager(UndineMailer parent) { /** * 全データを再読み込みする */ - public void reload() { - - long start = System.currentTimeMillis(); - - File folder = parent.getGroupFolder(); - File[] files = folder.listFiles(new FilenameFilter() { - public boolean accept(File dir, String name) { - return name.endsWith(".yml"); - } - }); - - groups = new HashMap(); - - if ( files != null ) { - for ( File f : files ) { - GroupData group = GroupData.loadFromFile(f); - groups.put(group.getName().toLowerCase(), group); - } - } - - // 特殊グループを追加する - GroupData all = new SpecialGroupAll(); - groups.put(all.getName().toLowerCase(), all); - GroupData allConnected = new SpecialGroupAllConnected(); - groups.put(allConnected.getName().toLowerCase(), allConnected); - GroupData allLogin = new SpecialGroupAllLogin(); - groups.put(allLogin.getName().toLowerCase(), allLogin); - - // アップグレード処理 - start = System.currentTimeMillis(); - - int total = 0; - for ( GroupData group : groups.values() ) { - if ( group.upgrade() ) { - saveGroupData(group); - total++; - } - } - - if ( total > 0 ) { - UndineMailer.getInstance().getLogger().info("Upgrade group data... Done. Time: " - + (System.currentTimeMillis() - start) + "ms, Data: " + total + "."); - } - } + public abstract void reload(); /** * グループを追加する。 @@ -115,84 +72,72 @@ public boolean accept(File dir, String name) { * 古いグループが上書きされてしまうことに注意する。 * @param group グループ */ - public void addGroup(GroupData group) { - String name = group.getName().toLowerCase(); - groups.put(name, group); - saveGroupData(group); - } + public abstract void addGroup(GroupData group); /** * 指定したグループ名のグループを取得する * @param name グループ名 * @return グループ */ - public GroupData getGroup(String name) { - name = name.toLowerCase(); + public abstract GroupData getGroup(String name); - // PEXから取得する - if ( name.startsWith(SpecialGroupPex.NAME_PREFIX) && pexGroupsCache != null ) { - if ( pexGroupsCache.containsKey(name) ) { - return pexGroupsCache.get(name); - } - } - - // グループから取得する - if ( groups.containsKey(name) ) { - return groups.get(name); - } - return null; - } + /** + * 指定したグループ名のグループをすべて取得する。 + * @param names グループ名のリスト + * @return グループのリスト + */ + public abstract ArrayList getGroups(List names); /** * 指定したグループ名のグループを削除する * @param name グループ名 */ - public void removeGroup(String name) { - name = name.toLowerCase(); - if ( groups.containsKey(name) ) { - groups.remove(name); - File folder = parent.getGroupFolder(); - File file = new File(folder, name + ".yml"); - file.delete(); - } - } + public abstract void removeGroup(String name); /** * 全てのグループ名を取得する * @return 全てのグループ名 */ - public ArrayList getAllGroupNames() { - ArrayList names = new ArrayList(); - for ( GroupData group : groups.values() ) { - names.add(group.getName()); - } - return names; - } + public abstract ArrayList getAllGroupNames(); /** * 全てのグループを取得する * @return 全てのグループ */ - public ArrayList getAllGroups() { - return new ArrayList(groups.values()); - } + public abstract ArrayList getAllGroups(); /** * 指定されたグループ名は既に存在するかどうかを確認する * @return 存在するかどうか */ - public boolean existGroupName(String name) { - return groups.keySet().contains(name.toLowerCase()); - } + public abstract boolean existGroupName(String name); /** - * 指定したグループを実データファイルに保存する + * 指定したグループを保存する * @param group グループ */ - public void saveGroupData(GroupData group) { - File folder = parent.getGroupFolder(); - File file = new File(folder, group.getName().toLowerCase() + ".yml"); - group.saveToFile(file); + public abstract void saveGroupData(GroupData group); + + /** + * @return すべてのプレイヤーを示すグループ + */ + public SpecialGroupAll getGroupAll() { + return groupAll; + } + + /** + * @return サーバーの起動している間に接続したすべてのプレイヤーを示すグループ + */ + public SpecialGroupAllConnected getGroupAllConnected() { + return groupAllConnected; + } + + /** + * @return 現在ログインしているすべてのプレイヤーを示すグループ。別のサーバーのプレイヤーは含まれない。 + * TODO: mailsenderのデータベーススキーマを変更して含ませる。 + */ + public SpecialGroupAllLogin getGroupAllLogin() { + return groupAllLogin; } /** @@ -209,13 +154,7 @@ public static boolean canUseNameFromGroup(String name) { * @param sender * @return オーナーのグループの個数 */ - public int getOwnerGroupCount(MailSender sender) { - int total = 0; - for ( GroupData group : groups.values() ) { - if ( group.getOwner().equals(sender) ) total++; - } - return total; - } + public abstract int getOwnerGroupCount(MailSender sender); /** * 指定したsenderは新規にグループを作成できるかどうかを返す @@ -239,17 +178,13 @@ public ArrayList getGroupsForList(MailSender sender) { ArrayList results = new ArrayList(); - for ( GroupData group : groups.values() ) { + for ( GroupData group : getAllGroups() ) { if ( group.isMember(sender) || group.canSend(sender) ) { results.add(group); } } - Collections.sort(results, new Comparator() { - public int compare(GroupData o1, GroupData o2) { - return o1.getName().compareToIgnoreCase(o2.getName()); - } - }); + Collections.sort(results, (o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName())); return results; } @@ -263,17 +198,13 @@ public ArrayList getGroupsForSelection(MailSender sender) { ArrayList results = new ArrayList(); - for ( GroupData group : groups.values() ) { + for ( GroupData group : getAllGroups() ) { if ( group.canSend(sender) ) { results.add(group); } } - Collections.sort(results, new Comparator() { - public int compare(GroupData o1, GroupData o2) { - return o1.getName().compareToIgnoreCase(o2.getName()); - } - }); + Collections.sort(results, (o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName())); // PermissionsExから動的にグループを取得して結合する for ( GroupData group : getPexGroups() ) { @@ -322,7 +253,7 @@ public void displayGroupList(MailSender sender, int page) { new MessageParts("[" + group.getName() + "]", ChatColor.AQUA); button.setClickEvent(ClickEventType.RUN_COMMAND, COMMAND + " detail " + group.getName()); - button.addHoverText(group.getHoverText()); + button.setHoverText(group.getHoverText()); msg.addParts(button); if ( group instanceof SpecialGroupAll ) { @@ -333,7 +264,7 @@ public void displayGroupList(MailSender sender, int page) { new String[]{group.getOwner().getName(), group.getMembers().size() + ""})); } - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); } if ( canMakeNewGroup(sender) ) { @@ -344,10 +275,10 @@ public void displayGroupList(MailSender sender, int page) { new MessageParts(Messages.get("GroupMakeNewGroup"), ChatColor.AQUA); button.setClickEvent(ClickEventType.SUGGEST_COMMAND, COMMAND + " create "); - button.addHoverText(Messages.get("GroupMakeNewGroupToolTip")); + button.setHoverText(Messages.get("GroupMakeNewGroupToolTip")); msg.addParts(button); - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); } sendPager(sender, COMMAND + " list", "", page, max, @@ -392,7 +323,7 @@ public void displayGroupSelection(MailSender sender, int page, String next) { new MessageParts("[" + group.getName() + "]", ChatColor.AQUA); button.setClickEvent(ClickEventType.RUN_COMMAND, next + " " + group.getName()); - button.addHoverText(group.getHoverText()); + button.setHoverText(group.getHoverText()); msg.addParts(button); if ( group instanceof SpecialGroupAll ) { @@ -403,7 +334,7 @@ public void displayGroupSelection(MailSender sender, int page, String next) { new String[]{group.getOwner().getName(), group.getMembers().size() + ""})); } - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); } if ( canMakeNewGroup(sender) ) { @@ -412,10 +343,10 @@ public void displayGroupSelection(MailSender sender, int page, String next) { MessageParts button = new MessageParts(Messages.get("GroupMakeNewGroup"), ChatColor.WHITE); - button.addHoverText(Messages.get("GroupMakeNewGroupToolTipForSelection")); + button.setHoverText(Messages.get("GroupMakeNewGroupToolTipForSelection")); msg.addParts(button); - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); } sendPager(sender, COMMAND + " list", " " + next, page, max, @@ -453,11 +384,7 @@ public void displayGroupDetailReadOnly(MailSender sender, GroupData group) { // メンバーを5人ごとに区切って表示する ArrayList members = group.getMembers(); - Collections.sort(members, new Comparator() { - public int compare(MailSender o1, MailSender o2) { - return o1.getName().compareToIgnoreCase(o2.getName()); - } - }); + Collections.sort(members, (o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName())); int size = members.size(); int max = (int)((size - 1) / 5) + 1; for ( int i=0; i members = group.getMembers(); - Collections.sort(members, new Comparator() { - public int compare(MailSender o1, MailSender o2) { - return o1.getName().compareToIgnoreCase(o2.getName()); - } - }); + Collections.sort(members, (o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName())); int size = members.size(); int max = (int)((size - 1) / PAGE_SIZE) + 1; @@ -531,20 +454,20 @@ public int compare(MailSender o1, MailSender o2) { delete.setClickEvent( ClickEventType.RUN_COMMAND, COMMAND + " remove " + group.getName() + " " + member.getName()); - delete.addHoverText(Messages.get("GroupDeleteMemberToolTip")); + delete.setHoverText(Messages.get("GroupDeleteMemberToolTip")); msg.addParts(delete); } else { MessageParts delete = new MessageParts( Messages.get("GroupDeleteMemberButton"), ChatColor.WHITE); - delete.addHoverText(Messages.get("GroupDeleteMemberOwnerToolTip")); + delete.setHoverText(Messages.get("GroupDeleteMemberOwnerToolTip")); msg.addParts(delete); } msg.addText(member.getName()); - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); } // メンバー追加ボタンと、設定ボタンを置く @@ -571,7 +494,7 @@ public int compare(MailSender o1, MailSender o2) { msg.addParts(addAddress); } - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); msg = new MessageComponent(); msg.addText(pre); @@ -582,7 +505,7 @@ public int compare(MailSender o1, MailSender o2) { COMMAND + " addalllogin " + group.getName()); msg.addParts(addall); - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); } MessageComponent msg = new MessageComponent(); @@ -594,7 +517,7 @@ public int compare(MailSender o1, MailSender o2) { COMMAND + " detail " + group.getName() + " setting"); msg.addParts(setting); - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); sendPager(sender, COMMAND + " detail " + group.getName(), "", page, max, Messages.get("DetailHorizontalParts"), @@ -630,7 +553,7 @@ public void displayGroupSetting(MailSender sender, GroupData group) { (mode != GroupPermissionMode.OWNER), (mode != GroupPermissionMode.MEMBER), (mode != GroupPermissionMode.EVERYONE), true); - sendMessageComponent(msgSend, sender); + sender.sendMessageComponent(msgSend); mode = group.getModifyMode(); sender.sendMessage(pre + Messages.get("GroupModifyPerm") @@ -643,7 +566,7 @@ public void displayGroupSetting(MailSender sender, GroupData group) { (mode != GroupPermissionMode.OWNER), (mode != GroupPermissionMode.MEMBER), false, false); - sendMessageComponent(msgMod, sender); + sender.sendMessageComponent(msgMod); mode = group.getDissolutionMode(); sender.sendMessage(pre + Messages.get("GroupDissolutionPerm") @@ -656,7 +579,7 @@ public void displayGroupSetting(MailSender sender, GroupData group) { (mode != GroupPermissionMode.OWNER), (mode != GroupPermissionMode.MEMBER), false, false); - sendMessageComponent(msgDis, sender); + sender.sendMessageComponent(msgDis); if ( group.canBreakup(sender) ) { MessageComponent msg = new MessageComponent(); @@ -667,7 +590,7 @@ public void displayGroupSetting(MailSender sender, GroupData group) { ClickEventType.RUN_COMMAND, COMMAND + " delete " + group.getName()); msg.addParts(breakup); - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); } MessageComponent msg = new MessageComponent(); @@ -678,7 +601,7 @@ public void displayGroupSetting(MailSender sender, GroupData group) { ClickEventType.RUN_COMMAND, COMMAND + " detail " + group.getName()); msg.addParts(breakup); - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); sender.sendMessage(Messages.get("DetailLastLine")); } @@ -762,7 +685,7 @@ private void sendPager(MailSender sender, String commandPre, String commandSuf, MessageParts returnButton = new MessageParts( Messages.get("Return"), ChatColor.AQUA); returnButton.setClickEvent(ClickEventType.RUN_COMMAND, returnCommand); - returnButton.addHoverText(Messages.get("ReturnToolTip")); + returnButton.setHoverText(Messages.get("ReturnToolTip")); msg.addParts(returnButton); msg.addText(" "); @@ -773,7 +696,7 @@ private void sendPager(MailSender sender, String commandPre, String commandSuf, firstLabel, ChatColor.AQUA); firstButton.setClickEvent(ClickEventType.RUN_COMMAND, commandPre + " 1" + commandSuf); - firstButton.addHoverText(firstToolTip); + firstButton.setHoverText(firstToolTip); msg.addParts(firstButton); msg.addText(" "); @@ -782,7 +705,7 @@ private void sendPager(MailSender sender, String commandPre, String commandSuf, prevLabel, ChatColor.AQUA); prevButton.setClickEvent(ClickEventType.RUN_COMMAND, commandPre + " " + (page - 1) + commandSuf); - prevButton.addHoverText(prevToolTip); + prevButton.setHoverText(prevToolTip); msg.addParts(prevButton); } else { @@ -797,7 +720,7 @@ private void sendPager(MailSender sender, String commandPre, String commandSuf, nextLabel, ChatColor.AQUA); nextButton.setClickEvent(ClickEventType.RUN_COMMAND, commandPre + " " + (page + 1) + commandSuf); - nextButton.addHoverText(nextToolTip); + nextButton.setHoverText(nextToolTip); msg.addParts(nextButton); msg.addText(" "); @@ -806,7 +729,7 @@ private void sendPager(MailSender sender, String commandPre, String commandSuf, lastLabel, ChatColor.AQUA); lastButton.setClickEvent(ClickEventType.RUN_COMMAND, commandPre + " " + max + commandSuf); - lastButton.addHoverText(lastToolTip); + lastButton.setHoverText(lastToolTip); msg.addParts(lastButton); } else { @@ -815,14 +738,37 @@ private void sendPager(MailSender sender, String commandPre, String commandSuf, msg.addText(" " + parts); - sendMessageComponent(msg, sender); + sender.sendMessageComponent(msg); + } + + /** + * PEXからグループを取得する。 + * @param name グループ名 + * @return PEXからインポートされたグループ + */ + public GroupData getPexGroup(String name) { + if ( name.startsWith(SpecialGroupPex.NAME_PREFIX) && pexGroupsCache != null ) { + if ( pexGroupsCache.containsKey(name) ) { + return pexGroupsCache.get(name); + } + } + + if (pexGroupsCache == null) { + for (GroupData group : getPexGroups()) { + if (group.getName().equals(name)) { + return group; + } + } + } + + return null; } /** * PEXからグループを取得してGroupDataに変換して返す * @return PEXからインポートされたグループ */ - private ArrayList getPexGroups() { + public ArrayList getPexGroups() { PermissionsExBridge pex = UndineMailer.getInstance().getPex(); ArrayList results = new ArrayList(); @@ -843,17 +789,4 @@ private ArrayList getPexGroups() { return results; } - - /** - * 指定されたメッセージコンポーネントを、指定されたMailSenderに送信する。 - * @param msg メッセージコンポーネント - * @param sender 送信先 - */ - private void sendMessageComponent(MessageComponent msg, MailSender sender) { - if ( sender instanceof MailSenderPlayer && sender.isOnline() ) { - msg.send(sender.getPlayer()); - } else if ( sender instanceof MailSenderConsole ) { - msg.send(Bukkit.getConsoleSender()); - } - } } diff --git a/src/main/java/org/bitbucket/ucchy/undine/group/GroupManagerFlatFile.java b/src/main/java/org/bitbucket/ucchy/undine/group/GroupManagerFlatFile.java new file mode 100644 index 0000000..f83824f --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/group/GroupManagerFlatFile.java @@ -0,0 +1,145 @@ +/* + * @author ucchy + * @license LGPLv3 + * @copyright Copyright ucchy 2015 + */ +package org.bitbucket.ucchy.undine.group; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.bitbucket.ucchy.undine.UndineMailer; +import org.bitbucket.ucchy.undine.sender.MailSender; + +/** + * メールグループをファイルで管理するクラス + * @author ucchy + */ +public class GroupManagerFlatFile extends GroupManager { + + private HashMap groups; + + /** + * コンストラクタ + * + * @param parent + */ + public GroupManagerFlatFile(UndineMailer parent) { + super(parent); + } + + /** + * 全データを再読み込みする + */ + public void reload() { + + long start = System.currentTimeMillis(); + + File folder = parent.getGroupFolder(); + File[] files = folder.listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.endsWith(".yml"); + } + }); + + groups = new HashMap(); + + if ( files != null ) { + for ( File f : files ) { + GroupData group = GroupDataFlatFile.loadFromFile(parent, f); + groups.put(group.getName().toLowerCase(), group); + } + } + + // 特殊グループを追加する + groups.put(groupAll.getName().toLowerCase(), groupAll); + groups.put(groupAllConnected.getName().toLowerCase(), groupAllConnected); + groups.put(groupAllLogin.getName().toLowerCase(), groupAllLogin); + + // アップグレード処理 + start = System.currentTimeMillis(); + + int total = 0; + for ( GroupData group : groups.values() ) { + if ( group.upgrade() ) { + saveGroupData(group); + total++; + } + } + + if ( total > 0 ) { + UndineMailer.getInstance().getLogger().info("Upgrade group data... Done. Time: " + + (System.currentTimeMillis() - start) + "ms, Data: " + total + "."); + } + } + + @Override + public void addGroup(GroupData group) { + String name = group.getName().toLowerCase(); + groups.put(name, group); + saveGroupData(group); + } + + @Override + public GroupData getGroup(String name) { + name = name.toLowerCase(); + GroupData pexGroup = getPexGroup(name); + return pexGroup != null ? pexGroup : groups.get(name); + } + + @Override + public ArrayList getGroups(List names) { + ArrayList groups = new ArrayList<>(); + for (String name : names) { + groups.add(getGroup(name)); + } + return groups; + } + + @Override + public void removeGroup(String name) { + name = name.toLowerCase(); + if ( groups.containsKey(name) ) { + groups.remove(name); + File folder = parent.getGroupFolder(); + File file = new File(folder, name + ".yml"); + file.delete(); + } + } + + @Override + public ArrayList getAllGroupNames() { + ArrayList names = new ArrayList(); + for ( GroupData group : groups.values() ) { + names.add(group.getName()); + } + return names; + } + + @Override + public ArrayList getAllGroups() { + return new ArrayList(groups.values()); + } + + @Override + public boolean existGroupName(String name) { + return groups.keySet().contains(name.toLowerCase()); + } + + @Override + public void saveGroupData(GroupData group) { + group.save(); + } + + @Override + public int getOwnerGroupCount(MailSender sender) { + int total = 0; + for ( GroupData group : groups.values() ) { + if ( group.getOwner().equals(sender) ) total++; + } + return total; + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroup.java b/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroup.java new file mode 100644 index 0000000..45ab8e7 --- /dev/null +++ b/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroup.java @@ -0,0 +1,181 @@ +/* + * @author ucchy + * @license LGPLv3 + * @copyright Copyright ucchy 2015 + */ +package org.bitbucket.ucchy.undine.group; + +import java.util.ArrayList; + +import org.bitbucket.ucchy.undine.UndineMailer; +import org.bitbucket.ucchy.undine.sender.MailSender; + +/** + * メールグループ + * @author ucchy + */ +public abstract class SpecialGroup extends GroupData { + + private MailSender owner; + private ArrayList members; + private GroupPermissionMode sendMode; + private GroupPermissionMode modifyMode; + private GroupPermissionMode dissolutionMode; + + /** + * コンストラクタ(継承クラス用) + * + * @param name グループ名 + */ + protected SpecialGroup(String name) { + super(null, name); + members = new ArrayList(); + sendMode = UndineMailer.getInstance().getUndineConfig().getSendModeDefault(); + modifyMode = UndineMailer.getInstance().getUndineConfig().getModifyModeDefault(); + dissolutionMode = UndineMailer.getInstance().getUndineConfig().getDissolutionModeDefault(); + } + + /** + * グループにメンバーを追加する + * + * @param member メンバー + */ + @Override + public void addMember(MailSender member) { + if (!members.contains(member)) { + members.add(member); + } + } + + /** + * グループからメンバーを削除する + * + * @param member メンバー + */ + @Override + public void removeMember(MailSender member) { + if (members.contains(member)) { + members.remove(member); + } + } + + /** + * グループのオーナーを取得する + * + * @return オーナー + */ + @Override + public MailSender getOwner() { + return owner; + } + + /** + * グループのオーナーを設定する + * + * @param owner + */ + @Override + public void setOwner(MailSender owner) { + this.owner = owner; + } + + /** + * グループのメンバーを取得する + * + * @return メンバー + */ + @Override + public ArrayList getMembers() { + return members; + } + + /** + * 指定されたsenderがオーナーかどうかを返す + * + * @param sender + * @return オーナーかどうか + */ + @Override + public boolean isOwner(MailSender sender) { + return owner.equals(sender); + } + + /** + * 送信権限モードを取得する + * + * @return sendMode + */ + @Override + public GroupPermissionMode getSendMode() { + return sendMode; + } + + /** + * 送信権限モードを設定する + * + * @param sendMode sendMode + */ + @Override + public void setSendMode(GroupPermissionMode sendMode) { + this.sendMode = sendMode; + } + + /** + * 変更権限モードを取得する + * + * @return modifyMode + */ + @Override + public GroupPermissionMode getModifyMode() { + return modifyMode; + } + + /** + * 変更権限モードを設定する + * + * @param modifyMode modifyMode + */ + @Override + public void setModifyMode(GroupPermissionMode modifyMode) { + this.modifyMode = modifyMode; + } + + /** + * 解散権限モードを取得する + * + * @return dissolutionMode + */ + @Override + public GroupPermissionMode getDissolutionMode() { + return dissolutionMode; + } + + /** + * 解散権限モードを設定する + * + * @param dissolutionMode dissolutionMode + */ + @Override + public void setDissolutionMode(GroupPermissionMode dissolutionMode) { + this.dissolutionMode = dissolutionMode; + } + + /** + * ファイルにグループを保存する + * @see org.bitbucket.ucchy.undine.group.GroupData#save() + * @deprecated このメソッドは実際は何も実行されません。 + */ + @Deprecated + @Override + public void save() { + // Do nothing. + } + + /** + * データのアップグレードを行う。 + * @return アップグレードを実行したかどうか + */ + protected boolean upgrade() { + return false; + } +} diff --git a/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupAll.java b/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupAll.java index d786816..a14577f 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupAll.java +++ b/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupAll.java @@ -5,7 +5,6 @@ */ package org.bitbucket.ucchy.undine.group; -import java.io.File; import java.util.ArrayList; import org.bitbucket.ucchy.undine.Messages; @@ -18,7 +17,7 @@ * 特殊グループ all * @author ucchy */ -public class SpecialGroupAll extends GroupData { +public class SpecialGroupAll extends SpecialGroup { public static final String NAME = "All"; @@ -29,8 +28,18 @@ public SpecialGroupAll() { super(NAME); setOwner(MailSender.getMailSender(Bukkit.getConsoleSender())); setSendMode(UndineMailer.getInstance().getUndineConfig().getSpecialGroupAllSendMode()); - setModifyMode(GroupPermissionMode.NEVER); - setDissolutionMode(GroupPermissionMode.NEVER); + super.setModifyMode(GroupPermissionMode.NEVER); + super.setDissolutionMode(GroupPermissionMode.NEVER); + } + + @Override + public void setDissolutionMode(GroupPermissionMode dissolutionMode) { + // Do nothing. + } + + @Override + public void setModifyMode(GroupPermissionMode modifyMode) { + // Do nothing. } /** @@ -41,7 +50,7 @@ public SpecialGroupAll() { @Override @Deprecated public ArrayList getMembers() { - return super.getMembers(); + return new ArrayList<>(); } /** @@ -55,18 +64,6 @@ public boolean isMember(MailSender sender) { return true; // 常にtrueを返す } - /** - * ファイルにグループを保存する - * @param file ファイル - * @see org.bitbucket.ucchy.undine.group.GroupData#saveToFile(java.io.File) - * @deprecated このメソッドは実際は何も実行されません。 - */ - @Override - @Deprecated - protected void saveToFile(File file) { - // do nothing. - } - /** * ホバー用のテキストを作成して返す * @return ホバー用のテキスト diff --git a/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupAllConnected.java b/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupAllConnected.java index e616c0e..e6f7209 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupAllConnected.java +++ b/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupAllConnected.java @@ -5,12 +5,12 @@ */ package org.bitbucket.ucchy.undine.group; -import java.io.File; import java.util.ArrayList; import org.bitbucket.ucchy.undine.Messages; import org.bitbucket.ucchy.undine.UndineMailer; import org.bitbucket.ucchy.undine.sender.MailSender; +import org.bitbucket.ucchy.undine.sender.MailSenderPlayer; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -18,7 +18,7 @@ * 特殊グループ AllConnected * @author ucchy */ -public class SpecialGroupAllConnected extends GroupData { +public class SpecialGroupAllConnected extends SpecialGroup { public static final String NAME = "AllConnected"; @@ -40,8 +40,8 @@ protected SpecialGroupAllConnected() { @Override public ArrayList getMembers() { ArrayList members = new ArrayList(); - for ( MailSender sender : UndineMailer.getInstance().getPlayerCache().values() ) { - members.add(sender); + for ( String uuid : UndineMailer.getInstance().getPlayerUuids() ) { + members.add(new MailSenderPlayer("$" + uuid)); } return members; } @@ -54,19 +54,10 @@ public ArrayList getMembers() { */ @Override public boolean isMember(MailSender sender) { - return UndineMailer.getInstance().getPlayerCache().containsValue(sender); - } - - /** - * ファイルにグループを保存する - * @param file ファイル - * @see org.bitbucket.ucchy.undine.group.GroupData#saveToFile(java.io.File) - * @deprecated このメソッドは実際は何も実行されません。 - */ - @Override - @Deprecated - protected void saveToFile(File file) { - // do nothing. + if ( !(sender instanceof MailSenderPlayer) ) { + return false; + } + return ((MailSenderPlayer)sender).isUuidCached(); } /** diff --git a/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupAllLogin.java b/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupAllLogin.java index 478b502..b80e19b 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupAllLogin.java +++ b/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupAllLogin.java @@ -5,7 +5,6 @@ */ package org.bitbucket.ucchy.undine.group; -import java.io.File; import java.util.ArrayList; import org.bitbucket.ucchy.undine.Messages; @@ -21,7 +20,7 @@ * 特殊グループ AllLogin * @author ucchy */ -public class SpecialGroupAllLogin extends GroupData { +public class SpecialGroupAllLogin extends SpecialGroup { public static final String NAME = "AllLogin"; @@ -60,18 +59,6 @@ public boolean isMember(MailSender sender) { return (sender instanceof MailSenderPlayer && sender.isOnline()); } - /** - * ファイルにグループを保存する - * @param file ファイル - * @see org.bitbucket.ucchy.undine.group.GroupData#saveToFile(java.io.File) - * @deprecated このメソッドは実際は何も実行されません。 - */ - @Override - @Deprecated - protected void saveToFile(File file) { - // do nothing. - } - /** * ホバー用のテキストを作成して返す * @return ホバー用のテキスト diff --git a/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupPex.java b/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupPex.java index 24409b3..e5d8d9e 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupPex.java +++ b/src/main/java/org/bitbucket/ucchy/undine/group/SpecialGroupPex.java @@ -5,9 +5,6 @@ */ package org.bitbucket.ucchy.undine.group; -import java.io.File; -import java.util.ArrayList; - import org.bitbucket.ucchy.undine.UndineMailer; import org.bitbucket.ucchy.undine.sender.MailSender; import org.bitbucket.ucchy.undine.sender.MailSenderDummy; @@ -16,7 +13,7 @@ * PEXからインポートされたグループ * @author ucchy */ -public class SpecialGroupPex extends GroupData { +public class SpecialGroupPex extends SpecialGroup { public static final String NAME_PREFIX = "(pex)"; @@ -37,17 +34,6 @@ public SpecialGroupPex(String name, GroupPermissionMode sendmode) { setSendMode(sendmode); } - /** - * グループのメンバーを取得する - * @see org.bitbucket.ucchy.undine.group.GroupData#getMembers() - * @deprecated このメソッドは期待した結果とは違う結果を返します。 - */ - @Override - @Deprecated - public ArrayList getMembers() { - return super.getMembers(); - } - /** * 指定されたsenderが、グループのメンバーかどうかを返す * @param sender @@ -60,16 +46,4 @@ public ArrayList getMembers() { public boolean isMember(MailSender sender) { return true; // 常にtrueを返す } - - /** - * ファイルにグループを保存する - * @param file ファイル - * @see org.bitbucket.ucchy.undine.group.GroupData#saveToFile(java.io.File) - * @deprecated このメソッドは実際は何も実行されません。 - */ - @Override - @Deprecated - protected void saveToFile(File file) { - // do nothing. - } } diff --git a/src/main/java/org/bitbucket/ucchy/undine/item/ItemConfigParseException.java b/src/main/java/org/bitbucket/ucchy/undine/item/ItemConfigParseException.java deleted file mode 100644 index 255ab45..0000000 --- a/src/main/java/org/bitbucket/ucchy/undine/item/ItemConfigParseException.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * @author ucchy - * @license LGPLv3 - * @copyright Copyright ucchy 2015 - */ -package org.bitbucket.ucchy.undine.item; - -/** - * ItemConfigParserでのパース処理の例外クラス - * @author ucchy - */ -public class ItemConfigParseException extends Exception { - - public ItemConfigParseException(String message) { - super(message); - } - - public ItemConfigParseException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/org/bitbucket/ucchy/undine/item/ItemConfigParser.java b/src/main/java/org/bitbucket/ucchy/undine/item/ItemConfigParser.java deleted file mode 100644 index 5c47a1c..0000000 --- a/src/main/java/org/bitbucket/ucchy/undine/item/ItemConfigParser.java +++ /dev/null @@ -1,523 +0,0 @@ -/* - * @author ucchy - * @license LGPLv3 - * @copyright Copyright ucchy 2015 - */ -package org.bitbucket.ucchy.undine.item; - -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.bukkit.Bukkit; -import org.bukkit.Color; -import org.bukkit.FireworkEffect; -import org.bukkit.FireworkEffect.Builder; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.BookMeta; -import org.bukkit.inventory.meta.EnchantmentStorageMeta; -import org.bukkit.inventory.meta.FireworkMeta; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.LeatherArmorMeta; -import org.bukkit.inventory.meta.SkullMeta; - -/** - * アイテム設定のパーサー - * @author ucchy - */ -public class ItemConfigParser { - - /** - * コンフィグセクションから、アイテム設定を読みだして、ItemStackを生成して返します。 - * @param section コンフィグセクション - * @return ItemStack - */ - public static ItemStack getItemFromSection(ConfigurationSection section) - throws ItemConfigParseException { - - if ( section == null ) { - return null; - } - - ItemStack item = null; - - if ( !section.contains("material") ) { - throw new ItemConfigParseException("Material tag was not found."); - } - - // 通常のアイテム設定 - - // materialは大文字に変換して読み込ませる - String mname = section.getString("material"); - Material material = Material.getMaterial(mname.toUpperCase()); - if ( material == null ) { - throw new ItemConfigParseException( - "Material name '" + mname + "' is invalid."); - } - if ( material == Material.AIR ) { - return new ItemStack(Material.AIR); - } - - // データ値はここで設定する - short data = (short)section.getInt("data", 0); - item = new ItemStack(material, 1, data); - - // アイテムの個数 - item.setAmount(section.getInt("amount", 1)); - - // アイテムの表示名 - if ( section.contains("display_name") ) { - String dname = section.getString("display_name"); - ItemMeta meta = item.getItemMeta(); - meta.setDisplayName(dname); - item.setItemMeta(meta); - } - - // アイテムの説明 - if ( section.contains("lore") ) { - List lore = section.getStringList("lore"); - ItemMeta meta = item.getItemMeta(); - meta.setLore(lore); - item.setItemMeta(meta); - } - - // エンチャント - if ( section.contains("enchants") ) { - ConfigurationSection enchants_sec = section.getConfigurationSection("enchants"); - for ( String type_str : enchants_sec.getKeys(false) ) { - Enchantment enchant = Enchantment.getByName(type_str); - - if ( enchant == null ) { - throw new ItemConfigParseException( - "Enchant type '" + type_str + "' is invalid."); - } - - int level = enchants_sec.getInt(type_str, 1); - if ( level < enchant.getStartLevel() ) { - level = enchant.getStartLevel(); - } else if ( level > 1000 ) { - level = 1000; - } - item.addUnsafeEnchantment(enchant, level); - } - } - - // 保存エンチャント - if ( section.contains("stored-enchants") - && item.getItemMeta() instanceof EnchantmentStorageMeta ) { - EnchantmentStorageMeta meta = (EnchantmentStorageMeta)item.getItemMeta(); - ConfigurationSection enchants_sec = section.getConfigurationSection("stored-enchants"); - for ( String type_str : enchants_sec.getKeys(false) ) { - Enchantment enchant = Enchantment.getByName(type_str); - - if ( enchant == null ) { - throw new ItemConfigParseException( - "Enchant type '" + type_str + "' is invalid."); - } - - int level = enchants_sec.getInt(type_str, 1); - meta.addStoredEnchant(enchant, level, true); - } - item.setItemMeta(meta); - } - - // 消耗度 - if ( section.contains("remain") ) { - - short remain = (short)section.getInt("remain"); - short durability = (short)(item.getType().getMaxDurability() - remain + 1); - if ( durability < 0 ) { - durability = 0; - } - item.setDurability(durability); - } - - // 革防具の染色設定 - if ( item.getType() == Material.LEATHER_BOOTS || - item.getType() == Material.LEATHER_LEGGINGS || - item.getType() == Material.LEATHER_CHESTPLATE || - item.getType() == Material.LEATHER_HELMET ) { - - LeatherArmorMeta lam = (LeatherArmorMeta)item.getItemMeta(); - int red = section.getInt("red", 160); - int blue = section.getInt("blue", 101); - int green = section.getInt("green", 64); - Color color = Color.fromBGR(blue, green, red); - lam.setColor(color); - item.setItemMeta(lam); - } - - // スカルの詳細設定 - if ( item.getType() == Material.SKULL_ITEM && - section.contains("owner") ) { - - SkullMeta sm = (SkullMeta)item.getItemMeta(); - if ( sm.setOwner(section.getString("owner")) ) { - item.setItemMeta(sm); - } - } - - // 本の詳細設定 - if ( item.getType() == Material.WRITTEN_BOOK || - item.getType() == Material.BOOK_AND_QUILL ) { - - BookMeta bm = (BookMeta)item.getItemMeta(); - boolean needToSet = false; - if ( section.contains("author") ) { - bm.setAuthor(section.getString("author")); - needToSet = true; - } - if ( section.contains("title") ) { - bm.setTitle(section.getString("title")); - needToSet = true; - } - if ( section.contains("pages") ) { - bm.setPages(section.getStringList("pages")); - needToSet = true; - } - if ( needToSet ) { - item.setItemMeta(bm); - } - } - - // ポーションの詳細設定 - if ( isCB19orLater() ) { - ItemConfigParserV19.addPotionInfoToItem(item, section); - } else { - ItemConfigParserLegacyPotion.addPotionInfoToItem(item, section); - } - - // 花火の詳細設定 - if ( item.getType() == Material.FIREWORK ) { - - FireworkMeta meta = (FireworkMeta)item.getItemMeta(); - meta.setPower(section.getInt("power", 1)); - - if ( section.contains("effects") ) { - - for ( String key : - section.getConfigurationSection("effects").getKeys(false) ) { - - ConfigurationSection effect_sec = - section.getConfigurationSection("effects." + key); - String tname = effect_sec.getString("type"); - if ( tname == null ) { -// throw new ItemConfigParseException( -// "Effect type tag was not found."); - continue; - } - FireworkEffect.Type type = getFireworkEffectTypeByName(tname); - if ( type == null ) { -// throw new ItemConfigParseException( -// "Effect type '" + tname + "' is invalid."); - continue; - } - - Builder effect = FireworkEffect.builder(); - effect.with(type); - effect.flicker(effect_sec.getBoolean("flicker", false)); - effect.trail(effect_sec.getBoolean("trail", false)); - - if ( effect_sec.contains("colors") ) { - for ( String ckey : - effect_sec.getConfigurationSection("colors").getKeys(false) ) { - - ConfigurationSection color_sec = - effect_sec.getConfigurationSection("colors." + ckey); - int red = color_sec.getInt("red", 255); - int blue = color_sec.getInt("blue", 255); - int green = color_sec.getInt("green", 255); - effect.withColor(Color.fromBGR(blue, green, red)); - } - } - - if ( effect_sec.contains("fades") ) { - for ( String fkey : - effect_sec.getConfigurationSection("fades").getKeys(false) ) { - - ConfigurationSection fade_sec = - effect_sec.getConfigurationSection("fades." + fkey); - int red = fade_sec.getInt("red", 255); - int blue = fade_sec.getInt("blue", 255); - int green = fade_sec.getInt("green", 255); - effect.withFade(Color.fromBGR(blue, green, red)); - } - } - - meta.addEffect(effect.build()); - } - } - - item.setItemMeta(meta); - } - - if ( isCB18orLater() ) { - // バナーの詳細設定 - item = ItemConfigParserV18.addBannerInfoToItem(section, item); - - // アイテムフラグの詳細設定 - item = ItemConfigParserV18.addItemFlagsToItem(section, item); - } - - return item; - } - - /** - * 指定されたコンフィグセクションに、指定されたItemStackの情報を保存します。 - * @param section コンフィグセクション - * @param item アイテム - */ - public static void setItemToSection(ConfigurationSection section, ItemStack item) { - - if ( section == null || item == null ) { - return; - } - - section.set("material", item.getType().toString()); - if ( item.getAmount() > 1 ) { - section.set("amount", item.getAmount()); - } - - short data = item.getDurability(); - if ( item.getDurability() > 0 && item.getType().getMaxDurability() > 1 ) { - int remain = item.getType().getMaxDurability() - item.getDurability() + 1; - section.set("remain", remain); - } else if ( data > 0 && item.getType() != Material.POTION ) { - section.set("data", data); - } - - if ( item.hasItemMeta() ) { - ItemMeta meta = item.getItemMeta(); - if ( meta.hasDisplayName() ) { - section.set("display_name", meta.getDisplayName()); - } - if ( meta.hasLore() ) { - section.set("lore", meta.getLore()); - } - } - - if ( item.getEnchantments().size() > 0 ) { - ConfigurationSection sub = section.createSection("enchants"); - for ( Enchantment ench : item.getEnchantments().keySet() ) { - sub.set(ench.getName(), item.getEnchantmentLevel(ench)); - } - } - - if ( item.getItemMeta() instanceof EnchantmentStorageMeta ) { - EnchantmentStorageMeta meta = (EnchantmentStorageMeta)item.getItemMeta(); - ConfigurationSection sub = section.createSection("stored-enchants"); - for ( Enchantment ench : meta.getStoredEnchants().keySet() ) { - sub.set(ench.getName(), meta.getStoredEnchantLevel(ench)); - } - } - - if ( item.getType() == Material.LEATHER_BOOTS || - item.getType() == Material.LEATHER_LEGGINGS || - item.getType() == Material.LEATHER_CHESTPLATE || - item.getType() == Material.LEATHER_HELMET ) { - - LeatherArmorMeta lam = (LeatherArmorMeta)item.getItemMeta(); - section.set("red", lam.getColor().getRed()); - section.set("blue", lam.getColor().getBlue()); - section.set("green", lam.getColor().getGreen()); - } - - if ( item.getType() == Material.SKULL_ITEM ) { - - SkullMeta sm = (SkullMeta)item.getItemMeta(); - if ( sm.hasOwner() ) { - section.set("owner", sm.getOwner()); - } - } - - if ( item.getType() == Material.WRITTEN_BOOK || - item.getType() == Material.BOOK_AND_QUILL ) { - - BookMeta bm = (BookMeta)item.getItemMeta(); - if ( bm.hasAuthor() ) { - section.set("author", bm.getAuthor()); - } - if ( bm.hasTitle() ) { - section.set("title", bm.getTitle()); - } - if ( bm.hasPages() ) { - section.set("pages", bm.getPages()); - } - } - - if ( isCB19orLater() ) { - ItemConfigParserV19.addPotionInfoToSection(item, section); - } else { - ItemConfigParserLegacyPotion.addPotionInfoToSection(item, section); - } - - if ( item.getType() == Material.FIREWORK ) { - - FireworkMeta meta = (FireworkMeta)item.getItemMeta(); - section.set("power", meta.getPower()); - - if ( meta.hasEffects() ) { - ConfigurationSection effectSection = section.createSection("effects"); - List effects = meta.getEffects(); - - for ( int i=0; i colors = effect.getColors(); - if ( colors.size() > 0 ) { - ConfigurationSection colorSection = sub.createSection("colors"); - for ( int j=0; j fades = effect.getFadeColors(); - if ( fades.size() > 0 ) { - ConfigurationSection fadeSection = sub.createSection("fades"); - for ( int j=0; j - * ただし、無効なバージョン番号(数値でないなど)が指定された場合はfalseに、 - * 2つのバージョンが完全一致した場合はtrueになる。 - */ - private static boolean isUpperVersion(String version, String border) { - - int hyphen = version.indexOf("-"); - if ( hyphen > 0 ) { - version = version.substring(0, hyphen); - } - - String[] versionArray = version.split("\\."); - int[] versionNumbers = new int[versionArray.length]; - for ( int i=0; i index) && (borderNumbers.length > index) ) { - if ( versionNumbers[index] > borderNumbers[index] ) { - return true; - } else if ( versionNumbers[index] < borderNumbers[index] ) { - return false; - } - index++; - } - if ( borderNumbers.length == index ) { - return true; - } else { - return false; - } - } -} diff --git a/src/main/java/org/bitbucket/ucchy/undine/item/ItemConfigParserLegacyPotion.java b/src/main/java/org/bitbucket/ucchy/undine/item/ItemConfigParserLegacyPotion.java deleted file mode 100644 index 62e3ec5..0000000 --- a/src/main/java/org/bitbucket/ucchy/undine/item/ItemConfigParserLegacyPotion.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * @author ucchy - * @license LGPLv3 - * @copyright Copyright ucchy 2016 - */ -package org.bitbucket.ucchy.undine.item; - -import java.util.List; - -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.PotionMeta; -import org.bukkit.potion.Potion; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; -import org.bukkit.potion.PotionType; - -/** - * アイテム設定のパーサー(従来形式のPotion用) - * @author ucchy - */ -@SuppressWarnings("deprecation") -public class ItemConfigParserLegacyPotion { - - /** - * 指定されたアイテムがポーションだったときに、メタ情報をセクションに保存する。 - * @param item - * @param section - */ - protected static void addPotionInfoToSection(ItemStack item, ConfigurationSection section) { - - if ( item.getType() != Material.POTION || isWaterBottle(item) ) { - return; - } - - cleanupInvalidExtendedPotionFlag(item); - - Potion potion = Potion.fromItemStack(item); - section.set("potion_type", potion.getType().toString()); - section.set("potion_level", potion.getLevel()); - if ( potion.isSplash() ) { - section.set("splash", true); - } - if ( potion.hasExtendedDuration() ) { - section.set("extend", true); - } - - PotionMeta meta = (PotionMeta)item.getItemMeta(); - if ( meta.hasCustomEffects() ) { - // カスタムポーションの設定 - - ConfigurationSection customSection = - section.createSection("custom_effects"); - List customs = meta.getCustomEffects(); - for ( int i=0; i type.getMaxLevel() ) { - amp = type.getMaxLevel(); - } - - Potion potion = new Potion(type, amp); - potion.setSplash(section.getBoolean("splash", false)); - if ( !type.isInstant() ) { - potion.setHasExtendedDuration(section.getBoolean("extend", false)); - } - potion.apply(item); - - // カスタムポーションの詳細設定 - if ( section.contains("custom_effects") ) { - - PotionMeta meta = (PotionMeta)item.getItemMeta(); - PotionEffectType mainType = null; - - for ( String key : - section.getConfigurationSection("custom_effects").getKeys(false) ) { - - ConfigurationSection custom_sec = - section.getConfigurationSection("custom_effects." + key); - String cname = custom_sec.getString("type"); - if ( cname == null ) { -// throw new ItemConfigParseException( -// "Potion type tag was not found."); - continue; - } - PotionEffectType ctype = PotionEffectType.getByName(cname.toUpperCase()); - if ( ctype == null ) { -// throw new ItemConfigParseException( -// "Potion type '" + cname + "' is invalid."); - continue; - } - if ( mainType == null ) { - mainType = ctype; - } - int amplifier = custom_sec.getInt("amplifier", 1); - int duration = custom_sec.getInt("duration", 100); - boolean ambient = custom_sec.getBoolean("ambient", true); - PotionEffect effect = new PotionEffect(ctype, duration, amplifier, ambient); - meta.addCustomEffect(effect, ambient); - } - - if ( mainType != null ) { - meta.setMainEffect(mainType); - } - - item.setItemMeta(meta); - } - } - - /** - * 指定された文字列に一致するPotionTypeを返します。 - * @param name - * @return - */ - private static PotionType getPotionTypeByName(String name) { - - for ( PotionType type : PotionType.values() ) { - if ( type.name().equalsIgnoreCase(name) ) { - return type; - } - } - return null; - } - - /** - * 指定されたポーションが、水ボトル(データ値が0)かどうかを確認します。 - * @param item ポーション - * @return 水ボトルかどうか - */ - private static boolean isWaterBottle(ItemStack item) { - return item.getDurability() == 0; - } - - /** - * ポーションのデータ値を調べ、タイプにそぐわないextendフラグが立っている場合、 - * 強制的にフラグを降ろします。 - * @param item ポーション - */ - private static void cleanupInvalidExtendedPotionFlag(ItemStack item) { - - short data = item.getDurability(); - int typeFlag = data & 0xF; - - if ( typeFlag == 5 || typeFlag == 12 ) { - // INSTANT_DAMAGE か INSTANT_HEAL なら、extendフラグを確認し、 - // フラグが立っているなら降ろす。 - - if ( (data & 0x40) > 0 ) { - data -= 0x40; - item.setDurability(data); - } - } - } -} diff --git a/src/main/java/org/bitbucket/ucchy/undine/item/ItemConfigParserV18.java b/src/main/java/org/bitbucket/ucchy/undine/item/ItemConfigParserV18.java deleted file mode 100644 index c3992cd..0000000 --- a/src/main/java/org/bitbucket/ucchy/undine/item/ItemConfigParserV18.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * @author ucchy - * @license LGPLv3 - * @copyright Copyright ucchy 2015 - */ -package org.bitbucket.ucchy.undine.item; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Set; - -import org.bukkit.DyeColor; -import org.bukkit.Material; -import org.bukkit.block.banner.Pattern; -import org.bukkit.block.banner.PatternType; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.inventory.ItemFlag; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.BannerMeta; -import org.bukkit.inventory.meta.ItemMeta; - -/** - * アイテム設定のパーサー for Bukkit v1.8 - * @author ucchy - */ -public class ItemConfigParserV18 { - - /** - * Bannerのメタデータを含める必要がある場合に、メタ情報を復帰して含めておく。 - * @param section - * @param item - * @return 変更後のItemStack - */ - protected static ItemStack addBannerInfoToItem( - ConfigurationSection section, ItemStack item) { - - if ( item.getType() != Material.BANNER ) { - return item; - } - - BannerMeta banner = (BannerMeta)item.getItemMeta(); - - if ( section.contains("basecolor") ) { - banner.setBaseColor(getDyeColorFromString(section.getString("basecolor"))); - } - - if ( section.contains("patterns") ) { - ConfigurationSection psec = section.getConfigurationSection("patterns"); - - HashMap patterns = new HashMap(); - - for ( String name : psec.getKeys(false) ) { - - // 数値にキャストできなさそうなら無視する - if ( !name.matches("[0-9]{1,9}") ) { - continue; - } - int index = Integer.parseInt(name); - - ConfigurationSection sub = psec.getConfigurationSection(name); - PatternType type = getPatternTypeFromString(sub.getString("type")); - DyeColor color = getDyeColorFromString(sub.getString("color")); - patterns.put(index, new Pattern(color, type)); - } - - // 序数の低い方から順にaddする - ArrayList indexes = new ArrayList(patterns.keySet()); - Collections.sort(indexes); - for ( int index : indexes ) { - banner.addPattern(patterns.get(index)); - } - } - - item.setItemMeta(banner); - - return item; - } - - /** - * 指定されたアイテムがバナーだったときに、メタ情報をセクションに保存する。 - * @param section - * @param item - */ - protected static ConfigurationSection addBannerInfoToSection( - ConfigurationSection section, ItemStack item) { - - if ( item.getType() != Material.BANNER ) { - return section; - } - - BannerMeta banner = (BannerMeta)item.getItemMeta(); - - if ( banner.getBaseColor() != null ) { - section.set("basecolor", banner.getBaseColor().toString()); - } - - List patterns = banner.getPatterns(); - if ( patterns.size() > 0 ) { - ConfigurationSection psec = section.createSection("patterns"); - - for ( int index=0; index flags = item.getItemMeta().getItemFlags(); - if ( flags == null || flags.size() == 0 ) return section; - - List flagList = new ArrayList(); - for ( ItemFlag flag : flags ) { - flagList.add(flag.name()); - } - section.set("itemflags", flagList); - - return section; - } - - private static DyeColor getDyeColorFromString(String code) { - - if ( code == null ) { - return DyeColor.WHITE; - } - for ( DyeColor c : DyeColor.values() ) { - if ( c.toString().equalsIgnoreCase(code) ) { - return c; - } - } - return DyeColor.WHITE; - } - - private static PatternType getPatternTypeFromString(String code) { - - if ( code == null ) { - return PatternType.BASE; - } - for ( PatternType type : PatternType.values() ) { - if ( type.toString().equalsIgnoreCase(code) ) { - return type; - } - } - return PatternType.BASE; - } - - private static ItemFlag getItemFlagFromString(String src) { - - if ( src == null ) return null; - for ( ItemFlag flag : ItemFlag.values() ) { - if ( flag.name().equalsIgnoreCase(src) ) { - return flag; - } - } - return null; - } -} diff --git a/src/main/java/org/bitbucket/ucchy/undine/item/ItemConfigParserV19.java b/src/main/java/org/bitbucket/ucchy/undine/item/ItemConfigParserV19.java deleted file mode 100644 index 19edd98..0000000 --- a/src/main/java/org/bitbucket/ucchy/undine/item/ItemConfigParserV19.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * @author ucchy - * @license LGPLv3 - * @copyright Copyright ucchy 2016 - */ -package org.bitbucket.ucchy.undine.item; - -import java.util.List; - -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.PotionMeta; -import org.bukkit.potion.PotionData; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; -import org.bukkit.potion.PotionType; - -/** - * アイテム設定のパーサー for Bukkit v1.9 - * @author ucchy - */ -public class ItemConfigParserV19 { - - /** - * 指定されたアイテムがポーション関連アイテムだったときに、メタ情報をセクションに保存する。 - * @param item - * @param section - */ - protected static void addPotionInfoToSection(ItemStack item, ConfigurationSection section) { - - if ( !isPotionRelatedItem(item) ) return; - - PotionMeta meta = (PotionMeta)item.getItemMeta(); - - // ベースポーションの設定 - PotionData base = meta.getBasePotionData(); - section.set("potion_type", base.getType().toString()); - if ( base.isUpgraded() ) { - section.set("upgrade", true); - } - if ( base.isExtended() ) { - section.set("extend", true); - } - - // カスタムポーションの設定 - if ( meta.hasCustomEffects() ) { - ConfigurationSection customSection = - section.createSection("custom_effects"); - List customs = meta.getCustomEffects(); - for ( int i=0; i { */ public abstract void sendMessage(String message); + /** + * 指定されたメッセージコンポーネントをこのMailSenderに送信する。 + * @param msg メッセージコンポーネント + */ + public abstract void sendMessageComponent(MessageComponent msg); + /** * BukkitのOfflinePlayerを取得する。 * @return OfflinePlayer @@ -193,11 +201,7 @@ public static MailSender getMailSender(CommandSender sender) { return new MailSenderConsole((ConsoleCommandSender)sender); } else if ( sender instanceof OfflinePlayer ) { OfflinePlayer player = (OfflinePlayer)sender; - if ( Utility.isCB178orLater() ) { - return new MailSenderPlayer("$" + player.getUniqueId().toString()); - } else { - return new MailSenderPlayer(player.getName()); - } + return new MailSenderPlayer("$" + UndineMailer.getInstance().getUUID(player.getName())); } return null; } diff --git a/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderBlock.java b/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderBlock.java index 409f76d..9e60165 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderBlock.java +++ b/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderBlock.java @@ -7,6 +7,8 @@ import java.util.List; +import com.github.ucchyocean.messaging.tellraw.MessageComponent; + import org.bitbucket.ucchy.undine.UndineMailer; import org.bukkit.Location; import org.bukkit.Material; @@ -23,7 +25,10 @@ */ public class MailSenderBlock extends MailSender { - BlockCommandSender sender; + private final BlockCommandSender sender; + + private final String name; + private final Location location; /** * コンストラクタ @@ -31,6 +36,14 @@ public class MailSenderBlock extends MailSender { */ public MailSenderBlock(BlockCommandSender sender) { this.sender = sender; + this.name = sender.getName(); + this.location = sender.getBlock().getLocation(); + } + + public MailSenderBlock(String name, Location location) { + this.sender = null; + this.name = name; + this.location = location; } /** @@ -59,8 +72,9 @@ public boolean isValidDestination() { */ @Override public String getName() { - if ( sender == null ) return "@"; - return sender.getName(); + if ( sender != null ) return sender.getName(); + if ( name != null ) return name; + return "@"; } /** @@ -70,8 +84,7 @@ public String getName() { */ @Override public String getDisplayName() { - if ( sender == null ) return "@"; - return sender.getName(); + return getName(); } /** @@ -83,6 +96,16 @@ public String getDisplayName() { public void sendMessage(String message) { // do nothing. } + + /** + * メッセージを送る、実際は何もせずにメッセージを捨てる + * @param message メッセージ + * @see org.bitbucket.ucchy.undine.sender.MailSender#sendMessageComponent(com.github.ucchyocean.messaging.tellraw.MessageComponent) + */ + @Override + public void sendMessageComponent(MessageComponent msg) { + // do nothing. + } /** * BukkitのOfflinePlayerを取得する。 @@ -111,8 +134,9 @@ public Player getPlayer() { */ @Override public String getWorldName() { - if ( sender == null || sender.getBlock() == null ) return ""; - return sender.getBlock().getWorld().getName(); + if ( sender != null && sender.getBlock() != null ) return sender.getBlock().getWorld().getName(); + if ( location != null && location.getWorld() != null ) return location.getWorld().getName(); + return ""; } /** @@ -156,7 +180,7 @@ public boolean isOp() { @Override public void setStringMetadata(String key, String value) { if ( sender == null || sender.getBlock() == null - || sender.getBlock().getType() != Material.COMMAND ) { + || !isCommandBlock(sender.getBlock().getType()) ) { return; } sender.getBlock().setMetadata(key, @@ -172,7 +196,7 @@ public void setStringMetadata(String key, String value) { @Override public String getStringMetadata(String key) { if ( sender == null || sender.getBlock() == null - || sender.getBlock().getType() != Material.COMMAND ) { + || !isCommandBlock(sender.getBlock().getType()) ) { return null; } List values = sender.getBlock().getMetadata(key); @@ -191,7 +215,7 @@ public String getStringMetadata(String key) { @Override public void setBooleanMetadata(String key, boolean value) { if ( sender == null || sender.getBlock() == null - || sender.getBlock().getType() != Material.COMMAND ) { + || !isCommandBlock(sender.getBlock().getType()) ) { return; } sender.getBlock().setMetadata(key, @@ -207,7 +231,7 @@ public void setBooleanMetadata(String key, boolean value) { @Override public boolean getBooleanMetadata(String key) { if ( sender == null || sender.getBlock() == null - || sender.getBlock().getType() != Material.COMMAND ) { + || !isCommandBlock(sender.getBlock().getType()) ) { return false; } List values = sender.getBlock().getMetadata(key); @@ -238,4 +262,8 @@ public boolean equals(CommandSender sender) { public String toString() { return getName(); } + + private boolean isCommandBlock(Material m) { + return !m.name().contains("MINECART") && m.name().contains("COMMAND"); + } } diff --git a/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderConsole.java b/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderConsole.java index c29f659..e122400 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderConsole.java +++ b/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderConsole.java @@ -7,6 +7,8 @@ import java.util.HashMap; +import com.github.ucchyocean.messaging.tellraw.MessageComponent; + import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.OfflinePlayer; @@ -82,6 +84,15 @@ public void sendMessage(String message) { sender.sendMessage(message); } + /** + * 指定されたメッセージコンポーネントをこのMailSenderに送信する。 + * @param msg メッセージコンポーネント + */ + @Override + public void sendMessageComponent(MessageComponent msg) { + msg.send(Bukkit.getConsoleSender()); + } + /** * BukkitのOfflinePlayerを取得する。 * @return 常にnullが返される diff --git a/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderDummy.java b/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderDummy.java index 8f2efc1..e6dd0ca 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderDummy.java +++ b/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderDummy.java @@ -5,6 +5,8 @@ */ package org.bitbucket.ucchy.undine.sender; +import com.github.ucchyocean.messaging.tellraw.MessageComponent; + import org.bukkit.Location; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; @@ -62,6 +64,14 @@ public void sendMessage(String message) { // do nothing. } + /** + * @see org.bitbucket.ucchy.undine.sender.MailSender#sendMessageComponent(com.github.ucchyocean.messaging.tellraw.MessageComponent) + */ + @Override + public void sendMessageComponent(MessageComponent msg) { + // do nothing. + } + /** * @see org.bitbucket.ucchy.undine.sender.MailSender#getOfflinePlayer() */ diff --git a/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderPlayer.java b/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderPlayer.java index 4410c02..59bb444 100644 --- a/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderPlayer.java +++ b/src/main/java/org/bitbucket/ucchy/undine/sender/MailSenderPlayer.java @@ -5,9 +5,9 @@ */ package org.bitbucket.ucchy.undine.sender; -import java.util.HashMap; import java.util.List; -import java.util.UUID; + +import com.github.ucchyocean.messaging.tellraw.MessageComponent; import org.bitbucket.ucchy.undine.UndineMailer; import org.bitbucket.ucchy.undine.Utility; @@ -41,11 +41,8 @@ public MailSenderPlayer(String nameOrUuid) { * @param player プレイヤー */ public MailSenderPlayer(OfflinePlayer player) { - if ( Utility.isCB178orLater() ) { - this.nameOrUuid = "$" + player.getUniqueId().toString(); - } else { - this.nameOrUuid = player.getName(); - } + String uuid = UndineMailer.getInstance().getUUID(player.getName()); + this.nameOrUuid = "$" + uuid; } /** @@ -69,6 +66,7 @@ public boolean isValidDestination() { if ( offline == null ) { offline = getOfflinePlayer(); } + return offline.hasPlayedBefore() || offline.isOnline(); } @@ -112,6 +110,17 @@ public void sendMessage(String message) { } } + /** + * 指定されたメッセージコンポーネントを、このMailSenderに送信する。 + * @param msg メッセージコンポーネント + */ + public void sendMessageComponent(MessageComponent msg) { + Player player = getPlayer(); + if ( player != null ) { + msg.send(player); + } + } + /** * BukkitのOfflinePlayerを取得する。 * @return OfflinePlayer @@ -122,7 +131,9 @@ public void sendMessage(String message) { public OfflinePlayer getOfflinePlayer() { if ( offline != null ) return offline; if ( nameOrUuid.startsWith("$") ) { - offline = Bukkit.getOfflinePlayer(UUID.fromString(nameOrUuid.substring(1))); + //offline = Bukkit.getOfflinePlayer(UUID.fromString(nameOrUuid.substring(1))); + String name = UndineMailer.getInstance().getName(nameOrUuid.substring(1)); + offline = Bukkit.getOfflinePlayer(name); } else { offline = Bukkit.getOfflinePlayer(nameOrUuid); } @@ -210,10 +221,11 @@ public void setStringMetadata(String key, String value) { if ( offline == null ) { offline = getOfflinePlayer(); } - if ( !offline.isOnline() ) { + Player player = offline.getPlayer(); + if ( !offline.isOnline() || player == null ) { return; } - offline.getPlayer().setMetadata(key, + player.setMetadata(key, new FixedMetadataValue(UndineMailer.getInstance(), value)); } @@ -228,10 +240,11 @@ public String getStringMetadata(String key) { if ( offline == null ) { offline = getOfflinePlayer(); } - if ( !offline.isOnline() ) { + Player player = offline.getPlayer(); + if ( !offline.isOnline() || player == null ) { return null; } - List values = offline.getPlayer().getMetadata(key); + List values = player.getMetadata(key); if ( values.size() == 0 ) { return null; } @@ -249,10 +262,11 @@ public void setBooleanMetadata(String key, boolean value) { if ( offline == null ) { offline = getOfflinePlayer(); } - if ( !offline.isOnline() ) { + Player player = offline.getPlayer(); + if ( !offline.isOnline() || player == null ) { return; } - offline.getPlayer().setMetadata(key, + player.setMetadata(key, new FixedMetadataValue(UndineMailer.getInstance(), value)); } @@ -267,15 +281,15 @@ public boolean getBooleanMetadata(String key) { if ( offline == null ) { offline = getOfflinePlayer(); } - if ( !offline.isOnline() ) { + Player player = offline.getPlayer(); + if ( !offline.isOnline() || player == null ) { return false; } - List values = offline.getPlayer().getMetadata(key); + List values = player.getMetadata(key); if ( values.size() == 0 ) { return false; } return values.get(0).asBoolean(); - } /** @@ -291,12 +305,23 @@ public boolean equals(CommandSender sender) { } OfflinePlayer player = (OfflinePlayer)sender; if ( nameOrUuid.startsWith("$") ) { - return nameOrUuid.equals("$" + player.getUniqueId().toString()); + return nameOrUuid.equals("$" + UndineMailer.getInstance().getUUID(player.getName())); } else { return nameOrUuid.equals(player.getName()); } } + /** + * このMailSenderPlayerのUUIDが、PlayerNameUuidCacheにキャッシュされているかどうかを返す + * @return キャッシュされているかどうか + */ + public boolean isUuidCached() { + upgrade(); + if ( !nameOrUuid.startsWith("$") ) return false; + String uuid = nameOrUuid.substring(1); + return UndineMailer.getInstance().getPlayerUuids().contains(uuid); + } + /** * IDを返す * @return CB178以降なら "$" + UUID を返す、CB175以前ならIDを返す @@ -321,33 +346,9 @@ public boolean upgrade() { if ( nameOrUuid.startsWith("$") ) return false; // nameOrUuidを、$ + UUID に変更する - String uuid = getUUID(nameOrUuid); + String uuid = UndineMailer.getInstance().getUUID(nameOrUuid); if ( uuid.equals("") ) return false; nameOrUuid = "$" + uuid; return true; } - - // アップグレード時に使用される、UUIDのキャッシュ - private static HashMap uuidCache; - - /** - * 指定された名前を持つプレイヤーのUUIDを取得する - * @param name プレイヤー名 - * @return UUID(文字列表記) - */ - private static String getUUID(String name) { - if ( uuidCache == null ) { - uuidCache = new HashMap(); - } - if ( !uuidCache.containsKey(name) ) { - @SuppressWarnings("deprecation") - OfflinePlayer player = Bukkit.getOfflinePlayer(name); - if ( player == null || player.getUniqueId() == null ) { - uuidCache.put(name, ""); - } else { - uuidCache.put(name, player.getUniqueId().toString()); - } - } - return uuidCache.get(name); - } } diff --git a/src/main/java/org/bitbucket/ucchy/undine/tellraw/ClickEventType.java b/src/main/java/org/bitbucket/ucchy/undine/tellraw/ClickEventType.java deleted file mode 100644 index fd6c2ed..0000000 --- a/src/main/java/org/bitbucket/ucchy/undine/tellraw/ClickEventType.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * @author ucchy - * @license LGPLv3 - * @copyright Copyright ucchy 2015 - */ -package org.bitbucket.ucchy.undine.tellraw; - -/** - * クリックイベント - * @author ucchy - */ -public enum ClickEventType { - - /** コマンドの実行 */ - RUN_COMMAND("run_command"), - - /** コマンドの提示 */ - SUGGEST_COMMAND("suggest_command"), - - /** リンク先へ飛ぶ */ - OPEN_URL("open_url"); - - private String text; - - /** - * コンストラクタ - * @param text - */ - private ClickEventType(String text) { - this.text = text; - } - - /** - * 文字列表現を返す - * @see java.lang.Enum#toString() - */ - public String toString() { - return text; - } -} diff --git a/src/main/java/org/bitbucket/ucchy/undine/tellraw/MessageComponent.java b/src/main/java/org/bitbucket/ucchy/undine/tellraw/MessageComponent.java deleted file mode 100644 index 501d8be..0000000 --- a/src/main/java/org/bitbucket/ucchy/undine/tellraw/MessageComponent.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * @author ucchy - * @license LGPLv3 - * @copyright Copyright ucchy 2015 - */ -package org.bitbucket.ucchy.undine.tellraw; - -import java.util.ArrayList; - -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -/** - * tellrawメッセージコンポーネント - * @author ucchy - */ -public class MessageComponent { - - private ArrayList parts; - - /** - * コンストラクタ - */ - public MessageComponent() { - parts = new ArrayList(); - } - - /** - * コンストラクタ - * @param parts メッセージパーツ - */ - public MessageComponent(ArrayList parts) { - this.parts = parts; - } - - /** - * テキストパーツを追加する - * @param text テキスト - */ - public void addText(String text) { - this.parts.add(new MessageParts(text)); - } - - /** - * テキストパーツを追加する - * @param text テキスト - * @param color テキスト色 - */ - public void addText(String text, ChatColor color) { - this.parts.add(new MessageParts(text, color)); - } - - /** - * テキストパーツを追加する - * @param parts テキストパーツ - */ - public void addParts(MessageParts parts) { - this.parts.add(parts); - } - - /** - * 指定されたsenderに、このコンポーネントを送信する。 - * 相手がプレイヤーならtellrawコマンドで、コンソールならプレーンなテキストデータで送る。 - * @param sender 送信先 - */ - public void send(CommandSender sender) { - if ( sender instanceof Player ) { - Player player = (Player)sender; - if ( player.isOnline() ) { - sendCommand(player); - } else { - // do nothing. - } - } else { - sender.sendMessage(buildPlain()); - } - } - - /** - * このコンポーネントが含んでいるパーツ数を返す - * @return パーツ数 - */ - public int getPartsSize() { - return parts.size(); - } - - /** - * このコンポーネントが含んでいるパーツを表示したときの、文字列の文字数トータルを返す - * @return 文字数 - */ - public int getTextLength() { - int total = 0; - for ( MessageParts part : parts ) { - total += part.buildPlain().length(); - } - return total; - } - - /** - * このコンポーネントをビルドして、tellrawのコマンド文字列を作成して返す - * @param name 実行先のプレイヤー名 - * @return ビルドされたコマンド文字列 - */ - private String build(String name) { - return String.format( - "tellraw %s {\"text\":\"\",\"extra\":[%s]}", - name, buildJoin(parts)); - } - - private void sendCommand(Player player) { - final String commandLine = build(player.getName()); - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), commandLine); - } - - private String buildPlain() { - StringBuffer buffer = new StringBuffer(); - for ( MessageParts part : parts ) { - buffer.append(part.buildPlain()); - } - return buffer.toString(); - } - - private static String buildJoin(ArrayList arr) { - StringBuffer buffer = new StringBuffer(); - for ( MessageParts s : arr ) { - if ( buffer.length() > 0 ) { - buffer.append(","); - } - buffer.append(s.build()); - } - return buffer.toString(); - } - - /** - * デバッグ用のエントリポイント - * @param args - */ - public static void main(String[] args) { - - // MessageComponentの使用例:相手に自殺ボタン付きメッセージを送る - MessageComponent msg = new MessageComponent(); - msg.addText("自殺ボタンはこちら→"); - MessageParts button = new MessageParts("[あぼーん]", ChatColor.BLUE); - button.setClickEvent(ClickEventType.RUN_COMMAND, "/kill"); - button.addHoverText("押しても後悔してはならない\n"); - button.addHoverText("絶対に後悔してはならない", ChatColor.GOLD); - msg.addParts(button); - msg.addText(" 押すなよ!絶対に押すなよ!!", ChatColor.RED); - - // 送信 - // msg.send(player); - - // デバッグ出力 - System.out.println(msg.build("ucchy")); - } -} diff --git a/src/main/java/org/bitbucket/ucchy/undine/tellraw/MessageParts.java b/src/main/java/org/bitbucket/ucchy/undine/tellraw/MessageParts.java deleted file mode 100644 index ba8b4e5..0000000 --- a/src/main/java/org/bitbucket/ucchy/undine/tellraw/MessageParts.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * @author ucchy - * @license LGPLv3 - * @copyright Copyright ucchy 2015 - */ -package org.bitbucket.ucchy.undine.tellraw; - -import java.util.ArrayList; -import java.util.HashSet; - -import org.bukkit.ChatColor; - -/** - * メッセージパーツ - * @author ucchy - */ -public class MessageParts { - - private String text; - private ChatColor color; - private ClickEventType ctype; - private String cvalue; - private HashSet flags; - private ArrayList hover; - - /** - * コンストラクタ - * @param text テキスト - */ - public MessageParts(String text) { - this.text = text; - this.flags = new HashSet(); - this.hover = new ArrayList(); - } - - /** - * コンストラクタ - * @param text テキスト - * @param color テキスト色 - */ - public MessageParts(String text, ChatColor color) { - this(text); - setColor(color); - } - - /** - * コンストラクタ - * @param text テキスト - * @param color テキスト色その1 - * @param color2 テキスト色その2 - */ - public MessageParts(String text, ChatColor color, ChatColor color2) { - this(text); - setColor(color); - setColor(color2); - } - - /** - * パーツに色を設定する - * @param color 色。装飾系も可。 - */ - public void setColor(ChatColor color) { - if ( color.isColor() ) { - this.color = color; - } else if ( color == ChatColor.BOLD ) { - flags.add("bold"); - } else if ( color == ChatColor.ITALIC ) { - flags.add("italic"); - } else if ( color == ChatColor.UNDERLINE ) { - flags.add("underlined"); - } else if ( color == ChatColor.STRIKETHROUGH ) { - flags.add("strikethrough"); - } else if ( color == ChatColor.MAGIC ) { - flags.add("obfuscated"); - } - } - - /** - * パーツにクリック動作を設定する - * @param type 動作の種類 - * @param value 設定値 - */ - public void setClickEvent(ClickEventType type, String value) { - ctype = type; - cvalue = value; - } - - /** - * ホバーテキストを追加する - * @param text ホバーテキスト - */ - public void addHoverText(String text) { - this.hover.add(new MessageParts(text)); - } - - /** - * ホバーテキストを1行追加する - * @param text ホバーテキスト - * @param color テキスト色 - */ - public void addHoverText(String text, ChatColor color) { - this.hover.add(new MessageParts(text, color)); - } - - /** - * このパーツをビルドして、tellrawのパラメータ文字列に変換する - * @return パラメータ文字列 - */ - public String build() { - - ArrayList items = new ArrayList(); - items.add("\"text\":\"" + text.replace("\"", "\\\"") + "\""); - if ( color != null ) { - items.add("\"color\":\"" + color.name().toLowerCase() + "\""); - } - for ( String flag : flags ) { - items.add("\"" + flag + "\":\"true\""); - } - if ( ctype != null ) { - items.add("\"clickEvent\":" - + "{\"action\":\"" + ctype.toString() + "\"," - + "\"value\":\"" + cvalue.replace("\"", "\\\"") + "\"}"); - } - if ( hover.size() > 0 ) { - items.add("\"hoverEvent\":" - + "{\"action\":\"show_text\"," - + "\"value\":{\"text\":\"\",\"extra\":[" - + joinHover() + "]}}"); - } - - return "{" + join(items) + "}"; - } - - /** - * このパーツを、tellrawを受け取れない人(コンソールなど)のために、 - * プレーンな文字列に変換して返す。 - * @return プレーンな文字列 - */ - public String buildPlain() { - if ( color != null ) return color + text; - return text; - } - - /** - * ホバーテキストを結合する - * @return 結合された文字列 - */ - private String joinHover() { - ArrayList items = new ArrayList(); - for ( MessageParts msg : hover ) { - if ( msg.color == null ) { - items.add("{\"text\":\"" + msg.text.replace("\"", "\\\"") + "\"}"); - } else { - items.add("{\"text\":\"" + msg.text.replace("\"", "\\\"") + "\"," - + "\"color\":\"" + msg.color.name().toLowerCase() + "\"}"); - } - } - return join(items); - } - - /** - * 文字列をコンマで結合する - * @param arr 文字列の配列 - * @return 結合された文字列 - */ - private static String join(ArrayList arr) { - StringBuffer buffer = new StringBuffer(); - for ( String s : arr ) { - if ( buffer.length() > 0 ) { - buffer.append(","); - } - buffer.append(s); - } - return buffer.toString(); - } -} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 78fea47..cfcbfbe 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,170 +1,183 @@ -# ${project.name} v${project.version} -# @author ucchy -# @license LGPLv3 -# @copyright Copyright ucchy 2015 - -# -------------------- Misc -------------------- - -# Message locale. -lang: en - -# Enable to send mail to oneself. -enableSendSelf: false - -# Display empty line count before display UI. -uiEmptyLines: 2 - -# Enable to use player list address book. -enablePlayerList: false - -# Limitation number of mail destination. -maxDestination: 10 - -# Limitation number of mail destination group. -maxDestinationGroup: 3 - -# Mail store period. -# If you set 30, the system deletes mails that 31 days passed from sending. -mailStorageTermDays: 30 - -# Mail spam protection seconds. -# If you set 15, players need 15 seconds, from sending a mail to sending next mail. -mailSpamProtectionSeconds: 15 - -# Mail notification delay seconds on player login. -loginNotificationDelaySeconds: 3 - -# -------------------- Attachment Item Box -------------------- - -# Enable the attachment item box. -enableAttachment: true - -# Attachment item box line size. Please set 1 - 6. -# ex) 1 -> you can attach 9 items per 1 mail. -# ex) 6 -> you can attach 54 items per 1 mail. -attachBoxSize: 1 - -# Max usage count of attachment item box per a player. -maxAttachmentBoxCount: 3 - -# World names that disable to open attachment item box. -disableWorldsToOpenAttachBox: [] - -# Specify the items that cannot be attached to the mail. -# For example, the following setting makes that gold apple and quartz block will not be attached. -# prohibitItemsToAttach: [GOLDEN_APPLE, QUARTZ_BLOCK] -# Possible item name, please see the following URL as a reference. -# https://github.com/ucchyocean/UndineMailer/blob/master/src/main/java/org/bitbucket/ucchy/undine/item/TradableMaterial.java#L16-L357 -prohibitItemsToAttach: [] - -# -------------------- C.O.D. Setting -------------------- - -# Enable C.O.D. money feature by using currency of economy plugin. -# If you want to use C.O.D. money feature, you need Vault and any economy plugin. -enableCODMoney: true - -# Enable C.O.D. item feature. -enableCODItem: true - -# When paying cash on delivery fee to the other player, -# if the amount of post-payment does not match, you can set whether an error. -# In such Essentials "max-money" setting, if you have set the upper limit of money, -# payment will be disappear. So please make this setting to true, for saving payment. -# If you are using such as taking a tax on payment in your economy plugin, -# so you will not be able to remittances, please make this setting to false. -depositErrorOnUnmatch: true - -# -------------------- Send Fee -------------------- - -# Enable mail sending fee. -enableSendFee: false - -# Sending fee per a destination. -sendFee: 10 - -# Attaching fee per an item. -attachFee: 10 - -# Attaching fee is needed whether per the item amount, or per the item stack. -attachFeePerAmount: false - -# COD money tax. Please specify parcentage. -# If you set "codMoneyTax: 10", and one of player set $300 as COD, -# this player need to pay the additional sending fee $30 (= $300 * 10%). -codMoneyTax: 0 - -# COD item tax. Please specify price per item amount. -# If you set "codItemTax: 10", and one of player set DIAMOND * 8 as COD, -# this player need to pay the additional sending fee $80 (= $10 * 8). -codItemTax: 0 - -# -------------------- Mail Group -------------------- - -# Max group creation num per a player. -maxCreateGroup: 5 - -# Max member num per a group. -maxGroupMember: 15 - -# Default mode of send mail permission. -# OP: only OP can send mail to this group. -# OWNER: owner and OP can send mail to this group. -# MEMBER: group members anyone can send mail to this group. -# EVERYONE: everyone can send mail to this group. -sendModeDefault: MEMBER - -# Default mode of members modification. -# OP: only OP can add/remove members. -# OWNER: owner and OP can add/remove members. -# MEMBER: group members anyone can add/remove members. -# EVERYONE: everyone can add/remove members. -modifyModeDefault: OWNER - -# Default mode of dissolution. -# OP: only OP can break-up group. -# OWNER: owner and OP can break-up group. -# MEMBER: group members anyone can break-up group. -# EVERYONE: everyone can break-up group. -dissolutionModeDefault: OWNER - -# Sending permission mode of special group "All" (all players group). -specialGroupAllSendMode: OP - -# Default of sending permission mode of special group "PEX" (PermissionsEx group). -# You can arrange this permission each groups by setting "send-mode" option value. -specialGroupPexSendMode: OP - -# Sending permission mode of special group "AllConnected" (all connected players without newcomers). -specialGroupAllConnectedSendMode: OP - -# Sending permission mode of special group "AllLogin" (all connecting players). -specialGroupAllLoginSendMode: OP - - -# -------------------- Welcome Mail -------------------- -# Welcome email is a mail sent to the player that was first connected to the server. -# You can change the message of welcome mail, by changing WelcomeMailBody of messages_en.yml file. - -# Enable welcome mail. -useWelcomeMail: true - -# Delay seconds to send welcome mail. -welcomeMailDelaySeconds: 30 - -# Attachment items of welcome mail. -welcomeMailAttachments: - attachment1: - material: STONE_SPADE - attachment2: - material: STONE_PICKAXE - attachment3: - material: STONE_AXE - attachment4: - material: STONE_SWORD - attachment5: - material: TORCH - amount: 64 - attachment6: - material: BREAD - amount: 16 - +# ${project.name} v${project.version} +# @author ucchy +# @license LGPLv3 +# @copyright Copyright ucchy 2015 + +# -------------------- Misc -------------------- + +# Message locale. +lang: en + +# Database settings +# flatfile, sqlite or mysql for databaseType. +databaseType: flatfile +mysqlUser: root +mysqlPass: password +mysqlHost: 127.0.0.1 +mysqlPort: 3306 +mysqlDBName: "undinemailer" + +# Enable to send mail to oneself. +enableSendSelf: false + +# Display empty line count before display UI. +uiEmptyLines: 2 + +# Enable to use player list address book. +enablePlayerList: false + +# Limitation number of mail destination. +maxDestination: 10 + +# Limitation number of mail destination group. +maxDestinationGroup: 3 + +# Mail store period. +# If you set 30, the system deletes mails that 31 days passed from sending. +mailStorageTermDays: 30 + +# Mail spam protection seconds. +# If you set 15, players need 15 seconds, from sending a mail to sending next mail. +mailSpamProtectionSeconds: 15 + +# Mail notification delay seconds on player login. +loginNotificationDelaySeconds: 3 + +# UUID online mode. +# true = Getting UUIDs from Mojang server. false = Getting UUIDS from local bukkit server. +uuidOnlineMode: false + +# -------------------- Attachment Item Box -------------------- + +# Enable the attachment item box. +enableAttachment: true + +# Attachment item box line size. Please set 1 - 6. +# ex) 1 -> you can attach 9 items per 1 mail. +# ex) 6 -> you can attach 54 items per 1 mail. +attachBoxSize: 1 + +# Max usage count of attachment item box per a player. +maxAttachmentBoxCount: 3 + +# World names that disable to open attachment item box. +disableWorldsToOpenAttachBox: [] + +# Specify the items that cannot be attached to the mail. +# For example, the following setting makes that gold apple and quartz block will not be attached. +# prohibitItemsToAttach: [GOLDEN_APPLE, QUARTZ_BLOCK] +# Possible item name, please see the following URL as a reference. +# https://github.com/ucchyocean/UndineMailer/blob/master/src/main/java/org/bitbucket/ucchy/undine/item/TradableMaterial.java#L19-L462 +prohibitItemsToAttach: [] + +# -------------------- C.O.D. Setting -------------------- + +# Enable C.O.D. money feature by using currency of economy plugin. +# If you want to use C.O.D. money feature, you need Vault and any economy plugin. +enableCODMoney: true + +# Enable C.O.D. item feature. +enableCODItem: true + +# When paying cash on delivery fee to the other player, +# if the amount of post-payment does not match, you can set whether an error. +# In such Essentials "max-money" setting, if you have set the upper limit of money, +# payment will be disappear. So please make this setting to true, for saving payment. +# If you are using such as taking a tax on payment in your economy plugin, +# so you will not be able to remittances, please make this setting to false. +depositErrorOnUnmatch: true + +# -------------------- Send Fee -------------------- + +# Enable mail sending fee. +enableSendFee: false + +# Sending fee per a destination. +sendFee: 10 + +# Attaching fee per an item. +attachFee: 10 + +# Attaching fee is needed whether per the item amount, or per the item stack. +attachFeePerAmount: false + +# COD money tax. Please specify parcentage. +# If you set "codMoneyTax: 10", and one of player set $300 as COD, +# this player need to pay the additional sending fee $30 (= $300 * 10%). +codMoneyTax: 0 + +# COD item tax. Please specify price per item amount. +# If you set "codItemTax: 10", and one of player set DIAMOND * 8 as COD, +# this player need to pay the additional sending fee $80 (= $10 * 8). +codItemTax: 0 + +# -------------------- Mail Group -------------------- + +# Max group creation num per a player. +maxCreateGroup: 5 + +# Max member num per a group. +maxGroupMember: 15 + +# Default mode of send mail permission. +# OP: only OP can send mail to this group. +# OWNER: owner and OP can send mail to this group. +# MEMBER: group members anyone can send mail to this group. +# EVERYONE: everyone can send mail to this group. +sendModeDefault: MEMBER + +# Default mode of members modification. +# OP: only OP can add/remove members. +# OWNER: owner and OP can add/remove members. +# MEMBER: group members anyone can add/remove members. +# EVERYONE: everyone can add/remove members. +modifyModeDefault: OWNER + +# Default mode of dissolution. +# OP: only OP can break-up group. +# OWNER: owner and OP can break-up group. +# MEMBER: group members anyone can break-up group. +# EVERYONE: everyone can break-up group. +dissolutionModeDefault: OWNER + +# Sending permission mode of special group "All" (all players group). +specialGroupAllSendMode: OP + +# Default of sending permission mode of special group "PEX" (PermissionsEx group). +# You can arrange this permission each groups by setting "send-mode" option value. +specialGroupPexSendMode: OP + +# Sending permission mode of special group "AllConnected" (all connected players without newcomers). +specialGroupAllConnectedSendMode: OP + +# Sending permission mode of special group "AllLogin" (all connecting players). +specialGroupAllLoginSendMode: OP + + +# -------------------- Welcome Mail -------------------- +# Welcome email is a mail sent to the player that was first connected to the server. +# You can change the message of welcome mail, by changing WelcomeMailBody of messages_en.yml file. + +# Enable welcome mail. +useWelcomeMail: true + +# Delay seconds to send welcome mail. +welcomeMailDelaySeconds: 30 + +# Attachment items of welcome mail. +welcomeMailAttachments: + attachment1: + material: STONE_SHOVEL + attachment2: + material: STONE_PICKAXE + attachment3: + material: STONE_AXE + attachment4: + material: STONE_SWORD + attachment5: + material: TORCH + amount: 64 + attachment6: + material: BREAD + amount: 16 + diff --git a/src/main/resources/config_ja.yml b/src/main/resources/config_ja.yml index cd7f66d..1f3290f 100644 --- a/src/main/resources/config_ja.yml +++ b/src/main/resources/config_ja.yml @@ -1,186 +1,199 @@ -# ${project.name} v${project.version} -# @author ucchy -# @license LGPLv3 -# @copyright Copyright ucchy 2015 - -# -------------------- 共通設定 -------------------- - -# メッセージの言語 -lang: ja - -# 自分自身に送信可能とするかどうか。 -enableSendSelf: false - -# UIを表示する系統のコマンドを実行したときに、 -# UIの前に挿入する空行の行数を設定します。 -# 0から9まで指定可能です。 -uiEmptyLines: 2 - -# プレイヤー名簿を利用するかどうか。 -# プレイヤー名簿では、サーバーに1度でも接続したことがあるプレイヤー全てが -# 一覧にリストアップされます。 -enablePlayerList: false - -# 指定できる宛先の最大数 -maxDestination: 10 - -# 指定できる宛先グループの最大数 -maxDestinationGroup: 3 - -# メールの保存期間(日数) -# 例えば30を指定した場合、送信してから31日経ったメールは自動的に削除されます。 -mailStorageTermDays: 30 - -# メールスパム保護期間(秒) -# 例えば15を指定した場合、メールを送信してから次のメールを送信できるまで、 -# 15秒の経過が必要になります。 -mailSpamProtectionSeconds: 15 - -# プレイヤーがログインした時に、未読一覧を表示するまでの時間(秒)。 -loginNotificationDelaySeconds: 3 - -# -------------------- アイテム添付設定 -------------------- - -# メールにアイテムの添付を可能にするかどうか。 -enableAttachment: true - -# メールの添付ボックスのサイズを指定します。 -# 1列(=9個)から6列(=54個)まで指定可能です。 -attachBoxSize: 1 - -# 同時に使用可能な添付ボックスの個数を指定します。 -maxAttachmentBoxCount: 3 - -# メールの添付ボックスを操作不可に設定したいワールドを指定します。 -# 例えば disableWorldsToOpenAttachment: ['sozai'] と設定すると、 -# sozaiというワールド名に居るプレイヤーは、送信メールの添付ボックスや -# 受信したメールの添付ボックスを開くことができなくなります。 -disableWorldsToOpenAttachBox: [] - -# メールに添付することができないアイテムを指定します。 -# 例えば prohibitItemsToAttach: [GOLDEN_APPLE, QUARTZ_BLOCK] と設定すると、 -# 金リンゴとクオーツブロックが添付できなくなります。 -# 指定可能なアイテム名は、下記のURLを参考にしてください。 -# https://github.com/ucchyocean/UndineMailer/blob/master/src/main/java/org/bitbucket/ucchy/undine/item/TradableMaterial.java#L16-L357 -prohibitItemsToAttach: [] - -# -------------------- 着払い設定 -------------------- - -# 経済プラグインの通貨を利用して、着払い料金を設定可能とするかどうか。 -# この機能を利用するには、Vaultと、経済プラグインを、一緒に導入する必要があります。 -enableCODMoney: true - -# 着払い料金として、アイテムをリクエストできるようにするかどうか。 -enableCODItem: true - -# 着払い料金を相手に支払う場合、入金後の金額が一致しない場合に、エラーとするかどうか。 -# Essentialsのmax-moneyなどで、お金の上限を設定している場合は、 -# 入金で上限を超えると送金した金額が消滅するので、この項目をtrueに設定してください。 -# 入金時に税金を取るような経済プラグインを利用している場合は、 -# 送金ができなくなりますので、この項目をfalseにしてください。 -depositErrorOnUnmatch: true - -# -------------------- 送信料金設定 -------------------- - -# メールを作成したりアイテムを添付したりするために料金を必要とするかどうか。 -# 経済プラグインとVaultが導入されている必要があります。 -enableSendFee: false - -# 1つの宛先にメールを作成する料金です。 -# 送信先が複数ある場合は、送信先の数だけ倍になります。 -sendFee: 10 - -# メールにアイテムを1個または1スタック添付する料金です。 -attachFee: 10 - -# 上記のattachFeeを、アイテム1個ごとに料金を付けるか、アイテム1スタックごとに料金を付けるか。 -# falseで1スタックごと、trueで1個ごとになります。 -attachFeePerAmount: false - -# 着払い料金に対する着払い税です。パーセンテージで指定してください。 -# 例えば、codMoneyTax: 10 とすると、着払い料金 $300 を指定したメールは、 -# 着払い料金の 10% の $30 が、追加で必要になります。 -codMoneyTax: 0 - -# 着払いアイテムに対する着払い税です。要求アイテム1個に対する金額で指定してください。 -# 例えば、codItemTax: 10 とすると、ダイアモンド8個を着払いアイテムとして指定すると、 -# $80 (=$10 * 8) の税金が、追加で必要になります。 -codItemTax: 0 - -# -------------------- メールグループ設定 -------------------- - -# 1プレイヤーが作成可能なグループの最大数。 -# OPや、'undine.group.infinite-create'パーミッションを持った人は、 -# この数値に関係なく無限に作成可能です。 -maxCreateGroup: 5 - -# 1グループに追加できる最大プレイヤー数 -# OPや、'undine.group.infinite-add-member'パーミッションを持った人は、 -# この数値に関係なく無限に追加可能です。 -maxGroupMember: 15 - -# グループへのメール送信権限のデフォルト -# OP → OPのみがグループへメール送信可能 -# OWNER → オーナーとOPがグループへメール送信可能 -# MEMBER → メンバーならグループへメール送信可能 -# EVERYONE → 誰でもグループへメール送信可能 -sendModeDefault: MEMBER - -# グループのメンバー変更権限のデフォルト -# OP → OPのみが変更可能 -# OWNER → オーナーとOPが変更可能 -# MEMBER → メンバーなら変更可能 -# EVERYONE → 誰でも変更可能 -modifyModeDefault: OWNER - -# グループの解散権限のデフォルト -# OP → OPのみが解散可能 -# OWNER → オーナーとOPが解散可能 -# MEMBER → メンバーなら解散可能 -# EVERYONE → 誰でも解散可能 -dissolutionModeDefault: OWNER - -# 特殊グループ All (全プレイヤーへのメールが送信できるグループ) への -# 送信権限を指定します。 -specialGroupAllSendMode: OP - -# 特殊グループ PEX (PermissionsExのグループ) への送信権限のデフォルトを指定します。 -# PermissionsExのグループのoption設定に、"send-mode" を設定することで、 -# グループごとに細かくSendModeを指定することが可能です。詳しくはフォーラムを参照してください。 -specialGroupPexSendMode: OP - -# 特殊グループ AllConnected (サーバーに接続したことのある全プレイヤーへのメールが送信できるグループ) への -# 送信権限を指定します。 -specialGroupAllConnectedSendMode: OP - -# 特殊グループ AllLogin (サーバーに接続中の全プレイヤーへのメールが送信できるグループ) への -# 送信権限を指定します。 -specialGroupAllLoginSendMode: OP - -# -------------------- ウェルカムメール設定 -------------------- -# ウェルカムメールは、サーバーに初接続したプレイヤーに送られるメールです。 -# ウェルカムメールのメール本文は、messages_ja.yml ファイルの WelcomeMailBody という項目で設定できます。 - -# ウェルカムメールを利用するかどうか。 -useWelcomeMail: true - -# 初接続のプレイヤーが接続してから、ウェルカムメールを送信するまでの時間(秒)。 -welcomeMailDelaySeconds: 30 - -# ウェルカムメールの添付アイテム設定です。 -welcomeMailAttachments: - attachment1: - material: STONE_SPADE - attachment2: - material: STONE_PICKAXE - attachment3: - material: STONE_AXE - attachment4: - material: STONE_SWORD - attachment5: - material: TORCH - amount: 64 - attachment6: - material: BREAD - amount: 16 - +# ${project.name} v${project.version} +# @author ucchy +# @license LGPLv3 +# @copyright Copyright ucchy 2015 + +# -------------------- 共通設定 -------------------- + +# メッセージの言語 +lang: ja + +# データベースの設定 +# databaseTypeにはmysqlかsqlite、あるいはflatfileを指定します。 +databaseType: flatfile +mysqlUser: root +mysqlPass: password +mysqlHost: 127.0.0.1 +mysqlPort: 3306 +mysqlDBName: "undinemailer" + +# 自分自身に送信可能とするかどうか。 +enableSendSelf: false + +# UIを表示する系統のコマンドを実行したときに、 +# UIの前に挿入する空行の行数を設定します。 +# 0から9まで指定可能です。 +uiEmptyLines: 2 + +# プレイヤー名簿を利用するかどうか。 +# プレイヤー名簿では、サーバーに1度でも接続したことがあるプレイヤー全てが +# 一覧にリストアップされます。 +enablePlayerList: false + +# 指定できる宛先の最大数 +maxDestination: 10 + +# 指定できる宛先グループの最大数 +maxDestinationGroup: 3 + +# メールの保存期間(日数) +# 例えば30を指定した場合、送信してから31日経ったメールは自動的に削除されます。 +mailStorageTermDays: 30 + +# メールスパム保護期間(秒) +# 例えば15を指定した場合、メールを送信してから次のメールを送信できるまで、 +# 15秒の経過が必要になります。 +mailSpamProtectionSeconds: 15 + +# プレイヤーがログインした時に、未読一覧を表示するまでの時間(秒)。 +loginNotificationDelaySeconds: 3 + +# UUID取得時のオンラインモード +# true = MojangのサーバーからUUIDを取得します。 false = ローカルのBukkitサーバーからUUIDを取得します。 +uuidOnlineMode: false + +# -------------------- アイテム添付設定 -------------------- + +# メールにアイテムの添付を可能にするかどうか。 +enableAttachment: true + +# メールの添付ボックスのサイズを指定します。 +# 1列(=9個)から6列(=54個)まで指定可能です。 +attachBoxSize: 1 + +# 同時に使用可能な添付ボックスの個数を指定します。 +maxAttachmentBoxCount: 3 + +# メールの添付ボックスを操作不可に設定したいワールドを指定します。 +# 例えば disableWorldsToOpenAttachment: ['sozai'] と設定すると、 +# sozaiというワールド名に居るプレイヤーは、送信メールの添付ボックスや +# 受信したメールの添付ボックスを開くことができなくなります。 +disableWorldsToOpenAttachBox: [] + +# メールに添付することができないアイテムを指定します。 +# 例えば prohibitItemsToAttach: [GOLDEN_APPLE, QUARTZ_BLOCK] と設定すると、 +# 金リンゴとクオーツブロックが添付できなくなります。 +# 指定可能なアイテム名は、下記のURLを参考にしてください。 +# https://github.com/ucchyocean/UndineMailer/blob/master/src/main/java/org/bitbucket/ucchy/undine/item/TradableMaterial.java#L19-L462 +prohibitItemsToAttach: [] + +# -------------------- 着払い設定 -------------------- + +# 経済プラグインの通貨を利用して、着払い料金を設定可能とするかどうか。 +# この機能を利用するには、Vaultと、経済プラグインを、一緒に導入する必要があります。 +enableCODMoney: true + +# 着払い料金として、アイテムをリクエストできるようにするかどうか。 +enableCODItem: true + +# 着払い料金を相手に支払う場合、入金後の金額が一致しない場合に、エラーとするかどうか。 +# Essentialsのmax-moneyなどで、お金の上限を設定している場合は、 +# 入金で上限を超えると送金した金額が消滅するので、この項目をtrueに設定してください。 +# 入金時に税金を取るような経済プラグインを利用している場合は、 +# 送金ができなくなりますので、この項目をfalseにしてください。 +depositErrorOnUnmatch: true + +# -------------------- 送信料金設定 -------------------- + +# メールを作成したりアイテムを添付したりするために料金を必要とするかどうか。 +# 経済プラグインとVaultが導入されている必要があります。 +enableSendFee: false + +# 1つの宛先にメールを作成する料金です。 +# 送信先が複数ある場合は、送信先の数だけ倍になります。 +sendFee: 10 + +# メールにアイテムを1個または1スタック添付する料金です。 +attachFee: 10 + +# 上記のattachFeeを、アイテム1個ごとに料金を付けるか、アイテム1スタックごとに料金を付けるか。 +# falseで1スタックごと、trueで1個ごとになります。 +attachFeePerAmount: false + +# 着払い料金に対する着払い税です。パーセンテージで指定してください。 +# 例えば、codMoneyTax: 10 とすると、着払い料金 $300 を指定したメールは、 +# 着払い料金の 10% の $30 が、追加で必要になります。 +codMoneyTax: 0 + +# 着払いアイテムに対する着払い税です。要求アイテム1個に対する金額で指定してください。 +# 例えば、codItemTax: 10 とすると、ダイアモンド8個を着払いアイテムとして指定すると、 +# $80 (=$10 * 8) の税金が、追加で必要になります。 +codItemTax: 0 + +# -------------------- メールグループ設定 -------------------- + +# 1プレイヤーが作成可能なグループの最大数。 +# OPや、'undine.group.infinite-create'パーミッションを持った人は、 +# この数値に関係なく無限に作成可能です。 +maxCreateGroup: 5 + +# 1グループに追加できる最大プレイヤー数 +# OPや、'undine.group.infinite-add-member'パーミッションを持った人は、 +# この数値に関係なく無限に追加可能です。 +maxGroupMember: 15 + +# グループへのメール送信権限のデフォルト +# OP → OPのみがグループへメール送信可能 +# OWNER → オーナーとOPがグループへメール送信可能 +# MEMBER → メンバーならグループへメール送信可能 +# EVERYONE → 誰でもグループへメール送信可能 +sendModeDefault: MEMBER + +# グループのメンバー変更権限のデフォルト +# OP → OPのみが変更可能 +# OWNER → オーナーとOPが変更可能 +# MEMBER → メンバーなら変更可能 +# EVERYONE → 誰でも変更可能 +modifyModeDefault: OWNER + +# グループの解散権限のデフォルト +# OP → OPのみが解散可能 +# OWNER → オーナーとOPが解散可能 +# MEMBER → メンバーなら解散可能 +# EVERYONE → 誰でも解散可能 +dissolutionModeDefault: OWNER + +# 特殊グループ All (全プレイヤーへのメールが送信できるグループ) への +# 送信権限を指定します。 +specialGroupAllSendMode: OP + +# 特殊グループ PEX (PermissionsExのグループ) への送信権限のデフォルトを指定します。 +# PermissionsExのグループのoption設定に、"send-mode" を設定することで、 +# グループごとに細かくSendModeを指定することが可能です。詳しくはフォーラムを参照してください。 +specialGroupPexSendMode: OP + +# 特殊グループ AllConnected (サーバーに接続したことのある全プレイヤーへのメールが送信できるグループ) への +# 送信権限を指定します。 +specialGroupAllConnectedSendMode: OP + +# 特殊グループ AllLogin (サーバーに接続中の全プレイヤーへのメールが送信できるグループ) への +# 送信権限を指定します。 +specialGroupAllLoginSendMode: OP + +# -------------------- ウェルカムメール設定 -------------------- +# ウェルカムメールは、サーバーに初接続したプレイヤーに送られるメールです。 +# ウェルカムメールのメール本文は、messages_ja.yml ファイルの WelcomeMailBody という項目で設定できます。 + +# ウェルカムメールを利用するかどうか。 +useWelcomeMail: true + +# 初接続のプレイヤーが接続してから、ウェルカムメールを送信するまでの時間(秒)。 +welcomeMailDelaySeconds: 30 + +# ウェルカムメールの添付アイテム設定です。 +welcomeMailAttachments: + attachment1: + material: STONE_SHOVEL + attachment2: + material: STONE_PICKAXE + attachment3: + material: STONE_AXE + attachment4: + material: STONE_SWORD + attachment5: + material: TORCH + amount: 64 + attachment6: + material: BREAD + amount: 16 + diff --git a/src/main/resources/messages_de.yml b/src/main/resources/messages_de.yml index d617152..e2ec9b9 100644 --- a/src/main/resources/messages_de.yml +++ b/src/main/resources/messages_de.yml @@ -1,257 +1,260 @@ -# ${project.name} v${project.version} -# @author Androkai, ucchy -# @license LGPLv3 -# @copyright Copyright ucchy 2015 - -PermissionDeniedCommand: '&cDir fehlt die nötige Berechtigung.' - -ErrorRequireArgument: '&cBitte gib die nötigen Parameter (%param) mit an.' -ErrorInvalidIndex: '&cDie angegebene Mail-Nummer %index ist ungültig.' -ErrorNoneReadPermission: '&cDu darfst diese Mail nicht lesen.' -ErrorNotFoundDestination: '&cDer angegbene Empfänger %dest konnte nicht gefunden werden.' -ErrorCannotSendSelf: '&cDu darfst dir selbst keine Mails schicken.' -ErrorInGameCommand: '&cDu kannst diesen Befehl nicht auf der Konsole ausführen.' -ErrorNotInEditmode: '&cDu kannst diesen Befehl nicht nutzen, weil du dich nicht im Bearbeiten-Modus befindest.' -ErrorTooManyDestination: '&cDu kannst nur an bis zu %num Empfänger gleichzeitig schreiben.' -ErrorAlreadyExistTo: '&cDer angegebene Empfänger ist bereits eingetragen.' -ErrorEmptyTo: '&cEs wurde kein Empfänger angegeben.' -ErrorItemAttachedYet: '&cEs sind noch Items an diese Mail angehangen. Bitte entferne diese zuerst.' -ErrorCannotSendMultiAttach: '&cDu kannst Item-Anhänge nicht an mehrere Empfänger gleichzeitig senden.' -ErrorYouDontHaveEnoughMoney: '&cDu hast nicht genügend Geld!' -ErrorYouDontHaveEnoughItem: '&cDu hast nicht genügend Items!' -ErrorFailToWithdraw: '&cFehler beim Abheben der Gebüren von deinem Konto.' -ErrorFailToDeposit: '&cFehler beim Überweisen des Betrags auf das Konto.' -ErrorCannotSetMoneyAndItem: '&cDu kannst nicht Geld und Items gleichzeitig anhängen.' -ErrorInvalidCostMoney: '&cDer angegebene C.O.D. Betrag %fee ist ungültig.' -ErrorInvalidCostItem: '&cDas angegbene C.O.D. Item %item ist ungültig.' -ErrorInvalidItem: '&cDas angegebene Item %item ist ungültig.' -ErrorNoneCancelAttachPermission: '&cDu kannst keine Anhänge entfernen, weil du nicht der Absender dieser Mail bist.' -ErrorNoneRefuseAttachPermission: '&cDu kannst keine Anhänge entfernen, weil du nicht der Emofänger dieser Mail bist.' -ErrorAlreadyAttachCancelled: '&cDer Anhang wurde bereits entfernt.' -ErrorAlreadyRecipientOpened: '&cDu kannst keine Anhänge entfernen, weil der Empfänger die Anhang-Box gerade offen hat.' -ErrorInvalidAttachBoxWorld: '&cIn dieser Welt dürfen keine Anhänge angehangen oder abgerufen werden.\nÖffne die Anhang-Box erneut, nachdem du eine andere Welt betreten hast.' -ErrorInvalidCommand: '&cDieser Befehl ist ungültig.' -ErrorAttachBoxCountExceed: '&cDu nutzt derzeit %num Anhang-Boxen.\nDa das Limit von %limit Boxen erreicht ist, kannst du keine weiten Anhänge erstellen.' -ErrorCannotSendAttachMailToAllBecauseCacheLoading: '&cDa die Vorbereitung der Spieler Cache-Daten noch nicht abgeschlossen ist, können Sie keine Anhänge zu versenden mit Mail an alle. Bitte versuchen Sie es nach einer Wartezeit für eine Weile.' -ErrorCannotListInitializingYet: '&cDie Mail-Liste kann nicht angezeigt werden, da der Mailer noch nicht eingerichtet wurde.' -ErrorCannotSendInitializingYet: '&cEs können keine neuen Mails gesendet werden, da der Mailer noch nicht eingerichtet wurde.' -ErrorAlreadyTrashed: '&cMail #%index befindet sich bereits im Papierkorb.' -ErrorNotTrashed: '&cMail #%index befindet sich nicht im Papierkorb.' -ErrorCannotDropBecauseUnread: '&cDu kannst Mail #%index nicht verschieben, weil sie noch nicht gelesen wurde.' -ErrorCannotDropBecauseAttached: '&cDu kannst Mail #%index nicht verschieben, weil sie noch Item-Anhänge hat.' -ErrorCannotSendSpamMail: '&cDu musst noch %remain Sekunden warten, bis du deine nächste Mail verschicken darfst.' -ErrorPlayerCacheIncomplete: '&cDa der Player -Cache noch nicht abgeschlossen ist, können Sie nicht erstellt werden AllConnected adressierte Post. Bitte senden Sie nach einer Wartezeit für eine Weile.' -ErrorCannotFoundLocation: '&cSie können nicht teleportieren zur Übertragung Punkt ist nicht auf die angegebene E-Mail aufgezeichnet.' -ErrorProhibitItemAttached: '&cArtikel %material angebracht ist untersagt.' - -ErrorInvalidGroupName: '&c%name kann nicht als Gruppenname genutzt werden.' -ErrorGroupIsAlreadyExist: '&cDie Gruppe %name existiert bereits.' -ErrorGroupNotExist: '&cDie Gruppe %name exisitiert nicht.' -ErrorGroupSendNotPermission: '&cDu hast keine Berechtigung, an die Gruppe %name zu schreiben.' -ErrorGroupModifyNotPermission: '&cDu hast keine Berechtigung, die Gruppe %name zu bearbeiten.' -ErrorGroupDeleteNotPermission: '&cDu hast keine Berechtigung, die Gruppe %name zu löschen.' -ErrorNotFoundPlayer: '&cSpieler %player wurde nicht gefunden.' -ErrorPlayerIsAlreadyMember: '&cSpieler %player ist bereits Mitglied.' -ErrorPlayerIsNotMember: '&cSpieler %player ist kein Mitglied.' -ErrorPlayerIsOwner: '&cSpieler %player kann nicht entfernt werden, weil er der Besitzer ist.' -ErrorGroupCreateLimitExceed: '&cDu kannst keine weiteren Gruppen erstellen, weil du das das Gruppen-Limit von %num erreicht hast.' -ErrorGroupMemberLimitExceed: '&cDu kannst keine weiteren Mitglieder hinzufügen, weil du das Mitglieder-Limit von %num erreicht hast.' -ErrorInvalidPermissionType: '&cDie angegebene Berechtigung vom Typ %type ist ungültig.' -ErrorInvalidPermissionValue: '&cDie angegebene Berechtigung vom Wert %value ist ungültig.' -ErrorEveryonePermissionInvalid: '&cBearbeiten- und Löschen-Berechtigung kann nicht für JEDEN gesetzt werden.' - -InformationReloading: '&aLade alle Mail- und Config-Dateien neu ein...' -InformationReload: '&aReload abgeschlossen.' -InformationYouGotMail: '&aDu hast eine neue Mail von &7%from&a erhalten!' -InformationYouSentMail: '&7Deine Mail wurde versand.' -InformationEditCancelled: '&7Mail-Erstellen abgebrochen.' -InformationPlayerJoin: '&7Du hast %unread ungelesene Mails.' -InformationItemDetail: '&7Item Information: &f%desc &7Handelbar?: &f%tradable' -InformationMultiAttachConfirm: '&7Du willst eine Mail mit Item-Anhang an mehrere Empfänger versenden.\nDiese Mail wird %num mal vervielfacht werden. Bist du sicher?' -InformationMultiAttachConfirmConsole: 'Zum Senden nutze den ''/mail send attachconfirm'' Befehl.' -InformationAttachWasCanceledBySender: '&7Absender %sender hat den Inhalt der Anhang-Box der Mail #%num zurückgezogen.' -InformationTrashed: '&7Mail #%index wurde in den Papierkorb verschoben.' -InformationTrashRestored: '&7Mail #%index wurde aus deinem Papierkorb wiederhergestellt.' -InformationTrashAllNeedConfirmation: '&7All deine gelesenen erhaltenen/gesendeten %num Mails werden in den Papierkorb verschoben.\nIst das OK? Dann bestätige dies bitte mit dem Befehl "/%command trash all confirm".' -InformationTrashAllDone: '&7%num Mail(s) wurden in den Papierkorb verschoben.' -InformationRestoreAllNeedConfirmation: '&7Alle %num Mail(s) aus deinem Papierkorb werden wiederhergestellt.\nIst das OK? Dann bestätige dies bitte mit dem Befehl "/%command trash restoreall confirm".' -InformationRestoreAllDone: '&7%num Mail(s) aus deinem Papierkorb wurden wiederhergestellt.' -InformationTeleported: '&aSie wurden an die Übertragungsstelle des Mail %index teleportiert.' - -InformationMakeGroup: '&7Neue Gruppe %name wurde erstellt.' -InformationDeleteGroup: '&7Gruppe %name wurde entfernt.' -InformationDeleteGroupConfirm: '&7Die Gruppe %name wird dauerhaft gelöscht. Bist du sicher?' -InformationGroupMemberAdd: '&7Spieler %player zur Gruppe %group hinzugefügt.' -InformationGroupMemberAddAllLogin: '&7%num Spieler zur Gruppe %group hinzugefügt.' -InformationGroupMemberRemove: '&7Spieler %player wurde aus der Gruppe %group entfernt.' -InformationGroupSetPermission: '&7%type Berechtigung der Gruppe %name wurde zu %value geändert.' - -InformationTipsReadCommandFail: '&7TIPS: Sie können E-Mail, indem Sie auf blauen Zahlen in Mail- Liste zu öffnen.' - -SummaryOpenThisMailToolTip: 'Öffne diese Mail' - -MailDetailTitle: 'Mail (#%number)' -MailDetailFromToLine: '&cVon: &f%from &cAn: &f%to' -MailDetailDateLine: '&cDatum: &f%date' -MailDetailMessageLine: '&cNachricht:' -MailDetailAttachmentsLine: '&cItem-Anhang:' -MailDetailAttachmentBox: '[Anhang-Box öffnen]' -MailDetailAttachmentBoxCancel: '[Item-Anhang abbrechen]' -MailDetailAttachmentBoxCancelToolTip: 'Brich den Item-Versand ab.\nDer Empfänger kann diese dann nicht mehr entgegennehmen.' -MailDetailAttachmentBoxCancelled: 'vom Absender abgebrochen' -MailDetailAttachmentBoxRefuse: '[Ablehnen]' -MailDetailAttachmentBoxRefuseToolTip: 'Lehne das Angebot ab und sende den Inhalt zum Absender zurück\nDu kannst optional einen Grund angeben.\nz.B.: \n&e/umail attach 1 refuse Viel zu teuer!' -MailDetailAttachmentBoxRefused: 'Vom Empfänger abgelehnt' -MailDetailAttachCostMoneyLine: ' &cC.O.D. Geld-Kosten, um Box zu öffnen: &f%fee' -MailDetailAttachCostItemLine: ' &cC.O.D. Item-Kosten, um Box zu öffnen: &f%item' -MailDetailAttachmentsOriginalLine: '&cItem-Anhang (original):' -MailDetailTrash: '[Löschen]' -MailDetailTrashRestore: '[Nachricht wiederherstellen]' -MailDetailReply: '[Antworten]' -MailDetailTeleport: '[Teleport zum Übertragungspunkt dieser Mail]' - -Editmode: 'Editier-Modus' - -EditmodeTitle: 'Mail-Editor' -EditmodeToDelete: '[x]' -EditmodeToDeleteToolTip: 'Entferne diesen Empfänger' -EditmodeTo: '[An]' -EditmodeToGroup: '[Gruppe]' -EditmodeToToolTip: 'Bearbeite diesen Empfänger' -EditmodeToAdd: '[Empfänger hinzufügen]' -EditmodeToAddToolTip: 'Klicke hier, um einen weiteren Empfänger\nhinzuzufügen und schreibe dessen\nSpielernamen in das Chatfenster' -EditmodeToAddress: '[Empfänger aus Spielerliste hinzufügen]' -EditmodeToGroupAdd: '[Empfängergruppe hinzufügen]' -EditmodeLineEditToolTip: 'Klicke hier, um eine Nachricht hinzuzufügen' -EditmodeLineDelete: '[x]' -EditmodeLineDeleteToolTip: 'Lösche diese Nachricht' -EditmodeLineAdd: '[weitere Zeilen hinzufügen]' -EditmodeAttach: '[Öffne Anhang-Box]' -EditmodeAttachNum: 'Angehangene Items: %num' -EditmodeCostMoney: '[Setze C.O.D. Geld-Kosten]' -EditmodeCostMoneyData: '[C.O.D. Geld-Kosten(%fee)]' -EditmodeCostMoneyToolTip: 'z.B.: &e/umail costmoney 30' -EditmodeCostMoneyRemove: '[x]' -EditmodeCostMoneyRemoveToolTip: 'Entferne C.O.D. Geld-Kosten' -EditmodeCostItem: '[Setze C.O.D. Item-Kosten]' -EditmodeCostItemData: '[C.O.D. Item-Kosten(%item)]' -EditmodeCostItemToolTip: 'Wenn du z.B. 5 Diamanten verlangen\nwillst, gib bitte folgendes ein:\n&e/umail costitem DIAMOND 5' -EditmodeCostItemRemove: '[x]' -EditmodeCostItemRemoveToolTip: 'Entferne C.O.D. Item-Kosten' -EditmodeSend: '[Senden]' -EditmodeCancel: '[Abbrechen]' - -EditmodeFeeInformation: '&7Diese Mail kostet %fee Porto.' -EditmodeFeeDetail: '&7(Mail-Porto: %mail + Gebüren für Anhänge: %item)' -EditmodeFeeDetailWithCODTax: '&7(Mail-Porto: %mail + Gebüren für Anhänge: %item + COD Gebüren: %cod)' -EditmodeFeeConfirm: '&7Möchtest du diese Mail trotzdem verschicken?' -EditmodeFeeResult: '&7%fee Porto bezahlt. (Kontostand: %remain)' - -BoxOpenCostMoneyInformation: '&7Um die Anhang-Box zu öffnen, müssen %fee Gebüren bezahlt werden.' -BoxOpenCostMoneyResult: '&7%fee Gebüren bezahlt. (Kontostand: %remain)' -BoxOpenCostMoneySenderResult: '&7Du hast %fee für dein C.O.D. von %to erhalten. (Kontostand: %remain)' -BoxOpenCostItemInformation: '&7Du brauchst %amount %material(s), um die Anhang-Box zu öffnen.' -BoxOpenCostItemResult: '&7Costed %amount %material(s).' -BoxOpenCostItemSenderResult: 'You got C.O.D. item %amount %material(s) from %to.' -BoxOpenCostConfirm: '&7Möchtest du das wirklich öffnen?' - -BoxRefuseSenderResult: 'Systemnachricht: %to hat das Angebot abgelehnt,\n für Mail #%num und dessen Inhalt zu zahlen.' -BoxRefuseSenderResultReason: 'Grund: ' - -ButtonOK: '[OK]' -ButtonCancel: '[Abbrechen]' - -InboxTitle: 'Posteingang (ungelesen: %unread)' -OutboxTitle: 'Postausgang' -TrashboxTitle: 'Papierkorb' - -Return: '[Zurück]' -FirstPage: '[<<]' -PrevPage: '[<]' -NextPage: '[>]' -LastPage: '[>>]' -ReturnToolTip: 'Zurück zum Index' -FirstPageToolTip: 'Erste Seite' -PrevPageToolTip: 'Vorherige Seite' -NextPageToolTip: 'Nächste Seite' -LastPageToolTip: 'Letzte Seite' -ReturnListToolTip: 'Zurück zur Liste' -FirstMailToolTip: 'Erste Mail' -PrevMailToolTip: 'Vorherige Mail' -NextMailToolTip: 'Nächste Mail' -LastMailToolTip: 'Letzte Mail' - -EditmodeBoxTitle: 'Anhang-Box (Bearbeiten-Modus)' -AttachmentBoxTitle: 'Anhang-Box (#%number)' - -PlayerListIndexTitle: 'Player Name List Index' -PlayerListTitle: 'Player Name List (%pre)' - -GroupListTitle: 'Gruppenliste (%num Gruppe/n)' -GroupDetailTitle: 'Gruppe "%name"' -GroupListSummayLine: '&7Besitzer: &f%owner &7Mitglieder: &f%num' -GroupMakeNewGroup: '[Neue Gruppe anlegen]' -GroupMakeNewGroupToolTip: '&fBitte anklicken und einen Namen für die neue Gruppe angeben.\n&e/ugroup create MeineGruppe' -GroupMakeNewGroupToolTipForSelection: 'Wenn du eine neue Gruppe anlegen willst,\nnutze den &e/ugroup &fBefehl, um die Gruppenverwaltungsansicht zu öffnen.' -GroupAddMember: '[Mitglied hinzufügen]' -GroupAddMemberAddress: '[Mitglied von Spielerliste hinzufügen]' -GroupAddMemberAllLogin: '[In alle Spieler hinzufügen]' -GroupDeleteMemberButton: '[x]' -GroupDeleteMemberToolTip: 'Dieses Mitglied entfernen.' -GroupDeleteMemberOwnerToolTip: 'Besitzer kann nicht entfernt werden.' -GroupOwnerLine: 'Besitzer: &f%owner' -GroupMemberLine: 'Mitglieder:' -GroupChangeSetting: '[Gruppeneinstellungen]' -GroupSettingTitle: 'Einstellungen der Gruppe "%name"' -GroupSendPerm: 'Berechtigung - Senden an diese Gruppe: ' -GroupModifyPerm: 'Berechtigung - Einstellungen/Mitglieder ändern: ' -GroupDissolutionPerm: 'Berechtigung - Gruppe löschen: ' -GroupPermNever: 'Niemand' -GroupPermOP: 'nur OP' -GroupPermOwner: 'nur Besitzer' -GroupPermMember: 'Mitglieder' -GroupPermEveryone: 'Jeder' -GroupPermChangeButton: '[%perm]' -GroupDeleteGroup: '[Diese Gruppe löschen]' -GroupReturnToMemberList: '[Zurück zur Mitgliederliste]' - -GroupSpecialAllSummary: 'Sondergruppe mit allen Spielern' -GroupSpecialAllMembers: 'Alle Spieler, einschließlich der neuen Ankömmlinge' -GroupSpecialAllConnectedMembers: 'Alle Spieler, die verbunden war' -GroupSpecialAllLoginMembers: 'Alle Spieler, die derzeit angeschlossen' - -WelcomeMailBody: 'Klicke hier, um diese Mail zu öffnen.\n\nWillkommen auf unserem Server!\nWenn du deine Mails checken willst, nutze bitte den "/mail" Befehl.\nWenn du eine Mail schreiben willst, nutze den "/mail write" Befehl.\nViel Spaß!' - -DateFormat: 'dd.MM HH:mm' - -ListVerticalParts: '&7| ' -ListHorizontalParts: '&7-----' -ListLastLine: '&7------------------------------' -DetailVerticalParts: '&7# ' -DetailHorizontalParts: '&7=====' -DetailLastLine: '&7==============================' - -HelpTitle: 'UndineMailer Befehlsübersicht' - -HelpCommand_inbox: '/mail inbox' -HelpCommand_outbox: '/mail outbox' -HelpCommand_trash: '/mail trash' -HelpCommand_text: '/mail text ' -HelpCommand_write: '/mail write' -HelpCommand_item: '/mail item' -HelpCommand_help: '/mail help' -HelpCommand_group: '/ugroup' -HelpCommand_reload: '/mail reload' - -HelpDescription_inbox: 'Zeige deinen Posteingang' -HelpDescription_outbox: 'Zeige deinen Postausgang' -HelpDescription_trash: 'Zeige deinen Papierkorb' -HelpDescription_text: 'Sende eine einfache Textnachricht' -HelpDescription_write: 'Schreibe eine neue Nachricht im Mail-Editor' -HelpDescription_item: 'Zeige Informationen zum Item in deiner Hand' -HelpDescription_help: 'Zeige die Befehlsübersicht' -HelpDescription_group: 'Zeige die Gruppenverwaltungansicht' -HelpDescription_reload: 'Lade alle Dateien neu ein' - -Yes: 'ja' -No: 'nein' +# ${project.name} v${project.version} +# @author Androkai, ucchy +# @license LGPLv3 +# @copyright Copyright ucchy 2015 + +PermissionDeniedCommand: '&cDir fehlt die nötige Berechtigung.' + +ErrorRequireArgument: '&cBitte gib die nötigen Parameter (%param) mit an.' +ErrorInvalidIndex: '&cDie angegebene Mail-Nummer %index ist ungültig.' +ErrorNoneReadPermission: '&cDu darfst diese Mail nicht lesen.' +ErrorNotFoundDestination: '&cDer angegbene Empfänger %dest konnte nicht gefunden werden.' +ErrorCannotSendSelf: '&cDu darfst dir selbst keine Mails schicken.' +ErrorInGameCommand: '&cDu kannst diesen Befehl nicht auf der Konsole ausführen.' +ErrorNotInEditmode: '&cDu kannst diesen Befehl nicht nutzen, weil du dich nicht im Bearbeiten-Modus befindest.' +ErrorTooManyDestination: '&cDu kannst nur an bis zu %num Empfänger gleichzeitig schreiben.' +ErrorAlreadyExistTo: '&cDer angegebene Empfänger ist bereits eingetragen.' +ErrorEmptyTo: '&cEs wurde kein Empfänger angegeben.' +ErrorItemAttachedYet: '&cEs sind noch Items an diese Mail angehangen. Bitte entferne diese zuerst.' +ErrorCannotSendMultiAttach: '&cDu kannst Item-Anhänge nicht an mehrere Empfänger gleichzeitig senden.' +ErrorYouDontHaveEnoughMoney: '&cDu hast nicht genügend Geld!' +ErrorYouDontHaveEnoughItem: '&cDu hast nicht genügend Items!' +ErrorFailToWithdraw: '&cFehler beim Abheben der Gebüren von deinem Konto.' +ErrorFailToDeposit: '&cFehler beim Überweisen des Betrags auf das Konto.' +ErrorCannotSetMoneyAndItem: '&cDu kannst nicht Geld und Items gleichzeitig anhängen.' +ErrorInvalidCostMoney: '&cDer angegebene Betrag %fee ist ungültig.' +ErrorInvalidCostItem: '&cDas angegbene Item %item ist ungültig.' +ErrorInvalidItem: '&cDas angegebene Item %item ist ungültig.' +ErrorNoneCancelAttachPermission: '&cDu kannst keine Anhänge entfernen, weil du nicht der Absender dieser Mail bist.' +ErrorNoneRefuseAttachPermission: '&cDu kannst keine Anhänge entfernen, weil du nicht der Empfänger dieser Mail bist.' +ErrorAlreadyAttachCancelled: '&cDer Anhang wurde bereits entfernt.' +ErrorAlreadyRecipientOpened: '&cDu kannst keine Anhänge entfernen, weil der Empfänger die Anhang-Box gerade offen hat.' +ErrorInvalidAttachBoxWorld: '&cIn dieser Welt dürfen keine Anhänge angehangen oder abgerufen werden.\nÖffne die Anhang-Box erneut, nachdem du eine andere Welt betreten hast.' +ErrorInvalidCommand: '&cDieser Befehl ist ungültig.' +ErrorAttachBoxCountExceed: '&cDu nutzt derzeit %num Anhang-Boxen.\nDa das Limit von %limit Boxen erreicht ist, kannst du keine weiten Anhänge erstellen.' +ErrorCannotSendAttachMailToAllBecauseCacheLoading: '&cDa die Generierung des Spieler-Cache noch nicht abgeschlossen ist, können noch keine Mails mit Anhängen an alle Spieler versendet werden. Bitte versuche es später noch einmal.' +ErrorCannotListInitializingYet: '&cDie Mail-Liste kann nicht angezeigt werden, da der Mailer noch nicht eingerichtet wurde.' +ErrorCannotSendInitializingYet: '&cEs können keine neuen Mails gesendet werden, da der Mailer noch nicht eingerichtet wurde.' +ErrorAlreadyTrashed: '&cMail #%index befindet sich bereits im Papierkorb.' +ErrorNotTrashed: '&cMail #%index befindet sich nicht im Papierkorb.' +ErrorCannotDropBecauseUnread: '&cDu kannst Mail #%index nicht verschieben, weil sie noch nicht gelesen wurde.' +ErrorCannotDropBecauseAttached: '&cDu kannst Mail #%index nicht verschieben, weil sie noch Item-Anhänge hat.' +ErrorCannotSendSpamMail: '&cDu musst noch %remain Sekunden warten, bis du deine nächste Mail verschicken darfst.' +ErrorPlayerCacheIncomplete: '&cDa die Generierung des Spieler-Cache noch nicht abgeschlossen ist, kannst du momentan noch keine Mails an alle bisher vorhanden Spieler senden. Bitte versuche es später noch einmal.' +ErrorCannotFoundLocation: '&cDu konntest nicht teleportiert werden, da der Übertragungspunkt der angegeben Mail nicht aufgezeichnet wurde.' +ErrorProhibitItemAttached: '&cDas Anhängen von %material ist nicht erlaubt.' +ErrorContainsProhibitItemInShulkerbox: '&cShulkerBox enthält verbotene Gegenstände.' + +ErrorInvalidGroupName: '&c%name kann nicht als Gruppenname genutzt werden.' +ErrorGroupIsAlreadyExist: '&cDie Gruppe %name existiert bereits.' +ErrorGroupNotExist: '&cDie Gruppe %name exisitiert nicht.' +ErrorGroupSendNotPermission: '&cDu hast keine Berechtigung, an die Gruppe %name zu schreiben.' +ErrorGroupModifyNotPermission: '&cDu hast keine Berechtigung, die Gruppe %name zu bearbeiten.' +ErrorGroupDeleteNotPermission: '&cDu hast keine Berechtigung, die Gruppe %name zu löschen.' +ErrorNotFoundPlayer: '&cSpieler %player wurde nicht gefunden.' +ErrorPlayerIsAlreadyMember: '&cSpieler %player ist bereits Mitglied.' +ErrorPlayerIsNotMember: '&cSpieler %player ist kein Mitglied.' +ErrorPlayerIsOwner: '&cSpieler %player kann nicht entfernt werden, weil er der Besitzer ist.' +ErrorGroupCreateLimitExceed: '&cDu kannst keine weiteren Gruppen erstellen, weil du das das Gruppen-Limit von %num erreicht hast.' +ErrorGroupMemberLimitExceed: '&cDu kannst keine weiteren Mitglieder hinzufügen, weil du das Mitglieder-Limit von %num erreicht hast.' +ErrorInvalidPermissionType: '&cDie angegebene Berechtigung vom Typ %type ist ungültig.' +ErrorInvalidPermissionValue: '&cDie angegebene Berechtigung vom Wert %value ist ungültig.' +ErrorEveryonePermissionInvalid: '&cBearbeiten- und Löschen-Berechtigung kann nicht für JEDEN gesetzt werden.' + +InformationReloading: '&aLade alle Mail- und Config-Dateien neu ein...' +InformationReload: '&aReload abgeschlossen.' +InformationYouGotMail: '&aDu hast eine neue Mail von &7%from&a erhalten!' +InformationYouSentMail: '&7Deine Mail wurde versand.' +InformationEditCancelled: '&7Mail-Erstellen abgebrochen.' +InformationPlayerJoin: '&7Du hast %unread ungelesene Mails.' +InformationItemDetail: '&7Item Information: &f%desc &7Handelbar?: &f%tradable' +InformationMultiAttachConfirm: '&7Du willst eine Mail mit Item-Anhang an mehrere Empfänger versenden.\nDiese Mail wird %num mal vervielfacht werden. Bist du sicher?' +InformationMultiAttachConfirmConsole: 'Zum Senden nutze den ''/mail send attachconfirm'' Befehl.' +InformationAttachWasCanceledBySender: '&7Absender %sender hat den Inhalt der Anhang-Box der Mail #%num zurückgezogen.' +InformationTrashed: '&7Mail #%index wurde in den Papierkorb verschoben.' +InformationTrashRestored: '&7Mail #%index wurde aus deinem Papierkorb wiederhergestellt.' +InformationTrashAllNeedConfirmation: '&7All deine gelesenen erhaltenen/gesendeten %num Mails werden in den Papierkorb verschoben.\nIst das OK? Dann bestätige dies bitte mit dem Befehl "/%command trash all confirm".' +InformationTrashAllDone: '&7%num Mail(s) wurden in den Papierkorb verschoben.' +InformationRestoreAllNeedConfirmation: '&7Alle %num Mail(s) aus deinem Papierkorb werden wiederhergestellt.\nIst das OK? Dann bestätige dies bitte mit dem Befehl "/%command trash restoreall confirm".' +InformationRestoreAllDone: '&7%num Mail(s) aus deinem Papierkorb wurden wiederhergestellt.' +InformationTeleported: '&aDu wurdest an den Übertragungspunkt der Mail %index teleportiert.' + +InformationMakeGroup: '&7Neue Gruppe %name wurde erstellt.' +InformationDeleteGroup: '&7Gruppe %name wurde entfernt.' +InformationDeleteGroupConfirm: '&7Die Gruppe %name wird dauerhaft gelöscht. Bist du sicher?' +InformationGroupMemberAdd: '&7Spieler %player zur Gruppe %group hinzugefügt.' +InformationGroupMemberAddAllLogin: '&7%num Spieler zur Gruppe %group hinzugefügt.' +InformationGroupMemberRemove: '&7Spieler %player wurde aus der Gruppe %group entfernt.' +InformationGroupSetPermission: '&7%type Berechtigung der Gruppe %name wurde zu %value geändert.' + +InformationTipsReadCommandFail: '&7TIP: Du kannst eine Mail ganz leicht öffnen, indem du in der Mailliste auf die blaue Zahl klickst.' + +SummaryOpenThisMailToolTip: 'Öffne diese Mail' + +MailDetailTitle: 'Mail (#%number)' +MailDetailFromToLine: '&cVon: &f%from &cAn: &f%to' +MailDetailDateLine: '&cDatum: &f%date' +MailDetailMessageLine: '&cNachricht:' +MailDetailAttachmentsLine: '&cItem-Anhang:' +MailDetailAttachmentBox: '[Anhang-Box öffnen]' +MailDetailAttachmentBoxCancel: '[Item-Anhang abbrechen]' +MailDetailAttachmentBoxCancelToolTip: 'Bricht den Item-Versand ab.\nDer Empfänger kann diese dann nicht mehr entgegennehmen.' +MailDetailAttachmentBoxCancelled: 'vom Absender abgebrochen' +MailDetailAttachmentBoxRefuse: '[Ablehnen]' +MailDetailAttachmentBoxRefuseToolTip: 'Lehne das Angebot ab und sende den Inhalt zum\nAbsender zurück. Du kannst optional einen\nGrund angeben.\nz.B.: &e/umail attach 1 refuse Viel zu teuer!' +MailDetailAttachmentBoxRefused: 'Vom Empfänger abgelehnt' +MailDetailAttachCostMoneyLine: ' &cGeld-Kosten, um Box zu öffnen: &f%fee' +MailDetailAttachCostItemLine: ' &cItem-Kosten, um Box zu öffnen: &f%item' +MailDetailAttachmentsOriginalLine: '&cItem-Anhang (Original):' +MailDetailTrash: '[Löschen]' +MailDetailTrashRestore: '[Nachricht wiederherstellen]' +MailDetailReply: '[Antworten]' +MailDetailTeleport: '[Teleport zum Übertragungspunkt dieser Mail]' + +Editmode: 'Editier-Modus' + +EditmodeTitle: 'Mail-Editor' +EditmodeToDelete: '[x]' +EditmodeToDeleteToolTip: 'Entferne diesen Empfänger' +EditmodeTo: '[An]' +EditmodeToGroup: '[Gruppe]' +EditmodeToToolTip: 'Bearbeite diesen Empfänger' +EditmodeToAdd: '[Empfänger hinzufügen]' +EditmodeToAddToolTip: 'Klicke hier, um einen weiteren Empfänger\nhinzuzufügen und schreibe dessen\nSpielernamen in das Chatfenster' +EditmodeToAddress: '[Empfänger aus Spielerliste hinzufügen]' +EditmodeToGroupAdd: '[Empfängergruppe hinzufügen]' +EditmodeLineEdit: '[Text%num]' +EditmodeLineEditToolTip: 'Klicke hier, um eine Nachricht hinzuzufügen' +EditmodeLineDelete: '[x]' +EditmodeLineDeleteToolTip: 'Lösche diese Nachricht' +EditmodeLineAdd: '[weitere Zeilen hinzufügen]' +EditmodeAttach: '[Öffne Anhang-Box]' +EditmodeAttachNum: 'Angehangene Items: %num' +EditmodeCostMoney: '[Füge Geld-Kosten hinzu]' +EditmodeCostMoneyData: '[Geld-Kosten (%fee)]' +EditmodeCostMoneyToolTip: 'z.B.: &e/umail costmoney 30' +EditmodeCostMoneyRemove: '[x]' +EditmodeCostMoneyRemoveToolTip: 'Entferne Geld-Kosten' +EditmodeCostItem: '[Füge Item-Kosten hinzu]' +EditmodeCostItemData: '[Item-Kosten (%item)]' +EditmodeCostItemToolTip: 'Wenn du z.B. 5 Diamanten verlangen\nwillst, gib bitte folgendes ein:\n&e/umail costitem DIAMOND 5' +EditmodeCostItemRemove: '[x]' +EditmodeCostItemRemoveToolTip: 'Entferne Item-Kosten' +EditmodeSend: '[Senden]' +EditmodeCancel: '[Abbrechen]' +EditmodeTipsMessage: '&7TIP: Klicke auf [Text1], um die Zeile zu bearbeiten.' + +EditmodeFeeInformation: '&7Diese Mail kostet %fee Porto.' +EditmodeFeeDetail: '&7(Mail-Porto: %mail + Gebüren für Anhänge: %item)' +EditmodeFeeDetailWithCODTax: '&7(Mail-Porto: %mail + Gebüren für Anhänge: %item + Handelsgebüren: %cod)' +EditmodeFeeConfirm: '&7Möchtest du diese Mail trotzdem versenden?' +EditmodeFeeResult: '&7%fee Porto bezahlt. (Kontostand: %remain)' + +BoxOpenCostMoneyInformation: '&7Um die Anhang-Box zu öffnen, müssen %fee Gebüren bezahlt werden.' +BoxOpenCostMoneyResult: 'Du hast &7%fee bezahlt. (Kontostand: %remain)' +BoxOpenCostMoneySenderResult: '&7Du hast für dein Angebot %fee von %to erhalten. (Kontostand: %remain)' +BoxOpenCostItemInformation: '&7Um die Anhang-Box zu öffnen, müssen %amount %material bezahlt werden.' +BoxOpenCostItemResult: '&7Du hast %amount %material bezahlt.' +BoxOpenCostItemSenderResult: 'Du hast für dein Angebot %amount %material von %to erhalten.' +BoxOpenCostConfirm: '&7Möchtest du die Box trotzdem öffnen?' + +BoxRefuseSenderResult: 'Systemnachricht: %to hat das Angebot abgelehnt,\n für Mail #%num und deren Inhalt zu zahlen.' +BoxRefuseSenderResultReason: 'Grund: ' + +ButtonOK: '[OK]' +ButtonCancel: '[Abbrechen]' + +InboxTitle: 'Posteingang (ungelesen: %unread)' +OutboxTitle: 'Postausgang' +TrashboxTitle: 'Papierkorb' + +Return: '[Zurück]' +FirstPage: '[<<]' +PrevPage: '[<]' +NextPage: '[>]' +LastPage: '[>>]' +ReturnToolTip: 'Zurück zum Index' +FirstPageToolTip: 'Erste Seite' +PrevPageToolTip: 'Vorherige Seite' +NextPageToolTip: 'Nächste Seite' +LastPageToolTip: 'Letzte Seite' +ReturnListToolTip: 'Zurück zur Liste' +FirstMailToolTip: 'Erste Mail' +PrevMailToolTip: 'Vorherige Mail' +NextMailToolTip: 'Nächste Mail' +LastMailToolTip: 'Letzte Mail' + +EditmodeBoxTitle: 'Anhang-Box (Bearbeiten-Modus)' +AttachmentBoxTitle: 'Anhang-Box (#%number)' + +PlayerListIndexTitle: 'Spielerliste Index' +PlayerListTitle: 'Spielerliste (%pre)' + +GroupListTitle: 'Gruppenliste (%num Gruppe/n)' +GroupDetailTitle: 'Gruppe "%name"' +GroupListSummayLine: '&7Besitzer: &f%owner &7Mitglieder: &f%num' +GroupMakeNewGroup: '[Neue Gruppe anlegen]' +GroupMakeNewGroupToolTip: '&fBitte anklicken und einen Namen für die neue Gruppe angeben.\n&e/ugroup create MeineGruppe' +GroupMakeNewGroupToolTipForSelection: 'Wenn du eine neue Gruppe anlegen willst,\nnutze den &e/ugroup &fBefehl, um die Gruppenverwaltungsansicht zu öffnen.' +GroupAddMember: '[Mitglied hinzufügen]' +GroupAddMemberAddress: '[Mitglied von Spielerliste hinzufügen]' +GroupAddMemberAllLogin: '[In alle Spieler hinzufügen]' +GroupDeleteMemberButton: '[x]' +GroupDeleteMemberToolTip: 'Dieses Mitglied entfernen.' +GroupDeleteMemberOwnerToolTip: 'Besitzer kann nicht entfernt werden.' +GroupOwnerLine: 'Besitzer: &f%owner' +GroupMemberLine: 'Mitglieder:' +GroupChangeSetting: '[Gruppeneinstellungen]' +GroupSettingTitle: 'Einstellungen der Gruppe "%name"' +GroupSendPerm: 'Berechtigung - Senden an diese Gruppe: ' +GroupModifyPerm: 'Berechtigung - Einstellungen/Mitglieder ändern: ' +GroupDissolutionPerm: 'Berechtigung - Gruppe löschen: ' +GroupPermNever: 'Niemand' +GroupPermOP: 'nur OP' +GroupPermOwner: 'nur Besitzer' +GroupPermMember: 'Mitglieder' +GroupPermEveryone: 'Jeder' +GroupPermChangeButton: '[%perm]' +GroupDeleteGroup: '[Diese Gruppe löschen]' +GroupReturnToMemberList: '[Zurück zur Mitgliederliste]' + +GroupSpecialAllSummary: 'Sondergruppe mit allen Spielern' +GroupSpecialAllMembers: 'Alle Spieler, einschließlich noch Hinzukommende' +GroupSpecialAllConnectedMembers: 'Alle Spieler, die bisher auf dem Server waren' +GroupSpecialAllLoginMembers: 'Alle Spieler, die derzeit online sind' + +WelcomeMailBody: 'Klicke hier, um diese Mail zu öffnen.\n\nWillkommen auf unserem Server!\nWenn du deine Mails checken willst, nutze bitte den "/mail" Befehl.\nWenn du eine Mail schreiben willst, nutze den "/mail write" Befehl.\nViel Spaß!' + +DateFormat: 'dd.MM HH:mm' + +ListVerticalParts: '&7| ' +ListHorizontalParts: '&7-----' +ListLastLine: '&7------------------------------' +DetailVerticalParts: '&7# ' +DetailHorizontalParts: '&7=====' +DetailLastLine: '&7==============================' + +HelpTitle: 'UndineMailer Befehlsübersicht' + +HelpCommand_inbox: '/mail inbox' +HelpCommand_outbox: '/mail outbox' +HelpCommand_trash: '/mail trash' +HelpCommand_text: '/mail text ' +HelpCommand_write: '/mail write' +HelpCommand_item: '/mail item' +HelpCommand_help: '/mail help' +HelpCommand_group: '/ugroup' +HelpCommand_reload: '/mail reload' + +HelpDescription_inbox: 'Zeigt deinen Posteingang' +HelpDescription_outbox: 'Zeigt deinen Postausgang' +HelpDescription_trash: 'Zeigt deinen Papierkorb' +HelpDescription_text: 'Sende eine einfache Textnachricht' +HelpDescription_write: 'Schreibe eine neue Nachricht im Mail-Editor' +HelpDescription_item: 'Zeigt Informationen zum Item in deiner Hand' +HelpDescription_help: 'Zeigt die Befehlsübersicht' +HelpDescription_group: 'Zeigt die Gruppenverwaltungansicht' +HelpDescription_reload: 'Lädt alle Dateien neu ein' + +Yes: 'ja' +No: 'nein' diff --git a/src/main/resources/messages_en.yml b/src/main/resources/messages_en.yml index 854b2fd..6b2af85 100644 --- a/src/main/resources/messages_en.yml +++ b/src/main/resources/messages_en.yml @@ -1,257 +1,260 @@ -# ${project.name} v${project.version} -# @author ucchy -# @license LGPLv3 -# @copyright Copyright ucchy 2015 - -PermissionDeniedCommand: '&cYou don''t have a permission.' - -ErrorRequireArgument: '&cPlease specify the parameters %param to the command.' -ErrorInvalidIndex: '&cThe specified mail index %index is invalid.' -ErrorNoneReadPermission: '&cCannot read this mail.' -ErrorNotFoundDestination: '&cThe specified destination %dest is not found.' -ErrorCannotSendSelf: '&cCannot send mail to yourself.' -ErrorInGameCommand: '&cYou cannot run this command from console.' -ErrorNotInEditmode: '&cBecause you are not in mail edit mode, you cannot use this command.' -ErrorTooManyDestination: '&cDestination that can be set to one of the e-mail is up to %num.' -ErrorAlreadyExistTo: '&cThe specified destination is contained already.' -ErrorEmptyTo: '&cDestination is empty.' -ErrorItemAttachedYet: '&cThe items are attached in your mail yet. Please empty it.' -ErrorCannotSendMultiAttach: '&cYou cannot send mail attached items to multiple destinations.' -ErrorYouDontHaveEnoughMoney: '&cYou don''t have enough money!' -ErrorYouDontHaveEnoughItem: '&cYou don''t have enough items!' -ErrorFailToWithdraw: '&cFail to withdraw fee from your account.' -ErrorFailToDeposit: '&cFail to deposit fee to his account.' -ErrorCannotSetMoneyAndItem: '&cYou cannot set both of money and item.' -ErrorInvalidCostMoney: '&cThe specified C.O.D. money %fee is invalid.' -ErrorInvalidCostItem: '&cThe specified C.O.D. item %item is invalid.' -ErrorInvalidItem: '&cThe specified item %item is invalid.' -ErrorNoneCancelAttachPermission: '&cBecause you are not sender of this mail, you cannot cancel attachments.' -ErrorNoneRefuseAttachPermission: '&cBecause you are not recipient of this mail, you cannot refuse attachments.' -ErrorAlreadyAttachCancelled: '&cThe attachment was cancelled already.' -ErrorAlreadyRecipientOpened: '&cBecause recipient had opened box, you cannot cancel attachments.' -ErrorInvalidAttachBoxWorld: '&cIn this world, sending and receiving of items is prohibited. After you move to a different world, please open the attached box.' -ErrorInvalidCommand: '&cThis command is invalid.' -ErrorAttachBoxCountExceed: '&cYou are using %num attached boxes now, because it exceeds the limit %limit, you cannot send attachments with mail!' -ErrorCannotSendAttachMailToAllBecauseCacheLoading: '&cSince the preparation of the players cache data has not been completed, you cannot send attachments with Mail to All. Please try again after waiting for a while.' -ErrorCannotListInitializingYet: '&cBecause mailer has not yet been initialized, cannot view the mail list.' -ErrorCannotSendInitializingYet: '&cBecause mailer has not yet been initialized, cannot send new mail.' -ErrorAlreadyTrashed: '&cMail #%index is already in trash box.' -ErrorNotTrashed: '&cMail #%index is not in trash box.' -ErrorCannotDropBecauseUnread: '&cBecause Mail #%index is unread yet, you cannot move this mail.' -ErrorCannotDropBecauseAttached: '&cBecause Mail #%index has attached item yet, you cannot move this mail.' -ErrorCannotSendSpamMail: '&cYou cannot send mail in succession. Until the next mail is to be sent, you need %remain seconds.' -ErrorPlayerCacheIncomplete: '&cSince the players cache has not been completed, you cannot send AllConnected addressed mail. Please send after waiting for a while.' -ErrorCannotFoundLocation: '&cYou cannot teleport for transmission point is not recorded to the specified mail.' -ErrorProhibitItemAttached: '&cItem %material is prohibited to attach.' - -ErrorInvalidGroupName: '&c%name cannot be used as a group name.' -ErrorGroupIsAlreadyExist: '&cGroup %name cannot be used because it already exists.' -ErrorGroupNotExist: '&cGroup %name does not exist.' -ErrorGroupSendNotPermission: '&cYou don''t have send mail permission to Group %name.' -ErrorGroupModifyNotPermission: '&cYou don''t have modify permission of Group %name.' -ErrorGroupDeleteNotPermission: '&cYou don''t have dissolution permission of Group %name.' -ErrorNotFoundPlayer: '&cPlayer %player is not found.' -ErrorPlayerIsAlreadyMember: '&cPlayer %player is already a member.' -ErrorPlayerIsNotMember: '&cPlayer %player is not a member.' -ErrorPlayerIsOwner: '&cCannot remove Player %player because he is owner.' -ErrorGroupCreateLimitExceed: '&cBecause you have exceeded the group creation limit %num, you can not create a new group.' -ErrorGroupMemberLimitExceed: '&cBecause it exceeded the number of members limit %num, you can not add members.' -ErrorInvalidPermissionType: '&cThe specified permission type %type is not correct.' -ErrorInvalidPermissionValue: '&cThe specified permission value %value is not correct.' -ErrorEveryonePermissionInvalid: '&cModify permission and dissolution permission can not be set to EVERYONE.' - -InformationReloading: '&aReloading all mails and configs data...' -InformationReload: '&aReload completed.' -InformationYouGotMail: '&aYou got new mail from &7%from&a!' -InformationYouSentMail: '&7Your mail has been sent.' -InformationEditCancelled: '&7Cancelled to make mail.' -InformationPlayerJoin: '&7You have %unread unread mail.' -InformationItemDetail: '&7Item Information: &f%desc &7Tradable?: &f%tradable' -InformationMultiAttachConfirm: '&7You will send item attached mail to multiple destinations. This mail will be replicated %num mails to send. Are you sure?' -InformationMultiAttachConfirmConsole: 'To send, run ''mail send attachconfirm'' command.' -InformationAttachWasCanceledBySender: '&7Sender %sender canceled the attachment item box of mail #%num.' -InformationTrashed: '&7Moved mail #%index to trash box.' -InformationTrashRestored: '&7Restored mail #%index from trash box.' -InformationTrashAllNeedConfirmation: '&7All of your readed received/sent %num mail(s) will be moved to trash box. If OK, please run the additional command "/%command trash all confirm".' -InformationTrashAllDone: '&7Moved %num mail(s) to trash box.' -InformationRestoreAllNeedConfirmation: '&7All of your trash box %num mail(s) will be restored from trash box. If OK, please run the additional command "/%command trash restoreall confirm".' -InformationRestoreAllDone: '&7Restored %num mail(s) from trash box.' -InformationTeleported: '&aYou were teleported to the transmission point of the mail %index.' - -InformationMakeGroup: '&7Created a new group %name.' -InformationDeleteGroup: '&7Deleted a group %name.' -InformationDeleteGroupConfirm: '&7Will delete the group %name. Are you sure?' -InformationGroupMemberAdd: '&7Added Player %player to the members of the group %group.' -InformationGroupMemberAddAllLogin: '&7Added %num player(s) to the members of the group %group.' -InformationGroupMemberRemove: '&7Removed Player %player from the members of the group %group.' -InformationGroupSetPermission: '&7Set %type permission of Group %name to %value.' - -InformationTipsReadCommandFail: '&7TIPS: You can open mail by clicking blue numbers in mail list.' - -SummaryOpenThisMailToolTip: 'Open this mail' - -MailDetailTitle: 'Mail (#%number)' -MailDetailFromToLine: '&cFrom: &f%from &cTo: &f%to' -MailDetailDateLine: '&cDate: &f%date' -MailDetailMessageLine: '&cMessage:' -MailDetailAttachmentsLine: '&cAttached Items:' -MailDetailAttachmentBox: '[Open Attachment Box]' -MailDetailAttachmentBoxCancel: '[Cancel Attachment Items]' -MailDetailAttachmentBoxCancelToolTip: 'Cancel the attached items, and get back it.\nRecipient will not be able to open box.' -MailDetailAttachmentBoxCancelled: 'Cancelled by sender' -MailDetailAttachmentBoxRefuse: '[Refuse]' -MailDetailAttachmentBoxRefuseToolTip: 'Refuse to receive items, \nand send back to sender.\nYou can write a reason \nof refusing (optional). ex: \n&e/umail attach 1 refuse Too expensive!!' -MailDetailAttachmentBoxRefused: 'Refused by recipient' -MailDetailAttachCostMoneyLine: ' &cC.O.D. money to open box: &f%fee' -MailDetailAttachCostItemLine: ' &cC.O.D. item to open box: &f%item' -MailDetailAttachmentsOriginalLine: '&cAttached Items (original):' -MailDetailTrash: '[Delete]' -MailDetailTrashRestore: '[Restore from trash]' -MailDetailReply: '[Reply]' -MailDetailTeleport: '[Teleport to the location of sending this mail]' - -Editmode: 'EditMode' - -EditmodeTitle: 'Mail Edit' -EditmodeToDelete: '[x]' -EditmodeToDeleteToolTip: 'Delete this recipient' -EditmodeTo: '[To]' -EditmodeToGroup: '[Group]' -EditmodeToToolTip: 'Edit recipient' -EditmodeToAdd: '[Add recipient]' -EditmodeToAddToolTip: 'To add new recipient,\nclick here and\ninput recipient player name to chat window.' -EditmodeToAddress: '[Add recipient from Player list]' -EditmodeToGroupAdd: '[Add recipient group]' -EditmodeLineEditToolTip: 'Click here to add your message' -EditmodeLineDelete: '[x]' -EditmodeLineDeleteToolTip: 'Delete this line' -EditmodeLineAdd: '[Add more lines]' -EditmodeAttach: '[Open attachment box]' -EditmodeAttachNum: 'Attachment Items: %num' -EditmodeCostMoney: '[Set C.O.D. money]' -EditmodeCostMoneyData: '[C.O.D. money(%fee)]' -EditmodeCostMoneyToolTip: 'ex: &e/umail costmoney 30' -EditmodeCostMoneyRemove: '[x]' -EditmodeCostMoneyRemoveToolTip: 'Remove C.O.D. money' -EditmodeCostItem: '[Set C.O.D. item]' -EditmodeCostItemData: '[C.O.D.(%item)]' -EditmodeCostItemToolTip: 'If you want to request 5 diamonds,\nplease run the following command:\n&e/umail costitem DIAMOND 5' -EditmodeCostItemRemove: '[x]' -EditmodeCostItemRemoveToolTip: 'Remove C.O.D. item' -EditmodeSend: '[Send]' -EditmodeCancel: '[Cancel]' - -EditmodeFeeInformation: '&7You need %fee for sending this mail.' -EditmodeFeeDetail: '&7(mail fee %mail + attachment fee %item)' -EditmodeFeeDetailWithCODTax: '&7(mail fee %mail + attachment fee %item + COD fee %cod)' -EditmodeFeeConfirm: '&7Are you sure to send?' -EditmodeFeeResult: '&7Costed %fee. (Balance: %remain)' - -BoxOpenCostMoneyInformation: '&7You need %fee for opening attachment box.' -BoxOpenCostMoneyResult: '&7Costed %fee. (Balance: %remain)' -BoxOpenCostMoneySenderResult: '&7You got C.O.D. money %fee from %to. (Balance: %remain)' -BoxOpenCostItemInformation: '&7You need %amount %material(s) for opening attachment box.' -BoxOpenCostItemResult: '&7Costed %amount %material(s).' -BoxOpenCostItemSenderResult: 'You got C.O.D. item %amount %material(s) from %to.' -BoxOpenCostConfirm: '&7Are you sure to open?' - -BoxRefuseSenderResult: 'System Message : %to refused to pay\n for mail #%num and its attached contents.' -BoxRefuseSenderResultReason: 'Reason: ' - -ButtonOK: '[OK]' -ButtonCancel: '[Cancel]' - -InboxTitle: 'Inbox (Unread %unread)' -OutboxTitle: 'Outbox' -TrashboxTitle: 'Trashbox' - -Return: '[Return]' -FirstPage: '[<<]' -PrevPage: '[<]' -NextPage: '[>]' -LastPage: '[>>]' -ReturnToolTip: 'Return to index' -FirstPageToolTip: 'First Page' -PrevPageToolTip: 'Prev Page' -NextPageToolTip: 'Next Page' -LastPageToolTip: 'Last Page' -ReturnListToolTip: 'Return to list' -FirstMailToolTip: 'Latest Mail' -PrevMailToolTip: 'Prev Mail' -NextMailToolTip: 'Next Mail' -LastMailToolTip: 'Last Mail' - -EditmodeBoxTitle: 'AttachmentBox (EditMode)' -AttachmentBoxTitle: 'AttachmentBox (#%number)' - -PlayerListIndexTitle: 'Player Name List Index' -PlayerListTitle: 'Player Name List (%pre)' - -GroupListTitle: 'Group List (%num groups)' -GroupDetailTitle: 'Group %name' -GroupListSummayLine: '&7Owner: &f%owner &7Members: &f%num' -GroupMakeNewGroup: '[Add new group]' -GroupMakeNewGroupToolTip: '&fPlease specify new group name.\n&e/ugroup create MyGroup' -GroupMakeNewGroupToolTipForSelection: 'If you want to register new group,\nrun &e/ugroup &fcommand to enter group management.' -GroupAddMember: '[Add member]' -GroupAddMemberAddress: '[Add member from Player list]' -GroupAddMemberAllLogin: '[Add all login players]' -GroupDeleteMemberButton: '[x]' -GroupDeleteMemberToolTip: 'Remove this member.' -GroupDeleteMemberOwnerToolTip: 'Cannot remove owner player.' -GroupOwnerLine: 'Owner: &f%owner' -GroupMemberLine: 'Member:' -GroupChangeSetting: '[Group Settings]' -GroupSettingTitle: 'Settings of Group %name' -GroupSendPerm: 'Send mail to this group permission: ' -GroupModifyPerm: 'Modify settings/members permission: ' -GroupDissolutionPerm: 'Break-up this group permission: ' -GroupPermNever: 'Never' -GroupPermOP: 'OP only' -GroupPermOwner: 'Owner only' -GroupPermMember: 'Members' -GroupPermEveryone: 'Everyone' -GroupPermChangeButton: '[%perm]' -GroupDeleteGroup: '[Break-up this group]' -GroupReturnToMemberList: '[Return to group members list]' - -GroupSpecialAllSummary: 'All Players Group' -GroupSpecialAllMembers: 'All players including new comers' -GroupSpecialAllConnectedMembers: 'All players that had connected' -GroupSpecialAllLoginMembers: 'All players currently connected' - -WelcomeMailBody: 'Click number to open this mail.\n\nWelcome to server!\nIf you want to read mails, please run "/mail" command.\nIf you want to write mail, please run "/mail write" command.\nHave fun!' - -DateFormat: 'MMM dd HH:mm' - -ListVerticalParts: '&7| ' -ListHorizontalParts: '&7-----' -ListLastLine: '&7------------------------------' -DetailVerticalParts: '&7# ' -DetailHorizontalParts: '&7=====' -DetailLastLine: '&7==============================' - -HelpTitle: 'UndineMailer Command Help' - -HelpCommand_inbox: '/mail inbox' -HelpCommand_outbox: '/mail outbox' -HelpCommand_trash: '/mail trash' -HelpCommand_text: '/mail text ' -HelpCommand_write: '/mail write' -HelpCommand_item: '/mail item' -HelpCommand_help: '/mail help' -HelpCommand_group: '/ugroup' -HelpCommand_reload: '/mail reload' - -HelpDescription_inbox: 'Check your inbox mails.' -HelpDescription_outbox: 'Check your sent mails.' -HelpDescription_trash: 'Check your deleted mails.' -HelpDescription_text: 'Send simple text mail.' -HelpDescription_write: 'Enter to mail creation mode.' -HelpDescription_item: 'Show the detailed information of item in your hand.' -HelpDescription_help: 'Show the help menu.' -HelpDescription_group: 'Show the group management menu.' -HelpDescription_reload: 'Reload all of data.' - -Yes: 'Yes' -No: 'No' +# ${project.name} v${project.version} +# @author ucchy +# @license LGPLv3 +# @copyright Copyright ucchy 2015 + +PermissionDeniedCommand: '&cYou don''t have a permission.' + +ErrorRequireArgument: '&cPlease specify the parameters %param to the command.' +ErrorInvalidIndex: '&cThe specified mail index %index is invalid.' +ErrorNoneReadPermission: '&cCannot read this mail.' +ErrorNotFoundDestination: '&cThe specified destination %dest is not found.' +ErrorCannotSendSelf: '&cCannot send mail to yourself.' +ErrorInGameCommand: '&cYou cannot run this command from console.' +ErrorNotInEditmode: '&cBecause you are not in mail edit mode, you cannot use this command.' +ErrorTooManyDestination: '&cDestination that can be set to one of the e-mail is up to %num.' +ErrorAlreadyExistTo: '&cThe specified destination is contained already.' +ErrorEmptyTo: '&cDestination is empty.' +ErrorItemAttachedYet: '&cThe items are attached in your mail yet. Please empty it.' +ErrorCannotSendMultiAttach: '&cYou cannot send mail attached items to multiple destinations.' +ErrorYouDontHaveEnoughMoney: '&cYou don''t have enough money!' +ErrorYouDontHaveEnoughItem: '&cYou don''t have enough items!' +ErrorFailToWithdraw: '&cFail to withdraw fee from your account.' +ErrorFailToDeposit: '&cFail to deposit fee to his account.' +ErrorCannotSetMoneyAndItem: '&cYou cannot set both of money and item.' +ErrorInvalidCostMoney: '&cThe specified C.O.D. money %fee is invalid.' +ErrorInvalidCostItem: '&cThe specified C.O.D. item %item is invalid.' +ErrorInvalidItem: '&cThe specified item %item is invalid.' +ErrorNoneCancelAttachPermission: '&cBecause you are not sender of this mail, you cannot cancel attachments.' +ErrorNoneRefuseAttachPermission: '&cBecause you are not recipient of this mail, you cannot refuse attachments.' +ErrorAlreadyAttachCancelled: '&cThe attachment was cancelled already.' +ErrorAlreadyRecipientOpened: '&cBecause recipient had opened box, you cannot cancel attachments.' +ErrorInvalidAttachBoxWorld: '&cIn this world, sending and receiving of items is prohibited. After you move to a different world, please open the attached box.' +ErrorInvalidCommand: '&cThis command is invalid.' +ErrorAttachBoxCountExceed: '&cYou are using %num attached boxes now, because it exceeds the limit %limit, you cannot send attachments with mail!' +ErrorCannotSendAttachMailToAllBecauseCacheLoading: '&cSince the preparation of the players cache data has not been completed, you cannot send attachments with Mail to All. Please try again after waiting for a while.' +ErrorCannotListInitializingYet: '&cBecause mailer has not yet been initialized, cannot view the mail list.' +ErrorCannotSendInitializingYet: '&cBecause mailer has not yet been initialized, cannot send new mail.' +ErrorAlreadyTrashed: '&cMail #%index is already in trash box.' +ErrorNotTrashed: '&cMail #%index is not in trash box.' +ErrorCannotDropBecauseUnread: '&cBecause Mail #%index is unread yet, you cannot move this mail.' +ErrorCannotDropBecauseAttached: '&cBecause Mail #%index has attached item yet, you cannot move this mail.' +ErrorCannotSendSpamMail: '&cYou cannot send mail in succession. Until the next mail is to be sent, you need %remain seconds.' +ErrorPlayerCacheIncomplete: '&cSince the players cache has not been completed, you cannot send AllConnected addressed mail. Please send after waiting for a while.' +ErrorCannotFoundLocation: '&cYou cannot teleport for transmission point is not recorded to the specified mail.' +ErrorProhibitItemAttached: '&cItem %material is prohibited to attach.' +ErrorContainsProhibitItemInShulkerbox: '&cShulkerBox contains prohibited item(s).' + +ErrorInvalidGroupName: '&c%name cannot be used as a group name.' +ErrorGroupIsAlreadyExist: '&cGroup %name cannot be used because it already exists.' +ErrorGroupNotExist: '&cGroup %name does not exist.' +ErrorGroupSendNotPermission: '&cYou don''t have send mail permission to Group %name.' +ErrorGroupModifyNotPermission: '&cYou don''t have modify permission of Group %name.' +ErrorGroupDeleteNotPermission: '&cYou don''t have dissolution permission of Group %name.' +ErrorNotFoundPlayer: '&cPlayer %player is not found.' +ErrorPlayerIsAlreadyMember: '&cPlayer %player is already a member.' +ErrorPlayerIsNotMember: '&cPlayer %player is not a member.' +ErrorPlayerIsOwner: '&cCannot remove Player %player because he is owner.' +ErrorGroupCreateLimitExceed: '&cBecause you have exceeded the group creation limit %num, you can not create a new group.' +ErrorGroupMemberLimitExceed: '&cBecause it exceeded the number of members limit %num, you can not add members.' +ErrorInvalidPermissionType: '&cThe specified permission type %type is not correct.' +ErrorInvalidPermissionValue: '&cThe specified permission value %value is not correct.' +ErrorEveryonePermissionInvalid: '&cModify permission and dissolution permission can not be set to EVERYONE.' + +InformationReloading: '&aReloading all mails and configs data...' +InformationReload: '&aReload completed.' +InformationYouGotMail: '&aYou got new mail from &7%from&a!' +InformationYouSentMail: '&7Your mail has been sent.' +InformationEditCancelled: '&7Cancelled to make mail.' +InformationPlayerJoin: '&7You have %unread unread mail.' +InformationItemDetail: '&7Item Information: &f%desc &7Tradable?: &f%tradable' +InformationMultiAttachConfirm: '&7You will send item attached mail to multiple destinations. This mail will be replicated %num mails to send. Are you sure?' +InformationMultiAttachConfirmConsole: 'To send, run ''mail send attachconfirm'' command.' +InformationAttachWasCanceledBySender: '&7Sender %sender canceled the attachment item box of mail #%num.' +InformationTrashed: '&7Moved mail #%index to trash box.' +InformationTrashRestored: '&7Restored mail #%index from trash box.' +InformationTrashAllNeedConfirmation: '&7All of your readed received/sent %num mail(s) will be moved to trash box. If OK, please run the additional command "/%command trash all confirm".' +InformationTrashAllDone: '&7Moved %num mail(s) to trash box.' +InformationRestoreAllNeedConfirmation: '&7All of your trash box %num mail(s) will be restored from trash box. If OK, please run the additional command "/%command trash restoreall confirm".' +InformationRestoreAllDone: '&7Restored %num mail(s) from trash box.' +InformationTeleported: '&aYou were teleported to the transmission point of the mail %index.' + +InformationMakeGroup: '&7Created a new group %name.' +InformationDeleteGroup: '&7Deleted a group %name.' +InformationDeleteGroupConfirm: '&7Will delete the group %name. Are you sure?' +InformationGroupMemberAdd: '&7Added Player %player to the members of the group %group.' +InformationGroupMemberAddAllLogin: '&7Added %num player(s) to the members of the group %group.' +InformationGroupMemberRemove: '&7Removed Player %player from the members of the group %group.' +InformationGroupSetPermission: '&7Set %type permission of Group %name to %value.' + +InformationTipsReadCommandFail: '&7TIP: You can open mail by clicking blue numbers in mail list.' + +SummaryOpenThisMailToolTip: 'Open this mail' + +MailDetailTitle: 'Mail (#%number)' +MailDetailFromToLine: '&cFrom: &f%from &cTo: &f%to' +MailDetailDateLine: '&cDate: &f%date' +MailDetailMessageLine: '&cMessage:' +MailDetailAttachmentsLine: '&cAttached Items:' +MailDetailAttachmentBox: '[Open Attachment Box]' +MailDetailAttachmentBoxCancel: '[Cancel Attachment Items]' +MailDetailAttachmentBoxCancelToolTip: 'Cancel the attached items, and get back it.\nRecipient will not be able to open box.' +MailDetailAttachmentBoxCancelled: 'Cancelled by sender' +MailDetailAttachmentBoxRefuse: '[Refuse]' +MailDetailAttachmentBoxRefuseToolTip: 'Refuse to receive items, \nand send back to sender.\nYou can write a reason \nof refusing (optional). ex: \n&e/umail attach 1 refuse Too expensive!!' +MailDetailAttachmentBoxRefused: 'Refused by recipient' +MailDetailAttachCostMoneyLine: ' &cC.O.D. money to open box: &f%fee' +MailDetailAttachCostItemLine: ' &cC.O.D. item to open box: &f%item' +MailDetailAttachmentsOriginalLine: '&cAttached Items (original):' +MailDetailTrash: '[Delete]' +MailDetailTrashRestore: '[Restore from trash]' +MailDetailReply: '[Reply]' +MailDetailTeleport: '[Teleport to the location of sending this mail]' + +Editmode: 'EditMode' + +EditmodeTitle: 'Mail Edit' +EditmodeToDelete: '[x]' +EditmodeToDeleteToolTip: 'Delete this recipient' +EditmodeTo: '[To]' +EditmodeToGroup: '[Group]' +EditmodeToToolTip: 'Edit recipient' +EditmodeToAdd: '[Add recipient]' +EditmodeToAddToolTip: 'To add new recipient,\nclick here and\ninput recipient player name to chat window.' +EditmodeToAddress: '[Add recipient from Player list]' +EditmodeToGroupAdd: '[Add recipient group]' +EditmodeLineEdit: '[M%num]' +EditmodeLineEditToolTip: 'Click here to add your message' +EditmodeLineDelete: '[x]' +EditmodeLineDeleteToolTip: 'Delete this line' +EditmodeLineAdd: '[Add more lines]' +EditmodeAttach: '[Open attachment box]' +EditmodeAttachNum: 'Attachment Items: %num' +EditmodeCostMoney: '[Set C.O.D. money]' +EditmodeCostMoneyData: '[C.O.D. money(%fee)]' +EditmodeCostMoneyToolTip: 'ex: &e/umail costmoney 30' +EditmodeCostMoneyRemove: '[x]' +EditmodeCostMoneyRemoveToolTip: 'Remove C.O.D. money' +EditmodeCostItem: '[Set C.O.D. item]' +EditmodeCostItemData: '[C.O.D.(%item)]' +EditmodeCostItemToolTip: 'If you want to request 5 diamonds,\nplease run the following command:\n&e/umail costitem DIAMOND 5' +EditmodeCostItemRemove: '[x]' +EditmodeCostItemRemoveToolTip: 'Remove C.O.D. item' +EditmodeSend: '[Send]' +EditmodeCancel: '[Cancel]' +EditmodeTipsMessage: '&7TIP: Click the [M1] to add your message to this line.' + +EditmodeFeeInformation: '&7You need %fee for sending this mail.' +EditmodeFeeDetail: '&7(mail fee %mail + attachment fee %item)' +EditmodeFeeDetailWithCODTax: '&7(mail fee %mail + attachment fee %item + COD fee %cod)' +EditmodeFeeConfirm: '&7Are you sure to send?' +EditmodeFeeResult: '&7Costed %fee. (Balance: %remain)' + +BoxOpenCostMoneyInformation: '&7You need %fee for opening attachment box.' +BoxOpenCostMoneyResult: '&7Costed %fee. (Balance: %remain)' +BoxOpenCostMoneySenderResult: '&7You got C.O.D. money %fee from %to. (Balance: %remain)' +BoxOpenCostItemInformation: '&7You need %amount %material(s) for opening attachment box.' +BoxOpenCostItemResult: '&7Costed %amount %material(s).' +BoxOpenCostItemSenderResult: 'You got C.O.D. item %amount %material(s) from %to.' +BoxOpenCostConfirm: '&7Are you sure to open?' + +BoxRefuseSenderResult: 'System Message : %to refused to pay\n for mail #%num and its attached contents.' +BoxRefuseSenderResultReason: 'Reason: ' + +ButtonOK: '[OK]' +ButtonCancel: '[Cancel]' + +InboxTitle: 'Inbox (Unread %unread)' +OutboxTitle: 'Outbox' +TrashboxTitle: 'Trashbox' + +Return: '[Return]' +FirstPage: '[<<]' +PrevPage: '[<]' +NextPage: '[>]' +LastPage: '[>>]' +ReturnToolTip: 'Return to index' +FirstPageToolTip: 'First Page' +PrevPageToolTip: 'Prev Page' +NextPageToolTip: 'Next Page' +LastPageToolTip: 'Last Page' +ReturnListToolTip: 'Return to list' +FirstMailToolTip: 'Latest Mail' +PrevMailToolTip: 'Prev Mail' +NextMailToolTip: 'Next Mail' +LastMailToolTip: 'Last Mail' + +EditmodeBoxTitle: 'AttachmentBox (EditMode)' +AttachmentBoxTitle: 'AttachmentBox (#%number)' + +PlayerListIndexTitle: 'Player Name List Index' +PlayerListTitle: 'Player Name List (%pre)' + +GroupListTitle: 'Group List (%num groups)' +GroupDetailTitle: 'Group %name' +GroupListSummayLine: '&7Owner: &f%owner &7Members: &f%num' +GroupMakeNewGroup: '[Add new group]' +GroupMakeNewGroupToolTip: '&fPlease specify new group name.\n&e/ugroup create MyGroup' +GroupMakeNewGroupToolTipForSelection: 'If you want to register new group,\nrun &e/ugroup &fcommand to enter group management.' +GroupAddMember: '[Add member]' +GroupAddMemberAddress: '[Add member from Player list]' +GroupAddMemberAllLogin: '[Add all login players]' +GroupDeleteMemberButton: '[x]' +GroupDeleteMemberToolTip: 'Remove this member.' +GroupDeleteMemberOwnerToolTip: 'Cannot remove owner player.' +GroupOwnerLine: 'Owner: &f%owner' +GroupMemberLine: 'Member:' +GroupChangeSetting: '[Group Settings]' +GroupSettingTitle: 'Settings of Group %name' +GroupSendPerm: 'Send mail to this group permission: ' +GroupModifyPerm: 'Modify settings/members permission: ' +GroupDissolutionPerm: 'Break-up this group permission: ' +GroupPermNever: 'Never' +GroupPermOP: 'OP only' +GroupPermOwner: 'Owner only' +GroupPermMember: 'Members' +GroupPermEveryone: 'Everyone' +GroupPermChangeButton: '[%perm]' +GroupDeleteGroup: '[Break-up this group]' +GroupReturnToMemberList: '[Return to group members list]' + +GroupSpecialAllSummary: 'All Players Group' +GroupSpecialAllMembers: 'All players including new comers' +GroupSpecialAllConnectedMembers: 'All players that had connected' +GroupSpecialAllLoginMembers: 'All players currently connected' + +WelcomeMailBody: 'Click number to open this mail.\n\nWelcome to server!\nIf you want to read mails, please run "/mail" command.\nIf you want to write mail, please run "/mail write" command.\nHave fun!' + +DateFormat: 'MMM dd HH:mm' + +ListVerticalParts: '&7| ' +ListHorizontalParts: '&7-----' +ListLastLine: '&7------------------------------' +DetailVerticalParts: '&7# ' +DetailHorizontalParts: '&7=====' +DetailLastLine: '&7==============================' + +HelpTitle: 'UndineMailer Command Help' + +HelpCommand_inbox: '/mail inbox' +HelpCommand_outbox: '/mail outbox' +HelpCommand_trash: '/mail trash' +HelpCommand_text: '/mail text ' +HelpCommand_write: '/mail write' +HelpCommand_item: '/mail item' +HelpCommand_help: '/mail help' +HelpCommand_group: '/ugroup' +HelpCommand_reload: '/mail reload' + +HelpDescription_inbox: 'Check your inbox mails.' +HelpDescription_outbox: 'Check your sent mails.' +HelpDescription_trash: 'Check your deleted mails.' +HelpDescription_text: 'Send simple text mail.' +HelpDescription_write: 'Enter to mail creation mode.' +HelpDescription_item: 'Show the detailed information of item in your hand.' +HelpDescription_help: 'Show the help menu.' +HelpDescription_group: 'Show the group management menu.' +HelpDescription_reload: 'Reload all of data.' + +Yes: 'Yes' +No: 'No' diff --git a/src/main/resources/messages_ja.yml b/src/main/resources/messages_ja.yml index dd3d10b..56d4eaa 100644 --- a/src/main/resources/messages_ja.yml +++ b/src/main/resources/messages_ja.yml @@ -1,257 +1,260 @@ -# ${project.name} v${project.version} -# @author ucchy -# @license LGPLv3 -# @copyright Copyright ucchy 2015 - -PermissionDeniedCommand: '&cパーミッションが無いため、実行できません。' - -ErrorRequireArgument: '&cコマンドのパラメータに %param を指定してください。' -ErrorInvalidIndex: '&c指定されたメール番号 %index が正しくありません。' -ErrorNoneReadPermission: '&c指定されたメールはあなた宛ではないので表示できません。' -ErrorNotFoundDestination: '&c宛先 %dest が見つかりません。' -ErrorCannotSendSelf: '&c自分自身にメールを送信することはできません。' -ErrorInGameCommand: '&cこのコマンドはゲーム内から実行してください。' -ErrorNotInEditmode: '&cあなたはメール編集中でないため、このコマンドを実行することができません。' -ErrorTooManyDestination: '&c一つのメールに設定できる宛先は、%numまでです。' -ErrorAlreadyExistTo: '&c既に指定されている宛先です。' -ErrorEmptyTo: '&c宛先が設定されていません。' -ErrorItemAttachedYet: '&cアイテムが添付されたままです。キャンセルする前に取り出してください。' -ErrorCannotSendMultiAttach: '&c添付アイテム付きのメールを、複数の宛先に出すことはできません。' -ErrorYouDontHaveEnoughMoney: '&c残金が足りません!' -ErrorYouDontHaveEnoughItem: '&c指定のアイテムが足りません!' -ErrorFailToWithdraw: '&c料金の引き落としに失敗しました。' -ErrorFailToDeposit: '&c相手への入金に失敗しました。' -ErrorCannotSetMoneyAndItem: '&c着払い料金と着払いアイテムを両方同時に設定することはできません。' -ErrorInvalidCostMoney: '&c指定された着払い料金 %fee が正しくありません。' -ErrorInvalidCostItem: '&c指定された着払いアイテム %item が正しくありません。' -ErrorInvalidItem: '&c指定されたアイテム %item が正しくありません。' -ErrorNoneCancelAttachPermission: '&c指定されたメールの送信者ではないので、キャンセルできません。' -ErrorNoneRefuseAttachPermission: '&c指定されたメールの受信者ではないので、受取拒否できません。' -ErrorAlreadyAttachCancelled: '&c既に添付アイテムはキャンセルされています。' -ErrorAlreadyRecipientOpened: '&c既に受信者がボックスを開いたため、キャンセルできません。' -ErrorInvalidAttachBoxWorld: '&cあなたのいるワールドは、アイテムの送受信が禁止されています。別のワールドに移動してから、添付ボックスを開いてください。' -ErrorInvalidCommand: '&cこのコマンドは利用不可に設定されています。' -ErrorAttachBoxCountExceed: '&cあなたは現在 %num個の添付ボックスを使用しており、制限数 %limitを超えているため、添付付きメールを送信することはできません。' -ErrorCannotSendAttachMailToAllBecauseCacheLoading: '&c現在、プレイヤーキャッシュデータの準備が完了していないので、添付付きメールをAll宛に送信することはできません。しばらく待ってから再実行してください。' -ErrorCannotListInitializingYet: '&cまだメールデータの初期化が完了していないため、メールリストを表示できません。' -ErrorCannotSendInitializingYet: '&cまだメールデータの初期化が完了していないため、メールを送信できません。' -ErrorAlreadyTrashed: '&cメール %index は、既にゴミ箱の中にあります。' -ErrorNotTrashed: '&cメール %index は、ゴミ箱の中にありません。' -ErrorCannotDropBecauseUnread: '&cメール %index は、未読のため移動することができません。' -ErrorCannotDropBecauseAttached: '&cメール %index は、添付アイテムが残っているため移動することができません。' -ErrorCannotSendSpamMail: '&c連続してメールを送信することはできません。次のメールが送信可能になるまで、%remain秒必要です。' -ErrorPlayerCacheIncomplete: '&cプレイヤーのキャッシュが完了していないため、AllConnected宛てメールが作成できません。しばらく待ってから送信してください。' -ErrorCannotFoundLocation: '&c指定されたメールには送信地点が記録されていないためテレポートできません。' -ErrorProhibitItemAttached: '&cアイテム %material の添付は禁止されています。' - -ErrorInvalidGroupName: '&c%name はグループ名として使用できません。' -ErrorGroupIsAlreadyExist: '&cグループ %name は既に存在するため使用できません。' -ErrorGroupNotExist: '&c%name というグループは存在しません。' -ErrorGroupSendNotPermission: '&cグループ %name にメールを送信する権限がありません。' -ErrorGroupModifyNotPermission: '&cグループ %name を編集する権限がありません。' -ErrorGroupDeleteNotPermission: '&cグループ %name を削除する権限がありません。' -ErrorNotFoundPlayer: '&cプレイヤー %player が見つかりません。' -ErrorPlayerIsAlreadyMember: '&cプレイヤー %player は既にメンバーです。' -ErrorPlayerIsNotMember: '&cプレイヤー %player はメンバーではありません。' -ErrorPlayerIsOwner: '&cプレイヤー %player はオーナーなので削除できません。' -ErrorGroupCreateLimitExceed: '&cあなたはグループの作成制限 %num を超えたため、新規グループを作成できません。' -ErrorGroupMemberLimitExceed: '&cグループのメンバー数制限 %num を超えたため、メンバーを追加できません。' -ErrorInvalidPermissionType: '&c指定された権限タイプ %type は正しくありません。' -ErrorInvalidPermissionValue: '&c指定された権限設定値 %value は正しくありません。' -ErrorEveryonePermissionInvalid: '&c変更権限と解散権限は、EVERYONE に設定することはできません。' - -InformationReloading: '&a全てのメールデータと設定データを再読み込みしています...' -InformationReload: '&aデータを再読み込みしました。' -InformationYouGotMail: '&7%from &aさんから新しいメールが届きました!' -InformationYouSentMail: '&7メールを送信しました。' -InformationEditCancelled: '&7メールの編集をキャンセルしました。' -InformationPlayerJoin: '&7未読のメールが %unread件あります。' -InformationItemDetail: '&7アイテム情報: &f%desc &7取引可能?: &f%tradable' -InformationMultiAttachConfirm: '&7複数の宛先に添付付きメールを送信しようとしています。\nこのメールは、%num通に複製されて送信されます。よろしいですか?' -InformationMultiAttachConfirmConsole: '送信する場合は、''mail send attachconfirm''を実行してください。' -InformationAttachWasCanceledBySender: '&7メール#%num の添付アイテムが、送信者 %sender によりキャンセルされました。' -InformationTrashed: '&7メール %index をゴミ箱へ移動しました。' -InformationTrashRestored: '&7メール %index をゴミ箱から戻しました。' -InformationTrashAllNeedConfirmation: '&7あなたの送信済みメール/既読の受信済みメール%num通を、全てゴミ箱に移動します。実行するには、"/%command trash all confirm" コマンドを実行してください。' -InformationTrashAllDone: '&7%num通のメールをゴミ箱へ移動しました。' -InformationRestoreAllNeedConfirmation: '&7あなたのゴミ箱メール%num通を、全て受信箱/送信箱に移動します。実行するには、"/%command trash restoreall confirm" コマンドを実行してください。' -InformationRestoreAllDone: '&7%num通のメールをゴミ箱から戻しました。' -InformationTeleported: '&aメール %index の送信地点にテレポートしました。' - -InformationMakeGroup: '&7新しいグループ %name を作成しました。' -InformationDeleteGroup: '&7グループ %name を削除しました。' -InformationDeleteGroupConfirm: '&7グループ %name を削除します。よろしいですか?' -InformationGroupMemberAdd: '&7プレイヤー %player を、グループ %group のメンバーに追加しました。' -InformationGroupMemberAddAllLogin: '&7%num人を、グループ %group のメンバーに追加しました。' -InformationGroupMemberRemove: '&7プレイヤー %player を、グループ %group のメンバーから削除しました。' -InformationGroupSetPermission: '&7グループ %name の %type 権限を %value にしました。' - -InformationTipsReadCommandFail: '&7TIPS: メールは、リスト内の青い番号をクリックすることでも読むことが可能です。' - -SummaryOpenThisMailToolTip: 'このメールを開く' - -MailDetailTitle: 'メール(#%number)' -MailDetailFromToLine: '&c送信者: &f%from &c宛先: &f%to' -MailDetailDateLine: '&c送信日時: &f%date' -MailDetailMessageLine: '&cメッセージ:' -MailDetailAttachmentsLine: '&c添付アイテム:' -MailDetailAttachmentBox: '[添付アイテムボックスを開く]' -MailDetailAttachmentBoxCancel: '[添付アイテムをキャンセルする]' -MailDetailAttachmentBoxCancelToolTip: '添付をキャンセルしアイテムを回収します。\n受信者はアイテムを取り出せなくなります。' -MailDetailAttachmentBoxCancelled: '送信者により添付がキャンセルされました' -MailDetailAttachmentBoxRefuse: '[受取拒否]' -MailDetailAttachmentBoxRefuseToolTip: '受け取りを拒否して、\n添付アイテムを送信元に差し戻します。\n拒否理由を書くことができます(省略可)。\n例: \n&e/umail attach 1 refuse 高すぎてお支払いできません…' -MailDetailAttachmentBoxRefused: '受信者により添付が受取拒否されました' -MailDetailAttachCostMoneyLine: ' &c着払い料金: &f%fee' -MailDetailAttachCostItemLine: ' &c着払いアイテム: &f%item' -MailDetailAttachmentsOriginalLine: '&c送信時の添付アイテム:' -MailDetailTrash: '[ゴミ箱に移動する]' -MailDetailTrashRestore: '[ゴミ箱から戻す]' -MailDetailReply: '[返信する]' -MailDetailTeleport: '[このメールの送信地点へテレポートする]' - -Editmode: '編集中' - -EditmodeTitle: 'メールの編集' -EditmodeToDelete: '[x]' -EditmodeToDeleteToolTip: 'この宛先を削除する' -EditmodeTo: '[宛先]' -EditmodeToGroup: '[宛先グループ]' -EditmodeToToolTip: '宛先を変更する' -EditmodeToAdd: '[宛先を追加する]' -EditmodeToAddToolTip: '宛先を追加するには、\nここをクリックして表示されるコマンドに続けて、\n宛先のプレイヤー名を入力してください。' -EditmodeToAddress: '[宛先をプレイヤー名簿から追加する]' -EditmodeToGroupAdd: '[宛先グループを追加する]' -EditmodeLineEditToolTip: 'このメッセージ行を編集する' -EditmodeLineDelete: '[x]' -EditmodeLineDeleteToolTip: 'このメッセージ行を削除する' -EditmodeLineAdd: '[メッセージ行を追加する]' -EditmodeAttach: '[添付ボックスを開く]' -EditmodeAttachNum: '添付アイテム数: %num' -EditmodeCostMoney: '[着払い料金を設定する]' -EditmodeCostMoneyData: '[着払い料金(%fee)]' -EditmodeCostMoneyToolTip: '設定例: &e/umail costmoney 30' -EditmodeCostMoneyRemove: '[x]' -EditmodeCostMoneyRemoveToolTip: '着払い料金を解除する' -EditmodeCostItem: '[着払いアイテムを設定する]' -EditmodeCostItemData: '[着払いアイテム(%item)]' -EditmodeCostItemToolTip: 'ダイヤを5個要求するには、\n&e/umail costitem DIAMOND 5\n&fと入力してください。' -EditmodeCostItemRemove: '[x]' -EditmodeCostItemRemoveToolTip: '着払いアイテムを解除する' -EditmodeSend: '[送信する]' -EditmodeCancel: '[キャンセル]' - -EditmodeFeeInformation: '&7このメールの送信に、%fee かかります。' -EditmodeFeeDetail: '&7(メール作成 %mail + アイテム添付 %item)' -EditmodeFeeDetailWithCODTax: '&7(メール作成 %mail + アイテム添付 %item + 着払い税 %cod)' -EditmodeFeeConfirm: '&7送信してもよろしいですか?' -EditmodeFeeResult: '&7%fee消費しました。(あなたの残金:%remain)' - -BoxOpenCostMoneyInformation: '&7この添付ボックスを開くのに、%fee かかります。' -BoxOpenCostMoneyResult: '&7%fee消費しました。(あなたの残金:%remain)' -BoxOpenCostMoneySenderResult: '&7%toさんから着払い料金%feeを取得しました。(あなたの残金:%remain)' -BoxOpenCostItemInformation: '&7この添付ボックスを開くのに、%materialが%amount個必要です。' -BoxOpenCostItemResult: '&7%materialを%amount個消費しました。' -BoxOpenCostItemSenderResult: '%toさんから、着払いアイテム %materialを%amount個返信されました。' -BoxOpenCostConfirm: '&7開いてもよろしいですか?' - -BoxRefuseSenderResult: 'システムメッセージ: %toさんが、\n メール#%num の添付アイテムを受け取り拒否しました。' -BoxRefuseSenderResultReason: '理由: ' - -ButtonOK: '[OK]' -ButtonCancel: '[キャンセル]' - -InboxTitle: '受信箱 (未読 %unread通)' -OutboxTitle: '送信箱' -TrashboxTitle: 'ゴミ箱' - -Return: '[戻る]' -FirstPage: '[<<]' -PrevPage: '[<前]' -NextPage: '[次>]' -LastPage: '[>>]' -ReturnToolTip: 'インデクスに戻る' -FirstPageToolTip: '最初のページへ' -PrevPageToolTip: '前のページヘ' -NextPageToolTip: '次のページへ' -LastPageToolTip: '最後のページヘ' -ReturnListToolTip: 'リストに戻る' -FirstMailToolTip: '最新のメールへ' -PrevMailToolTip: '前のメールヘ' -NextMailToolTip: '次のメールへ' -LastMailToolTip: '最後のメールヘ' - -EditmodeBoxTitle: '編集中メールの添付ボックス' -AttachmentBoxTitle: '添付ボックス(#%number)' - -PlayerListIndexTitle: 'プレイヤー名簿 インデクス' -PlayerListTitle: 'プレイヤー名簿 (%pre)' - -GroupListTitle: 'グループリスト (%num件)' -GroupDetailTitle: 'グループ %name' -GroupListSummayLine: '&7オーナー: &f%owner &7メンバー: &f%num人' -GroupMakeNewGroup: '[新しいグループを作る]' -GroupMakeNewGroupToolTip: '&f作成するグループ名を指定してください。\n&e/ugroup create 俺のグループ' -GroupMakeNewGroupToolTipForSelection: '新しいグループを作成するには、\n&e/ugroup &fコマンドを実行して\nグループ管理画面から作成してください。' -GroupAddMember: '[メンバー追加]' -GroupAddMemberAddress: '[プレイヤー名簿からメンバー追加]' -GroupAddMemberAllLogin: '[ログインしているプレイヤーを全員メンバーに追加]' -GroupDeleteMemberButton: '[x]' -GroupDeleteMemberToolTip: 'このメンバーを削除する' -GroupDeleteMemberOwnerToolTip: 'オーナーは削除できません' -GroupOwnerLine: 'オーナー: &f%owner' -GroupMemberLine: 'メンバー:' -GroupChangeSetting: '[グループ設定変更]' -GroupSettingTitle: 'グループ %name の設定' -GroupSendPerm: 'このグループへのメール送信権限: ' -GroupModifyPerm: 'このグループの設定変更権限: ' -GroupDissolutionPerm: 'このグループの解散権限: ' -GroupPermNever: '禁止' -GroupPermOP: 'OPのみ' -GroupPermOwner: 'オーナーのみ' -GroupPermMember: 'メンバー' -GroupPermEveryone: '誰でも' -GroupPermChangeButton: '[%perm に変更]' -GroupDeleteGroup: '[グループを解散する]' -GroupReturnToMemberList: '[グループのメンバーリストに戻る]' - -GroupSpecialAllSummary: '全員宛てメールグループ' -GroupSpecialAllMembers: 'これから接続するプレイヤーを含む全てのプレイヤー' -GroupSpecialAllConnectedMembers: '接続したことがある全てのプレイヤー' -GroupSpecialAllLoginMembers: '現在接続中の全てのプレイヤー' - -WelcomeMailBody: '←番号をクリックで開きます。\n\nサーバーへようこそ!\nメールを読むには /mail コマンドを、\nメールを書くには /mail write コマンドを実行してくださいね!' - -DateFormat: 'MM/dd HH:mm' - -ListVerticalParts: '&7| ' -ListHorizontalParts: '&7-----' -ListLastLine: '&7------------------------------' -DetailVerticalParts: '&7# ' -DetailHorizontalParts: '&7=====' -DetailLastLine: '&7==============================' - -HelpTitle: 'UndineMailerコマンドヘルプ' - -HelpCommand_inbox: '/mail inbox' -HelpCommand_outbox: '/mail outbox' -HelpCommand_trash: '/mail trash' -HelpCommand_text: '/mail text ' -HelpCommand_write: '/mail write' -HelpCommand_item: '/mail item' -HelpCommand_help: '/mail help' -HelpCommand_group: '/ugroup' -HelpCommand_reload: '/mail reload' - -HelpDescription_inbox: 'メール受信箱を開きます。' -HelpDescription_outbox: 'メール送信箱を開きます。' -HelpDescription_trash: 'ゴミ箱を開きます。' -HelpDescription_text: '単純なテキスト1行のメールを送信します。' -HelpDescription_write: 'メール編集画面を開きます。' -HelpDescription_item: '手に持ったアイテムの詳細を画面に表示します。' -HelpDescription_help: 'ヘルプメッセージを参照します。' -HelpDescription_group: 'グループ管理画面を表示します。' -HelpDescription_reload: 'データをリロードします。' - -Yes: 'はい' -No: 'いいえ' +# ${project.name} v${project.version} +# @author ucchy +# @license LGPLv3 +# @copyright Copyright ucchy 2015 + +PermissionDeniedCommand: '&cパーミッションが無いため、実行できません。' + +ErrorRequireArgument: '&cコマンドのパラメータに %param を指定してください。' +ErrorInvalidIndex: '&c指定されたメール番号 %index が正しくありません。' +ErrorNoneReadPermission: '&c指定されたメールはあなた宛ではないので表示できません。' +ErrorNotFoundDestination: '&c宛先 %dest が見つかりません。' +ErrorCannotSendSelf: '&c自分自身にメールを送信することはできません。' +ErrorInGameCommand: '&cこのコマンドはゲーム内から実行してください。' +ErrorNotInEditmode: '&cあなたはメール編集中でないため、このコマンドを実行することができません。' +ErrorTooManyDestination: '&c一つのメールに設定できる宛先は、%numまでです。' +ErrorAlreadyExistTo: '&c既に指定されている宛先です。' +ErrorEmptyTo: '&c宛先が設定されていません。' +ErrorItemAttachedYet: '&cアイテムが添付されたままです。キャンセルする前に取り出してください。' +ErrorCannotSendMultiAttach: '&c添付アイテム付きのメールを、複数の宛先に出すことはできません。' +ErrorYouDontHaveEnoughMoney: '&c残金が足りません!' +ErrorYouDontHaveEnoughItem: '&c指定のアイテムが足りません!' +ErrorFailToWithdraw: '&c料金の引き落としに失敗しました。' +ErrorFailToDeposit: '&c相手への入金に失敗しました。' +ErrorCannotSetMoneyAndItem: '&c着払い料金と着払いアイテムを両方同時に設定することはできません。' +ErrorInvalidCostMoney: '&c指定された着払い料金 %fee が正しくありません。' +ErrorInvalidCostItem: '&c指定された着払いアイテム %item が正しくありません。' +ErrorInvalidItem: '&c指定されたアイテム %item が正しくありません。' +ErrorNoneCancelAttachPermission: '&c指定されたメールの送信者ではないので、キャンセルできません。' +ErrorNoneRefuseAttachPermission: '&c指定されたメールの受信者ではないので、受取拒否できません。' +ErrorAlreadyAttachCancelled: '&c既に添付アイテムはキャンセルされています。' +ErrorAlreadyRecipientOpened: '&c既に受信者がボックスを開いたため、キャンセルできません。' +ErrorInvalidAttachBoxWorld: '&cあなたのいるワールドは、アイテムの送受信が禁止されています。別のワールドに移動してから、添付ボックスを開いてください。' +ErrorInvalidCommand: '&cこのコマンドは利用不可に設定されています。' +ErrorAttachBoxCountExceed: '&cあなたは現在 %num個の添付ボックスを使用しており、制限数 %limitを超えているため、添付付きメールを送信することはできません。' +ErrorCannotSendAttachMailToAllBecauseCacheLoading: '&c現在、プレイヤーキャッシュデータの準備が完了していないので、添付付きメールをAll宛に送信することはできません。しばらく待ってから再実行してください。' +ErrorCannotListInitializingYet: '&cまだメールデータの初期化が完了していないため、メールリストを表示できません。' +ErrorCannotSendInitializingYet: '&cまだメールデータの初期化が完了していないため、メールを送信できません。' +ErrorAlreadyTrashed: '&cメール %index は、既にゴミ箱の中にあります。' +ErrorNotTrashed: '&cメール %index は、ゴミ箱の中にありません。' +ErrorCannotDropBecauseUnread: '&cメール %index は、未読のため移動することができません。' +ErrorCannotDropBecauseAttached: '&cメール %index は、添付アイテムが残っているため移動することができません。' +ErrorCannotSendSpamMail: '&c連続してメールを送信することはできません。次のメールが送信可能になるまで、%remain秒必要です。' +ErrorPlayerCacheIncomplete: '&cプレイヤーのキャッシュが完了していないため、AllConnected宛てメールが作成できません。しばらく待ってから送信してください。' +ErrorCannotFoundLocation: '&c指定されたメールには送信地点が記録されていないためテレポートできません。' +ErrorProhibitItemAttached: '&cアイテム %material の添付は禁止されています。' +ErrorContainsProhibitItemInShulkerbox: '&cShulkerBox内に、添付禁止のアイテムが含まれています。' + +ErrorInvalidGroupName: '&c%name はグループ名として使用できません。' +ErrorGroupIsAlreadyExist: '&cグループ %name は既に存在するため使用できません。' +ErrorGroupNotExist: '&c%name というグループは存在しません。' +ErrorGroupSendNotPermission: '&cグループ %name にメールを送信する権限がありません。' +ErrorGroupModifyNotPermission: '&cグループ %name を編集する権限がありません。' +ErrorGroupDeleteNotPermission: '&cグループ %name を削除する権限がありません。' +ErrorNotFoundPlayer: '&cプレイヤー %player が見つかりません。' +ErrorPlayerIsAlreadyMember: '&cプレイヤー %player は既にメンバーです。' +ErrorPlayerIsNotMember: '&cプレイヤー %player はメンバーではありません。' +ErrorPlayerIsOwner: '&cプレイヤー %player はオーナーなので削除できません。' +ErrorGroupCreateLimitExceed: '&cあなたはグループの作成制限 %num を超えたため、新規グループを作成できません。' +ErrorGroupMemberLimitExceed: '&cグループのメンバー数制限 %num を超えたため、メンバーを追加できません。' +ErrorInvalidPermissionType: '&c指定された権限タイプ %type は正しくありません。' +ErrorInvalidPermissionValue: '&c指定された権限設定値 %value は正しくありません。' +ErrorEveryonePermissionInvalid: '&c変更権限と解散権限は、EVERYONE に設定することはできません。' + +InformationReloading: '&a全てのメールデータと設定データを再読み込みしています...' +InformationReload: '&aデータを再読み込みしました。' +InformationYouGotMail: '&7%from &aさんから新しいメールが届きました!' +InformationYouSentMail: '&7メールを送信しました。' +InformationEditCancelled: '&7メールの編集をキャンセルしました。' +InformationPlayerJoin: '&7未読のメールが %unread件あります。' +InformationItemDetail: '&7アイテム情報: &f%desc &7取引可能?: &f%tradable' +InformationMultiAttachConfirm: '&7複数の宛先に添付付きメールを送信しようとしています。\nこのメールは、%num通に複製されて送信されます。よろしいですか?' +InformationMultiAttachConfirmConsole: '送信する場合は、''mail send attachconfirm''を実行してください。' +InformationAttachWasCanceledBySender: '&7メール#%num の添付アイテムが、送信者 %sender によりキャンセルされました。' +InformationTrashed: '&7メール %index をゴミ箱へ移動しました。' +InformationTrashRestored: '&7メール %index をゴミ箱から戻しました。' +InformationTrashAllNeedConfirmation: '&7あなたの送信済みメール/既読の受信済みメール%num通を、全てゴミ箱に移動します。実行するには、"/%command trash all confirm" コマンドを実行してください。' +InformationTrashAllDone: '&7%num通のメールをゴミ箱へ移動しました。' +InformationRestoreAllNeedConfirmation: '&7あなたのゴミ箱メール%num通を、全て受信箱/送信箱に移動します。実行するには、"/%command trash restoreall confirm" コマンドを実行してください。' +InformationRestoreAllDone: '&7%num通のメールをゴミ箱から戻しました。' +InformationTeleported: '&aメール %index の送信地点にテレポートしました。' + +InformationMakeGroup: '&7新しいグループ %name を作成しました。' +InformationDeleteGroup: '&7グループ %name を削除しました。' +InformationDeleteGroupConfirm: '&7グループ %name を削除します。よろしいですか?' +InformationGroupMemberAdd: '&7プレイヤー %player を、グループ %group のメンバーに追加しました。' +InformationGroupMemberAddAllLogin: '&7%num人を、グループ %group のメンバーに追加しました。' +InformationGroupMemberRemove: '&7プレイヤー %player を、グループ %group のメンバーから削除しました。' +InformationGroupSetPermission: '&7グループ %name の %type 権限を %value にしました。' + +InformationTipsReadCommandFail: '&7TIPS: メールは、リスト内の青い番号をクリックすることでも読むことが可能です。' + +SummaryOpenThisMailToolTip: 'このメールを開く' + +MailDetailTitle: 'メール(#%number)' +MailDetailFromToLine: '&c送信者: &f%from &c宛先: &f%to' +MailDetailDateLine: '&c送信日時: &f%date' +MailDetailMessageLine: '&cメッセージ:' +MailDetailAttachmentsLine: '&c添付アイテム:' +MailDetailAttachmentBox: '[添付アイテムボックスを開く]' +MailDetailAttachmentBoxCancel: '[添付アイテムをキャンセルする]' +MailDetailAttachmentBoxCancelToolTip: '添付をキャンセルしアイテムを回収します。\n受信者はアイテムを取り出せなくなります。' +MailDetailAttachmentBoxCancelled: '送信者により添付がキャンセルされました' +MailDetailAttachmentBoxRefuse: '[受取拒否]' +MailDetailAttachmentBoxRefuseToolTip: '受け取りを拒否して、\n添付アイテムを送信元に差し戻します。\n拒否理由を書くことができます(省略可)。\n例: \n&e/umail attach 1 refuse 高すぎてお支払いできません…' +MailDetailAttachmentBoxRefused: '受信者により添付が受取拒否されました' +MailDetailAttachCostMoneyLine: ' &c着払い料金: &f%fee' +MailDetailAttachCostItemLine: ' &c着払いアイテム: &f%item' +MailDetailAttachmentsOriginalLine: '&c送信時の添付アイテム:' +MailDetailTrash: '[ゴミ箱に移動する]' +MailDetailTrashRestore: '[ゴミ箱から戻す]' +MailDetailReply: '[返信する]' +MailDetailTeleport: '[このメールの送信地点へテレポートする]' + +Editmode: '編集中' + +EditmodeTitle: 'メールの編集' +EditmodeToDelete: '[x]' +EditmodeToDeleteToolTip: 'この宛先を削除する' +EditmodeTo: '[宛先]' +EditmodeToGroup: '[宛先グループ]' +EditmodeToToolTip: '宛先を変更する' +EditmodeToAdd: '[宛先を追加する]' +EditmodeToAddToolTip: '宛先を追加するには、\nここをクリックして表示されるコマンドに続けて、\n宛先のプレイヤー名を入力してください。' +EditmodeToAddress: '[宛先をプレイヤー名簿から追加する]' +EditmodeToGroupAdd: '[宛先グループを追加する]' +EditmodeLineEdit: '[M%num]' +EditmodeLineEditToolTip: 'このメッセージ行を編集する' +EditmodeLineDelete: '[x]' +EditmodeLineDeleteToolTip: 'このメッセージ行を削除する' +EditmodeLineAdd: '[メッセージ行を追加する]' +EditmodeAttach: '[添付ボックスを開く]' +EditmodeAttachNum: '添付アイテム数: %num' +EditmodeCostMoney: '[着払い料金を設定する]' +EditmodeCostMoneyData: '[着払い料金(%fee)]' +EditmodeCostMoneyToolTip: '設定例: &e/umail costmoney 30' +EditmodeCostMoneyRemove: '[x]' +EditmodeCostMoneyRemoveToolTip: '着払い料金を解除する' +EditmodeCostItem: '[着払いアイテムを設定する]' +EditmodeCostItemData: '[着払いアイテム(%item)]' +EditmodeCostItemToolTip: 'ダイヤを5個要求するには、\n&e/umail costitem DIAMOND 5\n&fと入力してください。' +EditmodeCostItemRemove: '[x]' +EditmodeCostItemRemoveToolTip: '着払いアイテムを解除する' +EditmodeSend: '[送信する]' +EditmodeCancel: '[キャンセル]' +EditmodeTipsMessage: '&7TIPS: [M1]をクリックすると、メッセージを編集することができます。' + +EditmodeFeeInformation: '&7このメールの送信に、%fee かかります。' +EditmodeFeeDetail: '&7(メール作成 %mail + アイテム添付 %item)' +EditmodeFeeDetailWithCODTax: '&7(メール作成 %mail + アイテム添付 %item + 着払い税 %cod)' +EditmodeFeeConfirm: '&7送信してもよろしいですか?' +EditmodeFeeResult: '&7%fee消費しました。(あなたの残金:%remain)' + +BoxOpenCostMoneyInformation: '&7この添付ボックスを開くのに、%fee かかります。' +BoxOpenCostMoneyResult: '&7%fee消費しました。(あなたの残金:%remain)' +BoxOpenCostMoneySenderResult: '&7%toさんから着払い料金%feeを取得しました。(あなたの残金:%remain)' +BoxOpenCostItemInformation: '&7この添付ボックスを開くのに、%materialが%amount個必要です。' +BoxOpenCostItemResult: '&7%materialを%amount個消費しました。' +BoxOpenCostItemSenderResult: '%toさんから、着払いアイテム %materialを%amount個返信されました。' +BoxOpenCostConfirm: '&7開いてもよろしいですか?' + +BoxRefuseSenderResult: 'システムメッセージ: %toさんが、\n メール#%num の添付アイテムを受け取り拒否しました。' +BoxRefuseSenderResultReason: '理由: ' + +ButtonOK: '[OK]' +ButtonCancel: '[キャンセル]' + +InboxTitle: '受信箱 (未読 %unread通)' +OutboxTitle: '送信箱' +TrashboxTitle: 'ゴミ箱' + +Return: '[戻る]' +FirstPage: '[<<]' +PrevPage: '[<前]' +NextPage: '[次>]' +LastPage: '[>>]' +ReturnToolTip: 'インデクスに戻る' +FirstPageToolTip: '最初のページへ' +PrevPageToolTip: '前のページヘ' +NextPageToolTip: '次のページへ' +LastPageToolTip: '最後のページヘ' +ReturnListToolTip: 'リストに戻る' +FirstMailToolTip: '最新のメールへ' +PrevMailToolTip: '前のメールヘ' +NextMailToolTip: '次のメールへ' +LastMailToolTip: '最後のメールヘ' + +EditmodeBoxTitle: '編集中メールの添付ボックス' +AttachmentBoxTitle: '添付ボックス(#%number)' + +PlayerListIndexTitle: 'プレイヤー名簿 インデクス' +PlayerListTitle: 'プレイヤー名簿 (%pre)' + +GroupListTitle: 'グループリスト (%num件)' +GroupDetailTitle: 'グループ %name' +GroupListSummayLine: '&7オーナー: &f%owner &7メンバー: &f%num人' +GroupMakeNewGroup: '[新しいグループを作る]' +GroupMakeNewGroupToolTip: '&f作成するグループ名を指定してください。\n&e/ugroup create 俺のグループ' +GroupMakeNewGroupToolTipForSelection: '新しいグループを作成するには、\n&e/ugroup &fコマンドを実行して\nグループ管理画面から作成してください。' +GroupAddMember: '[メンバー追加]' +GroupAddMemberAddress: '[プレイヤー名簿からメンバー追加]' +GroupAddMemberAllLogin: '[ログインしているプレイヤーを全員メンバーに追加]' +GroupDeleteMemberButton: '[x]' +GroupDeleteMemberToolTip: 'このメンバーを削除する' +GroupDeleteMemberOwnerToolTip: 'オーナーは削除できません' +GroupOwnerLine: 'オーナー: &f%owner' +GroupMemberLine: 'メンバー:' +GroupChangeSetting: '[グループ設定変更]' +GroupSettingTitle: 'グループ %name の設定' +GroupSendPerm: 'このグループへのメール送信権限: ' +GroupModifyPerm: 'このグループの設定変更権限: ' +GroupDissolutionPerm: 'このグループの解散権限: ' +GroupPermNever: '禁止' +GroupPermOP: 'OPのみ' +GroupPermOwner: 'オーナーのみ' +GroupPermMember: 'メンバー' +GroupPermEveryone: '誰でも' +GroupPermChangeButton: '[%perm に変更]' +GroupDeleteGroup: '[グループを解散する]' +GroupReturnToMemberList: '[グループのメンバーリストに戻る]' + +GroupSpecialAllSummary: '全員宛てメールグループ' +GroupSpecialAllMembers: 'これから接続するプレイヤーを含む全てのプレイヤー' +GroupSpecialAllConnectedMembers: '接続したことがある全てのプレイヤー' +GroupSpecialAllLoginMembers: '現在接続中の全てのプレイヤー' + +WelcomeMailBody: '←番号をクリックで開きます。\n\nサーバーへようこそ!\nメールを読むには /mail コマンドを、\nメールを書くには /mail write コマンドを実行してくださいね!' + +DateFormat: 'MM/dd HH:mm' + +ListVerticalParts: '&7| ' +ListHorizontalParts: '&7-----' +ListLastLine: '&7------------------------------' +DetailVerticalParts: '&7# ' +DetailHorizontalParts: '&7=====' +DetailLastLine: '&7==============================' + +HelpTitle: 'UndineMailerコマンドヘルプ' + +HelpCommand_inbox: '/mail inbox' +HelpCommand_outbox: '/mail outbox' +HelpCommand_trash: '/mail trash' +HelpCommand_text: '/mail text ' +HelpCommand_write: '/mail write' +HelpCommand_item: '/mail item' +HelpCommand_help: '/mail help' +HelpCommand_group: '/ugroup' +HelpCommand_reload: '/mail reload' + +HelpDescription_inbox: 'メール受信箱を開きます。' +HelpDescription_outbox: 'メール送信箱を開きます。' +HelpDescription_trash: 'ゴミ箱を開きます。' +HelpDescription_text: '単純なテキスト1行のメールを送信します。' +HelpDescription_write: 'メール編集画面を開きます。' +HelpDescription_item: '手に持ったアイテムの詳細を画面に表示します。' +HelpDescription_help: 'ヘルプメッセージを参照します。' +HelpDescription_group: 'グループ管理画面を表示します。' +HelpDescription_reload: 'データをリロードします。' + +Yes: 'はい' +No: 'いいえ' diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index fa6ad9f..2871560 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,204 +1,205 @@ -name: ${project.name} -version: ${project.version} -main: org.bitbucket.ucchy.undine.UndineMailer -description: UndineMailer - Mail Plugin -softdepend: [Vault,PermissionsEx] -author: ucchy - -commands: - mail: - description: Command of UndineMailer - aliases: [undine,umail] - usage: | - / inbox [page] - Check your inbox mails. - / outbox [page] - Check your sent mails. - / trash [page] - Check your deleted mails. - / text - Send simple text mail. - / write - Enter to mail creation mode. - / item - Show the detailed information of item in your hand. - / help - Show the help menu. - / reload - Reload all of data. - permission: undine.command - permission-message: 'You don''t have a permission "".' - - undinelist: - description: Command of Player list - aliases: [ulist,uindex] - usage: | - / - display player index button page. - permission: undine.list - permission-message: 'You don''t have a permission "".' - - undinegroup: - description: Command of Mail group - aliases: [ugroup] - usage: | - / - Show the group management menu. - permission: undine.group.command - permission-message: 'You don''t have a permission "".' - -permissions: - undine.command: - description: Permission of command. - default: true - undine.inbox: - description: Permission of inbox command. - default: true - undine.outbox: - description: Permission of outbox command. - default: true - undine.trash: - description: Permission of trash command. - default: true - undine.read: - description: Permission of read command. - default: true - undine.read-all: - description: Permission of read other's mail. - default: op - undine.text: - description: Permission of text command. - default: true - undine.write: - description: Permission of write command. - default: true - undine.to: - description: Permission of to command. - default: true - undine.message: - description: Permission of message command. - default: true - undine.attach: - description: Permission of attach command. - default: true - undine.attach-infinity: - description: Permission of infinity use of attachment box. - default: op - undine.attach-sendmail: - description: Permission of opening the attachment box of send mail. - default: true - undine.attach-inboxmail: - description: Permission of opening the attachment box of inbox mail. - default: true - undine.command-attach: - description: Permission of adding attachment items by using command. - default: op - undine.multiple-attach: - description: Permission of sending item attached mail to multiple destinations. - default: op - undine.costmoney: - description: Permission of costmoney command. - default: true - undine.costitem: - description: Permission of costitem command. - default: true - undine.send: - description: Permission of send command. - default: true - undine.cancel: - description: Permission of send command. - default: true - undine.item: - description: Permission of item command. - default: true - undine.teleport: - description: Permission of teleporting to the location of sending. - default: op - undine.help: - description: Permission of help command. - default: true - undine.reload: - description: Permission of reload command. - default: op - - undine.list: - description: Permission of list command. - default: true - - undine.group.command: - description: Permission of group command. - default: true - undine.group.create: - description: Permission of group create command. - default: true - undine.group.delete: - description: Permission of group delete command. - default: true - undine.group.list: - description: Permission of group list command. - default: true - undine.group.detail: - description: Permission of group detail command. - default: true - undine.group.add: - description: Permission of group add command. - default: true - undine.group.addalllogin: - description: Permission of group addAllLogin command. - default: true - undine.group.remove: - description: Permission of group remove command. - default: true - undine.group.perm: - description: Permission of group perm command. - default: true - undine.group.infinite-create: - description: Permission of infinite create groups. - default: op - undine.group.infinite-add-member: - description: Permission of infinite add members. - default: op - undine.group.modify-all: - description: Permission of modification all groups. - default: op - undine.group.dissolution-all: - description: Permission of break-up all groups. - default: op - - undine.group.*: - description: All Permissions of Undine Group Management. - children: - undine.group.command: true - undine.group.create: true - undine.group.delete: true - undine.group.list: true - undine.group.detail: true - undine.group.add: true - undine.group.addalllogin: true - undine.group.remove: true - undine.group.perm: true - undine.group.infinite-create: true - undine.group.infinite-add-member: true - undine.group.send-all: true - undine.group.modify-all: true - undine.group.dissolution-all: true - - undine.*: - description: All Permissions of UndineMailer. - children: - undine.command: true - undine.inbox: true - undine.outbox: true - undine.trash: true - undine.read: true - undine.read-all: true - undine.text: true - undine.write: true - undine.to: true - undine.message: true - undine.attach: true - undine.attach-infinity: true - undine.attach-sendmail: true - undine.attach-inboxmail: true - undine.command-attach: true - undine.multiple-attach: true - undine.costmoney: true - undine.costitem: true - undine.send: true - undine.cancel: true - undine.item: true - undine.teleport: true - undine.help: true - undine.reload: true - undine.list: true - undine.group.*: true +name: ${project.name} +version: ${project.version} +main: org.bitbucket.ucchy.undine.UndineMailer +description: UndineMailer - Mail Plugin +softdepend: [Vault,PermissionsEx] +author: ucchy +api-version: 1.13 + +commands: + mail: + description: Command of UndineMailer + aliases: [undine,umail] + usage: | + / inbox [page] - Check your inbox mails. + / outbox [page] - Check your sent mails. + / trash [page] - Check your deleted mails. + / text - Send simple text mail. + / write - Enter to mail creation mode. + / item - Show the detailed information of item in your hand. + / help - Show the help menu. + / reload - Reload all of data. + permission: undine.command + permission-message: 'You don''t have a permission "".' + + undinelist: + description: Command of Player list + aliases: [ulist,uindex] + usage: | + / - display player index button page. + permission: undine.list + permission-message: 'You don''t have a permission "".' + + undinegroup: + description: Command of Mail group + aliases: [ugroup] + usage: | + / - Show the group management menu. + permission: undine.group.command + permission-message: 'You don''t have a permission "".' + +permissions: + undine.command: + description: Permission of command. + default: true + undine.inbox: + description: Permission of inbox command. + default: true + undine.outbox: + description: Permission of outbox command. + default: true + undine.trash: + description: Permission of trash command. + default: true + undine.read: + description: Permission of read command. + default: true + undine.read-all: + description: Permission of read other's mail. + default: op + undine.text: + description: Permission of text command. + default: true + undine.write: + description: Permission of write command. + default: true + undine.to: + description: Permission of to command. + default: true + undine.message: + description: Permission of message command. + default: true + undine.attach: + description: Permission of attach command. + default: true + undine.attach-infinity: + description: Permission of infinity use of attachment box. + default: op + undine.attach-sendmail: + description: Permission of opening the attachment box of send mail. + default: true + undine.attach-inboxmail: + description: Permission of opening the attachment box of inbox mail. + default: true + undine.command-attach: + description: Permission of adding attachment items by using command. + default: op + undine.multiple-attach: + description: Permission of sending item attached mail to multiple destinations. + default: op + undine.costmoney: + description: Permission of costmoney command. + default: true + undine.costitem: + description: Permission of costitem command. + default: true + undine.send: + description: Permission of send command. + default: true + undine.cancel: + description: Permission of send command. + default: true + undine.item: + description: Permission of item command. + default: true + undine.teleport: + description: Permission of teleporting to the location of sending. + default: op + undine.help: + description: Permission of help command. + default: true + undine.reload: + description: Permission of reload command. + default: op + + undine.list: + description: Permission of list command. + default: true + + undine.group.command: + description: Permission of group command. + default: true + undine.group.create: + description: Permission of group create command. + default: true + undine.group.delete: + description: Permission of group delete command. + default: true + undine.group.list: + description: Permission of group list command. + default: true + undine.group.detail: + description: Permission of group detail command. + default: true + undine.group.add: + description: Permission of group add command. + default: true + undine.group.addalllogin: + description: Permission of group addAllLogin command. + default: true + undine.group.remove: + description: Permission of group remove command. + default: true + undine.group.perm: + description: Permission of group perm command. + default: true + undine.group.infinite-create: + description: Permission of infinite create groups. + default: op + undine.group.infinite-add-member: + description: Permission of infinite add members. + default: op + undine.group.modify-all: + description: Permission of modification all groups. + default: op + undine.group.dissolution-all: + description: Permission of break-up all groups. + default: op + + undine.group.*: + description: All Permissions of Undine Group Management. + children: + undine.group.command: true + undine.group.create: true + undine.group.delete: true + undine.group.list: true + undine.group.detail: true + undine.group.add: true + undine.group.addalllogin: true + undine.group.remove: true + undine.group.perm: true + undine.group.infinite-create: true + undine.group.infinite-add-member: true + undine.group.send-all: true + undine.group.modify-all: true + undine.group.dissolution-all: true + + undine.*: + description: All Permissions of UndineMailer. + children: + undine.command: true + undine.inbox: true + undine.outbox: true + undine.trash: true + undine.read: true + undine.read-all: true + undine.text: true + undine.write: true + undine.to: true + undine.message: true + undine.attach: true + undine.attach-infinity: true + undine.attach-sendmail: true + undine.attach-inboxmail: true + undine.command-attach: true + undine.multiple-attach: true + undine.costmoney: true + undine.costitem: true + undine.send: true + undine.cancel: true + undine.item: true + undine.teleport: true + undine.help: true + undine.reload: true + undine.list: true + undine.group.*: true diff --git a/src/test/java/org/bitbucket/ucchy/undine/UUIDResolverTest.java b/src/test/java/org/bitbucket/ucchy/undine/UUIDResolverTest.java new file mode 100644 index 0000000..e08e69e --- /dev/null +++ b/src/test/java/org/bitbucket/ucchy/undine/UUIDResolverTest.java @@ -0,0 +1,87 @@ +/* + * @author ucchy + * @license LGPLv3 + * @copyright Copyright ucchy 2020 + */ +package org.bitbucket.ucchy.undine; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Map; + +import junit.framework.TestCase; + +/** + * + * @author ucchy + */ +public class UUIDResolverTest extends TestCase { + + public void testGetUUID() { + + // UUIDのオンライン解決をテストする + + String myName = "ucchy"; + String myUuid = "9603ae84-5be8-40af-af14-a62ed0f14a29"; + + UUIDResolver resolver = new UUIDResolver(true); + + long timeStart = System.currentTimeMillis(); + String uuid = resolver.getUUIDFromName(myName, new Date()); + System.out.println("ucchy uuid is " + uuid + ". Time : " + (System.currentTimeMillis() - timeStart)); + assertTrue(myUuid.equals(uuid)); + + timeStart = System.currentTimeMillis(); + String uuid2 = resolver.getUUIDFromName(myName, new Date()); + System.out.println("ucchy uuid is " + uuid2 + ". Time : " + (System.currentTimeMillis() - timeStart)); + assertTrue(myUuid.equals(uuid2)); + + // Date date29DaysBefore = new Date(System.currentTimeMillis() - 1000L*24*3600* 29); +// timeStart = System.currentTimeMillis(); +// String uuid6 = PCGFPluginLibBridge.getUUIDFromName(myName, true, true, date29DaysBefore); +// System.out.println("ucchy uuid is " + uuid6 + ". Time : " + (System.currentTimeMillis() - timeStart)); +// assertTrue(myUuid.equals(uuid6)); +// +// Date date35DaysBefore = new Date(System.currentTimeMillis() - 1000L*24*3600* 35); +// timeStart = System.currentTimeMillis(); +// String uuid3 = PCGFPluginLibBridge.getUUIDFromName(myName, true, true, date35DaysBefore); +// System.out.println("ucchy uuid is " + uuid3 + ". Time : " + (System.currentTimeMillis() - timeStart)); +// assertTrue(myUuid.equals(uuid3)); +// +// Date date70DaysBefore = new Date(System.currentTimeMillis() - 1000L*24*3600* 70); +// timeStart = System.currentTimeMillis(); +// String uuid4 = PCGFPluginLibBridge.getUUIDFromName(myName, true, true, date70DaysBefore); +// System.out.println("ucchy uuid is " + uuid4 + ". Time : " + (System.currentTimeMillis() - timeStart)); +// assertTrue(myUuid.equals(uuid4)); +// +// timeStart = System.currentTimeMillis(); +// String uuid5 = PCGFPluginLibBridge.getUUIDFromName(myName, true, true, new Date()); +// System.out.println("ucchy uuid is " + uuid5 + ". Time : " + (System.currentTimeMillis() - timeStart)); +// assertTrue(myUuid.equals(uuid5)); + + timeStart = System.currentTimeMillis(); + ArrayList names = new ArrayList<>(); + for ( String n : new String[] {myName, "kotarobo", "RoboMWM", "foo", "bar", "uber", "aiueo", + "test", "testttt", "testtttttttt", "thisIsInvalidID", "oooooooooo", "xxxxdddddxxxxxx"} ) { + names.add(n); + } + Map results = resolver.getUUIDsFromNames(names); + System.out.println("getUUIDsFromNames. Time : " + (System.currentTimeMillis() - timeStart)); + for ( String key : results.keySet() ) { + System.out.println(" - " + key + " --> " + results.get(key)); + } + + + + timeStart = System.currentTimeMillis(); + String name1 = resolver.getNameFromUUID(myUuid); + System.out.println(myUuid + " is " + name1 + ". Time : " + (System.currentTimeMillis() - timeStart)); + assertTrue(myName.equals(name1)); + + timeStart = System.currentTimeMillis(); + String name2 = resolver.getNameFromUUID("aiueo"); + System.out.println("aiueo is " + name2 + ". Time : " + (System.currentTimeMillis() - timeStart)); + assertTrue(name2 == null); + } + +}