diff --git a/LICENSE.txt b/LICENSE.txt index f0156c5..02bbb60 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,165 +1,165 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the Library. \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0865061..fc911a8 100644 --- a/pom.xml +++ b/pom.xml @@ -1,198 +1,206 @@ - - 4.0.0 - syam.motdmanager - MotdManager - 1.5-SNAPSHOT - MotdManager - CraftBukkit MotdManager Plugin - - - - UTF-8 - UNKNOWN - - - - - - syamn - syam - admin@sakura-server.net - http://twitter.com/starlightp - SakuraServer - http://sakura-server.net/ - - - - - - - GNU Lesser General Public License 3.0 - LICENSE.txt - repo - - - - - - scm:git:git://github.com/syamn/MotdManager.git - https://github.com/syamn/MotdManager.git - master - scm:git:git@github.com:syamn/MotdManager.git - - - - - jenkins - http://ci.sakura-server.net/ - - - - - ${project.name} - ${basedir}/src/main/java/ - - - - - - com.google.code.maven-replacer-plugin - replacer - 1.5.0 - - - prepare-package - - replace - - - - - target/classes/plugin.yml - - - version-number-unknown - ${project.version}-b${project.build.number} - - - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2 - - - attach-sources - package - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-site-plugin - 3.1 - - ja - UTF-8 - UTF-8 - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.8.1 - - - javadoc-jar - verify - - jar - - - - - - - - - - . - true - ${basedir}/src/main/resources/ - - plugin.yml - config.yml - - - - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.8.1 - - UTF-8 - - - - - - - - - - Bukkit Official - http://repo.bukkit.org/content/groups/public/ - - - - - - - - org.bukkit - bukkit - 1.6.1-R0.1-SNAPSHOT - compile - - - - - - - jenkins - - - env.BUILD_NUMBER - - - - ${env.BUILD_NUMBER} - - - - + + 4.0.0 + syam.motdmanager + MotdManager + 1.5-SNAPSHOT + MotdManager + CraftBukkit MotdManager Plugin + + + + UTF-8 + UNKNOWN + + + + + + syamn + syam + admin@sakura-server.net + http://twitter.com/starlightp + SakuraServer + http://sakura-server.net/ + + + + + + + GNU Lesser General Public License 3.0 + LICENSE.txt + repo + + + + + + scm:git:git://github.com/syamn/MotdManager.git + https://github.com/syamn/MotdManager.git + master + scm:git:git@github.com:syamn/MotdManager.git + + + + + jenkins + http://ci.sakura-server.net/ + + + + + ${project.name} + ${basedir}/src/main/java/ + + + + + com.google.code.maven-replacer-plugin + replacer + 1.5.0 + + + prepare-package + + replace + + + + + target/classes/plugin.yml + + + version-number-unknown + ${project.version}-b${project.build.number} + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.7 + 1.7 + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2 + + + attach-sources + package + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-site-plugin + 3.1 + + ja + UTF-8 + UTF-8 + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.8.1 + + + javadoc-jar + verify + + jar + + + + + + + + + + . + true + ${basedir}/src/main/resources/ + + plugin.yml + config.yml + + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.8.1 + + UTF-8 + + + + + + + + + + Bukkit Official + http://repo.bukkit.org/content/groups/public/ + + + + + + + + org.bukkit + bukkit + LATEST + compile + + + + + + + jenkins + + + env.BUILD_NUMBER + + + + ${env.BUILD_NUMBER} + + + + \ No newline at end of file diff --git a/src/main/java/eu/ac3_servers/dev/motdmanager/PlayerListener.java b/src/main/java/eu/ac3_servers/dev/motdmanager/PlayerListener.java new file mode 100644 index 0000000..e3061c6 --- /dev/null +++ b/src/main/java/eu/ac3_servers/dev/motdmanager/PlayerListener.java @@ -0,0 +1,41 @@ +/** + * + */ +package eu.ac3_servers.dev.motdmanager; + +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; + +import syam.motdmanager.MotdManager; + +public class PlayerListener implements Listener { + + private StorageConfig config; + private MotdManager plugin; + + /** + * + */ + public PlayerListener(MotdManager plugin, StorageConfig config) { + + this.plugin = plugin; + this.config = config; + + } + + @EventHandler + public void onPostLogin(PlayerJoinEvent e) { + + Player player = e.getPlayer(); + String address = player.getAddress().getAddress().getHostAddress(); + plugin.debug("Player joined: " + player.getName() + " | " + address); + address = "ips." + (address.replaceFirst("/", "").replaceAll("\\.", "-")); + plugin.debug("Storing the username to: \"" + address + "\""); + this.config.getConfig().set(address, player.getName()); + this.config.saveConfig(); + + } + +} diff --git a/src/main/java/eu/ac3_servers/dev/motdmanager/StorageConfig.java b/src/main/java/eu/ac3_servers/dev/motdmanager/StorageConfig.java new file mode 100644 index 0000000..17a1114 --- /dev/null +++ b/src/main/java/eu/ac3_servers/dev/motdmanager/StorageConfig.java @@ -0,0 +1,71 @@ +package eu.ac3_servers.dev.motdmanager; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.util.logging.Level; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.Plugin; + +public class StorageConfig { + + private Plugin plugin; + + public StorageConfig(Plugin plugin) { + this.plugin = plugin; + } + + private FileConfiguration customConfig = null; + private File customConfigFile = null; + + public void reloadConfig() { + if (customConfigFile == null) { + customConfigFile = new File(this.plugin.getDataFolder(), "ips.yml"); + } + customConfig = YamlConfiguration.loadConfiguration(customConfigFile); + + // Look for defaults in the jar + try { + Reader defConfigStream = new InputStreamReader(this.plugin.getResource("ips.yml"), "UTF8"); + if (defConfigStream != null) { + YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream); + customConfig.setDefaults(defConfig); + } + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + this.plugin.getLogger().severe("Couldn't reload custom storage."); + } + } + + public FileConfiguration getConfig() { + if (customConfig == null) { + reloadConfig(); + } + return customConfig; + } + + public void saveConfig() { + if (customConfig == null || customConfigFile == null) { + return; + } + try { + getConfig().save(customConfigFile); + } catch (IOException ex) { + this.plugin.getLogger().log(Level.SEVERE, "Could not save config to " + customConfigFile, ex); + } + } + + public void saveDefaultConfig() { + if (customConfigFile == null) { + customConfigFile = new File(this.plugin.getDataFolder(), "ips.yml"); + } + if (!customConfigFile.exists()) { + plugin.saveResource("ips.yml", false); + } + } + +} diff --git a/src/main/java/syam/motdmanager/ConfigurationManager.java b/src/main/java/syam/motdmanager/ConfigurationManager.java index f972ea1..d4d07cc 100644 --- a/src/main/java/syam/motdmanager/ConfigurationManager.java +++ b/src/main/java/syam/motdmanager/ConfigurationManager.java @@ -1,313 +1,318 @@ -/** - * MotdManager - Package: syam.motdmanager - * Created: 2012/11/02 6:02:34 - */ -package syam.motdmanager; - -import java.io.DataInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.net.URL; -import java.net.URLConnection; -import java.nio.channels.FileChannel; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Logger; - -import org.bukkit.configuration.file.FileConfiguration; - -import syam.motdmanager.util.Util; - -/** - * ConfigurationManager (ConfigurationManager.java) - * @author syam(syamn) - */ -public class ConfigurationManager { - /* Current config.yml file version! */ - private final int latestVersion = 1; - - // Logger - private static final Logger log = MotdManager.log; - private static final String logPrefix = MotdManager.logPrefix; - private static final String msgPrefix = MotdManager.msgPrefix; - - private MotdManager plugin; - private FileConfiguration conf; - - private static File pluginDir = new File("plugins", "MotdManager"); - - // デフォルトの設定定数 - @SuppressWarnings("serial") - private final List defMotdList = new ArrayList() {{add("&cThis is default MOTD!");}}; - - // 設定項目 - /* Basic Configs */ - private List motdList = defMotdList; - private boolean useMaxPlayers = false; - private int fakeMaxPlayers = 20; - - private boolean debug = false; - - /** - * コンストラクタ - * @param plugin - */ - public ConfigurationManager(final MotdManager plugin){ - this.plugin = plugin; - pluginDir = this.plugin.getDataFolder(); - } - - /** - * 設定をファイルから読み込む - * @param initialLoad 初回ロードかどうか - */ - public void loadConfig(final boolean initialLoad) throws Exception{ - // ディレクトリ作成 - createDirs(); - - // 設定ファイルパス取得 - File file = new File(pluginDir, "config.yml"); - // 無ければデフォルトコピー - if (!file.exists()){ - extractResource(System.getProperty("file.separator") + "config.yml", pluginDir, false, true); - log.info(logPrefix+ "config.yml is not found! Created default config.yml!"); - } - - plugin.reloadConfig(); - - // 先にバージョンチェック - checkver(plugin.getConfig().getInt("ConfigVersion", 1)); - - /* Basic Configs */ - if (plugin.getConfig().get("MotdList") != null){ - motdList = plugin.getConfig().getStringList("MotdList"); - }else{ - motdList = defMotdList; - } - String fakeMaxPlayersStr = plugin.getConfig().getString("FakeMaxPlayer", "disable"); - if (Util.isInteger(fakeMaxPlayersStr)){ - fakeMaxPlayers = Integer.parseInt(fakeMaxPlayersStr); - useMaxPlayers = true; - }else{ - useMaxPlayers = false; - } - - /* Debug Configs */ - debug = plugin.getConfig().getBoolean("Debug", false); - - } - - // 設定 getter/setter ここから - /* Basic Configs */ - public List getMotdList(){ - return this.motdList; - } - public List addMotdList(final String motd){ - this.motdList.add(motd); - return this.getMotdList(); - } - public String removeMotdList(final int index){ - if (index < 0 || index >= motdList.size()){ - return null; - } - return this.motdList.remove(index); - } - - public int getFakeMaxPlayers(){ - return this.fakeMaxPlayers; - } - public void setFakeMaxPlayers(final int count){ - this.fakeMaxPlayers = count; - } - - public boolean getUseFakeMaxPlayers(){ - return useMaxPlayers; - } - public void setUseFakeMaxPlayers(final boolean use){ - this.useMaxPlayers = use; - } - - /* Debug Configs */ - public boolean isDebug(){ - return this.debug; - } - // 設定 getter/setter ここまで - - /** - * 設定ファイルに設定を書き込む - * @throws Exception - */ - public boolean save(){ - plugin.getConfig().set("MotdList", getMotdList()); - plugin.getConfig().set("FakeMaxPlayer", (useMaxPlayers) ? fakeMaxPlayers : "disable"); - try { - plugin.getConfig().save(new File(plugin.getDataFolder() + System.getProperty("file.separator") + "config.yml")); - } catch (IOException ex) { - log.warning(logPrefix+ "Couldn't write config file!"); - ex.printStackTrace(); - return false; - } - return true; - } - - /** - * 必要なディレクトリ群を作成する - */ - private void createDirs(){ - createDir(plugin.getDataFolder()); - } - - /** - * 存在しないディレクトリを作成する - * @param dir File 作成するディレクトリ - */ - private static void createDir(final File dir) { - // 既に存在すれば作らない - if (dir.isDirectory()){ - return; - } - if (!dir.mkdir()){ - log.warning(logPrefix+ "Can't create directory: "+dir.getName()); - } - } - - /** - * 設定ファイルのバージョンをチェックする - * @param ver - */ - private void checkver(final int ver){ - // compare configuration file version - if (ver < latestVersion){ - // first, rename old configuration - final String destName = "oldconfig-v" + ver + ".yml"; - String srcPath = new File(pluginDir, "config.yml").getPath(); - String destPath = new File(pluginDir, destName).getPath(); - try{ - copyTransfer(srcPath, destPath); - log.info("Copied old config.yml to "+destName+"!"); - }catch(Exception ex){ - log.warning("Failed to copy old config.yml!"); - } - - // force copy config.yml and languages - extractResource("/config.yml", pluginDir, true, false); - - plugin.reloadConfig(); - conf = plugin.getConfig(); - - log.info("Deleted existing configuration file and generate a new one!"); - } - } - - /** - * リソースファイルをファイルに出力する - * @param from 出力元のファイルパス - * @param to 出力先のファイルパス - * @param force jarファイルの更新日時より新しいファイルが既にあっても強制的に上書きするか - * @param checkenc 出力元のファイルを環境によって適したエンコードにするかどうか - * @author syam - */ - static void extractResource(String from, File to, boolean force, boolean checkenc){ - File of = to; - - // ファイル展開先がディレクトリならファイルに変換、ファイルでなければ返す - if (to.isDirectory()){ - String filename = new File(from).getName(); - of = new File(to, filename); - }else if(!of.isFile()){ - log.warning(logPrefix+ "not a file:" + of); - return; - } - - // ファイルが既に存在する場合は、forceフラグがtrueでない限り展開しない - if (of.exists() && !force){ - return; - } - - OutputStream out = null; - InputStream in = null; - InputStreamReader reader = null; - OutputStreamWriter writer =null; - DataInputStream dis = null; - try{ - // jar内部のリソースファイルを取得 - URL res = MotdManager.class.getResource(from); - if (res == null){ - log.warning(logPrefix+ "Can't find "+ from +" in plugin Jar file"); - return; - } - URLConnection resConn = res.openConnection(); - resConn.setUseCaches(false); - in = resConn.getInputStream(); - - if (in == null){ - log.warning(logPrefix+ "Can't get input stream from " + res); - }else{ - // 出力処理 ファイルによって出力方法を変える - if (checkenc){ - // 環境依存文字を含むファイルはこちら環境 - - reader = new InputStreamReader(in, "UTF-8"); - writer = new OutputStreamWriter(new FileOutputStream(of)); // 出力ファイルのエンコードは未指定 = 自動で変わるようにする - - int text; - while ((text = reader.read()) != -1){ - writer.write(text); - } - }else{ - // そのほか - - out = new FileOutputStream(of); - byte[] buf = new byte[1024]; // バッファサイズ - int len = 0; - while((len = in.read(buf)) >= 0){ - out.write(buf, 0, len); - } - } - } - }catch (Exception ex){ - ex.printStackTrace(); - }finally{ - // 後処理 - try{ - if (out != null) - out.close(); - if (in != null) - in.close(); - if (reader != null) - reader.close(); - if (writer != null) - writer.close(); - }catch (Exception ex){} - } - } - - /** - * コピー元のパス[srcPath]から、コピー先のパス[destPath]へファイルのコピーを行います。 - * コピー処理にはFileChannel#transferToメソッドを利用します。 - * コピー処理終了後、入力・出力のチャネルをクローズします。 - * @param srcPath コピー元のパス - * @param destPath コピー先のパス - * @throws IOException 何らかの入出力処理例外が発生した場合 - */ - public static void copyTransfer(String srcPath, String destPath) throws IOException { - FileChannel srcChannel = new FileInputStream(srcPath).getChannel(); - FileChannel destChannel = new FileOutputStream(destPath).getChannel(); - try { - srcChannel.transferTo(0, srcChannel.size(), destChannel); - } finally { - srcChannel.close(); - destChannel.close(); - } - } - - public static File getJarFile(){ - return new File("plugins", "MotdManager.jar"); - } -} +/** + * MotdManager - Package: syam.motdmanager + * Created: 2012/11/02 6:02:34 + */ +package syam.motdmanager; + +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.URL; +import java.net.URLConnection; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +import org.bukkit.configuration.file.FileConfiguration; + +import syam.motdmanager.util.Util; + +/** + * ConfigurationManager (ConfigurationManager.java) + * @author syam(syamn) + */ +public class ConfigurationManager { + /* Current config.yml file version! */ + private final int latestVersion = 1; + + // Logger + private static final Logger log = MotdManager.log; + private static final String logPrefix = MotdManager.logPrefix; + @SuppressWarnings("unused") + private static final String msgPrefix = MotdManager.msgPrefix; + + private MotdManager plugin; + @SuppressWarnings("unused") + private FileConfiguration conf; + + private static File pluginDir = new File("plugins", "MotdManager"); + + // デフォルトの設定定数 + @SuppressWarnings("serial") + private final List defMotdList = new ArrayList() {{add("&cThis is default MOTD!");}}; + + // 設定項目 + /* Basic Configs */ + private List motdList = defMotdList; + private boolean useMaxPlayers = false; + private int fakeMaxPlayers = 20; + + private boolean debug = false; + + /** + * コンストラクタ + * @param plugin + */ + public ConfigurationManager(final MotdManager plugin){ + this.plugin = plugin; + pluginDir = this.plugin.getDataFolder(); + } + + /** + * 設定をファイルから読み込む + * @param initialLoad 初回ロードかどうか + */ + public void loadConfig(final boolean initialLoad) throws Exception{ + // ディレクトリ作成 + createDirs(); + + // 設定ファイルパス取得 + File file = new File(pluginDir, "config.yml"); + // 無ければデフォルトコピー + if (!file.exists()){ + extractResource(System.getProperty("file.separator") + "config.yml", pluginDir, false, true); + log.info(logPrefix+ "config.yml is not found! Created default config.yml!"); + } + + plugin.reloadConfig(); + + // 先にバージョンチェック + checkver(plugin.getConfig().getInt("ConfigVersion", 1)); + + /* Basic Configs */ + if (plugin.getConfig().get("MotdList") != null){ + motdList = plugin.getConfig().getStringList("MotdList"); + }else{ + motdList = defMotdList; + } + String fakeMaxPlayersStr = plugin.getConfig().getString("FakeMaxPlayer", "disable"); + if (Util.isInteger(fakeMaxPlayersStr)){ + fakeMaxPlayers = Integer.parseInt(fakeMaxPlayersStr); + useMaxPlayers = true; + }else{ + useMaxPlayers = false; + } + + /* Debug Configs */ + debug = plugin.getConfig().getBoolean("Debug", false); + + } + + // 設定 getter/setter ここから + /* Basic Configs */ + public List getMotdList(){ + return this.motdList; + } + public List addMotdList(final String motd){ + this.motdList.add(motd); + return this.getMotdList(); + } + public String removeMotdList(final int index){ + if (index < 0 || index >= motdList.size()){ + return null; + } + return this.motdList.remove(index); + } + + public int getFakeMaxPlayers(){ + return this.fakeMaxPlayers; + } + public void setFakeMaxPlayers(final int count){ + this.fakeMaxPlayers = count; + } + + public boolean getUseFakeMaxPlayers(){ + return useMaxPlayers; + } + public void setUseFakeMaxPlayers(final boolean use){ + this.useMaxPlayers = use; + } + + /* Debug Configs */ + public boolean isDebug(){ + return this.debug; + } + // 設定 getter/setter ここまで + + /** + * 設定ファイルに設定を書き込む + * @throws Exception + */ + public boolean save(){ + plugin.getConfig().set("MotdList", getMotdList()); + plugin.getConfig().set("FakeMaxPlayer", (useMaxPlayers) ? fakeMaxPlayers : "disable"); + try { + plugin.getConfig().save(new File(plugin.getDataFolder() + System.getProperty("file.separator") + "config.yml")); + } catch (IOException ex) { + log.warning(logPrefix+ "Couldn't write config file!"); + ex.printStackTrace(); + return false; + } + return true; + } + + /** + * 必要なディレクトリ群を作成する + */ + private void createDirs(){ + createDir(plugin.getDataFolder()); + } + + /** + * 存在しないディレクトリを作成する + * @param dir File 作成するディレクトリ + */ + private static void createDir(final File dir) { + // 既に存在すれば作らない + if (dir.isDirectory()){ + return; + } + if (!dir.mkdir()){ + log.warning(logPrefix+ "Can't create directory: "+dir.getName()); + } + } + + /** + * 設定ファイルのバージョンをチェックする + * @param ver + */ + private void checkver(final int ver){ + // compare configuration file version + if (ver < latestVersion){ + // first, rename old configuration + final String destName = "oldconfig-v" + ver + ".yml"; + String srcPath = new File(pluginDir, "config.yml").getPath(); + String destPath = new File(pluginDir, destName).getPath(); + try{ + copyTransfer(srcPath, destPath); + log.info("Copied old config.yml to "+destName+"!"); + }catch(Exception ex){ + log.warning("Failed to copy old config.yml!"); + } + + // force copy config.yml and languages + extractResource("/config.yml", pluginDir, true, false); + + plugin.reloadConfig(); + conf = plugin.getConfig(); + + log.info("Deleted existing configuration file and generate a new one!"); + } + } + + /** + * リソースファイルをファイルに出力する + * @param from 出力元のファイルパス + * @param to 出力先のファイルパス + * @param force jarファイルの更新日時より新しいファイルが既にあっても強制的に上書きするか + * @param checkenc 出力元のファイルを環境によって適したエンコードにするかどうか + * @author syam + */ + static void extractResource(String from, File to, boolean force, boolean checkenc){ + File of = to; + + // ファイル展開先がディレクトリならファイルに変換、ファイルでなければ返す + if (to.isDirectory()){ + String filename = new File(from).getName(); + of = new File(to, filename); + }else if(!of.isFile()){ + log.warning(logPrefix+ "not a file:" + of); + return; + } + + // ファイルが既に存在する場合は、forceフラグがtrueでない限り展開しない + if (of.exists() && !force){ + return; + } + + OutputStream out = null; + InputStream in = null; + InputStreamReader reader = null; + OutputStreamWriter writer =null; + @SuppressWarnings("unused") + DataInputStream dis = null; + try{ + // jar内部のリソースファイルを取得 + URL res = MotdManager.class.getResource(from); + if (res == null){ + log.warning(logPrefix+ "Can't find "+ from +" in plugin Jar file"); + return; + } + URLConnection resConn = res.openConnection(); + resConn.setUseCaches(false); + in = resConn.getInputStream(); + + if (in == null){ + log.warning(logPrefix+ "Can't get input stream from " + res); + }else{ + // 出力処理 ファイルによって出力方法を変える + if (checkenc){ + // 環境依存文字を含むファイルはこちら環境 + + reader = new InputStreamReader(in, "UTF-8"); + writer = new OutputStreamWriter(new FileOutputStream(of)); // 出力ファイルのエンコードは未指定 = 自動で変わるようにする + + int text; + while ((text = reader.read()) != -1){ + writer.write(text); + } + }else{ + // そのほか + + out = new FileOutputStream(of); + byte[] buf = new byte[1024]; // バッファサイズ + int len = 0; + while((len = in.read(buf)) >= 0){ + out.write(buf, 0, len); + } + } + } + }catch (Exception ex){ + ex.printStackTrace(); + }finally{ + // 後処理 + try{ + if (out != null) + out.close(); + if (in != null) + in.close(); + if (reader != null) + reader.close(); + if (writer != null) + writer.close(); + }catch (Exception ex){} + } + } + + /** + * コピー元のパス[srcPath]から、コピー先のパス[destPath]へファイルのコピーを行います。 + * コピー処理にはFileChannel#transferToメソッドを利用します。 + * コピー処理終了後、入力・出力のチャネルをクローズします。 + * @param srcPath コピー元のパス + * @param destPath コピー先のパス + * @throws IOException 何らかの入出力処理例外が発生した場合 + */ + public static void copyTransfer(String srcPath, String destPath) throws IOException { + @SuppressWarnings("resource") + FileChannel srcChannel = new FileInputStream(srcPath).getChannel(); + @SuppressWarnings("resource") + FileChannel destChannel = new FileOutputStream(destPath).getChannel(); + try { + srcChannel.transferTo(0, srcChannel.size(), destChannel); + } finally { + srcChannel.close(); + destChannel.close(); + } + } + + public static File getJarFile(){ + return new File("plugins", "MotdManager.jar"); + } +} diff --git a/src/main/java/syam/motdmanager/MotdManager.java b/src/main/java/syam/motdmanager/MotdManager.java index 81dca2f..0b95027 100644 --- a/src/main/java/syam/motdmanager/MotdManager.java +++ b/src/main/java/syam/motdmanager/MotdManager.java @@ -1,230 +1,252 @@ -/** - * MotdManager - Package: syam.motdmanager - * Created: 2012/11/02 6:00:01 - */ -package syam.motdmanager; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.plugin.PluginDescriptionFile; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; - -import syam.motdmanager.command.AddCommand; -import syam.motdmanager.command.BaseCommand; -import syam.motdmanager.command.HelpCommand; -import syam.motdmanager.command.ListCommand; -import syam.motdmanager.command.MaxplayerCommand; -import syam.motdmanager.command.ReloadCommand; -import syam.motdmanager.command.RemoveCommand; -import syam.motdmanager.listener.ServerListener; -import syam.motdmanager.util.Actions; -import syam.motdmanager.util.Metrics; - -/** - * MotdManager (MotdManager.java) - * @author syam(syamn) - */ -public class MotdManager extends JavaPlugin{ - // ** Logger ** - public final static Logger log = Logger.getLogger("Minecraft"); - public final static String logPrefix = "[MotdManager] "; - public final static String msgPrefix = "&6[MotdManager] &f"; - - // ** Listener ** - ServerListener serverListener = new ServerListener(this); - - // ** Commands ** - private List commands = new ArrayList(); - - // ** Private Classes ** - private ConfigurationManager config; - - // ** Replaces ** - private String mcVersion = ""; - - // ** Instance ** - private static MotdManager instance; - - /** - * プラグイン起動処理 - */ - @Override - public void onEnable(){ - instance = this; - - PluginManager pm = getServer().getPluginManager(); - config = new ConfigurationManager(this); - - // loadconfig - try{ - config.loadConfig(true); - }catch (Exception ex){ - log.warning(logPrefix+"an error occured while trying to load the config file."); - ex.printStackTrace(); - } - - // プラグインを無効にした場合進まないようにする - if (!pm.isPluginEnabled(this)){ - return; - } - - // Regist Listeners - pm.registerEvents(serverListener, this); - - // コマンド登録 - registerCommands(); - - // Building replaces - try { - buildReplaces(); - }catch (Exception ex){ - log.warning("Could not build replace strings! (Check plugin update!)"); - ex.printStackTrace(); - } - - // メッセージ表示 - PluginDescriptionFile pdfFile=this.getDescription(); - log.info("["+pdfFile.getName()+"] version "+pdfFile.getVersion()+" is enabled!"); - - setupMetrics(); // mcstats - } - - /** - * プラグイン停止処理 - */ - @Override - public void onDisable(){ - // メッセージ表示 - PluginDescriptionFile pdfFile=this.getDescription(); - log.info("["+pdfFile.getName()+"] version "+pdfFile.getVersion()+" is disabled!"); - } - - public void buildReplaces(){ - // mcVersion - final Matcher matcher = Pattern.compile("\\(MC: (.+)\\)").matcher(Bukkit.getVersion()); - this.mcVersion = (matcher.find()) ? matcher.group(1) : "Unknown"; - } - - /** - * コマンドを登録 - */ - private void registerCommands(){ - // Intro Commands - commands.add(new HelpCommand()); - - // Motd Commands - commands.add(new AddCommand()); - commands.add(new RemoveCommand()); - commands.add(new ListCommand()); - - // Other Commands - commands.add(new MaxplayerCommand()); - - // Admin Commands - commands.add(new ReloadCommand()); - } - - /** - * Metricsセットアップ - */ - private void setupMetrics(){ - try { - Metrics metrics = new Metrics(this); - metrics.start(); - } catch (IOException ex) { - log.warning(logPrefix+"cant send metrics data!"); - ex.printStackTrace(); - } - } - - /** - * コマンドが呼ばれた - */ - @Override - public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String args[]){ - if (cmd.getName().equalsIgnoreCase("motdmanager")){ - if(args.length == 0){ - // 引数ゼロはヘルプ表示 - args = new String[]{"help"}; - } - - outer: - for (BaseCommand command : commands.toArray(new BaseCommand[0])){ - String[] cmds = command.getName().split(" "); - for (int i = 0; i < cmds.length; i++){ - if (i >= args.length || !cmds[i].equalsIgnoreCase(args[i])){ - continue outer; - } - // 実行 - return command.run(this, sender, args, commandLabel); - } - } - // 有効コマンドなし ヘルプ表示 - new HelpCommand().run(this, sender, args, commandLabel); - return true; - } - return false; - } - - public void debug(final String msg){ - if (config.isDebug()){ - log.info(logPrefix+ "[DEBUG]" + msg); - } - } - - public String formatting(String string){ - debug("Formatting..: " + string); - string = replacing(string); - debug("Replaced: " + string); - string = Actions.coloring(string); - debug("Formatted: " + string); - return string; - } - - /** - * 変数の置換を行う - * @param motd - * @return - */ - private String replacing(final String motd){ - if (motd == null) return null; - return motd - .replaceAll("%ver", mcVersion) - .replaceAll("%players", String.valueOf(Bukkit.getOnlinePlayers().length)) - ; - } - - /* getter */ - /** - * コマンドを返す - * @return List - */ - public List getCommands(){ - return commands; - } - - /** - * 設定マネージャを返す - * @return ConfigurationManager - */ - public ConfigurationManager getConfigs() { - return config; - } - - /** - * インスタンスを返す - * @return MotdManagerインスタンス - */ - public static MotdManager getInstance(){ - return instance; - } -} +/** + * MotdManager - Package: syam.motdmanager + * Created: 2012/11/02 6:00:01 + */ +package syam.motdmanager; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + +import eu.ac3_servers.dev.motdmanager.PlayerListener; +import eu.ac3_servers.dev.motdmanager.StorageConfig; +import syam.motdmanager.command.AddCommand; +import syam.motdmanager.command.BaseCommand; +import syam.motdmanager.command.HelpCommand; +import syam.motdmanager.command.ListCommand; +import syam.motdmanager.command.MaxplayerCommand; +import syam.motdmanager.command.ReloadCommand; +import syam.motdmanager.command.RemoveCommand; +import syam.motdmanager.listener.ServerListener; +import syam.motdmanager.util.Actions; +import syam.motdmanager.util.Metrics; + +/** + * MotdManager (MotdManager.java) + * @author syam(syamn) + */ +public class MotdManager extends JavaPlugin{ + // ** Logger ** + public final static Logger log = Logger.getLogger("Minecraft"); + public final static String logPrefix = "[MotdManager] "; + public final static String msgPrefix = "&6[MotdManager] &f"; + + // ** Listener ** + ServerListener serverListener = new ServerListener(this); + + // ** Commands ** + private List commands = new ArrayList(); + + // ** Private Classes ** + private ConfigurationManager config; + + // ** Replaces ** + private String mcVersion = ""; + private StorageConfig storage; + + // ** Instance ** + private static MotdManager instance; + + /** + * プラグイン起動処理 + */ + @Override + public void onEnable(){ + instance = this; + + PluginManager pm = getServer().getPluginManager(); + config = new ConfigurationManager(this); + + // loadconfig + try{ + config.loadConfig(true); + }catch (Exception ex){ + log.warning(logPrefix+"an error occured while trying to load the config file."); + ex.printStackTrace(); + } + + // プラグインを無効にした場合進まないようにする + if (!pm.isPluginEnabled(this)){ + return; + } + + // Regist Listeners + pm.registerEvents(serverListener, this); + + // コマンド登録 + registerCommands(); + + // Building replaces + try { + buildReplaces(); + }catch (Exception ex){ + log.warning("Could not build replace strings! (Check plugin update!)"); + ex.printStackTrace(); + } + + this.storage = new StorageConfig(this); + if(!(new File(getDataFolder(), "ips.yml")).exists()) + this.storage.saveDefaultConfig(); + + pm.registerEvents(new PlayerListener(this, this.storage), this); + log.info("[MOTDManager mod] Player listener registered!"); + + // メッセージ表示 + PluginDescriptionFile pdfFile=this.getDescription(); + log.info("["+pdfFile.getName()+"] version "+pdfFile.getVersion()+" is enabled!"); + + setupMetrics(); // mcstats + } + + /** + * プラグイン停止処理 + */ + @Override + public void onDisable(){ + // メッセージ表示 + PluginDescriptionFile pdfFile=this.getDescription(); + this.storage.saveConfig(); + log.info("["+pdfFile.getName()+"] version "+pdfFile.getVersion()+" is disabled!"); + } + + public void buildReplaces(){ + // mcVersion + final Matcher matcher = Pattern.compile("\\(MC: (.+)\\)").matcher(Bukkit.getVersion()); + this.mcVersion = (matcher.find()) ? matcher.group(1) : "Unknown"; + } + + /** + * コマンドを登録 + */ + private void registerCommands(){ + // Intro Commands + commands.add(new HelpCommand()); + + // Motd Commands + commands.add(new AddCommand()); + commands.add(new RemoveCommand()); + commands.add(new ListCommand()); + + // Other Commands + commands.add(new MaxplayerCommand()); + + // Admin Commands + commands.add(new ReloadCommand()); + } + + /** + * Metricsセットアップ + */ + private void setupMetrics(){ + try { + Metrics metrics = new Metrics(this); + metrics.start(); + } catch (IOException ex) { + log.warning(logPrefix+"cant send metrics data!"); + ex.printStackTrace(); + } + } + + /** + * コマンドが呼ばれた + */ + @SuppressWarnings("unused") + @Override + public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String args[]){ + if (cmd.getName().equalsIgnoreCase("motdmanager")){ + if(args.length == 0){ + // 引数ゼロはヘルプ表示 + args = new String[]{"help"}; + } + + outer: + for (BaseCommand command : commands.toArray(new BaseCommand[0])){ + String[] cmds = command.getName().split(" "); + for (int i = 0; i < cmds.length; i++){ + if (i >= args.length || !cmds[i].equalsIgnoreCase(args[i])){ + continue outer; + } + // 実行 + return command.run(this, sender, args, commandLabel); + } + } + // 有効コマンドなし ヘルプ表示 + new HelpCommand().run(this, sender, args, commandLabel); + return true; + } + return false; + } + + public void debug(final String msg){ + if (config.isDebug()){ + log.info(logPrefix+ "[DEBUG]" + msg); + } + } + + public String formatting(String string, String address){ + debug("Formatting..: " + string); + string = replacing(string, address); + debug("Replaced: " + string); + string = Actions.coloring(string); + debug("Formatted: " + string); + return string; + } + + /** + * 変数の置換を行う + * @param motd + * @param address + * @return + */ + private String replacing(final String motd, String address){ + if (motd == null) return null; + return motd + .replaceAll("%ver", mcVersion) + .replaceAll("%players", String.valueOf(Bukkit.getOnlinePlayers().size())) + .replaceAll("%player", getUserFromAddress(address.replaceFirst("/", "").replaceAll("\\.", "-"))) + ; + } + + private String getUserFromAddress(String address) { + String username = this.storage.getConfig().getString("ips." + address); + debug("Username: " + username); + if(username == null || address == "127.0.0.1") return "user"; + return username; + } + + /* getter */ + /** + * コマンドを返す + * @return List + */ + public List getCommands(){ + return commands; + } + + /** + * 設定マネージャを返す + * @return ConfigurationManager + */ + public ConfigurationManager getConfigs() { + return config; + } + + /** + * インスタンスを返す + * @return MotdManagerインスタンス + */ + public static MotdManager getInstance(){ + return instance; + } +} diff --git a/src/main/java/syam/motdmanager/Perms.java b/src/main/java/syam/motdmanager/Perms.java index 86f8d3e..738be7d 100644 --- a/src/main/java/syam/motdmanager/Perms.java +++ b/src/main/java/syam/motdmanager/Perms.java @@ -1,49 +1,49 @@ -/** - * MotdManager - Package: syam.motdmanager - * Created: 2012/11/02 6:14:14 - */ -package syam.motdmanager; - -import org.bukkit.permissions.Permissible; - -/** - * Perms (Perms.java) - * @author syam(syamn) - */ -public enum Perms { - /* 権限ノード */ - // Motd Commands - ADD ("add"), - REMOVE ("remove"), - LIST ("list"), - - // Other Commands - MAXPLAYER ("maxplayer"), - - // Admin Commands - RELOAD ("reload"), - - ; - - // ノードヘッダー - final String HEADER = "motdmanager."; - private String node; - - /** - * コンストラクタ - * @param node 権限ノード - */ - Perms(final String node){ - this.node = HEADER + node; - } - - /** - * 指定したプレイヤーが権限を持っているか - * @param player Permissible. Player, CommandSender etc - * @return boolean - */ - public boolean has(final Permissible perm){ - if (perm == null) return false; - return perm.hasPermission(node); // only support SuperPerms - } -} +/** + * MotdManager - Package: syam.motdmanager + * Created: 2012/11/02 6:14:14 + */ +package syam.motdmanager; + +import org.bukkit.permissions.Permissible; + +/** + * Perms (Perms.java) + * @author syam(syamn) + */ +public enum Perms { + /* 権限ノード */ + // Motd Commands + ADD ("add"), + REMOVE ("remove"), + LIST ("list"), + + // Other Commands + MAXPLAYER ("maxplayer"), + + // Admin Commands + RELOAD ("reload"), + + ; + + // ノードヘッダー + final String HEADER = "motdmanager."; + private String node; + + /** + * コンストラクタ + * @param node 権限ノード + */ + Perms(final String node){ + this.node = HEADER + node; + } + + /** + * 指定したプレイヤーが権限を持っているか + * @param player Permissible. Player, CommandSender etc + * @return boolean + */ + public boolean has(final Permissible perm){ + if (perm == null) return false; + return perm.hasPermission(node); // only support SuperPerms + } +} diff --git a/src/main/java/syam/motdmanager/command/AddCommand.java b/src/main/java/syam/motdmanager/command/AddCommand.java index a79b74b..a67b139 100644 --- a/src/main/java/syam/motdmanager/command/AddCommand.java +++ b/src/main/java/syam/motdmanager/command/AddCommand.java @@ -1,42 +1,42 @@ -/** - * MotdManager - Package: syam.motdmanager.command - * Created: 2012/11/02 6:32:33 - */ -package syam.motdmanager.command; - -import syam.motdmanager.Perms; -import syam.motdmanager.exception.CommandException; -import syam.motdmanager.util.Actions; -import syam.motdmanager.util.Util; - -/** - * AddCommand (AddCommand.java) - * @author syam(syamn) - */ -public class AddCommand extends BaseCommand{ - public AddCommand(){ - bePlayer = false; - name = "add"; - argLength = 1; - usage = " <- add to motd list"; - } - - @Override - public void execute() throws CommandException { - String motd = Util.join(args, " "); - config.addMotdList(motd); - if (!config.save()){ - throw new CommandException("&cFailed to save configuration file!"); - } - motd = plugin.formatting(motd); - Actions.message(sender, "&aAdded MOTD: &f" + motd); - if (motd.length() > 200){ // 閾値 0~237 文字 超えるとCommunicationError - Actions.message(sender, "&4WARN:&c Too long strings! May this cause Communication Error! (length: " + motd.length() + ")"); - } - } - - @Override - public boolean permission() { - return Perms.ADD.has(sender); - } -} +/** + * MotdManager - Package: syam.motdmanager.command + * Created: 2012/11/02 6:32:33 + */ +package syam.motdmanager.command; + +import syam.motdmanager.Perms; +import syam.motdmanager.exception.CommandException; +import syam.motdmanager.util.Actions; +import syam.motdmanager.util.Util; + +/** + * AddCommand (AddCommand.java) + * @author syam(syamn) + */ +public class AddCommand extends BaseCommand{ + public AddCommand(){ + bePlayer = false; + name = "add"; + argLength = 1; + usage = " <- add to motd list"; + } + + @Override + public void execute() throws CommandException { + String motd = Util.join(args, " "); + config.addMotdList(motd); + if (!config.save()){ + throw new CommandException("&cFailed to save configuration file!"); + } + motd = plugin.formatting(motd, "127.0.0.1"); + Actions.message(sender, "&aAdded MOTD: &f" + motd); + if (motd.length() > 200){ // 閾値 0~237 文字 超えるとCommunicationError + Actions.message(sender, "&4WARN:&c Too long strings! May this cause Communication Error! (length: " + motd.length() + ")"); + } + } + + @Override + public boolean permission() { + return Perms.ADD.has(sender); + } +} diff --git a/src/main/java/syam/motdmanager/command/ListCommand.java b/src/main/java/syam/motdmanager/command/ListCommand.java index 1fbad8a..a36c4b6 100644 --- a/src/main/java/syam/motdmanager/command/ListCommand.java +++ b/src/main/java/syam/motdmanager/command/ListCommand.java @@ -1,42 +1,42 @@ -/** - * MotdManager - Package: syam.motdmanager.command - * Created: 2012/11/02 7:06:11 - */ -package syam.motdmanager.command; - -import java.util.List; - -import syam.motdmanager.Perms; -import syam.motdmanager.exception.CommandException; -import syam.motdmanager.util.Actions; - -/** - * ListCommand (ListCommand.java) - * @author syam(syamn) - */ -public class ListCommand extends BaseCommand{ - public ListCommand(){ - bePlayer = false; - name = "list"; - argLength = 0; - usage = "<- see motd list"; - } - - @Override - public void execute() throws CommandException { - final List motds = config.getMotdList(); - if (motds.size() == 0){ - throw new CommandException("&cMotd list is empty! Add motd to /" + this.command + " add "); - } - - Actions.message(sender, "&e-- MotdManager : MotdList (" + motds.size() + ") --"); - for (int i = 0, n = motds.size(); i < n; i++){ - Actions.message(sender, "&7" + (i + 1) + ". &f" + plugin.formatting(motds.get(i))); - } - } - - @Override - public boolean permission() { - return Perms.LIST.has(sender); - } -} +/** + * MotdManager - Package: syam.motdmanager.command + * Created: 2012/11/02 7:06:11 + */ +package syam.motdmanager.command; + +import java.util.List; + +import syam.motdmanager.Perms; +import syam.motdmanager.exception.CommandException; +import syam.motdmanager.util.Actions; + +/** + * ListCommand (ListCommand.java) + * @author syam(syamn) + */ +public class ListCommand extends BaseCommand{ + public ListCommand(){ + bePlayer = false; + name = "list"; + argLength = 0; + usage = "<- see motd list"; + } + + @Override + public void execute() throws CommandException { + final List motds = config.getMotdList(); + if (motds.size() == 0){ + throw new CommandException("&cMotd list is empty! Add motd to /" + this.command + " add "); + } + + Actions.message(sender, "&e-- MotdManager : MotdList (" + motds.size() + ") --"); + for (int i = 0, n = motds.size(); i < n; i++){ + Actions.message(sender, "&7" + (i + 1) + ". &f" + plugin.formatting(motds.get(i), "127.0.0.1")); + } + } + + @Override + public boolean permission() { + return Perms.LIST.has(sender); + } +} diff --git a/src/main/java/syam/motdmanager/command/RemoveCommand.java b/src/main/java/syam/motdmanager/command/RemoveCommand.java index ad83a96..e3da9c2 100644 --- a/src/main/java/syam/motdmanager/command/RemoveCommand.java +++ b/src/main/java/syam/motdmanager/command/RemoveCommand.java @@ -1,46 +1,46 @@ -/** - * MotdManager - Package: syam.motdmanager.command - * Created: 2012/11/02 7:14:28 - */ -package syam.motdmanager.command; - -import syam.motdmanager.Perms; -import syam.motdmanager.exception.CommandException; -import syam.motdmanager.util.Actions; -import syam.motdmanager.util.Util; - -/** - * RemoveCommand (RemoveCommand.java) - * @author syam(syamn) - */ -public class RemoveCommand extends BaseCommand{ - public RemoveCommand(){ - bePlayer = false; - name = "remove"; - argLength = 1; - usage = " <- remove from motd list"; - } - - @Override - public void execute() throws CommandException { - final String idstr = args.get(0).trim(); - if (!Util.isInteger(idstr)){ - throw new CommandException("&cIndex must be a number: " + idstr); - } - final int id = Integer.parseInt(idstr); - final String motd = config.removeMotdList(id - 1); - if (motd == null){ - throw new CommandException("&cInvalid number: " + idstr); - } - if (!config.save()){ - throw new CommandException("&cFailed to save configuration file!"); - } - - Actions.message(sender, "&aRemoved MOTD: &7" + id + ". &f" + plugin.formatting(motd)); - } - - @Override - public boolean permission() { - return Perms.REMOVE.has(sender); - } -} +/** + * MotdManager - Package: syam.motdmanager.command + * Created: 2012/11/02 7:14:28 + */ +package syam.motdmanager.command; + +import syam.motdmanager.Perms; +import syam.motdmanager.exception.CommandException; +import syam.motdmanager.util.Actions; +import syam.motdmanager.util.Util; + +/** + * RemoveCommand (RemoveCommand.java) + * @author syam(syamn) + */ +public class RemoveCommand extends BaseCommand{ + public RemoveCommand(){ + bePlayer = false; + name = "remove"; + argLength = 1; + usage = " <- remove from motd list"; + } + + @Override + public void execute() throws CommandException { + final String idstr = args.get(0).trim(); + if (!Util.isInteger(idstr)){ + throw new CommandException("&cIndex must be a number: " + idstr); + } + final int id = Integer.parseInt(idstr); + final String motd = config.removeMotdList(id - 1); + if (motd == null){ + throw new CommandException("&cInvalid number: " + idstr); + } + if (!config.save()){ + throw new CommandException("&cFailed to save configuration file!"); + } + + Actions.message(sender, "&aRemoved MOTD: &7" + id + ". &f" + plugin.formatting(motd, "127.0.0.1..")); + } + + @Override + public boolean permission() { + return Perms.REMOVE.has(sender); + } +} diff --git a/src/main/java/syam/motdmanager/listener/ServerListener.java b/src/main/java/syam/motdmanager/listener/ServerListener.java index a4efd94..0c7b643 100644 --- a/src/main/java/syam/motdmanager/listener/ServerListener.java +++ b/src/main/java/syam/motdmanager/listener/ServerListener.java @@ -1,61 +1,66 @@ -/** - * MotdManager - Package: syam.motdmanager.listener - * Created: 2012/11/02 7:31:54 - */ -package syam.motdmanager.listener; - -import java.util.List; -import java.util.Random; -import java.util.logging.Logger; - -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.server.ServerListPingEvent; - -import syam.motdmanager.MotdManager; - -/** - * ServerListener (ServerListener.java) - * @author syam(syamn) - */ -public class ServerListener implements Listener{ - private static final Logger log = MotdManager.log; - private static final String logPrefix = MotdManager.logPrefix; - private static final String msgPrefix = MotdManager.msgPrefix; - - private final MotdManager plugin; - private final Random random; - - public ServerListener(final MotdManager plugin){ - this.plugin = plugin; - this.random = new Random(); - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onServerListPing(final ServerListPingEvent event){ - String debugmsg = "Get ping from " + event.getAddress().getHostAddress() + "!"; - - final String motd = plugin.formatting(chooseMsg()); - if (motd != null) { - event.setMotd(motd); - debugmsg += " Motd: '" + motd + "'"; - } - if (plugin.getConfigs().getUseFakeMaxPlayers()){ - event.setMaxPlayers(plugin.getConfigs().getFakeMaxPlayers()); - debugmsg += " FakeMaxPlayers: '" + plugin.getConfigs().getFakeMaxPlayers() + "'"; - } - - plugin.debug(debugmsg); - } - - /** - * ランダムでMOTDリストから1つ選択する - * @return - */ - private String chooseMsg(){ - final List motds = plugin.getConfigs().getMotdList(); - if (motds.size() == 0) return null; - return motds.get(random.nextInt(motds.size())); - } -} +/** + * MotdManager - Package: syam.motdmanager.listener + * Created: 2012/11/02 7:31:54 + */ +package syam.motdmanager.listener; + +import java.util.List; +import java.util.Random; +import java.util.logging.Logger; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.server.ServerListPingEvent; + +import syam.motdmanager.MotdManager; + +/** + * ServerListener (ServerListener.java) + * @author syam(syamn) + */ +public class ServerListener implements Listener{ + @SuppressWarnings("unused") + private static final Logger log = MotdManager.log; + @SuppressWarnings("unused") + private static final String logPrefix = MotdManager.logPrefix; + @SuppressWarnings("unused") + private static final String msgPrefix = MotdManager.msgPrefix; + + private final MotdManager plugin; + private final Random random; + + public ServerListener(final MotdManager plugin){ + this.plugin = plugin; + this.random = new Random(); + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onServerListPing(final ServerListPingEvent event){ + String debugmsg = "Get ping from " + event.getAddress().getHostAddress() + "!"; + + String address = event.getAddress().getHostAddress(); + + final String motd = plugin.formatting(chooseMsg(), address); + if (motd != null) { + event.setMotd(motd); + debugmsg += " Motd: '" + motd + "'"; + } + if (plugin.getConfigs().getUseFakeMaxPlayers()){ + event.setMaxPlayers(plugin.getConfigs().getFakeMaxPlayers()); + debugmsg += " FakeMaxPlayers: '" + plugin.getConfigs().getFakeMaxPlayers() + "'"; + } + + plugin.debug(debugmsg); + } + + /** + * ランダムでMOTDリストから1つ選択する + * @return + */ + private String chooseMsg(){ + final List motds = plugin.getConfigs().getMotdList(); + if (motds.size() == 0) return null; + return motds.get(random.nextInt(motds.size())); + } +} diff --git a/src/main/java/syam/motdmanager/listener/package-info.java b/src/main/java/syam/motdmanager/listener/package-info.java index e0bc0d5..10a1d3c 100644 --- a/src/main/java/syam/motdmanager/listener/package-info.java +++ b/src/main/java/syam/motdmanager/listener/package-info.java @@ -1,4 +1,4 @@ -/** - * Bukkit listeners. - */ +/** + * Bukkit listeners. + */ package syam.motdmanager.listener; \ No newline at end of file diff --git a/src/main/java/syam/motdmanager/package-info.java b/src/main/java/syam/motdmanager/package-info.java index 3535419..bd39d47 100644 --- a/src/main/java/syam/motdmanager/package-info.java +++ b/src/main/java/syam/motdmanager/package-info.java @@ -1,4 +1,4 @@ -/** - * Plugin main classes. - */ +/** + * Plugin main classes. + */ package syam.motdmanager; \ No newline at end of file diff --git a/src/main/java/syam/motdmanager/util/Actions.java b/src/main/java/syam/motdmanager/util/Actions.java index 4a32195..878f32d 100644 --- a/src/main/java/syam/motdmanager/util/Actions.java +++ b/src/main/java/syam/motdmanager/util/Actions.java @@ -1,286 +1,290 @@ -/** - * MotdManager - Package: syam.motdmanager.util - * Created: 2012/11/02 6:10:12 - */ -package syam.motdmanager.util; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.logging.Logger; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.OfflinePlayer; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.PlayerInventory; - -import syam.motdmanager.MotdManager; - -/** - * Actions (Actions.java) - * @author syam(syamn) - */ -public class Actions { - // Logger - private static final Logger log = MotdManager.log; - private static final String logPrefix = MotdManager.logPrefix; - private static final String msgPrefix = MotdManager.msgPrefix; - - private final MotdManager plugin; - - private static final List colors = new ArrayList(16){ - private static final long serialVersionUID = -4095100564568189453L; - { - add("\u00A70"); - add("\u00A71"); - add("\u00A72"); - add("\u00A73"); - add("\u00A74"); - add("\u00A75"); - add("\u00A76"); - add("\u00A77"); - add("\u00A78"); - add("\u00A79"); - add("\u00A7a"); - add("\u00A7b"); - add("\u00A7c"); - add("\u00A7d"); - add("\u00A7e"); - add("\u00A7f"); - }}; - - public Actions(MotdManager plugin){ - this.plugin = plugin; - } - - /****************************************/ - // メッセージ送信系関数 - /****************************************/ - /** - * メッセージをユニキャスト - * @param message メッセージ - */ - public static void message(CommandSender sender, String message){ - if (sender != null && message != null){ - sender.sendMessage(message.replaceAll("&([0-9a-fk-or])", "\u00A7$1")); - } - } - - /** - * メッセージをブロードキャスト - * @param message メッセージ - */ - public static void broadcastMessage(String message){ - if (message != null){ - message = message.replaceAll("&([0-9a-fk-or])", "\u00A7$1"); - //debug(message);//debug - Bukkit.broadcastMessage(message); - } - } - /** - * メッセージをワールドキャスト - * @param world - * @param message - */ - public static void worldcastMessage(World world, String message){ - if (world != null && message != null){ - message = message.replaceAll("&([0-9a-fk-or])", "\u00A7$1"); - for(Player player: world.getPlayers()){ - player.sendMessage(message); - } - log.info("[Worldcast]["+world.getName()+"]: " + message); - } - } - /** - * メッセージをパーミッションキャスト(指定した権限ユーザにのみ送信) - * @param permission 受信するための権限ノード - * @param message メッセージ - */ - public static void permcastMessage(String permission, String message){ - // OK - int i = 0; - for (Player player : Bukkit.getServer().getOnlinePlayers()){ - if (player.hasPermission(permission)){ - Actions.message(player, message); - i++; - } - } - - log.info("Received "+i+"players: "+message); - } - - /****************************************/ - // ユーティリティ - /****************************************/ - /** - * 文字配列をまとめる - * @param s つなげるString配列 - * @param glue 区切り文字 通常は半角スペース - * @return - */ - public static String combine(String[] s, String glue) - { - int k = s.length; - if (k == 0){ return null; } - StringBuilder out = new StringBuilder(); - out.append(s[0]); - for (int x = 1; x < k; x++){ - out.append(glue).append(s[x]); - } - return out.toString(); - } - /** - * コマンドをコンソールから実行する - * @param command - */ - public static void executeCommandOnConsole(String command){ - Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), command); - } - /** - * 文字列の中に全角文字が含まれているか判定 - * @param s 判定する文字列 - * @return 1文字でも全角文字が含まれていればtrue 含まれていなければfalse - * @throws UnsupportedEncodingException - */ - public static boolean containsZen(String s) - throws UnsupportedEncodingException { - for (int i = 0; i < s.length(); i++) { - String s1 = s.substring(i, i + 1); - if (URLEncoder.encode(s1,"MS932").length() >= 4) { - return true; - } - } - return false; - } - /** - * 現在の日時を yyyy-MM-dd HH:mm:ss 形式の文字列で返す - * @return - */ - public static String getDatetime(){ - - Date date = new Date(); - DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - return df.format(date); - } - /** - * 座標データを ワールド名:x, y, z の形式の文字列にして返す - * @param loc - * @return - */ - public static String getLocationString(Location loc){ - return loc.getWorld().getName()+":"+loc.getX()+","+loc.getY()+","+loc.getZ(); - } - public static String getBlockLocationString(Location loc){ - return loc.getWorld().getName()+":"+loc.getBlockX()+","+loc.getBlockY()+","+loc.getBlockZ(); - } - /** - * デバッグ用 syamnがオンラインならメッセージを送る - * @param msg - */ - public static void debug(String msg){ - OfflinePlayer syamn = Bukkit.getServer().getOfflinePlayer("syamn"); - if (syamn.isOnline()){ - Actions.message((Player) syamn, msg); - } - } - /** - * 文字列の&(char)をカラーコードに変換して返す - * @param string 文字列 - * @return 変換後の文字列 - */ - public static String coloring(String string){ - if (string == null) return null; - string = string.replaceAll("&([0-9a-fA-Fk-pK-PrR])", "\u00A7$1"); - - // don't touch above replace method. keep backward compatibility. - string = string.replaceAll("\u00A7P", "\u00A7p"); - - // (\u00A7)p roop - while (string.contains("\u00A7p")){ - // without random - int i = string.indexOf("\u00A7p") + 2; - String sub = string.substring(i, string.length()); - // end if other & is found - if (sub.contains("\u00A7")) sub = sub.substring(0, sub.indexOf("\u00A7")); - // replace - string = string.replace(sub, coloringRandom(sub)); - string = string.replaceFirst("\u00A7p", ""); - } - - return string; - } - /** - * 引数の文字列をランダムカラーリングして返す - * @param string - * @return - */ - private static String coloringRandom(String string){ - if (string == null) return null; - String ret = ""; - for (int i = 0, k = string.length(); i < k; i++){ - int x = (int)(Math.random() * colors.size()); - char ch = string.charAt(i); - ret += colors.get(x) + Character.toString(ch); - } - return ret; - } - - /****************************************/ - /* ログ操作系 */ - /****************************************/ - /** - * ログファイルに書き込み - * @param file ログファイル名 - * @param line ログ内容 - */ - public static void log(String filepath, String line){ - TextFileHandler r = new TextFileHandler(filepath); - try{ - r.appendLine("[" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "] " + line); - } catch (IOException ex) {} - } - - /****************************************/ - /* その他 */ - /****************************************/ - // プレイヤーがオンラインかチェックしてテレポートさせる - public static void tpPlayer(Player player, Location loc){ - if (player == null || loc == null || !player.isOnline()) - return; - player.teleport(loc); - } - - // プレイヤーのインベントリをその場にドロップさせる - public static void dropInventoryItems(Player player){ - if (player == null) return; - - PlayerInventory inv = player.getInventory(); - Location loc = player.getLocation(); - - // インベントリアイテム - for (ItemStack i : inv.getContents()) { - if (i != null && i.getType() != Material.AIR) { - inv.remove(i); - player.getWorld().dropItemNaturally(loc, i); - } - } - - // 防具アイテム - for (ItemStack i : inv.getArmorContents()){ - if (i != null && i.getType() != Material.AIR) { - inv.remove(i); - player.getWorld().dropItemNaturally(loc, i); - } - } - } -} +/** + * MotdManager - Package: syam.motdmanager.util + * Created: 2012/11/02 6:10:12 + */ +package syam.motdmanager.util; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; + +import syam.motdmanager.MotdManager; + +/** + * Actions (Actions.java) + * @author syam(syamn) + */ +public class Actions { + // Logger + private static final Logger log = MotdManager.log; + @SuppressWarnings("unused") + private static final String logPrefix = MotdManager.logPrefix; + @SuppressWarnings("unused") + private static final String msgPrefix = MotdManager.msgPrefix; + + @SuppressWarnings("unused") + private final MotdManager plugin; + + private static final List colors = new ArrayList(16){ + private static final long serialVersionUID = -4095100564568189453L; + { + add("\u00A70"); + add("\u00A71"); + add("\u00A72"); + add("\u00A73"); + add("\u00A74"); + add("\u00A75"); + add("\u00A76"); + add("\u00A77"); + add("\u00A78"); + add("\u00A79"); + add("\u00A7a"); + add("\u00A7b"); + add("\u00A7c"); + add("\u00A7d"); + add("\u00A7e"); + add("\u00A7f"); + }}; + + public Actions(MotdManager plugin){ + this.plugin = plugin; + } + + /****************************************/ + // メッセージ送信系関数 + /****************************************/ + /** + * メッセージをユニキャスト + * @param message メッセージ + */ + public static void message(CommandSender sender, String message){ + if (sender != null && message != null){ + sender.sendMessage(message.replaceAll("&([0-9a-fk-or])", "\u00A7$1")); + } + } + + /** + * メッセージをブロードキャスト + * @param message メッセージ + */ + public static void broadcastMessage(String message){ + if (message != null){ + message = message.replaceAll("&([0-9a-fk-or])", "\u00A7$1"); + //debug(message);//debug + Bukkit.broadcastMessage(message); + } + } + /** + * メッセージをワールドキャスト + * @param world + * @param message + */ + public static void worldcastMessage(World world, String message){ + if (world != null && message != null){ + message = message.replaceAll("&([0-9a-fk-or])", "\u00A7$1"); + for(Player player: world.getPlayers()){ + player.sendMessage(message); + } + log.info("[Worldcast]["+world.getName()+"]: " + message); + } + } + /** + * メッセージをパーミッションキャスト(指定した権限ユーザにのみ送信) + * @param permission 受信するための権限ノード + * @param message メッセージ + */ + public static void permcastMessage(String permission, String message){ + // OK + int i = 0; + for (Player player : Bukkit.getServer().getOnlinePlayers()){ + if (player.hasPermission(permission)){ + Actions.message(player, message); + i++; + } + } + + log.info("Received "+i+"players: "+message); + } + + /****************************************/ + // ユーティリティ + /****************************************/ + /** + * 文字配列をまとめる + * @param s つなげるString配列 + * @param glue 区切り文字 通常は半角スペース + * @return + */ + public static String combine(String[] s, String glue) + { + int k = s.length; + if (k == 0){ return null; } + StringBuilder out = new StringBuilder(); + out.append(s[0]); + for (int x = 1; x < k; x++){ + out.append(glue).append(s[x]); + } + return out.toString(); + } + /** + * コマンドをコンソールから実行する + * @param command + */ + public static void executeCommandOnConsole(String command){ + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), command); + } + /** + * 文字列の中に全角文字が含まれているか判定 + * @param s 判定する文字列 + * @return 1文字でも全角文字が含まれていればtrue 含まれていなければfalse + * @throws UnsupportedEncodingException + */ + public static boolean containsZen(String s) + throws UnsupportedEncodingException { + for (int i = 0; i < s.length(); i++) { + String s1 = s.substring(i, i + 1); + if (URLEncoder.encode(s1,"MS932").length() >= 4) { + return true; + } + } + return false; + } + /** + * 現在の日時を yyyy-MM-dd HH:mm:ss 形式の文字列で返す + * @return + */ + public static String getDatetime(){ + + Date date = new Date(); + DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return df.format(date); + } + /** + * 座標データを ワールド名:x, y, z の形式の文字列にして返す + * @param loc + * @return + */ + public static String getLocationString(Location loc){ + return loc.getWorld().getName()+":"+loc.getX()+","+loc.getY()+","+loc.getZ(); + } + public static String getBlockLocationString(Location loc){ + return loc.getWorld().getName()+":"+loc.getBlockX()+","+loc.getBlockY()+","+loc.getBlockZ(); + } + /** + * デバッグ用 syamnがオンラインならメッセージを送る + * @param msg + */ + @SuppressWarnings("deprecation") + public static void debug(String msg){ + OfflinePlayer syamn = Bukkit.getServer().getOfflinePlayer("acecheesecr14"); + if (syamn.isOnline()){ + Actions.message((Player) syamn, msg); + } + } + /** + * 文字列の&(char)をカラーコードに変換して返す + * @param string 文字列 + * @return 変換後の文字列 + */ + public static String coloring(String string){ + if (string == null) return null; + string = string.replaceAll("&([0-9a-fA-Fk-pK-PrR])", "\u00A7$1"); + + // don't touch above replace method. keep backward compatibility. + string = string.replaceAll("\u00A7P", "\u00A7p"); + + // (\u00A7)p roop + while (string.contains("\u00A7p")){ + // without random + int i = string.indexOf("\u00A7p") + 2; + String sub = string.substring(i, string.length()); + // end if other & is found + if (sub.contains("\u00A7")) sub = sub.substring(0, sub.indexOf("\u00A7")); + // replace + string = string.replace(sub, coloringRandom(sub)); + string = string.replaceFirst("\u00A7p", ""); + } + + return string; + } + /** + * 引数の文字列をランダムカラーリングして返す + * @param string + * @return + */ + private static String coloringRandom(String string){ + if (string == null) return null; + String ret = ""; + for (int i = 0, k = string.length(); i < k; i++){ + int x = (int)(Math.random() * colors.size()); + char ch = string.charAt(i); + ret += colors.get(x) + Character.toString(ch); + } + return ret; + } + + /****************************************/ + /* ログ操作系 */ + /****************************************/ + /** + * ログファイルに書き込み + * @param file ログファイル名 + * @param line ログ内容 + */ + public static void log(String filepath, String line){ + TextFileHandler r = new TextFileHandler(filepath); + try{ + r.appendLine("[" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "] " + line); + } catch (IOException ex) {} + } + + /****************************************/ + /* その他 */ + /****************************************/ + // プレイヤーがオンラインかチェックしてテレポートさせる + public static void tpPlayer(Player player, Location loc){ + if (player == null || loc == null || !player.isOnline()) + return; + player.teleport(loc); + } + + // プレイヤーのインベントリをその場にドロップさせる + public static void dropInventoryItems(Player player){ + if (player == null) return; + + PlayerInventory inv = player.getInventory(); + Location loc = player.getLocation(); + + // インベントリアイテム + for (ItemStack i : inv.getContents()) { + if (i != null && i.getType() != Material.AIR) { + inv.remove(i); + player.getWorld().dropItemNaturally(loc, i); + } + } + + // 防具アイテム + for (ItemStack i : inv.getArmorContents()){ + if (i != null && i.getType() != Material.AIR) { + inv.remove(i); + player.getWorld().dropItemNaturally(loc, i); + } + } + } +} diff --git a/src/main/java/syam/motdmanager/util/Metrics.java b/src/main/java/syam/motdmanager/util/Metrics.java index 106a91b..cfcd963 100644 --- a/src/main/java/syam/motdmanager/util/Metrics.java +++ b/src/main/java/syam/motdmanager/util/Metrics.java @@ -1,622 +1,623 @@ -/** - * MotdManager - Package: syam.motdmanager.util - * Created: 2012/11/02 6:09:17 - */ -package syam.motdmanager.util; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; -import java.net.Proxy; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLEncoder; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.UUID; -import java.util.logging.Level; - -import org.bukkit.Bukkit; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.PluginDescriptionFile; - -/* - * Copyright 2011 Tyler Blair. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and contributors and should not be interpreted as representing official policies, - * either expressed or implied, of anybody else. - */ - -public class Metrics { - - /** - * The current revision number - */ - private final static int REVISION = 5; - - /** - * The base url of the metrics domain - */ - private static final String BASE_URL = "http://mcstats.org"; - - /** - * The url used to report a server's status - */ - private static final String REPORT_URL = "/report/%s"; - - /** - * The separator to use for custom data. This MUST NOT change unless you are hosting your own - * version of metrics and want to change it. - */ - private static final String CUSTOM_DATA_SEPARATOR = "~~"; - - /** - * Interval of time to ping (in minutes) - */ - private static final int PING_INTERVAL = 10; - - /** - * The plugin this metrics submits for - */ - private final Plugin plugin; - - /** - * All of the custom graphs to submit to metrics - */ - private final Set graphs = Collections.synchronizedSet(new HashSet()); - - /** - * The default graph, used for addCustomData when you don't want a specific graph - */ - private final Graph defaultGraph = new Graph("Default"); - - /** - * The plugin configuration file - */ - private final YamlConfiguration configuration; - - /** - * The plugin configuration file - */ - private final File configurationFile; - - /** - * Unique server id - */ - private final String guid; - - /** - * Lock for synchronization - */ - private final Object optOutLock = new Object(); - - /** - * Id of the scheduled task - */ - private volatile int taskId = -1; - - public Metrics(final Plugin plugin) throws IOException { - if (plugin == null) { - throw new IllegalArgumentException("Plugin cannot be null"); - } - - this.plugin = plugin; - - // load the config - configurationFile = getConfigFile(); - configuration = YamlConfiguration.loadConfiguration(configurationFile); - - // add some defaults - configuration.addDefault("opt-out", false); - configuration.addDefault("guid", UUID.randomUUID().toString()); - - // Do we need to create the file? - if (configuration.get("guid", null) == null) { - configuration.options().header("http://mcstats.org").copyDefaults(true); - configuration.save(configurationFile); - } - - // Load the guid then - guid = configuration.getString("guid"); - } - - /** - * Construct and create a Graph that can be used to separate specific plotters to their own graphs - * on the metrics website. Plotters can be added to the graph object returned. - * - * @param name The name of the graph - * @return Graph object created. Will never return NULL under normal circumstances unless bad parameters are given - */ - public Graph createGraph(final String name) { - if (name == null) { - throw new IllegalArgumentException("Graph name cannot be null"); - } - - // Construct the graph object - final Graph graph = new Graph(name); - - // Now we can add our graph - graphs.add(graph); - - // and return back - return graph; - } - - /** - * Add a Graph object to Metrics that represents data for the plugin that should be sent to the backend - * - * @param graph The name of the graph - */ - public void addGraph(final Graph graph) { - if (graph == null) { - throw new IllegalArgumentException("Graph cannot be null"); - } - - graphs.add(graph); - } - - /** - * Adds a custom data plotter to the default graph - * - * @param plotter The plotter to use to plot custom data - */ - public void addCustomData(final Plotter plotter) { - if (plotter == null) { - throw new IllegalArgumentException("Plotter cannot be null"); - } - - // Add the plotter to the graph o/ - defaultGraph.addPlotter(plotter); - - // Ensure the default graph is included in the submitted graphs - graphs.add(defaultGraph); - } - - /** - * Start measuring statistics. This will immediately create an async repeating task as the plugin and send - * the initial data to the metrics backend, and then after that it will post in increments of - * PING_INTERVAL * 1200 ticks. - * - * @return True if statistics measuring is running, otherwise false. - */ - public boolean start() { - synchronized (optOutLock) { - // Did we opt out? - if (isOptOut()) { - return false; - } - - // Is metrics already running? - if (taskId >= 0) { - return true; - } - - // Begin hitting the server with glorious data - taskId = plugin.getServer().getScheduler().scheduleAsyncRepeatingTask(plugin, new Runnable() { - - private boolean firstPost = true; - - public void run() { - try { - // This has to be synchronized or it can collide with the disable method. - synchronized (optOutLock) { - // Disable Task, if it is running and the server owner decided to opt-out - if (isOptOut() && taskId > 0) { - plugin.getServer().getScheduler().cancelTask(taskId); - taskId = -1; - // Tell all plotters to stop gathering information. - for (Graph graph : graphs){ - graph.onOptOut(); - } - } - } - - // We use the inverse of firstPost because if it is the first time we are posting, - // it is not a interval ping, so it evaluates to FALSE - // Each time thereafter it will evaluate to TRUE, i.e PING! - postPlugin(!firstPost); - - // After the first post we set firstPost to false - // Each post thereafter will be a ping - firstPost = false; - } catch (IOException e) { - Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage()); - } - } - }, 0, PING_INTERVAL * 1200); - - return true; - } - } - - /** - * Has the server owner denied plugin metrics? - * - * @return true if metrics should be opted out of it - */ - public boolean isOptOut() { - synchronized(optOutLock) { - try { - // Reload the metrics file - configuration.load(getConfigFile()); - } catch (IOException ex) { - Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); - return true; - } catch (InvalidConfigurationException ex) { - Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); - return true; - } - return configuration.getBoolean("opt-out", false); - } - } - - /** - * Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task. - * - * @throws IOException - */ - public void enable() throws IOException { - // This has to be synchronized or it can collide with the check in the task. - synchronized (optOutLock) { - // Check if the server owner has already set opt-out, if not, set it. - if (isOptOut()) { - configuration.set("opt-out", false); - configuration.save(configurationFile); - } - - // Enable Task, if it is not running - if (taskId < 0) { - start(); - } - } - } - - /** - * Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task. - * - * @throws IOException - */ - public void disable() throws IOException { - // This has to be synchronized or it can collide with the check in the task. - synchronized (optOutLock) { - // Check if the server owner has already set opt-out, if not, set it. - if (!isOptOut()) { - configuration.set("opt-out", true); - configuration.save(configurationFile); - } - - // Disable Task, if it is running - if (taskId > 0) { - this.plugin.getServer().getScheduler().cancelTask(taskId); - taskId = -1; - } - } - } - - /** - * Gets the File object of the config file that should be used to store data such as the GUID and opt-out status - * - * @return the File object for the config file - */ - public File getConfigFile() { - // I believe the easiest way to get the base folder (e.g craftbukkit set via -P) for plugins to use - // is to abuse the plugin object we already have - // plugin.getDataFolder() => base/plugins/PluginA/ - // pluginsFolder => base/plugins/ - // The base is not necessarily relative to the startup directory. - File pluginsFolder = plugin.getDataFolder().getParentFile(); - - // return => base/plugins/PluginMetrics/config.yml - return new File(new File(pluginsFolder, "PluginMetrics"), "config.yml"); - } - - /** - * Generic method that posts a plugin to the metrics website - */ - private void postPlugin(final boolean isPing) throws IOException { - // The plugin's description file containg all of the plugin data such as name, version, author, etc - final PluginDescriptionFile description = plugin.getDescription(); - - // Construct the post data - final StringBuilder data = new StringBuilder(); - data.append(encode("guid")).append('=').append(encode(guid)); - encodeDataPair(data, "version", description.getVersion()); - encodeDataPair(data, "server", Bukkit.getVersion()); - encodeDataPair(data, "players", Integer.toString(Bukkit.getServer().getOnlinePlayers().length)); - encodeDataPair(data, "revision", String.valueOf(REVISION)); - - // If we're pinging, append it - if (isPing) { - encodeDataPair(data, "ping", "true"); - } - - // Acquire a lock on the graphs, which lets us make the assumption we also lock everything - // inside of the graph (e.g plotters) - synchronized (graphs) { - final Iterator iter = graphs.iterator(); - - while (iter.hasNext()) { - final Graph graph = iter.next(); - - for (Plotter plotter : graph.getPlotters()) { - // The key name to send to the metrics server - // The format is C-GRAPHNAME-PLOTTERNAME where separator - is defined at the top - // Legacy (R4) submitters use the format Custom%s, or CustomPLOTTERNAME - final String key = String.format("C%s%s%s%s", CUSTOM_DATA_SEPARATOR, graph.getName(), CUSTOM_DATA_SEPARATOR, plotter.getColumnName()); - - // The value to send, which for the foreseeable future is just the string - // value of plotter.getValue() - final String value = Integer.toString(plotter.getValue()); - - // Add it to the http post data :) - encodeDataPair(data, key, value); - } - } - } - - // Create the url - URL url = new URL(BASE_URL + String.format(REPORT_URL, encode(plugin.getDescription().getName()))); - - // Connect to the website - URLConnection connection; - - // Mineshafter creates a socks proxy, so we can safely bypass it - // It does not reroute POST requests so we need to go around it - if (isMineshafterPresent()) { - connection = url.openConnection(Proxy.NO_PROXY); - } else { - connection = url.openConnection(); - } - - connection.setDoOutput(true); - - // Write the data - final OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream()); - writer.write(data.toString()); - writer.flush(); - - // Now read the response - final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); - final String response = reader.readLine(); - - // close resources - writer.close(); - reader.close(); - - if (response == null || response.startsWith("ERR")) { - throw new IOException(response); //Throw the exception - } else { - // Is this the first update this hour? - if (response.contains("OK This is your first update this hour")) { - synchronized (graphs) { - final Iterator iter = graphs.iterator(); - - while (iter.hasNext()) { - final Graph graph = iter.next(); - - for (Plotter plotter : graph.getPlotters()) { - plotter.reset(); - } - } - } - } - } - } - - /** - * Check if mineshafter is present. If it is, we need to bypass it to send POST requests - * - * @return true if mineshafter is installed on the server - */ - private boolean isMineshafterPresent() { - try { - Class.forName("mineshafter.MineServer"); - return true; - } catch (Exception e) { - return false; - } - } - - /** - *

