From 2619773538c92f0b87f2cccf53d079e44d53e9b9 Mon Sep 17 00:00:00 2001 From: yuko fuyutsuki Date: Tue, 7 Nov 2017 22:15:51 +0900 Subject: [PATCH] Data restored --- .editorconfig | 8 + .gitignore | 2 + ISSUE_TEMPLATE.md | 34 ++ LICENSE | 21 + README.md | 123 +++++- assets/Texter.png | Bin 0 -> 5954 bytes plugin.yml | 35 ++ resources/config.yml | 38 ++ resources/crfts.json | 26 ++ resources/fts.json | 2 + src/Texter/EventListener.php | 83 ++++ src/Texter/Main.php | 330 +++++++++++++++ src/Texter/TexterApi.php | 319 ++++++++++++++ src/Texter/commands/TxtAdmCommand.php | 113 +++++ src/Texter/commands/TxtAdmCommandOld.php | 113 +++++ src/Texter/commands/TxtCommand.php | 216 ++++++++++ src/Texter/commands/TxtCommandOld.php | 217 ++++++++++ src/Texter/language/Lang.php | 94 +++++ src/Texter/language/eng.json | 57 +++ src/Texter/language/jpn.json | 56 +++ src/Texter/task/CheckUpdateTask.php | 38 ++ src/Texter/task/WorldGetTask.php | 43 ++ src/Texter/task/WorldGetTaskOld.php | 43 ++ src/Texter/text/CantRemoveFloatingText.php | 9 + src/Texter/text/FloatingText.php | 308 ++++++++++++++ src/Texter/text/Text.php | 454 ++++++++++++++++++++ src/Texter/utils/TunedConfig.php | 462 +++++++++++++++++++++ 27 files changed, 3242 insertions(+), 2 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 ISSUE_TEMPLATE.md create mode 100644 LICENSE create mode 100644 assets/Texter.png create mode 100644 plugin.yml create mode 100644 resources/config.yml create mode 100644 resources/crfts.json create mode 100644 resources/fts.json create mode 100644 src/Texter/EventListener.php create mode 100644 src/Texter/Main.php create mode 100644 src/Texter/TexterApi.php create mode 100644 src/Texter/commands/TxtAdmCommand.php create mode 100644 src/Texter/commands/TxtAdmCommandOld.php create mode 100644 src/Texter/commands/TxtCommand.php create mode 100644 src/Texter/commands/TxtCommandOld.php create mode 100644 src/Texter/language/Lang.php create mode 100644 src/Texter/language/eng.json create mode 100644 src/Texter/language/jpn.json create mode 100644 src/Texter/task/CheckUpdateTask.php create mode 100644 src/Texter/task/WorldGetTask.php create mode 100644 src/Texter/task/WorldGetTaskOld.php create mode 100644 src/Texter/text/CantRemoveFloatingText.php create mode 100644 src/Texter/text/FloatingText.php create mode 100644 src/Texter/text/Text.php create mode 100644 src/Texter/utils/TunedConfig.php diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..aaf2809 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +# http://editorconfig.org/ +root = yes + +[*] +indent_size = 2 +indent_style = space +end_of_line = lf +charset = utf-8 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8fb9836 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/config.yml +/*.json diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..acb1f3d --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,34 @@ +### 必須項目 (Required items) + + +* ServerOS: + + +* PHP version: + + +* Minecraft version: + + +* Texter version: + + +* issue(s): diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a8c0e68 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 yuko fuyutsuki + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 29a7ef8..b32d784 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,121 @@ -# Texter -This is a PocketMine-MP plugin that can add, remove and update FloatingTextParticle. +[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/fuyutsuki/Texter/blob/master/LICENSE) +[![Github All Releases](https://img.shields.io/github/downloads/fuyutsuki/Texter/total.svg)](https://github.com/fuyutsuki/Texter/releases) + +## Overview +Select Language: [English](#eng), [日本語](#jpn) + +*** + +## English + +## Notice +This plugin is **fork** of *yuko fuyutsuki`s* plugin developed by *p-NLab*. +It is distributed under MIT license. + + + +## Texter +Texter is plugin that displays and deletes FloatingTextPerticle supported to multi-world. +Latest: ver **2.2.4** _Papilio dehaanii(カラスアゲハ)_ + +### Supporting +- [x] Multi-language (eng, jpn) +- [x] Multi-world display +- [x] Minecraft:PE v1.1.x +- [x] Minecraft(Bedrock) v1.2.0.x + +### Commands +#### General command +| \ |command|argument|alias| +|:--:|:--:|:--:|:--:| +|Add text|`/txt add`|` [text]`|`/txt a`| +|Remove text|`/txt remove`|`<ID>`|`/txt r`| +|Update text|`/txt update`|`<title, text> <ID> <message>`|`/txt u`| +|Help|`/txt or /txt help`|`none`|`/txt ?`| + +#### Management command +| \ |command|argument|alias| +|:--:|:--:|:--:|:--:| +|Remove all|`/txtadm allremove`|`none`|`/tadm ar`| +|Remove texts/user|`/txtadm userremove`|`<username>`|`/tadm ur`| +|Info|`/txtadm info`|`none`|`/tadm i`| +|Help|`/txtadm or /txtadm help`|`none`|`/tadm ?`| + +**Please use `#` for line breaks.** + +### json notation +```json +anythingUniqueValue: { + "WORLD" : "worldName", + "Xvec" : 128, + "Yvec" : 90, + "Zvec" : 128, + "TITLE" : "title", + "TEXT" : "1st Line#2nd Line..." +} +``` +It is output as follows. +<img src="https://cloud.githubusercontent.com/assets/16377174/24609877/642d64f6-18b7-11e7-9b38-488e0ada3f1e.JPG" width="320px"> + +*** +<a name="jpn"></a> +## 日本語 + +## お知らせ +このプラグインは *p-NLab* が開発する、*yuko fuyutsuki* さんのプラグインの**フォーク**です。 +MITライセンスの下で配布されています。 + +<!-- +## !! 注意 !! +このブランチは開発中です。多くのバグを含む可能性があります。 +--> + +## Texter +TexterはFloatingTextPerticleを複数ワールドに渡り表示、削除ができるプラグインです。 +最新バージョン: **2.2.4** _Papilio dehaanii(カラスアゲハ)_ + +### 対応状況 +- [x] 複数言語 (eng, jpn) +- [x] 複数ワールドの表示 +- [x] Minecraft:PE v1.1.x +- [x] Minecraft(Bedrockエンジン) v1.2.x + +### コマンド +#### 一般用コマンド +| \ |コマンド|引数|エイリアス| +|:--:|:--:|:--:|:--:| +|浮き文字追加|`/txt add`|`<タイトル> [テキスト]`|`/txt a`| +|浮き文字削除|`/txt remove`|`<ID>`|`/txt r`| +|浮き文字更新|`/txt update`|`<タイトル, テキスト> <ID> <メッセージ>`|`/txt u`| +|ヘルプ|`/txt or /txt help`|`無し`|`/txt ?`| + +#### 管理用コマンド +| \ |コマンド|引数|エイリアス| +|:--:|:--:|:--:|:--:| +|浮き文字すべて削除|`/txtadm allremove`|`none`|`/tadm ar`| +|ユーザーの浮き文字を削除|`/txtadm userremove`|`<username>`|`/tadm ur`| +|浮き文字の各種情報を見る|`/txtadm info`|`none`|`/tadm i`| +|ヘルプ|`/txtadm or /txtadm help`|`none`|`/tadm ?`| + +**改行の際には `#` を使用してください。** + +### json記法 +```json +{ + "一意な文字列": { + "WORLD" : "world", + "Xvec" : 128, + "Yvec" : 90, + "Zvec" : 128, + "TITLE" : "title", + "TEXT" : "1st Line#2nd Line" + } +} +``` + +こう書くことで以下のように出力されます。 +<img src="https://cloud.githubusercontent.com/assets/16377174/24609877/642d64f6-18b7-11e7-9b38-488e0ada3f1e.JPG" width="320px"> diff --git a/assets/Texter.png b/assets/Texter.png new file mode 100644 index 0000000000000000000000000000000000000000..e37e83aae52d8cf4029ce74948e21ac48d4860bb GIT binary patch literal 5954 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 literal 0 HcmV?d00001 diff --git a/plugin.yml b/plugin.yml new file mode 100644 index 0000000..5ed2d15 --- /dev/null +++ b/plugin.yml @@ -0,0 +1,35 @@ +name: Texter +main: Texter\Main +version: 2.2.4 +api: + - 3.0.0 + - 3.0.0-ALPHA8 + - 3.0.0-ALPHA9 #pmmp/PocketMine-MP + +author: yuko_fuyutsuki +authors: +# SpecialThanks(敬称略) +- mfmfnek0 +- onebone +- ogiwara +- Shootsta_ERU0531 +- Toganon +- ShadowArt +- Marron0421 +website: https://twitter.com/_riu_m + +permissions: + texter.*: + default: op + description: "permission that allows player to use texter" + children: + texter.command.*: + default: op + description: "permission that allows player to use texter" + children: + texter.command.txt: + default: true # もしopのみ実行させたい場合は op と記述してください + description: "permission that allows player to use /txt" + texter.command.txtadm: + default: op + description: "permission that allows player to use /txtadm" diff --git a/resources/config.yml b/resources/config.yml new file mode 100644 index 0000000..7c1d121 --- /dev/null +++ b/resources/config.yml @@ -0,0 +1,38 @@ +configVersion: 22 + +# 日本語 ガイド +# eng guide + +# Texter 設定ファイル +# Texter config file + +# 言語を設定してください (eng or jpn) +# set language (eng or jpn) +language: jpn + +# タイムゾーンを設定してください +# set timezone +timezone: Asia/Tokyo + +# もし起動時にアップデートの通知が必要ならtrue, 必要ないならfalseにしてください +# When set to true, update notification is displayed at startup +checkUpdate: true + +# コマンドを使用不可にしたい場合はfalseにしてください +# If you want to disable the command, prease set it to false +canUseCommands: true + +# 文字数の制限をする場合は文字数を, 不要なら -1 にしてください +# To limit the number of characters, set it to that, if unnecessary, set it to -1. +limit: 50 + +# 改行数の制限をする場合は改行回数を、不要なら -1 にしてください +# To limit the number of line feeds, set it to that, if unnecessary, set it to -1. +feed: 3 + +# テキストを設置不可能なワールドを設定する場合はワールド名を指定してください +# Please specify the world name when setting the world where text can not be set up +world: + # example + # - world + # - world-2 diff --git a/resources/crfts.json b/resources/crfts.json new file mode 100644 index 0000000..2d29f6f --- /dev/null +++ b/resources/crfts.json @@ -0,0 +1,26 @@ +{ + "0":{ + "WORLD": "world", + "Xvec": 128, + "Yvec": 90, + "Zvec": 128, + "TITLE": "タイトル", + "TEXT": "テキスト(\nを入れると改行)" + }, + "1":{ + "WORLD": "world", + "Xvec": 128, + "Yvec": 90, + "Zvec": 130, + "TITLE": "タイトル", + "TEXT": "テキスト(\nを入れると改行)" + }, + "2":{ + "WORLD": "world", + "Xvec": 128, + "Yvec": 90, + "Zvec": 132, + "TITLE": "タイトル", + "TEXT": "テキスト(\nを入れると改行)" + } +} diff --git a/resources/fts.json b/resources/fts.json new file mode 100644 index 0000000..2c63c08 --- /dev/null +++ b/resources/fts.json @@ -0,0 +1,2 @@ +{ +} diff --git a/src/Texter/EventListener.php b/src/Texter/EventListener.php new file mode 100644 index 0000000..a4b8fe0 --- /dev/null +++ b/src/Texter/EventListener.php @@ -0,0 +1,83 @@ +<?php +namespace Texter; + +# Pocketmine +use pocketmine\Player; +use pocketmine\event\{ + Listener, + player\PlayerJoinEvent, + entity\EntityLevelChangeEvent}; + +# Texter +use Texter\task\{ + WorldGetTask, + WorldGetTaskOld}; +use Texter\text\{ + CantRemoveFloatingText as CRFT, + FloatingText as FT}; + +/** + * EventListener + */ +class EventListener implements Listener{ + + /** @var Main $main */ + private $main = null; + /** @var TexterApi $api */ + private $api = null; + + public function __construct(Main $main){ + $this->main = $main; + $this->api = $main->getApi(); + } + + public function onJoin(PlayerJoinEvent $e){ + $p = $e->getPlayer(); + $lev = $p->getLevel(); + $crfts = $this->api->getCrftsByLevel($lev); + if (!empty($crfts)) { + foreach ($crfts as $crft) { + $crft->sendToPlayer($p, CRFT::SEND_TYPE_ADD); + } + } + $fts = $this->api->getFtsByLevel($lev); + if (!empty($fts)) { + foreach ($fts as $ft) { + $ft->sendToPlayer($p, FT::SEND_TYPE_ADD); + } + } + } + + public function onLevelChange(EntityLevelChangeEvent $e){ + $p = $e->getEntity(); + if ($p instanceof Player) { + $lev = $p->getLevel(); + $crfts = $this->api->getCrftsByLevel($lev); + if (!empty($crfts)) { + foreach ($crfts as $crft) { + $crft->sendToPlayer($p, CRFT::SEND_TYPE_REMOVE); + } + } + $fts = $this->api->getFtsByLevel($lev); + if (!empty($fts)) { + foreach ($fts as $ft) { + $ft->sendToPlayer($p, FT::SEND_TYPE_REMOVE); + } + } + switch (strtolower($this->main->getServer()->getName())) { + case 'pocketmine-mp': + $task = new WorldGetTask($this->main, $p); + $this->main->getServer()->getScheduler()->scheduleDelayedTask($task, 20); + break; + + // NOTE: Confirmed + case 'genisyspro': + case 'leveryl': + default: + $task = new WorldGetTaskOld($this->main, $p); + $this->main->getServer()->getScheduler()->scheduleDelayedTask($task, 20); + break; + } + } + } +} diff --git a/src/Texter/Main.php b/src/Texter/Main.php new file mode 100644 index 0000000..9c02f07 --- /dev/null +++ b/src/Texter/Main.php @@ -0,0 +1,330 @@ +<?php + +/** + * ## English + * + * Texter, the display FloatingTextPerticle plugin for PocketMine-MP + * Copyright (c) 2017 yuko fuyutsuki < https://github.com/fuyutsuki > + * + * Released under the "MIT license". + * You should have received a copy of the MIT license + * along with this program. If not, + * < https://opensource.org/licenses/mit-license >. + * + * --------------------------------------------------------------------- + * ## 日本語 + * + * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです。 + * Copyright (c) 2017 yuko fuyutsuki < https://github.com/fuyutsuki > + * + * このソフトウェアは"MITライセンス"下で配布されています。 + * あなたはこのプログラムと共にMITライセンスのコピーを受け取ったはずです。 + * 受け取っていない場合、下記のURLからご覧ください。 + * < https://opensource.org/licenses/mit-license > + */ + +namespace Texter; + +# Pocketmine +use pocketmine\Player; +use pocketmine\Server; +use pocketmine\command\{ + Command, + CommandSender}; +use pocketmine\entity\Entity; +use pocketmine\event\{ + Listener, + entity\EntityLevelChangeEvent, + player\PlayerJoinEvent}; +use pocketmine\item\Item; +use pocketmine\level\{ + Level, + Position}; +use pocketmine\math\Vector3; +use pocketmine\plugin\PluginBase; +use pocketmine\utils\TextFormat as TF; + +# Texter +use Texter\EventListener; +use Texter\TexterApi; +use Texter\commands\{ + TxtCommand, + TxtCommandOld, + TxtAdmCommand, + TxtAdmCommandOld}; +use Texter\language\Lang; +use Texter\text\{ + CantRemoveFloatingText as CRFT, + FloatingText as FT}; +use Texter\task\{ + CheckUpdateTask, + WorldGetTask}; +use Texter\utils\TunedConfig as Config; + +define("DS", DIRECTORY_SEPARATOR); + +class Main extends PluginBase { + + const NAME = "Texter"; + const VERSION = "v2.2.4"; + const CODENAME = "Papilio dehaanii(カラスアゲハ)"; + + const FILE_CONFIG = "config.yml"; + const FILE_CRFTP = "crftps.json";// for old format + const FILE_CRFT = "crfts.json"; + const FILE_FTP = "ftps.json";// for old format + const FILE_FT = "fts.json"; + + const CONFIG_VERSION = 22; + + /** @var bool $devmode */ + public $devmode = false; + /** @var string $dir */ + public $dir = ""; + /** @var Config $config */ + private $config = null; + /** @var TexterApi $api */ + private $api = null; + /** @var Lang $language */ + private $language = null; + /** @var array $crfts */ + private $crfts = []; + /** @var array $fts */ + private $fts = []; + + /****************************************************************************/ + /* Public functions */ + + /** + * TexterApiを取得します + * @return TexterApi $this->api + */ + public function getApi(): TexterApi{ + return $this->api; + } + + /** + * 文字数制限のための文字数を取得します + * @return int + */ + public function getCharaLimit(): int{ + return (int)$this->config->get("limit"); + } + + /** + * 改行数制限のための改行数を設定します + * @return int + */ + public function getFeedLimit(): int{ + return (int)$this->config->get("feed"); + } + + /** + * ワールド制限のためのワールド名を配列で取得します + * @return array + */ + public function getWorldLimit(): array{ + $worlds = $this->config->get("world"); + if ($worlds !== false) { + return array_flip($worlds); + }else { + return []; + } + } + + /****************************************************************************/ + /* PMMP Api */ + + public function onLoad(){ + $this->loadFiles(); + $this->initApi(); + $this->registerCommands(); + $this->checkUpdate(); + $this->setTimezone(); + } + + public function onEnable(){ + $this->prepareTexts(); + $listener = new EventListener($this); + $this->getServer()->getPluginManager()->registerEvents($listener, $this); + $this->getLogger()->info(TF::GREEN.self::NAME." ".self::VERSION." - ".TF::BLUE."\"".self::CODENAME."\" ".TF::GREEN.$this->language->transrateString("on.enable")); + } + + /****************************************************************************/ + /* Private functions */ + + private function loadFiles(){ + $this->dir = $this->getDataFolder(); + // + if(!file_exists($this->dir)){ + mkdir($this->dir); + } + if(!file_exists($this->dir.self::FILE_CONFIG)){ + file_put_contents($this->dir.self::FILE_CONFIG, $this->getResource(self::FILE_CONFIG)); + } + // config.yml + $this->config = new Config($this->dir.self::FILE_CONFIG, Config::YAML); + // Lang + $lang = $this->config->get("language"); + if ($lang !== false) { + $this->language = new Lang($this, $lang); + $this->getLogger()->info(TF::GREEN.$this->language->transrateString("lang.registered", ["{lang}"], [$lang])); + }else { + $this->getLogger()->error("Invalid language settings. If you have any questions, please contact the issue."); + } + if(!file_exists($this->dir.self::FILE_CRFT)){ + if (!file_exists($this->dir.self::FILE_CRFTP)) { + file_put_contents($this->dir.self::FILE_CRFT, $this->getResource(self::FILE_CRFT)); + }else { + $tmpOld = new Config($this->dir.self::FILE_CRFTP, Config::JSON); + $tmpOldData = $tmpOld->getAll(); + file_put_contents($this->dir.self::FILE_CRFT, []); + $tmpNew = new Config($this->dir.self::FILE_CRFT, Config::JSON); + $tmpNew->setAll($tmpOldData); + $tmpNew->save(); + unlink($this->dir.self::FILE_CRFTP); + $this->getLogger()->info(TF::GREEN.$this->language->transrateString("transfer.crftp")); + } + } + if(!file_exists($this->dir.self::FILE_FT)){ + if (!file_exists($this->dir.self::FILE_FTP)) { + file_put_contents($this->dir.self::FILE_FT, $this->getResource(self::FILE_FT)); + }else { + $tmpOld = new Config($this->dir.self::FILE_FTP, Config::JSON); + $tmpOldData = $tmpOld->getAll(); + file_put_contents($this->dir.self::FILE_FT, []); + $tmpNew = new Config($this->dir.self::FILE_FT, Config::JSON); + $tmpNew->setAll($tmpOldData); + $tmpNew->save(); + unlink($this->dir.self::FILE_FTP); + $this->getLogger()->info(TF::GREEN.$this->language->transrateString("transfer.ftp")); + } + } + // crfts.json + $crft_config = new Config($this->dir.self::FILE_CRFT, Config::JSON); + $this->crfts = $crft_config->getAll(); + // fts.json + $ft_config = new Config($this->dir.self::FILE_FT, Config::JSON); + $this->fts = $ft_config->getAll(); + // CheckConfigVersion + if (!$this->config->exists("configVersion") || + $this->config->get("configVersion") < self::CONFIG_VERSION) { + $this->getLogger()->notice($this->language->transrateString("config.update", ["{newer}"], [self::CONFIG_VERSION])); + } + } + + private function initApi(){ + $this->api = new TexterApi($this); + } + + private function registerCommands(){ + if ((bool)$this->config->get("canUseCommands")) { + $map = $this->getServer()->getCommandMap(); + switch (strtolower($this->getServer()->getName())) { + case 'pocketmine-mp': + $commands = [ + new TxtCommand($this), + new TxtAdmCommand($this) + ]; + break; + + // NOTE: Confirmed + case 'genisyspro': + case 'leveryl': + default: + $commands = [ + new TxtCommandOld($this), + new TxtAdmCommandOld($this) + ]; + break; + } + $map->registerAll(self::NAME, $commands); + $this->getLogger()->info(TF::GREEN.$this->language->transrateString("commands.registered")); + }else { + $this->getLogger()->info(TF::RED.$this->language->transrateString("commands.unavailable")); + } + } + + private function checkUpdate(){ + if ((bool)$this->config->get("checkUpdate")) { + try { + $async = new CheckUpdateTask(); + $this->getServer()->getScheduler()->scheduleAsyncTask($async); + } catch (\Exception $e) { + $this->getLogger()->warning($e->getMessage()); + } + } + if (strpos(self::VERSION, "-") !== false) { + $this->getLogger()->notice($this->language->transrateString("version.pre")); + $this->devmode = true; + } + } + + public function versionCompare(array $data){ + $curver = str_replace("v", "", self::VERSION); + $newver = str_replace("v", "", $data[0]["name"]); + if ($this->getDescription()->getVersion() !== $curver) { + $this->getLogger()->warning($this->messages->get("version.warning")); + } + if (version_compare($newver, $curver, "=")) { + $this->getLogger()->notice($this->language->transrateString("update.unnecessary", ["{curver}"], [$curver])); + }elseif (version_compare($newver, $curver, ">")){ + $this->getLogger()->notice($this->language->transrateString("update.available.1", ["{newver}", "{curver}"], [$newver, $curver])); + $this->getLogger()->notice($this->language->transrateString("update.available.2")); + $this->getLogger()->notice($this->language->transrateString("update.available.3", ["{url}"], [$data[0]["html_url"]])); + } + } + + private function setTimezone(){ + $timezone = $this->config->get("timezone"); + if ($timezone !== false) { + date_default_timezone_set($timezone); + $this->getLogger()->info(TF::GREEN.$this->language->transrateString("timezone", ["{zone}"], [$timezone])); + } + } + + private function prepareTexts(){ + if (!empty($this->crfts)) { + foreach ($this->crfts as $value) { + $title = isset($value["TITLE"]) ? str_replace("#", "\n", $value["TITLE"]) : ""; + $text = isset($value["TEXT"]) ? str_replace("#", "\n", $value["TEXT"]) : ""; + if (is_null($value["WORLD"]) || $value["WORLD"] === "default"){ + $value["WORLD"] = $this->getServer()->getDefaultLevel()->getName(); + } + // + if ($this->getServer()->loadLevel($value["WORLD"])) { + $level = $this->getServer()->getLevelByName($value["WORLD"]); + $crft = new CRFT($level, $value["Xvec"], $value["Yvec"], $value["Zvec"], $title, $text); + if ($crft->failed) { + $message = $this->language->transrateString("txt.failed"); + $this->getLogger()->notice($message); + } + }else { + $message = $this->language->transrateString("world.not.exists", ["{world}"], [$value["WORLD"]]); + $this->getLogger()->notice($message); + } + } + } + if (!empty($this->fts)) { + foreach ($this->fts as $value) { + $title = isset($value["TITLE"]) ? str_replace("#", "\n", $value["TITLE"]) : ""; + $text = isset($value["TEXT"]) ? str_replace("#", "\n", $value["TEXT"]) : ""; + if (is_null($value["WORLD"]) || $value["WORLD"] === "default"){ + $value["WORLD"] = $this->getServer()->getDefaultLevel()->getName(); + } + // + if ($this->getServer()->loadLevel($value["WORLD"])) { + $level = $this->getServer()->getLevelByName($value["WORLD"]); + $ft = new FT($level, $value["Xvec"], $value["Yvec"], $value["Zvec"], $title, $text, strtolower($value["OWNER"])); + if ($ft->failed) { + $message = $this->language->transrateString("txt.failed"); + $this->getLogger()->notice($message); + } + }else { + $message = $this->language->transrateString("world.not.exists", ["{world}"], [$value["WORLD"]]); + $this->getLogger()->notice($message); + } + } + } + } +} diff --git a/src/Texter/TexterApi.php b/src/Texter/TexterApi.php new file mode 100644 index 0000000..2dded79 --- /dev/null +++ b/src/Texter/TexterApi.php @@ -0,0 +1,319 @@ +<?php + +/** + * ## English + * + * Texter, the display FloatingTextPerticle plugin for PocketMine-MP + * Copyright (c) 2017 yuko fuyutsuki < https://github.com/fuyutsuki > + * + * Released under the "MIT license". + * You should have received a copy of the MIT license + * along with this program. If not, see + * < https://opensource.org/licenses/mit-license >. + * + * --------------------------------------------------------------------- + * ## 日本語 + * + * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです。 + * Copyright (c) 2017 yuko fuyutsuki < https://github.com/fuyutsuki > + * + * このソフトウェアは"MITライセンス"下で配布されています。 + * あなたはこのプログラムと共にMITライセンスのコピーを受け取ったはずです。 + * 受け取っていない場合、下記のURLからご覧ください。 + * < https://opensource.org/licenses/mit-license > + */ + +namespace Texter; + +# Pocketmine +use pocketmine\Player; +use pocketmine\entity\Entity; +use pocketmine\item\Item; +use pocketmine\level\{ + Level, + Position}; +use pocketmine\math\Vector3; +use pocketmine\utils\{ + TextFormat as TF, + UUID}; + +# Texter +use Texter\Main; +use Texter\language\Lang; +use Texter\text\{ + CantRemoveFloatingText as CRFT, + FloatingText as FT}; +use Texter\utils\TunedConfig as Config; + +/** + * TexterApi + */ +class TexterAPI{ + + /** @var TexterAPI */ + private static $instance = null; + /** @var Config $crft_config */ + private $crft_config = null; + /** @var Config $ft_config */ + private $ft_config = null; + /** @var array $crfts[$levelName][] = $pk */ + private $crfts = []; + /** @var array $ft[$levelName][] = $pk */ + private $fts = []; + + public function __construct(Main $main){ + self::$instance = $this; + $this->main = $main; + $this->language = Lang::getInstance(); + $this->crft_config = new Config($main->getDataFolder().Main::FILE_CRFT, Config::JSON); + $this->crft_config->setAll([]); + $this->ft_config = new Config($main->getDataFolder().Main::FILE_FT, Config::JSON); + $this->ft_config->setAll([]); + } + + /****************************************************************************/ + /* get/set(情報取得/変更) 関連 */ + /** + * インスタンスを取得 + * @return TexterApi + */ + public static function getInstance(): TexterApi{ + return self::$instance; + } + + /** + * 現在使用中の言語を取得します + * @return string "eng"|"jpn" + */ + public function getLanguage(): string{ + return $this->language->getLang(); + } + + /** + * テキストをファイルに保存します + * @param CRFT $text + * @param bool $new 新規かどうか + * @return bool + */ + public function saveCrft(CRFT $text, bool $new = false): bool{ + $levelName = $text->level->getName(); + $key = $levelName . $text->z . $text->x . $text->y; + if ($new) { // 新規 + if ($this->crft_config->exists($key)) { // 既設 + return false; + }else { + $this->crfts[$levelName][$text->eid] = $text; + } + } + $data = [ + "WORLD" => $levelName, + "Xvec" => sprintf('%0.1f', $text->x), + "Yvec" => sprintf('%0.1f', $text->y), + "Zvec" => sprintf('%0.1f', $text->z), + "TITLE" => $text->title, + "TEXT" => $text->text + ]; + $this->crft_config->set($key, $data); + $this->crft_config->save(); + return true; + } + + /** + * テキストをファイルに保存します + * @param FT $text + * @param bool $new 新規かどうか + * @return bool + */ + public function saveFt(FT $text, bool $new = false): bool{ + $levelName = $text->level->getName(); + $key = $levelName . $text->z . $text->x . $text->y; + if ($new) { // 新規 + if ($this->ft_config->exists($key)) { // 既設 + return false; + }else { + $this->fts[$levelName][$text->eid] = $text; + } + } + $data = [ + "WORLD" => $levelName, + "Xvec" => sprintf('%0.1f', $text->x), + "Yvec" => sprintf('%0.1f', $text->y), + "Zvec" => sprintf('%0.1f', $text->z), + "TITLE" => $text->title, + "TEXT" => $text->text, + "OWNER" => $text->owner + ]; + $this->ft_config->set($key, $data); + $this->ft_config->save(); + return true; + } + + /** + * テキストをファイルから消去します + * @param FT $text + * @return bool true + */ + public function removeText(FT $text): bool{ + $levelName = $text->level->getName(); + $key = $levelName . $text->z . $text->x . $text->y; + $this->ft_config->remove($key); + $this->ft_config->save(); + unset($this->fts[$levelName][$text->eid]); + return true; + } + + /** + * すべてのcrftを返します + * @return array $this->crfts + */ + public function getCrfts(): array{ + return $this->crfts; + } + + /** + * 指定されたワールドのすべてのcrftを返します + * @param Level $level + * @return array + */ + public function getCrftsByLevel(Level $level): array{ + $levelName = $level->getName(); + if (!isset($this->crfts[$levelName])) { + return []; + }else { + return $this->crfts[$levelName]; + } + } + + /** + * 指定されたワールドのすべてのcrftを返します + * @param string $levelName + * @return array + */ + public function getCrftsByLevelName(string $levelName): array{ + if (!isset($this->crfts[$levelName])) { + return []; + }else { + return $this->crfts[$levelName]; + } + } + + /** + * 指定されたワールド, eidのcrftを取得します + * @param string $levelName + * @param int $entityId + * @return null|CRFT + */ + public function getCrft(string $levelName, int $entityId){ + if (!isset($this->crfts[$levelName][$entityId])) { + return null; + }else{ + return $this->crfts[$levelName][$entityId]; + } + } + + /** + * crftの個数を返します + * @return int + */ + public function getCrftsCount(): int{ + $cc = 0; + $crfts = $this->getCrfts(); + if ($crfts !== false) { + foreach ($crfts as $levCrfts) { + foreach ($levCrfts as $crft) { + ++$cc; + } + } + } + return $cc; + } + + /** + * すべてのftを返します + * @return array $this->fts + */ + public function getFts(): array{ + return $this->fts; + } + + /** + * 指定されたワールドのすべてのftを返します + * @param Level $level + * @return array + */ + public function getFtsByLevel(Level $level): array{ + $levelName = $level->getName(); + if (!isset($this->fts[$levelName])) { + return []; + }else { + return $this->fts[$levelName]; + } + } + + /** + * 指定されたワールドのすべてのftを返します + * @param string $levelName + * @return array + */ + public function getFtsByLevelName(string $levelName): array{ + if (!isset($this->fts[$levelName])) { + return []; + }else { + return $this->fts[$levelName]; + } + } + + /** + * 指定されたユーザー名所有のftをすべて取得します + * @param string $name + * @return array + */ + public function getFtsByName(string $name): array{ + $name = strtolower($name); + $fts = $this->getFts(); + if (empty($fts)) { + return []; + }else { + $return = []; + foreach ($fts as $levFts) { + foreach ($levFts as $ft) { + if ($ft->owner === $name) { + $return[] = $ft; + } + } + } + return $return; + } + } + + /** + * 指定されたワールド,eidのftを取得します + * @param string $levelName + * @param int $entityId + * @return null|FT + */ + public function getFt(string $levelName, int $entityId){ + if (!isset($this->fts[$levelName][$entityId])) { + return null; + }else{ + return $this->fts[$levelName][$entityId]; + } + } + + /** + * ftの個数を返します + * @return int + */ + public function getFtsCount(): int{ + $fc = 0; + $fts = $this->getFts(); + if ($fts !== false) { + foreach ($fts as $levFts) { + foreach ($levFts as $fts) { + ++$fc; + } + } + } + return $fc; + } +} diff --git a/src/Texter/commands/TxtAdmCommand.php b/src/Texter/commands/TxtAdmCommand.php new file mode 100644 index 0000000..160875b --- /dev/null +++ b/src/Texter/commands/TxtAdmCommand.php @@ -0,0 +1,113 @@ +<?php + +namespace Texter\commands; + +# Pocketmine +use pocketmine\Player; +use pocketmine\command\{ + Command, + CommandSender}; +use pocketmine\math\Vector3; +use pocketmine\utils\TextFormat as TF; + +# Texter +use Texter\Main; +use Texter\language\Lang; +use Texter\text\FloatingText as FT; +/** + * TxtAdmCommand + */ +class TxtAdmCommand extends Command{ + + /** @var string $help */ + private $help = ""; + + public function __construct(Main $main){ + $this->main = $main; + $this->api = $main->getAPI(); + $this->lang = Lang::getInstance(); + parent::__construct("txtadm", $this->lang->transrateString("command.description.txtadm"), "/txtadm <ar | ur | info>", ["tadm"]);//登録 + // + $this->setPermission("texter.command.txtadm"); + // + $this->help = $this->lang->transrateString("command.txt.usage")."\n"; + $this->help .= $this->lang->transrateString("command.txtadm.usage.allremove")."\n"; + $this->help .= $this->lang->transrateString("command.txtadm.usage.userremove")."\n"; + $this->help .= $this->lang->transrateString("command.txtadm.usage.info"); + } + + public function execute(CommandSender $sender, string $label, array $args){ + if (!$this->main->isEnabled()) return false; + if (!$this->testPermission($sender)) return false; + if (isset($args[0])) { + $name = $sender->getName(); + $lev = $sender->level; + $levn = $lev->getName(); + switch (strtolower($args[0])) { // subCommand + case 'allremove': + case 'ar': + $fts = $this->api->getFts(); + $count = $this->api->getFtsCount(); + if ($fts === 0) { + $message = $this->lang->transrateString("command.txtadm.notexists"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + }else { + foreach ($fts as $levFts) { + foreach ($levFts as $ft) { + $ft->remove(); + } + } + $message = $this->lang->transrateString("command.txtadm.allremove", ["{count}"], [$count]); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + } + break; + + case 'userremove': + case 'ur': + if (isset($args[1])) { // username + $fts = $this->api->getFtsByName($args[1]); + if (empty($fts)) { + $message = $this->lang->transrateString("txt.user.doesn`t.exists"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + }else { + $message = $this->lang->transrateString("command.txtadm.userremove", ["{user}"], [$args[1]]); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + } + }else { + $message = $this->lang->transrateString("command.txtadm.usage.ur"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + } + break; + + case 'info': + case 'i': + $crfts = $this->api->getCrftsCount(); + $fts = $this->api->getFtsCount(); + $message = TF::AQUA . Lang::PREFIX . "\n"; + $message .= TF::AQUA . "crfts: " . TF::GOLD . $crfts . "\n"; + $message .= TF::AQUA . "fts: " . TF::GOLD . $fts . "\n"; + $message .= TF::GRAY . Main::NAME . " " . Main::VERSION . " - " . Main::CODENAME; + $sender->sendMessage($message); + break; + + case 'help': + case 'h': + case '?': + default: + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $this->help); + break; + + case 'test': + if ($this->main->devmode) { + for ($i=1; $i<51; $i++) { + $ft = new FT($lev, $s->x+$i, $s->y, $s->z, "test", "§$i", $name); + } + } + break; + } + }else { + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $this->help); + } + return true; + } +} diff --git a/src/Texter/commands/TxtAdmCommandOld.php b/src/Texter/commands/TxtAdmCommandOld.php new file mode 100644 index 0000000..e5ec67d --- /dev/null +++ b/src/Texter/commands/TxtAdmCommandOld.php @@ -0,0 +1,113 @@ +<?php + +namespace Texter\commands; + +# Pocketmine +use pocketmine\Player; +use pocketmine\command\{ + Command, + CommandSender}; +use pocketmine\math\Vector3; +use pocketmine\utils\TextFormat as TF; + +# Texter +use Texter\Main; +use Texter\language\Lang; +use Texter\text\FloatingText as FT; +/** + * TxtAdmCommand(old) + */ +class TxtAdmCommandOld extends Command{ + + /** @var string $help */ + private $help = ""; + + public function __construct(Main $main){ + $this->main = $main; + $this->api = $main->getAPI(); + $this->lang = Lang::getInstance(); + parent::__construct("txtadm", $this->lang->transrateString("command.description.txtadm"), "/txtadm <ar | ur | info>", ["tadm"]);//登録 + // + $this->setPermission("texter.command.txtadm"); + // + $this->help = $this->lang->transrateString("command.txt.usage")."\n"; + $this->help .= $this->lang->transrateString("command.txtadm.usage.allremove")."\n"; + $this->help .= $this->lang->transrateString("command.txtadm.usage.userremove")."\n"; + $this->help .= $this->lang->transrateString("command.txtadm.usage.info"); + } + + public function execute(CommandSender $sender, $label, array $args){ + if (!$this->main->isEnabled()) return false; + if (!$this->testPermission($sender)) return false; + if (isset($args[0])) { + $name = $sender->getName(); + $lev = $sender->level; + $levn = $lev->getName(); + switch (strtolower($args[0])) { // subCommand + case 'allremove': + case 'ar': + $fts = $this->api->getFts(); + $count = $this->api->getFtsCount(); + if ($fts === 0) { + $message = $this->lang->transrateString("command.txtadm.notexists"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + }else { + foreach ($fts as $levFts) { + foreach ($levFts as $ft) { + $ft->remove(); + } + } + $message = $this->lang->transrateString("command.txtadm.allremove", ["{count}"], [$count]); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + } + break; + + case 'userremove': + case 'ur': + if (isset($args[1])) { // username + $fts = $this->api->getFtsByName($args[1]); + if (empty($fts)) { + $message = $this->lang->transrateString("txt.user.doesn`t.exists"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + }else { + $message = $this->lang->transrateString("command.txtadm.userremove", ["{user}"], [$args[1]]); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + } + }else { + $message = $this->lang->transrateString("command.txtadm.usage.ur"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + } + break; + + case 'info': + case 'i': + $crfts = $this->api->getCrftsCount(); + $fts = $this->api->getFtsCount(); + $message = TF::AQUA . Lang::PREFIX . "\n"; + $message .= TF::AQUA . "crfts: " . TF::GOLD . $crfts . "\n"; + $message .= TF::AQUA . "fts: " . TF::GOLD . $fts . "\n"; + $message .= TF::GRAY . Main::NAME . " " . Main::VERSION . " - " . Main::CODENAME; + $sender->sendMessage($message); + break; + + case 'help': + case 'h': + case '?': + default: + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $this->help); + break; + + case 'test': + if ($this->main->devmode) { + for ($i=1; $i<51; $i++) { + $ft = new FT($lev, $s->x+$i, $s->y, $s->z, "test", "§$i", $name); + } + } + break; + } + }else { + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $this->help); + } + return true; + } +} diff --git a/src/Texter/commands/TxtCommand.php b/src/Texter/commands/TxtCommand.php new file mode 100644 index 0000000..73b3c26 --- /dev/null +++ b/src/Texter/commands/TxtCommand.php @@ -0,0 +1,216 @@ +<?php +namespace Texter\commands; + +# Pocketmine +use pocketmine\Player; +use pocketmine\command\{ + Command, + CommandSender}; +use pocketmine\math\Vector3; +use pocketmine\utils\TextFormat as TF; + +# Texter +use Texter\Main; +use Texter\language\Lang; +use Texter\text\FloatingText as FT; + +/** + * TxtCommand + */ +class TxtCommand extends Command{ + + /** @var string $help */ + private $help = ""; + + public function __construct(Main $main){ + $this->main = $main; + $this->api = $main->getAPI(); + $this->lang = Lang::getInstance(); + parent::__construct("txt", $this->lang->transrateString("command.description.txt"), "/txt <add | remove | update | help>");//登録 + // + $this->setPermission("texter.command.txt"); + // + $this->help = $this->lang->transrateString("command.txt.usage")."\n"; + $this->help .= $this->lang->transrateString("command.txt.usage.add")."\n"; + $this->help .= $this->lang->transrateString("command.txt.usage.remove")."\n"; + $this->help .= $this->lang->transrateString("command.txt.usage.update")."\n"; + $this->help .= $this->lang->transrateString("command.txt.usage.indent"); + // + $this->lim = $this->main->getCharaLimit(); + $this->feed = $this->main->getFeedLimit(); + $this->world = $this->main->getWorldLimit(); + } + + public function execute(CommandSender $sender, string $label, array $args){ + if (!$this->main->isEnabled()) return false; + if (!$this->testPermission($sender)) return false; + if ($sender instanceof Player) { + $lev = $sender->level; + $levn = $lev->getName(); + if (array_key_exists($levn, $this->world)) { + $message = $this->lang->transrateString("command.txt.world"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + }else { + if (isset($args[0])) { + switch (strtolower($args[0])) { // subCommand + case 'add': + case 'a': + if (!empty($args[1])) { // Title + $title = str_replace("#", "\n", $args[1]); + if (!empty($args[2])) { // Text + $texts = array_slice($args, 2); + $text = str_replace("#", "\n", implode(" ", $texts)); + }else { + $text = ""; + } + if (!$sender->isOp()) { + $message = $this->checkTextLimit($title.$text); + if ($message !== true) { + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + return true; + } + $title = TF::clean($title); + $text = TF::clean($text); + } + $x = sprintf('%0.1f', $sender->x); + $y = sprintf('%0.1f', $sender->y + 1); + $z = sprintf('%0.1f', $sender->z); + $name = $sender->getName(); + $ft = new FT($lev, $x, $y, $z, $title, $text, $name); + if ($ft->failed) { + $message = $this->lang->transrateString("txt.exists"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + }else { + $message = $this->lang->transrateString("command.txt.set"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + } + }else { + $message = $this->lang->transrateString("command.txt.usage.add"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + } + break; + + case 'remove': + case 'r': + if (isset($args[1])) { // entityId + $eid = (int)$args[1]; + $ft = $this->api->getFt($levn, $eid); + if ($ft !== null) { + if ($ft->canEditFt($sender)) { + $ft->remove(); + $message = $this->lang->transrateString("command.txt.remove"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + }else { + $message = $this->lang->transrateString("command.txt.permission"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + } + }else { + $message = $this->lang->transrateString("txt.doesn`t.exists"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + } + }else { + $message = $this->lang->transrateString("command.txt.usage.remove"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + } + break; + + case 'update': + case 'u': + if (isset($args[1]) && isset($args[2]) && !empty($args[3])) { + // eid && title" or "text" && contents + $eid = (int)$args[1]; + $ft = $this->api->getFt($levn, $eid); + if ($ft !== null) { + if ($ft->canEditFt($sender)) { + switch (strtolower($args[2])) { + case 'title': + $title = str_replace("#", "\n", $args[3]); + if (!$sender->isOp()) { + $message = $this->checkTextLimit($title.$ft->text); + if ($message !== true) { + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + return true; + } + $title = TF::clean($title); + } + $ft->setTitle($args[3]); + $message = $this->lang->transrateString("command.txt.updated"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + break; + + case 'text': + if (!empty($args[3])) { + $texts = array_slice($args, 3); + $text = str_replace("#", "\n", implode(" ", $texts)); + }else { + $text = ""; + } + if (!$sender->isOp()) { + $message = $this->checkTextLimit($ft->title.$text); + if ($message !== true) { + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + return true; + } + $text = TF::clean($text); + } + $ft->setText($text); + $message = $this->lang->transrateString("command.txt.updated"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + break; + + default: + $message = $this->lang->transrateString("command.txt.usage.update"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + break; + } + }else { + $message = $this->lang->transrateString("command.txt.permission"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + } + }else { + $message = $this->lang->transrateString("txt.doesn`t.exists"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + } + }else { + $message = $this->lang->transrateString("command.txt.usage.update"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + } + break; + + case 'help': + case 'h': + case '?': + default: + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $this->help); + break; + } + }else { + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $this->help); + } + } + }else { + $message = $this->lang->transrateString("command.console"); + $this->main->getLogger()->info(TF::RED . $message); + } + return true; + } + + /** + * テキストのルールに違反していないか確かめます + * @param string $text + * @return string|bool + */ + private function checkTextLimit(string $text){ + if ($this->lim > -1 && mb_strlen($text, "UTF-8") > $this->lim) { + $message = $this->lang->transrateString("command.txt.limit", ["{limit}"], [$this->lim]); + return $message; + }else { + if ($this->feed > -1 && mb_substr_count($text, "\n" , "UTF-8") > $this->feed) { + $message = $this->lang->transrateString("command.txt.feed", ["{feed}"], [$this->feed]); + return $message; + }else { + return true; + } + } + } +} diff --git a/src/Texter/commands/TxtCommandOld.php b/src/Texter/commands/TxtCommandOld.php new file mode 100644 index 0000000..64f45a4 --- /dev/null +++ b/src/Texter/commands/TxtCommandOld.php @@ -0,0 +1,217 @@ +<?php +namespace Texter\commands; + +# Pocketmine +use pocketmine\Player; +use pocketmine\command\{ + Command, + CommandSender}; +use pocketmine\math\Vector3; +use pocketmine\utils\TextFormat as TF; + +# Texter +use Texter\Main; +use Texter\language\Lang; +use Texter\text\FloatingText as FT; + +/** + * TxtCommand(old) + */ +class TxtCommandOld extends Command{ + + /** @var string $help */ + private $help = ""; + + public function __construct(Main $main){ + $this->main = $main; + $this->api = $main->getAPI(); + $this->lang = Lang::getInstance(); + parent::__construct("txt", $this->lang->transrateString("command.description.txt"), "/txt <add | remove | update | help>");//登録 + // + $this->setPermission("texter.command.txt"); + // + $this->help = $this->lang->transrateString("command.txt.usage")."\n"; + $this->help .= $this->lang->transrateString("command.txt.usage.add")."\n"; + $this->help .= $this->lang->transrateString("command.txt.usage.remove")."\n"; + $this->help .= $this->lang->transrateString("command.txt.usage.update")."\n"; + $this->help .= $this->lang->transrateString("command.txt.usage.indent"); + // + $this->lim = $this->main->getCharaLimit(); + $this->feed = $this->main->getFeedLimit(); + $this->world = $this->main->getWorldLimit(); + } + + public function execute(CommandSender $sender, $label, array $args){ + if (!$this->main->isEnabled()) return false; + if (!$this->testPermission($sender)) return false; + if ($sender instanceof Player) { + $lev = $sender->level; + $levn = $lev->getName(); + if (array_key_exists($levn, $this->world)) { + $message = $this->lang->transrateString("command.txt.world"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + }else { + if (isset($args[0])) { + switch (strtolower($args[0])) { // subCommand + case 'add': + case 'a': + if (!empty($args[1])) { // Title + $title = str_replace("#", "\n", $args[1]); + + if (!empty($args[2])) { // Text + $texts = array_slice($args, 2); + $text = str_replace("#", "\n", implode(" ", $texts)); + }else { + $text = ""; + } + if (!$sender->isOp()) { + $message = $this->checkTextLimit($title.$text); + if ($message !== true) { + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + return true; + } + $title = TF::clean($title); + $text = TF::clean($text); + } + $x = sprintf('%0.1f', $sender->x); + $y = sprintf('%0.1f', $sender->y + 1); + $z = sprintf('%0.1f', $sender->z); + $name = $sender->getName(); + $ft = new FT($lev, $x, $y, $z, $title, $text, $name); + if ($ft->failed) { + $message = $this->lang->transrateString("txt.exists"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + }else { + $message = $this->lang->transrateString("command.txt.set"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + } + }else { + $message = $this->lang->transrateString("command.txt.usage.add"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + } + break; + + case 'remove': + case 'r': + if (isset($args[1])) { // entityId + $eid = (int)$args[1]; + $ft = $this->api->getFt($levn, $eid); + if ($ft !== null) { + if ($ft->canEditFt($sender)) { + $ft->remove(); + $message = $this->lang->transrateString("command.txt.remove"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + }else { + $message = $this->lang->transrateString("command.txt.permission"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + } + }else { + $message = $this->lang->transrateString("txt.doesn`t.exists"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + } + }else { + $message = $this->lang->transrateString("command.txt.usage.remove"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + } + break; + + case 'update': + case 'u': + if (isset($args[1]) && isset($args[2]) && !empty($args[3])) { + // eid && title" or "text" && contents + $eid = (int)$args[1]; + $ft = $this->api->getFt($levn, $eid); + if ($ft !== null) { + if ($ft->canEditFt($sender)) { + switch (strtolower($args[2])) { + case 'title': + $title = str_replace("#", "\n", $args[3]); + $title = TF::clean($title); + if (!$sender->isOp()) { + $message = $this->checkTextLimit($title.$ft->text); + if ($message !== true) { + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + return true; + } + } + $ft->setTitle($args[3]); + $message = $this->lang->transrateString("command.txt.updated"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + break; + + case 'text': + if (!empty($args[3])) { + $texts = array_slice($args, 3); + $text = str_replace("#", "\n", implode(" ", $texts)); + $text = TF::clean($text); + }else { + $text = ""; + } + if (!$sender->isOp()) { + $message = $this->checkTextLimit($ft->title.$text); + if ($message !== true) { + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + return true; + } + } + $ft->setText($text); + $message = $this->lang->transrateString("command.txt.updated"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + break; + + default: + $message = $this->lang->transrateString("command.txt.usage.update"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + break; + } + }else { + $message = $this->lang->transrateString("command.txt.permission"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + } + }else { + $message = $this->lang->transrateString("txt.doesn`t.exists"); + $sender->sendMessage(TF::RED . Lang::PREFIX . $message); + } + }else { + $message = $this->lang->transrateString("command.txt.usage.update"); + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $message); + } + break; + + case 'help': + case 'h': + case '?': + default: + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $this->help); + break; + } + }else { + $sender->sendMessage(TF::AQUA . Lang::PREFIX . $this->help); + } + } + }else { + $message = $this->lang->transrateString("command.console"); + $this->main->getLogger()->info(TF::RED . $message); + } + return true; + } + + /** + * テキストのルールに違反していないか確かめます + * @param string $text + * @return string|bool + */ + private function checkTextLimit(string $text){ + if ($this->lim > -1 && mb_strlen($text, "UTF-8") > $this->lim) { + $message = $this->lang->transrateString("command.txt.limit", ["{limit}"], [$this->lim]); + return $message; + }else { + if ($this->feed > -1 && mb_substr_count($text, "\n" , "UTF-8") > $this->feed) { + $message = $this->lang->transrateString("command.txt.feed", ["{feed}"], [$this->feed]); + return $message; + }else { + return true; + } + } + } +} diff --git a/src/Texter/language/Lang.php b/src/Texter/language/Lang.php new file mode 100644 index 0000000..8b3071c --- /dev/null +++ b/src/Texter/language/Lang.php @@ -0,0 +1,94 @@ +<?php +namespace Texter\language; + +use Texter\Main; +use Texter\utils\TunedConfig as Config; + +/** + * 言語選択、文字列翻訳など + */ +class Lang { + + /** + * 利用可能な言語 + * Available languages + */ + const JPN = "jpn"; + const ENG = "eng"; + + const PREFIX = "[Texter] "; + + /** @var string $dir */ + public $dir = ""; + /** @var Main $main */ + private $main = null; + /** @var Lang $instance */ + private static $instance = null; + /** @var Config $config */ + private $config = null; + /** @var string $language */ + private $language = ""; + + public function __construct(Main $main, string $lang) { + self::$instance = $this; + $this->main = $main; + $this->dir = $main->getDataFolder(); + $this->setLang($lang); + } + + /** + * 静的にインスタンス取得 + * @return Lang + */ + public static function getInstance(): Lang{ + return self::$instance; + } + + /** + * 現在使用中の言語を取得 + * @return string $this->language + */ + public function getLang(): string{ + return $this->language; + } + + /** + * 使用する言語を取得 + * @param string $lang + * @return string $this->language + */ + public function setLang(string $lang): string{ + switch (strtolower($lang)) { + case self::ENG: + $this->language = self::ENG; + break; + + case self::JPN: + $this->language = self::JPN; + break; + + default: + $this->language = self::ENG; + break; + } + $this->lang = new Config(__DIR__.DS.$this->language.".json", Config::JSON); + return $this->language; + } + + /** + * 翻訳 + * @param string $key + * @param array $search = [] + * @param array $replace = [] + * @return string + */ + public function transrateString(string $key, array $search = [], array $replace = []): string{ + $result = $this->lang->get($key); + if ($result !== false) { + $result = str_replace($search, $replace, $result); + return $result; + }else { + return $key; + } + } +} diff --git a/src/Texter/language/eng.json b/src/Texter/language/eng.json new file mode 100644 index 0000000..2192f65 --- /dev/null +++ b/src/Texter/language/eng.json @@ -0,0 +1,57 @@ +{ + "on.enable": "is Enabled.", + "on.disable": "is Disabled.", + "lang.registered": "Language was set to {lang}.", + "config.update": "config.yml version {newer} => Please delete config.yml once.", + "transfer.crftp": "Successful file transfer from crftps.json to crfts.json.", + "transfer.ftp": "Successful file transfer from ftps.json to fts.json.", + + "commands.registered": "Command was set to enabled.", + "commands.unavailable": "Command was set to disabled.", + + "version.pre": "This version is under development. Operation may be unstable.", + "version.warning": "Detected version change, you may not be able to receive support.", + + "update.unnecessary": "Newest version: v{curver} / No update required.", + "update.available.1": "Newest version: v{newver} / Current version: v{curver}", + "update.available.2": "Update is available.", + "update.available.3": "URL: {url}", + + "timezone": "Timezone was set to {zone}.", + + "world.not.exists": "The listed world ({world}) does not exist.", + "txt.type.invalid": "The type of the specified text is invalid.", + + "command.description.txt": "Operate floating texts.", + "command.description.txtadm": "Manage floating texts.", + + "command.txt.usage": "Usage:", + "command.txt.usage.add": "/txt a(dd) <title> [message]", + "command.txt.usage.remove": "/txt r(emove) <ID>", + "command.txt.usage.update": "/txt u(pdate) <ID> <title | text> <message>", + "command.txt.usage.indent": "When breaking a line, insert §6#§b", + + "command.txt.set": "Placed floating text.", + "command.txt.world": "You can`t manipulate floating letters in this world.", + "command.txt.limit": "The number of characters is limited by {limit}.", + "command.txt.feed": "Line feeds are limited to {feed} times.", + "command.txt.limit": "The number of characters is limited by {limit}.", + "command.txt.remove": "Removed floating text.", + "command.txt.updated": "Updated floating text.", + "command.txt.permission": "You don`t have permission to edit the specified floating text.", + + "command.txtadm.usage.allremove": "/t(xt)adm a(ll)r(emove)", + "command.txtadm.usage.userremove": "/t(xt)adm u(ser)r(emove) <userName>", + "command.txtadm.usage.info": "/txtadm info", + + "command.txtadm.allremove": "{count} floating texts were deleted.", + "command.txtadm.userremove": "All floating texts of {user} have been deleted.", + "command.txtadm.notexists": "Any floating texts doesn`t exists.", + + "command.console": "Please use from within game.", + + "txt.failed": "Failed to generate floating text.", + "txt.exists": "Floating text already exists at the specified coordinates.", + "txt.doesn`t.exists": "The specified ID floating text does not exist.", + "txt.user.doesn`t.exists": "The float text of the specified user does not exist." +} diff --git a/src/Texter/language/jpn.json b/src/Texter/language/jpn.json new file mode 100644 index 0000000..8cf0800 --- /dev/null +++ b/src/Texter/language/jpn.json @@ -0,0 +1,56 @@ +{ + "on.enable": "が読み込まれました。", + "on.disable": "が無効化されました。", + "lang.registered": "言語は{lang} に設定されました。", + "config.update": "config.yml {newer} => 一度config.ymlを削除してください。", + "transfer.crftp": "crftps.json から crfts.json へのファイル移行に成功しました。", + "transfer.ftp": "ftps.json から fts.json へのファイル移行に成功しました。", + + "commands.registered": "コマンドは使用可能に設定されました。", + "commands.unavailable": "コマンドは使用不能に設定されました。", + + "version.pre": "このバージョンは開発中のものです。動作が不安定な場合があります。", + "version.warning": "バージョンの変更を検知しました。バグが発生した場合、製作者は一切の責任を負いません。", + + "update.unnecessary": "最新バージョン: v{curver} / アップデートは必要ありません", + "update.available.1": "最新バージョン: v{newver} / 現在のバージョン: v{curver}", + "update.available.2": "アップデートが利用可能です。", + "update.available.3": "URL: {url}", + + "timezone": "タイムゾーンは {zone} に設定されました。", + + "world.not.exists": "記載されたワールド名 {world} は存在しません。", + "txt.type.invalid": "指定されたテキストの型は無効です。", + + "command.description.txt": "浮き文字を操作します。", + "command.description.txtadm": "浮き文字の管理をします。", + + "command.txt.usage": "使用方法:", + "command.txt.usage.add": "/txt a(dd) <タイトル> <メッセージ>", + "command.txt.usage.remove": "/txt r(emove) <ID>", + "command.txt.usage.update": "/txt u(pdate) <ID> <title | text> <テキスト>", + "command.txt.usage.indent": "改行したい場合は§6#§bを挿入", + + "command.txt.set": "浮き文字を設置しました。", + "command.txt.world": "このワールドでは浮き文字を操作できません。", + "command.txt.limit": "{limit} までに文字数が制限されています。", + "command.txt.feed": "改行は {feed} 回までに制限されています。", + "command.txt.remove": "浮き文字を削除しました。", + "command.txt.updated": "浮き文字を更新しました。", + "command.txt.permission": "指定された浮き文字を扱う権限がありません。", + + "command.txtadm.usage.allremove": "/t(xt)adm a(ll)r(emove)", + "command.txtadm.usage.userremove": "/t(xt)adm u(ser)r(emove) <ユーザー名>", + "command.txtadm.usage.info": "/txtadm info", + + "command.txtadm.allremove": "{count} 個の浮き文字を削除しました。", + "command.txtadm.userremove": "{user} の浮き文字をすべて削除しました。", + "command.txtadm.notexists": "浮き文字が一つもありません。", + + "command.console": "ゲーム内から使用してください。", + + "txt.failed": "浮き文字の生成に失敗しました。", + "txt.exists": "指定された座標に既に浮き文字が存在します。", + "txt.doesn`t.exists": "指定されたIDの浮き文字が存在しません。", + "txt.user.doesn`t.exists": "指定されたユーザーの浮き文字は存在しません。" +} diff --git a/src/Texter/task/CheckUpdateTask.php b/src/Texter/task/CheckUpdateTask.php new file mode 100644 index 0000000..d1d7eda --- /dev/null +++ b/src/Texter/task/CheckUpdateTask.php @@ -0,0 +1,38 @@ +<?php + +namespace Texter\task; + +use pocketmine\Server; +use pocketmine\Player; +use pocketmine\scheduler\AsyncTask; + +/** + * バージョンの確認 + */ +class CheckUpdateTask extends AsyncTask{ + + public function onRun(){ + $curl = curl_init(); + curl_setopt_array($curl, [ + CURLOPT_URL => "https://api.github.com/repos/fuyutsuki/Texter/releases", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_USERAGENT => "getGitHubAPI", + CURLOPT_SSL_VERIFYPEER => false + ]); + $json = curl_exec($curl); + + $errorno = curl_errno($curl); + if ($errorno) { + $error = curl_error($curl); + throw new \Exception($error); + } + curl_close($curl); + $data = json_decode($json, true); + $this->setResult($data); + } + + public function onCompletion(Server $server){ + $main = $server->getPluginManager()->getPlugin("Texter"); + $main->versionCompare($this->getResult()); + } +} diff --git a/src/Texter/task/WorldGetTask.php b/src/Texter/task/WorldGetTask.php new file mode 100644 index 0000000..36be56e --- /dev/null +++ b/src/Texter/task/WorldGetTask.php @@ -0,0 +1,43 @@ +<?php + +namespace Texter\task; + +# Pocketmine +use pocketmine\Player; +use pocketmine\scheduler\PluginTask; + +# Texter +use Texter\Main; +use Texter\text\{ + CantRemoveFloatingText as CRFT, + FloatingText as FT}; + +/** + * 1秒遅らせて移動後のワールドを取得するタスク + */ +class WorldGetTask extends PluginTask{ + + public function __construct(Main $main, Player $p){ + parent::__construct($main); + $this->api = $main->getApi(); + $this->p = $p; + } + + public function onRun(int $tick){ + $p = $this->p; + $lev = $p->getLevel(); + // + $crfts = $this->api->getCrftsByLevel($lev); + if (!empty($crfts)) { + foreach ($crfts as $crft) { + $crft->sendToPlayer($p, CRFT::SEND_TYPE_ADD); + } + } + $fts = $this->api->getFtsByLevel($lev); + if (!empty($fts)) { + foreach ($fts as $ft) { + $ft->sendToPlayer($p, FT::SEND_TYPE_ADD); + } + } + } +} diff --git a/src/Texter/task/WorldGetTaskOld.php b/src/Texter/task/WorldGetTaskOld.php new file mode 100644 index 0000000..ac5d860 --- /dev/null +++ b/src/Texter/task/WorldGetTaskOld.php @@ -0,0 +1,43 @@ +<?php + +namespace Texter\task; + +# Pocketmine +use pocketmine\Player; +use pocketmine\scheduler\PluginTask; + +# Texter +use Texter\Main; +use Texter\text\{ + CantRemoveFloatingText as CRFT, + FloatingText as FT}; + +/** + * 1秒遅らせて移動後のワールドを取得するタスク(Old) + */ +class WorldGetTaskOld extends PluginTask{ + + public function __construct(Main $main, Player $p){ + parent::__construct($main); + $this->api = $main->getApi(); + $this->p = $p; + } + + public function onRun($tick){ + $p = $this->p; + $lev = $p->getLevel(); + // + $crfts = $this->api->getCrftsByLevel($lev); + if (!empty($crfts)) { + foreach ($crfts as $crft) { + $crft->sendToPlayer($p, CRFT::SEND_TYPE_ADD); + } + } + $fts = $this->api->getFtsByLevel($lev); + if (!empty($fts)) { + foreach ($fts as $ft) { + $ft->sendToPlayer($p, FT::SEND_TYPE_ADD); + } + } + } +} diff --git a/src/Texter/text/CantRemoveFloatingText.php b/src/Texter/text/CantRemoveFloatingText.php new file mode 100644 index 0000000..0871234 --- /dev/null +++ b/src/Texter/text/CantRemoveFloatingText.php @@ -0,0 +1,9 @@ +<?php +namespace Texter\text; + +/** + * CantRemoveFloatingText + */ +class CantRemoveFloatingText extends Text{ + // NOTE: empty +} diff --git a/src/Texter/text/FloatingText.php b/src/Texter/text/FloatingText.php new file mode 100644 index 0000000..d832324 --- /dev/null +++ b/src/Texter/text/FloatingText.php @@ -0,0 +1,308 @@ +<?php +namespace Texter\text; + +# Pocketmine +use pocketmine\Player; +use pocketmine\Server; +use pocketmine\entity\Entity; +use pocketmine\level\{ + Level, + Position}; +use pocketmine\math\Vector3; +use pocketmine\item\Item; +use pocketmine\utils\{ + TextFormat as TF, + UUID}; + +# Texter +use Texter\TexterApi; +use Texter\text\Text; +use Texter\language\Lang; + +/** + * FloatingText + */ +class FloatingText extends Text{ + + /** @var string $owner */ + public $owner = null; + + /** + * コンストラクタ + * @Override + * @param Level $level + * @param int|float $x = 0 + * @param int|float $y = 0 + * @param int|float $z = 0 + * @param string $title = "" + * @param string $text = "" + * @param string $owner = "" + */ + public function __construct(Level $level, $x = 0, $y = 0, $z = 0, string $title = "", string $text = "", string $owner = ""){ + $this->level = $level; + $this->x = $x; + $this->y = $y; + $this->z = $z; + $this->title = $title; + $this->text = $text; + $this->owner = strtolower($owner); + $this->eid = Entity::$entityCount++; + $this->api = TexterApi::getInstance(); + if ($this->api->saveFt($this, true)) { + $this->sendToLevel(self::SEND_TYPE_ADD); + }else { + $this->failed = true; + } + } + + /** + * X座標を変更します + * @Override + * @param int|float $x + * @return bool + */ + public function setX($x): bool{ + if (is_numeric($x)) { + $tmpX = $this->x; + $this->x = $x; + if ($this->api->saveFt($this)) { + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + }else { + $this->x = $tmpX; + } + } + return false; + } + + /** + * Y座標を変更します + * @Override + * @param int|float $y + * @return bool + */ + public function setY($y): bool{ + if (is_numeric($y)) { + $tmpY = $this->y; + $this->y = $y; + if ($this->api->saveFt($this)) { + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + }else { + $this->y = $tmpY; + } + } + return false; + } + + /** + * Z座標を変更します + * @Override + * @param int|float $z + * @return bool + */ + public function setZ($z): bool{ + if (is_numeric($z)) { + $tmpZ = $this->z; + $this->z = $z; + if ($this->api->saveFt($this)) { + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + }else { + $this->z = $tmpZ; + } + } + return false; + } + + /** + * Levelを変更します + * @Override + * @param Level $level + * @return bool + */ + public function setLevel(Level $level): bool{ + $this->sendToLevel(self::SEND_TYPE_REMOVE); + $tmpLev = $this->level; + $this->level = $level; + if ($this->api->saveFt($this)) { + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + }else { + $this->level = $tmpLev; + $this->sendToLevel(self::SEND_TYPE_ADD); + return false; + } + } + + /** + * Levelを変更します + * @Override + * @param string $levelName + * @return bool + */ + public function setLevelByName(string $levelName): bool{ + $level = Server::getInstance()->getLevelByName($levelName); + if ($level !== null) { + $this->sendToLevel(self::SEND_TYPE_REMOVE); + $tmpLev = $this->level; + $this->level = $level; + if ($this->api->saveFt($this)) { + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + }else { + $this->level = $tmpLev; + $this->sendToLevel(self::SEND_TYPE_ADD); + } + } + return false; + } + + /** + * 座標を変更します + * @Override + * @param Vector3 $pos + * @return bool true + */ + public function setCoord(Vector3 $pos): bool{ + $tmpX = $this->x; + $tmpY = $this->y; + $tmpZ = $this->z; + $this->x = $pos->x; + $this->y = $pos->y; + $this->z = $pos->z; + if ($this->api->saveFt($this)) { + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + }else { + $this->x = $tmpX; + $this->y = $tmpY; + $this->z = $tmpZ; + return false; + } + } + + /** + * タイトルを変更します, # で改行です. + * @Override + * @param string $title + * @return bool true + */ + public function setTitle(string $title): bool{ + $this->title = str_replace("#", "\n", $title); + $this->api->saveFt($this); + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + } + + /** + * テキストを変更します, # で改行です + * @Override + * @param string $text + * @return bool true + */ + public function setText(string $text): bool{ + $this->text = str_replace("#", "\n", $text); + $this->api->saveFt($this); + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + } + + /** + * プレイヤーに送信します + * @Override + * @param Player $player + * @param int $type + * @return bool + */ + public function sendToPlayer(Player $player, int $type): bool{ + switch ($type) { + case self::SEND_TYPE_ADD: + $pk = $this->getAsAddPacket(); + if ($this->canEditFt($player)) { + $pk->metadata[4][1] = TF::GRAY . "[" . $this->eid . "] " . TF::WHITE . $pk->metadata[4][1]; + } + $player->dataPacket($pk); + break; + + case self::SEND_TYPE_REMOVE: + $pk = $this->getAsRemovePacket(); + $player->dataPacket($pk); + break; + + default: + return false; + break; + } + return true; + } + + /** + * テキストを更新します + * @Override + * @param int $type + * @return bool true + */ + public function sendToLevel(int $type): bool{ + switch ($type) { + case self::SEND_TYPE_ADD: + $pk = $this->getAsAddPacket(); + $players = $this->level->getPlayers(); + foreach ($players as $player) { + if ($this->canEditFt($player)) { + $pk->metadata[4][1] = TF::GRAY . "[" . $this->eid . "] " . TF::WHITE . $pk->metadata[4][1]; + } + $player->dataPacket($pk); + } + break; + + case self::SEND_TYPE_REMOVE: + $this->api->removeText($this); + $pk = $this->getAsRemovePacket(); + $players = $this->level->getPlayers(); + foreach ($players as $player) { + $player->dataPacket($pk); + } + break; + + default: + return false; + break; + } + return true; + } + + /** + * 所有者を取得します + * @return string $this->owner + */ + public function getOwner(): string{ + return $this->owner; + } + + /** + * 所有者を変更します + * @param string $owner + * @return bool true + */ + public function setOwner(string $owner): bool{ + $this->owner = strtolower($owner); + $this->api->saveFt($this); + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + } + + /** + * テキストを操作できるか確認します + * @param Player $player + * @return bool + */ + public function canEditFt(Player $player): bool{ + $name = strtolower($player->getName()); + if ($player->isOp() || $this->owner === $name) { + return true; + }else { + return false; + } + } +} diff --git a/src/Texter/text/Text.php b/src/Texter/text/Text.php new file mode 100644 index 0000000..f556dca --- /dev/null +++ b/src/Texter/text/Text.php @@ -0,0 +1,454 @@ +<?php +namespace Texter\text; + +# Pocketmine +use pocketmine\Player; +use pocketmine\Server; +use pocketmine\entity\Entity; +use pocketmine\level\{ + Level, + Position}; +use pocketmine\math\Vector3; +use pocketmine\network\mcpe\protocol\{ + AddPlayerPacket, + RemoveEntityPacket}; +use pocketmine\item\Item; +use pocketmine\utils\{ + TextFormat as TF, + UUID}; + +# Texter +use Texter\TexterApi; +use Texter\language\Lang; + +/** + * Text + */ +abstract class Text{ + + /** @link $this->sendTo***() */ + const SEND_TYPE_ADD = 0; + const SEND_TYPE_REMOVE = 1; + + /** @var TexterApi */ + protected $api = null; + /** @var float $x */ + public $x = 0.0; + /** @var float $y */ + public $y = 0.0; + /** @var float $z */ + public $z = 0.0; + /** @var Level $level */ + public $level = null; + /** @var string $title */ + public $title = ""; + /** @var string $text */ + public $text = ""; + /** @var bool $invisible */ + public $invisible = false; + /** @var int $eid */ + public $eid = 0; + /** @var bool $failed */ + public $failed = false; + + /** + * コンストラクタ + * @param Level $level + * @param int|float $x = 0 + * @param int|float $y = 0 + * @param int|float $z = 0 + * @param Vector3 $pos + * @param string $title = "" + * @param string $text = "" + */ + public function __construct(Level $level, $x = 0, $y = 0, $z = 0, string $title = "", string $text = ""){ + $this->level = $level; + $this->x = $x; + $this->y = $y; + $this->z = $z; + $this->title = $title; + $this->text = $text; + $this->eid = Entity::$entityCount++; + $this->api = TexterApi::getInstance(); + if ($this->api->saveCrft($this, true)) { + $this->sendToLevel(self::SEND_TYPE_ADD); + }else { + $this->failed = true; + } + } + + /** + * X座標を取得します + * @return int|float $this->x + */ + public function getX(){ + return $this->x; + } + + /** + * X座標を変更します + * @param int|float $x + * @return bool + */ + public function setX($x): bool{ + if (is_numeric($x)) { + $tmpX = $this->x; + $this->x = $x; + if ($this->api->saveCrft($this)) { + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + }else { + $this->x = $tmpX; + } + } + return false; + } + + /** + * Y座標を取得します + * @return int|float $this->y + */ + public function getY(){ + return $this->y; + } + + /** + * Y座標を変更します + * @param int|float $y + * @return bool + */ + public function setY($y): bool{ + if (is_numeric($y)) { + $tmpY = $this->y; + $this->y = $y; + if ($this->api->saveCrft($this)) { + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + }else { + $this->y = $tmpY; + } + } + return false; + } + + /** + * Z座標を取得します + * @return int|float $this->z + */ + public function getZ(){ + return $this->z; + } + + /** + * Z座標を変更します + * @param int|float $z + * @return bool + */ + public function setZ($z): bool{ + if (is_numeric($z)) { + $tmpZ = $this->z; + $this->z = $z; + if ($this->api->saveCrft($this)) { + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + }else { + $this->z = $tmpZ; + } + } + return false; + } + + /** + * Levelを取得します + * @return Level $this->level + */ + public function getLevel(): Level{ + return $this->level; + } + + /** + * Levelを変更します + * @param Level $level + * @return bool + */ + public function setLevel(Level $level): bool{ + $this->sendToLevel(self::SEND_TYPE_REMOVE); + $tmpLev = $this->level; + $this->level = $level; + if ($this->api->saveCrft($this)) { + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + }else { + $this->level = $tmpLev; + $this->sendToLevel(self::SEND_TYPE_ADD); + return false; + } + } + + /** + * Levelを変更します + * @param string $levelName + * @return bool + */ + public function setLevelByName(string $levelName): bool{ + $level = Server::getInstance()->getLevelByName($levelName); + if ($level !== null) { + $this->sendToLevel(self::SEND_TYPE_REMOVE); + $tmpLev = $this->level; + $this->level = $level; + if ($this->api->saveCrft($this)) { + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + }else { + $this->level = $tmpLev; + $this->sendToLevel(self::SEND_TYPE_ADD); + } + } + return false; + } + + /** + * 座標をVector3オブジェクトとして取得します + * @return Vector3 $this->pos + */ + public function getAsVector3(){ + return new Vector3($this->x, $this->y, $this->z); + } + + /** + * 座標をPositionオブジェクトとして取得します + * @return Position + */ + public function getAsPosition(){ + return new Position($this->x, $this->y, $this->z, $this->level); + } + + /** + * 座標を変更します + * @param Vector3 $pos + * @return bool true + */ + public function setCoord(Vector3 $pos): bool{ + $tmpX = $this->x; + $tmpY = $this->y; + $tmpZ = $this->z; + $this->x = $pos->x; + $this->y = $pos->y; + $this->z = $pos->z; + if ($this->api->saveCrft($this)) { + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + }else { + $this->x = $tmpX; + $this->y = $tmpY; + $this->z = $tmpZ; + return false; + } + } + + /** + * タイトルを取得します + * @return string $this->title + */ + public function getTitle(): string{ + return $this->title; + } + + /** + * タイトルを変更します, # で改行です. + * @param string $title + * @return bool true + */ + public function setTitle(string $title): bool{ + $this->title = str_replace("#", "\n", $title); + $this->api->saveCrft($this); + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + } + + /** + * テキストを取得します + * @return string $this->text + */ + public function getText(): string{ + return $this->text; + } + + /** + * テキストを変更します, # で改行です + * @param string $text + * @return bool true + */ + public function setText(string $text): bool{ + $this->text = str_replace("#", "\n", $text); + $this->api->saveCrft($this); + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + } + + /** + * 不可視かどうか取得します + * @return bool + */ + public function isInvisible(): bool{ + return $this->invisible; + } + + /** + * 不可視かどうか変更します + * @param bool $bool + * @return bool true + */ + public function setInvisible(bool $bool): bool{ + $this->invisible = $bool; + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + } + + /** + * エンティティIDを取得します + * @return int $this->eid + */ + public function getEntityId(): int{ + return $this->eid; + } + + /** + * エンティティIDを変更します + * @param int $eid + * @return bool true + */ + public function setEntityId(int $eid): bool{ + $this->sendToLevel(self::SEND_TYPE_REMOVE); + $this->eid = $eid; + $this->sendToLevel(self::SEND_TYPE_ADD); + return true; + } + + /** + * AddPlayerPacketとして取得します + * @return AddPlayerPacket $pk + */ + public function getAsAddPacket(): AddPlayerPacket{ + $pk = new AddPlayerPacket(); + $pk->uuid = UUID::fromRandom(); + $pk->username = "text"; + $pk->eid = $this->eid;// for GenisysPro + $pk->entityUniqueId = $this->eid; + $pk->entityRuntimeId = $this->eid;// ...huh? + $x = (float)sprintf('%0.1f', $this->x); + $y = (float)sprintf('%0.1f', $this->y); + $z = (float)sprintf('%0.1f', $this->z); + $pk->x = $x; + $pk->y = $y; + $pk->z = $z; + $pk->position = new Vector3($x, $y, $z);// for 1.2~ + $pk->motion = new Vector3(); + $pk->item = Item::get(Item::AIR); + $flags = 0; + $flags |= 1 << Entity::DATA_FLAG_CAN_SHOW_NAMETAG; + $flags |= 1 << Entity::DATA_FLAG_ALWAYS_SHOW_NAMETAG; + $flags |= 1 << Entity::DATA_FLAG_IMMOBILE; + if ($this->invisible) { + $flags |= 1 << Entity::DATA_FLAG_INVISIBLE; + } + $pk->metadata = [ + Entity::DATA_FLAGS => [Entity::DATA_TYPE_LONG, $flags], + Entity::DATA_NAMETAG => [Entity::DATA_TYPE_STRING, $this->title . TF::RESET . TF::WHITE . ($this->text !== "" ? "\n" . $this->text : "")], + Entity::DATA_SCALE => [Entity::DATA_TYPE_FLOAT, 0] + ]; + return $pk; + } + + /** + * RemoveEntityPacketとして取得します + * @return RemoveEntityPacket $pk + */ + public function getAsRemovePacket(): RemoveEntityPacket{ + $pk = new RemoveEntityPacket(); + $pk->eid = $this->eid; + $pk->entityUniqueId = $this->eid; + return $pk; + } + + /** + * プレイヤーに送信します + * @param Player $player + * @param int $type + * @return bool + */ + public function sendToPlayer(Player $player, int $type): bool{ + switch ($type) { + case self::SEND_TYPE_ADD: + $pk = $this->getAsAddPacket(); + $player->dataPacket($pk); + break; + + case self::SEND_TYPE_REMOVE: + $pk = $this->getAsRemovePacket(); + $player->dataPacket($pk); + break; + + default: + return false; + break; + } + return true; + } + + /** + * ワールドに送信します + * @param int $type + * @return bool true + */ + public function sendToLevel(int $type): bool{ + switch ($type) { + case self::SEND_TYPE_ADD: + $pk = $this->getAsAddPacket(); + $players = $this->level->getPlayers(); + foreach ($players as $player) { + $player->dataPacket($pk); + } + break; + + case self::SEND_TYPE_REMOVE: + $this->api->removeText($this); + $pk = $this->getAsRemovePacket(); + $players = $this->level->getPlayers(); + foreach ($players as $player) { + $player->dataPacket($pk); + } + break; + + default: + return false; + break; + } + return true; + } + + /** + * @link $this->sendToLevel(self::SEND_TYPE_REMOVE); + */ + public function remove(){ + $this->sendToLevel(self::SEND_TYPE_REMOVE); + } + + /** + * 処理に失敗したかを取得します + * @return bool + */ + public function getFailed(): bool{ + return $this->failed; + } + + /** + * 処理に失敗したかどうかを変更します + * @param bool $value + * @return bool true + */ + public function setFailed(bool $value): bool{ + $this->failed = $value; + return true; + } +} diff --git a/src/Texter/utils/TunedConfig.php b/src/Texter/utils/TunedConfig.php new file mode 100644 index 0000000..712fa68 --- /dev/null +++ b/src/Texter/utils/TunedConfig.php @@ -0,0 +1,462 @@ +<?php + +/* + * + * ____ _ _ __ __ _ __ __ ____ + * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \ + * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) | + * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/ + * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_| + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * @author PocketMine Team + * @link http://www.pocketmine.net/ +*/ + +namespace Texter\utils; + +use pocketmine\scheduler\FileWriteTask; +use pocketmine\Server; + +use Texter\Main; + +/** + * Class Config + * + * Config Class for simple config manipulation of multiple formats. + */ +class TunedConfig{ + const DETECT = -1; //Detect by file extension + const PROPERTIES = 0; // .properties + const CNF = self::PROPERTIES; // .cnf + const JSON = 1; // .js, .json + const YAML = 2; // .yml, .yaml + //const EXPORT = 3; // .export, .xport + const SERIALIZED = 4; // .sl + const ENUM = 5; // .txt, .list, .enum + const ENUMERATION = self::ENUM; + + /** @var array */ + private $config = []; + + private $nestedCache = []; + + /** @var string */ + private $file; + /** @var boolean */ + private $correct = false; + /** @var integer */ + private $type = self::DETECT; + + public static $formats = [ + "properties" => self::PROPERTIES, + "cnf" => self::CNF, + "conf" => self::CNF, + "config" => self::CNF, + "json" => self::JSON, + "js" => self::JSON, + "yml" => self::YAML, + "yaml" => self::YAML, + //"export" => self::EXPORT, + //"xport" => self::EXPORT, + "sl" => self::SERIALIZED, + "serialize" => self::SERIALIZED, + "txt" => self::ENUM, + "list" => self::ENUM, + "enum" => self::ENUM, + ]; + + /** + * @param string $file Path of the file to be loaded + * @param int $type Config type to load, -1 by default (detect) + * @param array $default Array with the default values that will be written to the file if it did not exist + * @param null &$correct Sets correct to true if everything has been loaded correctly + */ + public function __construct($file, $type = self::DETECT, $default = [], &$correct = null){ + $this->load($file, $type, $default); + $correct = $this->correct; + } + + /** + * Removes all the changes in memory and loads the file again + */ + public function reload(){ + $this->config = []; + $this->nestedCache = []; + $this->correct = false; + $this->load($this->file, $this->type); + } + + /** + * @param $str + * + * @return mixed + */ + public static function fixYAMLIndexes($str){ + return preg_replace("#^([ ]*)([a-zA-Z_]{1}[ ]*)\\:$#m", "$1\"$2\":", $str); + } + + /** + * @param $file + * @param int $type + * @param array $default + * + * @return bool + */ + public function load($file, $type = self::DETECT, $default = []){ + $this->correct = true; + $this->type = (int) $type; + $this->file = $file; + if(!is_array($default)){ + $default = []; + } + if(!file_exists($file)){ + $this->config = $default; + $this->save(); + }else{ + if($this->type === self::DETECT){ + $extension = explode(".", basename($this->file)); + $extension = strtolower(trim(array_pop($extension))); + if(isset(self::$formats[$extension])){ + $this->type = self::$formats[$extension]; + }else{ + $this->correct = false; + } + } + if($this->correct === true){ + $content = file_get_contents($this->file); + switch($this->type){ + case self::PROPERTIES: + case self::CNF: + $this->parseProperties($content); + break; + case self::JSON: + $this->config = json_decode($content, true); + break; + case self::YAML: + $content = self::fixYAMLIndexes($content); + $this->config = yaml_parse($content); + break; + case self::SERIALIZED: + $this->config = unserialize($content); + break; + case self::ENUM: + $this->parseList($content); + break; + default: + $this->correct = false; + + return false; + } + if(!is_array($this->config)){ + $this->config = $default; + } + if($this->fillDefaults($default, $this->config) > 0){ + $this->save(); + } + }else{ + return false; + } + } + + return true; + } + + /** + * @return boolean + */ + public function check(){ + return $this->correct === true; + } + + /** + * @param bool $async + * + * @return boolean + */ + public function save($async = false){ + if($this->correct === true){ + try{ + $content = null; + switch($this->type){ + case self::PROPERTIES: + case self::CNF: + $content = $this->writeProperties(); + break; + case self::JSON: + $content = json_encode($this->config, JSON_PRETTY_PRINT | JSON_BIGINT_AS_STRING | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + break; + case self::YAML: + $content = yaml_emit($this->config, YAML_UTF8_ENCODING); + break; + case self::SERIALIZED: + $content = serialize($this->config); + break; + case self::ENUM: + $content = implode("\r\n", array_keys($this->config)); + break; + } + + if($async){ + Server::getInstance()->getScheduler()->scheduleAsyncTask(new FileWriteTask($this->file, $content)); + }else{ + file_put_contents($this->file, $content); + } + }catch(\Throwable $e){ + $logger = Server::getInstance()->getLogger(); + $logger->critical("Could not save Config " . $this->file . ": " . $e->getMessage()); + if(\pocketmine\DEBUG > 1 and $logger instanceof MainLogger){ + $logger->logException($e); + } + } + + return true; + }else{ + return false; + } + } + + /** + * @param $k + * + * @return boolean|mixed + */ + public function __get($k){ + return $this->get($k); + } + + /** + * @param $k + * @param $v + */ + public function __set($k, $v){ + $this->set($k, $v); + } + + /** + * @param $k + * + * @return boolean + */ + public function __isset($k){ + return $this->exists($k); + } + + /** + * @param $k + */ + public function __unset($k){ + $this->remove($k); + } + + /** + * @param $key + * @param $value + */ + public function setNested($key, $value){ + $vars = explode(".", $key); + $base = array_shift($vars); + + if(!isset($this->config[$base])){ + $this->config[$base] = []; + } + + $base =& $this->config[$base]; + + while(count($vars) > 0){ + $baseKey = array_shift($vars); + if(!isset($base[$baseKey])){ + $base[$baseKey] = []; + } + $base =& $base[$baseKey]; + } + + $base = $value; + $this->nestedCache[$key] = $value; + } + + /** + * @param $key + * @param mixed $default + * + * @return mixed + */ + public function getNested($key, $default = null){ + if(isset($this->nestedCache[$key])){ + return $this->nestedCache[$key]; + } + + $vars = explode(".", $key); + $base = array_shift($vars); + if(isset($this->config[$base])){ + $base = $this->config[$base]; + }else{ + return $default; + } + + while(count($vars) > 0){ + $baseKey = array_shift($vars); + if(is_array($base) and isset($base[$baseKey])){ + $base = $base[$baseKey]; + }else{ + return $default; + } + } + + return $this->nestedCache[$key] = $base; + } + + /** + * @param $k + * @param mixed $default + * + * @return boolean|mixed + */ + public function get($k, $default = false){ + return ($this->correct and isset($this->config[$k])) ? $this->config[$k] : $default; + } + + /** + * @param string $k key to be set + * @param mixed $v value to set key + */ + public function set($k, $v = true){ + $this->config[$k] = $v; + foreach($this->nestedCache as $nestedKey => $nvalue){ + if(substr($nestedKey, 0, strlen($k) + 1) === ($k . ".")){ + unset($this->nestedCache[$nestedKey]); + } + } + } + + /** + * @param array $v + */ + public function setAll($v){ + $this->config = $v; + } + + /** + * @param $k + * @param bool $lowercase If set, searches Config in single-case / lowercase. + * + * @return boolean + */ + public function exists($k, $lowercase = false){ + if($lowercase === true){ + $k = strtolower($k); //Convert requested key to lower + $array = array_change_key_case($this->config, CASE_LOWER); //Change all keys in array to lower + return isset($array[$k]); //Find $k in modified array + }else{ + return isset($this->config[$k]); + } + } + + /** + * @param $k + */ + public function remove($k){ + unset($this->config[$k]); + } + + /** + * @param bool $keys + * + * @return array + */ + public function getAll($keys = false){ + return ($keys === true ? array_keys($this->config) : $this->config); + } + + /** + * @param array $defaults + */ + public function setDefaults(array $defaults){ + $this->fillDefaults($defaults, $this->config); + } + + /** + * @param $default + * @param $data + * + * @return integer + */ + private function fillDefaults($default, &$data){ + $changed = 0; + foreach($default as $k => $v){ + if(is_array($v)){ + if(!isset($data[$k]) or !is_array($data[$k])){ + $data[$k] = []; + } + $changed += $this->fillDefaults($v, $data[$k]); + }elseif(!isset($data[$k])){ + $data[$k] = $v; + ++$changed; + } + } + + return $changed; + } + + /** + * @param $content + */ + private function parseList($content){ + foreach(explode("\n", trim(str_replace("\r\n", "\n", $content))) as $v){ + $v = trim($v); + if($v == ""){ + continue; + } + $this->config[$v] = true; + } + } + + /** + * @return string + */ + private function writeProperties(){ + $content = "#Properties Config file\r\n#" . date("D M j H:i:s T Y") . "\r\n"; + foreach($this->config as $k => $v){ + if(is_bool($v) === true){ + $v = $v === true ? "on" : "off"; + }elseif(is_array($v)){ + $v = implode(";", $v); + } + $content .= $k . "=" . $v . "\r\n"; + } + + return $content; + } + + /** + * @param $content + */ + private function parseProperties($content){ + if(preg_match_all('/([a-zA-Z0-9\-_\.]*)=([^\r\n]*)/u', $content, $matches) > 0){ //false or 0 matches + foreach($matches[1] as $i => $k){ + $v = trim($matches[2][$i]); + switch(strtolower($v)){ + case "on": + case "true": + case "yes": + $v = true; + break; + case "off": + case "false": + case "no": + $v = false; + break; + } + if(isset($this->config[$k])){ + MainLogger::getLogger()->debug("[Config] Repeated property " . $k . " on file " . $this->file); + } + $this->config[$k] = $v; + } + } + } + +}