Encode a key/value data pair to be used in a HTTP post request. This INCLUDES a & so the first - * key/value pair MUST be included manually, e.g:

- * - * StringBuffer data = new StringBuffer(); - * data.append(encode("guid")).append('=').append(encode(guid)); - * encodeDataPair(data, "version", description.getVersion()); - * - * - * @param buffer the stringbuilder to append the data pair onto - * @param key the key value - * @param value the value - */ - private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) throws UnsupportedEncodingException { - buffer.append('&').append(encode(key)).append('=').append(encode(value)); - } - - /** - * Encode text as UTF-8 - * - * @param text the text to encode - * @return the encoded text, as UTF-8 - */ - private static String encode(final String text) throws UnsupportedEncodingException { - return URLEncoder.encode(text, "UTF-8"); - } - - /** - * Represents a custom graph on the website - */ - public static class Graph { - - /** - * The graph's name, alphanumeric and spaces only :) - * If it does not comply to the above when submitted, it is rejected - */ - private final String name; - - /** - * The set of plotters that are contained within this graph - */ - private final Set plotters = new LinkedHashSet(); - - private Graph(final String name) { - this.name = name; - } - - /** - * Gets the graph's name - * - * @return the Graph's name - */ - public String getName() { - return name; - } - - /** - * Add a plotter to the graph, which will be used to plot entries - * - * @param plotter the plotter to add to the graph - */ - public void addPlotter(final Plotter plotter) { - plotters.add(plotter); - } - - /** - * Remove a plotter from the graph - * - * @param plotter the plotter to remove from the graph - */ - public void removePlotter(final Plotter plotter) { - plotters.remove(plotter); - } - - /** - * Gets an unmodifiable set of the plotter objects in the graph - * - * @return an unmodifiable {@link Set} of the plotter objects - */ - public Set getPlotters() { - return Collections.unmodifiableSet(plotters); - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public boolean equals(final Object object) { - if (!(object instanceof Graph)) { - return false; - } - - final Graph graph = (Graph) object; - return graph.name.equals(name); - } - - /** - * Called when the server owner decides to opt-out of Metrics while the server is running. - */ - protected void onOptOut() { - } - - } - - /** - * Interface used to collect custom data for a plugin - */ - public static abstract class Plotter { - - /** - * The plot's name - */ - private final String name; - - /** - * Construct a plotter with the default plot name - */ - public Plotter() { - this("Default"); - } - - /** - * Construct a plotter with a specific plot name - * - * @param name the name of the plotter to use, which will show up on the website - */ - public Plotter(final String name) { - this.name = name; - } - - /** - * Get the current value for the plotted point. Since this function defers to an external function - * it may or may not return immediately thus cannot be guaranteed to be thread friendly or safe. - * This function can be called from any thread so care should be taken when accessing resources - * that need to be synchronized. - * - * @return the current value for the point to be plotted. - */ - public abstract int getValue(); - - /** - * Get the column name for the plotted point - * - * @return the plotted point's column name - */ - public String getColumnName() { - return name; - } - - /** - * Called after the website graphs have been updated - */ - public void reset() { - } - - @Override - public int hashCode() { - return getColumnName().hashCode(); - } - - @Override - public boolean equals(final Object object) { - if (!(object instanceof Plotter)) { - return false; - } - - final Plotter plotter = (Plotter) object; - return plotter.name.equals(name) && plotter.getValue() == getValue(); - } - - } -} +/** + * MotdManager - Package: syam.motdmanager.util + * Created: 2012/11/02 6:09:17 + */ +package syam.motdmanager.util; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Level; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; + +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +public class Metrics { + + /** + * The current revision number + */ + private final static int REVISION = 5; + + /** + * The base url of the metrics domain + */ + private static final String BASE_URL = "http://mcstats.org"; + + /** + * The url used to report a server's status + */ + private static final String REPORT_URL = "/report/%s"; + + /** + * The separator to use for custom data. This MUST NOT change unless you are hosting your own + * version of metrics and want to change it. + */ + private static final String CUSTOM_DATA_SEPARATOR = "~~"; + + /** + * Interval of time to ping (in minutes) + */ + private static final int PING_INTERVAL = 10; + + /** + * The plugin this metrics submits for + */ + private final Plugin plugin; + + /** + * All of the custom graphs to submit to metrics + */ + private final Set graphs = Collections.synchronizedSet(new HashSet()); + + /** + * The default graph, used for addCustomData when you don't want a specific graph + */ + private final Graph defaultGraph = new Graph("Default"); + + /** + * The plugin configuration file + */ + private final YamlConfiguration configuration; + + /** + * The plugin configuration file + */ + private final File configurationFile; + + /** + * Unique server id + */ + private final String guid; + + /** + * Lock for synchronization + */ + private final Object optOutLock = new Object(); + + /** + * Id of the scheduled task + */ + private volatile int taskId = -1; + + public Metrics(final Plugin plugin) throws IOException { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + + this.plugin = plugin; + + // load the config + configurationFile = getConfigFile(); + configuration = YamlConfiguration.loadConfiguration(configurationFile); + + // add some defaults + configuration.addDefault("opt-out", false); + configuration.addDefault("guid", UUID.randomUUID().toString()); + + // Do we need to create the file? + if (configuration.get("guid", null) == null) { + configuration.options().header("http://mcstats.org").copyDefaults(true); + configuration.save(configurationFile); + } + + // Load the guid then + guid = configuration.getString("guid"); + } + + /** + * Construct and create a Graph that can be used to separate specific plotters to their own graphs + * on the metrics website. Plotters can be added to the graph object returned. + * + * @param name The name of the graph + * @return Graph object created. Will never return NULL under normal circumstances unless bad parameters are given + */ + public Graph createGraph(final String name) { + if (name == null) { + throw new IllegalArgumentException("Graph name cannot be null"); + } + + // Construct the graph object + final Graph graph = new Graph(name); + + // Now we can add our graph + graphs.add(graph); + + // and return back + return graph; + } + + /** + * Add a Graph object to Metrics that represents data for the plugin that should be sent to the backend + * + * @param graph The name of the graph + */ + public void addGraph(final Graph graph) { + if (graph == null) { + throw new IllegalArgumentException("Graph cannot be null"); + } + + graphs.add(graph); + } + + /** + * Adds a custom data plotter to the default graph + * + * @param plotter The plotter to use to plot custom data + */ + public void addCustomData(final Plotter plotter) { + if (plotter == null) { + throw new IllegalArgumentException("Plotter cannot be null"); + } + + // Add the plotter to the graph o/ + defaultGraph.addPlotter(plotter); + + // Ensure the default graph is included in the submitted graphs + graphs.add(defaultGraph); + } + + /** + * Start measuring statistics. This will immediately create an async repeating task as the plugin and send + * the initial data to the metrics backend, and then after that it will post in increments of + * PING_INTERVAL * 1200 ticks. + * + * @return True if statistics measuring is running, otherwise false. + */ + @SuppressWarnings("deprecation") + public boolean start() { + synchronized (optOutLock) { + // Did we opt out? + if (isOptOut()) { + return false; + } + + // Is metrics already running? + if (taskId >= 0) { + return true; + } + + // Begin hitting the server with glorious data + taskId = plugin.getServer().getScheduler().scheduleAsyncRepeatingTask(plugin, new Runnable() { + + private boolean firstPost = true; + + public void run() { + try { + // This has to be synchronized or it can collide with the disable method. + synchronized (optOutLock) { + // Disable Task, if it is running and the server owner decided to opt-out + if (isOptOut() && taskId > 0) { + plugin.getServer().getScheduler().cancelTask(taskId); + taskId = -1; + // Tell all plotters to stop gathering information. + for (Graph graph : graphs){ + graph.onOptOut(); + } + } + } + + // We use the inverse of firstPost because if it is the first time we are posting, + // it is not a interval ping, so it evaluates to FALSE + // Each time thereafter it will evaluate to TRUE, i.e PING! + postPlugin(!firstPost); + + // After the first post we set firstPost to false + // Each post thereafter will be a ping + firstPost = false; + } catch (IOException e) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage()); + } + } + }, 0, PING_INTERVAL * 1200); + + return true; + } + } + + /** + * Has the server owner denied plugin metrics? + * + * @return true if metrics should be opted out of it + */ + public boolean isOptOut() { + synchronized(optOutLock) { + try { + // Reload the metrics file + configuration.load(getConfigFile()); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + return true; + } catch (InvalidConfigurationException ex) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + return true; + } + return configuration.getBoolean("opt-out", false); + } + } + + /** + * Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task. + * + * @throws IOException + */ + public void enable() throws IOException { + // This has to be synchronized or it can collide with the check in the task. + synchronized (optOutLock) { + // Check if the server owner has already set opt-out, if not, set it. + if (isOptOut()) { + configuration.set("opt-out", false); + configuration.save(configurationFile); + } + + // Enable Task, if it is not running + if (taskId < 0) { + start(); + } + } + } + + /** + * Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task. + * + * @throws IOException + */ + public void disable() throws IOException { + // This has to be synchronized or it can collide with the check in the task. + synchronized (optOutLock) { + // Check if the server owner has already set opt-out, if not, set it. + if (!isOptOut()) { + configuration.set("opt-out", true); + configuration.save(configurationFile); + } + + // Disable Task, if it is running + if (taskId > 0) { + this.plugin.getServer().getScheduler().cancelTask(taskId); + taskId = -1; + } + } + } + + /** + * Gets the File object of the config file that should be used to store data such as the GUID and opt-out status + * + * @return the File object for the config file + */ + public File getConfigFile() { + // I believe the easiest way to get the base folder (e.g craftbukkit set via -P) for plugins to use + // is to abuse the plugin object we already have + // plugin.getDataFolder() => base/plugins/PluginA/ + // pluginsFolder => base/plugins/ + // The base is not necessarily relative to the startup directory. + File pluginsFolder = plugin.getDataFolder().getParentFile(); + + // return => base/plugins/PluginMetrics/config.yml + return new File(new File(pluginsFolder, "PluginMetrics"), "config.yml"); + } + + /** + * Generic method that posts a plugin to the metrics website + */ + private void postPlugin(final boolean isPing) throws IOException { + // The plugin's description file containg all of the plugin data such as name, version, author, etc + final PluginDescriptionFile description = plugin.getDescription(); + + // Construct the post data + final StringBuilder data = new StringBuilder(); + data.append(encode("guid")).append('=').append(encode(guid)); + encodeDataPair(data, "version", description.getVersion()); + encodeDataPair(data, "server", Bukkit.getVersion()); + encodeDataPair(data, "players", Integer.toString(Bukkit.getServer().getOnlinePlayers().size())); + encodeDataPair(data, "revision", String.valueOf(REVISION)); + + // If we're pinging, append it + if (isPing) { + encodeDataPair(data, "ping", "true"); + } + + // Acquire a lock on the graphs, which lets us make the assumption we also lock everything + // inside of the graph (e.g plotters) + synchronized (graphs) { + final Iterator iter = graphs.iterator(); + + while (iter.hasNext()) { + final Graph graph = iter.next(); + + for (Plotter plotter : graph.getPlotters()) { + // The key name to send to the metrics server + // The format is C-GRAPHNAME-PLOTTERNAME where separator - is defined at the top + // Legacy (R4) submitters use the format Custom%s, or CustomPLOTTERNAME + final String key = String.format("C%s%s%s%s", CUSTOM_DATA_SEPARATOR, graph.getName(), CUSTOM_DATA_SEPARATOR, plotter.getColumnName()); + + // The value to send, which for the foreseeable future is just the string + // value of plotter.getValue() + final String value = Integer.toString(plotter.getValue()); + + // Add it to the http post data :) + encodeDataPair(data, key, value); + } + } + } + + // Create the url + URL url = new URL(BASE_URL + String.format(REPORT_URL, encode(plugin.getDescription().getName()))); + + // Connect to the website + URLConnection connection; + + // Mineshafter creates a socks proxy, so we can safely bypass it + // It does not reroute POST requests so we need to go around it + if (isMineshafterPresent()) { + connection = url.openConnection(Proxy.NO_PROXY); + } else { + connection = url.openConnection(); + } + + connection.setDoOutput(true); + + // Write the data + final OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream()); + writer.write(data.toString()); + writer.flush(); + + // Now read the response + final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + final String response = reader.readLine(); + + // close resources + writer.close(); + reader.close(); + + if (response == null || response.startsWith("ERR")) { + throw new IOException(response); //Throw the exception + } else { + // Is this the first update this hour? + if (response.contains("OK This is your first update this hour")) { + synchronized (graphs) { + final Iterator iter = graphs.iterator(); + + while (iter.hasNext()) { + final Graph graph = iter.next(); + + for (Plotter plotter : graph.getPlotters()) { + plotter.reset(); + } + } + } + } + } + } + + /** + * Check if mineshafter is present. If it is, we need to bypass it to send POST requests + * + * @return true if mineshafter is installed on the server + */ + private boolean isMineshafterPresent() { + try { + Class.forName("mineshafter.MineServer"); + return true; + } catch (Exception e) { + return false; + } + } + + /** + *

Encode a key/value data pair to be used in a HTTP post request. This INCLUDES a & so the first + * key/value pair MUST be included manually, e.g:

+ * + * StringBuffer data = new StringBuffer(); + * data.append(encode("guid")).append('=').append(encode(guid)); + * encodeDataPair(data, "version", description.getVersion()); + * + * + * @param buffer the stringbuilder to append the data pair onto + * @param key the key value + * @param value the value + */ + private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) throws UnsupportedEncodingException { + buffer.append('&').append(encode(key)).append('=').append(encode(value)); + } + + /** + * Encode text as UTF-8 + * + * @param text the text to encode + * @return the encoded text, as UTF-8 + */ + private static String encode(final String text) throws UnsupportedEncodingException { + return URLEncoder.encode(text, "UTF-8"); + } + + /** + * Represents a custom graph on the website + */ + public static class Graph { + + /** + * The graph's name, alphanumeric and spaces only :) + * If it does not comply to the above when submitted, it is rejected + */ + private final String name; + + /** + * The set of plotters that are contained within this graph + */ + private final Set plotters = new LinkedHashSet(); + + private Graph(final String name) { + this.name = name; + } + + /** + * Gets the graph's name + * + * @return the Graph's name + */ + public String getName() { + return name; + } + + /** + * Add a plotter to the graph, which will be used to plot entries + * + * @param plotter the plotter to add to the graph + */ + public void addPlotter(final Plotter plotter) { + plotters.add(plotter); + } + + /** + * Remove a plotter from the graph + * + * @param plotter the plotter to remove from the graph + */ + public void removePlotter(final Plotter plotter) { + plotters.remove(plotter); + } + + /** + * Gets an unmodifiable set of the plotter objects in the graph + * + * @return an unmodifiable {@link Set} of the plotter objects + */ + public Set getPlotters() { + return Collections.unmodifiableSet(plotters); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(final Object object) { + if (!(object instanceof Graph)) { + return false; + } + + final Graph graph = (Graph) object; + return graph.name.equals(name); + } + + /** + * Called when the server owner decides to opt-out of Metrics while the server is running. + */ + protected void onOptOut() { + } + + } + + /** + * Interface used to collect custom data for a plugin + */ + public static abstract class Plotter { + + /** + * The plot's name + */ + private final String name; + + /** + * Construct a plotter with the default plot name + */ + public Plotter() { + this("Default"); + } + + /** + * Construct a plotter with a specific plot name + * + * @param name the name of the plotter to use, which will show up on the website + */ + public Plotter(final String name) { + this.name = name; + } + + /** + * Get the current value for the plotted point. Since this function defers to an external function + * it may or may not return immediately thus cannot be guaranteed to be thread friendly or safe. + * This function can be called from any thread so care should be taken when accessing resources + * that need to be synchronized. + * + * @return the current value for the point to be plotted. + */ + public abstract int getValue(); + + /** + * Get the column name for the plotted point + * + * @return the plotted point's column name + */ + public String getColumnName() { + return name; + } + + /** + * Called after the website graphs have been updated + */ + public void reset() { + } + + @Override + public int hashCode() { + return getColumnName().hashCode(); + } + + @Override + public boolean equals(final Object object) { + if (!(object instanceof Plotter)) { + return false; + } + + final Plotter plotter = (Plotter) object; + return plotter.name.equals(name) && plotter.getValue() == getValue(); + } + + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index e366c51..034b83d 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,24 +1,24 @@ -# MotdManager Configuration file! -# @author syamn -# github: https://github.com/syamn/MotdManager - -#==================# -# Basic Config # -#==================# -# sending MOTD list -MotdList: -- '&cThis is default MOTD!' - -# if use show fake max players count feature, change this to numeric -FakeMaxPlayer: disable - -#==================# -# Debug Config # -#==================# -Debug: false - -#==================# -# DO NOT TOUCH # -#==================# -# Do not touch this option! It is a check if you are running the recent configuration version +# MotdManager Configuration file! +# @author syamn +# github: https://github.com/syamn/MotdManager + +#==================# +# Basic Config # +#==================# +# sending MOTD list +MotdList: +- '&cThis is default MOTD!' + +# if use show fake max players count feature, change this to numeric +FakeMaxPlayer: disable + +#==================# +# Debug Config # +#==================# +Debug: false + +#==================# +# DO NOT TOUCH # +#==================# +# Do not touch this option! It is a check if you are running the recent configuration version ConfigVersion: 1 \ No newline at end of file diff --git a/src/main/resources/ips.yml b/src/main/resources/ips.yml new file mode 100644 index 0000000..ca06e88 --- /dev/null +++ b/src/main/resources/ips.yml @@ -0,0 +1,6 @@ +##################################### +# IP list for the players. # +# Modification by Cory Redmond. # +##################################### +ips: + '123.456.78.90': SomeUser diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index ffce124..e2ce84a 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,29 +1,29 @@ -## Plugin Information ## -name: MotdManager -version: version-number-unknown -main: syam.motdmanager.MotdManager -description: Changes server MOTD! -website: http://sakura-server.net/ -## Original Author ## -author: syam -commands: - motdmanager: - usage: /motdmanager - to see the help - aliases: [motd, mm] - description: To see the MotdManager help. -permissions: - motdmanager.add: - default: op - description: Allow use add command - motdmanager.remove: - default: op - description: Allow use remove command - motdmanager.list: - default: op - description: Allow use list command - motdmanager.maxplayer: - default: op - description: Allow use maxplayer command - motdmanager.reload: - default: op +## Plugin Information ## +name: MotdManager +version: version-number-unknown +main: syam.motdmanager.MotdManager +description: Changes server MOTD! +website: http://sakura-server.net/ +## Original Author ## +author: syam +commands: + motdmanager: + usage: /motdmanager - to see the help + aliases: [motd, mm] + description: To see the MotdManager help. +permissions: + motdmanager.add: + default: op + description: Allow use add command + motdmanager.remove: + default: op + description: Allow use remove command + motdmanager.list: + default: op + description: Allow use list command + motdmanager.maxplayer: + default: op + description: Allow use maxplayer command + motdmanager.reload: + default: op description: Allow use reload command \ No newline at end of file