
<?php
echo "PocketMine-MP plugin Texter v3.4.6
This file has been generated using DevTools v1.13.5 at Fri, 17 Apr 2020 16:42:52 +0900
----------------
";

if(extension_loaded("phar")){
	$phar = new \Phar(__FILE__);
	foreach($phar->getMetadata() as $key => $value){
		echo ucfirst($key) . ": " . (is_array($value) ? implode(", ", $value) : $value) . "\n";
	}
}

__HALT_COMPILER(); ?>
Z  )             a:9:{s:4:"name";s:6:"Texter";s:7:"version";s:5:"3.4.6";s:4:"main";s:22:"tokyo\pmmp\Texter\Core";s:3:"api";s:6:"3.10.0";s:6:"depend";s:0:"";s:11:"description";s:0:"";s:7:"authors";a:10:{i:0;s:8:"mfmfnek0";i:1;s:7:"onebone";i:2;s:7:"ogiwara";i:3;s:16:"Shootsta_ERU0531";i:4;s:7:"Toganon";i:5;s:9:"ShadowArt";i:6;s:10:"Marron0421";i:7;s:7:"No4NaMe";i:8;s:8:"SuperYYT";i:9;s:11:"SuperAdam47";}s:7:"website";s:35:"https://github.com/fuyutsuki/Texter";s:12:"creationDate";i:1587109372;}   assets/Logo.png=  `^=  @_R         assets/Texter.png  `^  , V         LICENSE]  `^]  =
      
   plugin.ymlu  `^u  Z      	   README.md  `^  F34         resources/config.yml  `^  	Zo         resources/ft.json   `^   $XN         resources/language/en_us.iniK  `^K  -         resources/language/ja_jp.ini  `^  -*         resources/language/ru_ru.ini  `^  }t         resources/language/tr_tr.ini  `^  .2         resources/language/zh_cn.ini'  `^'  +         resources/uft.json  `^        6   src/tokyo/pmmp/Texter/command/sub/TexterSubCommand.phpV  `^V  8      ,   src/tokyo/pmmp/Texter/command/sub/TxtAdd.php:  `^:  g      -   src/tokyo/pmmp/Texter/command/sub/TxtEdit.phpS  `^S  T      -   src/tokyo/pmmp/Texter/command/sub/TxtList.php  `^  ړt'      -   src/tokyo/pmmp/Texter/command/sub/TxtMove.php6  `^6   RU      /   src/tokyo/pmmp/Texter/command/sub/TxtRemove.php0  `^0  ϲ      ,   src/tokyo/pmmp/Texter/command/TxtCommand.php  `^  ?         src/tokyo/pmmp/Texter/Core.php  `^  L
      )   src/tokyo/pmmp/Texter/data/ConfigData.php8  `^8  5E      #   src/tokyo/pmmp/Texter/data/Data.php  `^  h$g      /   src/tokyo/pmmp/Texter/data/FloatingTextData.php  `^  y      :   src/tokyo/pmmp/Texter/data/UnremovableFloatingTextData.phpJ	  `^J	  y      '   src/tokyo/pmmp/Texter/EventListener.php
  `^
  7      #   src/tokyo/pmmp/Texter/i18n/Lang.php
  `^
  ͘I۶      '   src/tokyo/pmmp/Texter/i18n/Language.php  `^  W|]h      .   src/tokyo/pmmp/Texter/task/CheckUpdateTask.phpK	  `^K	  &      /   src/tokyo/pmmp/Texter/task/PrepareTextsTask.phpx  `^x  	      ,   src/tokyo/pmmp/Texter/task/SendTextsTask.php&
  `^&
  N%      +   src/tokyo/pmmp/Texter/text/FloatingText.php#  `^#  j      #   src/tokyo/pmmp/Texter/text/Text.php  `^  !ݶ      6   src/tokyo/pmmp/Texter/text/UnremovableFloatingText.php:	  `^:	  Yp      #   src/tokyo/pmmp/Texter/TexterApi.php  `^  E      <   src/tokyo/pmmp/Texter/libs/jojoe77777/FormAPI/CustomForm.phpI  `^I  !      6   src/tokyo/pmmp/Texter/libs/jojoe77777/FormAPI/Form.php  `^  |      9   src/tokyo/pmmp/Texter/libs/jojoe77777/FormAPI/FormAPI.php]  `^]  d      ;   src/tokyo/pmmp/Texter/libs/jojoe77777/FormAPI/ModalForm.phpR  `^R  ^O      <   src/tokyo/pmmp/Texter/libs/jojoe77777/FormAPI/SimpleForm.php  `^  b         virus-infections.json  `^  z|      PNG

   IHDR   @   @   iq   	pHYs    +  IDATx[\slm4ZD"2Bӌ!4Dq{q1HH=Hă qIi<3H"KRhUtfw.ݍ3O>}ߒ|~`U/-Zp-;k^jZV?`1nV,[p"&
tj,G#.Kj9,ns,#f`vcjƲ4|4Cn&ѩCA0,!7vj/w6HZO|ahri34J[Bl;5SxJlԯioxoacokj87]
'5	f`~|-@\X[Lxw]h27u(kޘwpDt|LfG||"ڵuiqμyfΞ1i>9병0}G5?nեdT
-g~pۡ:SBwqQ `XdE(`NxZ*'{QE+1Z*ޗܶLr-7q{G$׉*nJ,dvKVu@_D3Od)CV.0rRdvEV
xKp/d$k@\YS1v7R,,`3ޮ}3.Óg 'Z@\"+;)%!D6y1K,+	p!tv~¯e%!u
R-Ww{Ĳ.+G?(	%_Y-T4JJȋWZs
#H('))cVoHKXHBrHwEɻbN7J*8|7`RbCx(WUKŭddBQ
~eUKG=+P\ݪX<H)OO
7aXY&.>f^Xx)ؙk<LVUKŖr]+n|?_\SiF/0e
>)"}U{D{TKd*ʕA<DGyaO2͓YmX#Do^O3ɡ[؆W%|,L?JP(Wp>o[tTܒtӍ"6K8^l\h@se㛏UDߊiE7+≿DXTLkRF+!vҳ>}ӧO)˿%(<o-t    IENDB`PNG

   IHDR     s   m   	pHYs  \F  \FCA  vIDATxMy :DdaXavP"0?k'Zd;2x7lA1^(#Ƌl.d!ݙVtW1}B                                                                                                                                                                                                                                                                                       ݩ_>6䷿߶    8zzt' &=   @5    P   F   Tc, z  FH{[oP's% =   @5    PTnh-    F"[Q5HU 	Q{hF S`  
 ռA]В    P   F   Tň(j `  
1,îa6H9Q7 `@
(>LT
   RXyS=4o?V-m=   @5    P (IM`k   @  j`k'0Kޟ-%=   @5  )XwqT 4`kѳBx)|m{  (Ȟ> kR|~{{`A  P   FݦMMYm'\
VY0/X v#l |Ph=Z@ * O (_ En>7F@ZzR)^MdX  5 SN:Un{ 8Fgz8$dj%_   p"|(Ԁlѯ  2=  *zNY@ ؍77s~)|m{j=4@6 6G^/q=@L/_]~
>  v	 #׈RJnb)\7Cp;
ShHlѯvrewnӦ.vsGiVs sڍrc=  $ 9R䩂 ! ˧O9T`IT5N?WMJM  0%V[NLEFSN   CH~՟k{cp)+*~-m,-k1NE p0_ܤBe@CŻ FV>(*bsT9:|~7VF( xk8j@&_mY7~o	@  p1@:* O綇#3  \9ԺǡM4p SFڭ=}wsw^wN1 nĖSShtmVt@  &U@*(mpPμ|NFkZӔz )yA(;݈-?þ-iYTЎ	@  ؂AE[j! ѯ :p)+*={nzp!'_bcSݴ3E ¦t[u3<z]:9 !͹)}C1
H,Hsw^{95T{ 2|O-EOT w6uiVSK]{js!z>< TAyn{   Fhl<wG쵛{STAׯN>!۞]QM XFl9U1شg|N==H KэTo%CUTy<=t V[Ne9 |N;
*)0dw9 Mzl6~Ѽv佲;ڍrc=WWas>޿o ̼QFHcFG+;?wߜҩr;7  :rG~Yе~>i:i[ֆUv>(x/[~+rsw^{95T=qrU
Lͩ8(ܯrsjK @<UP*XW M ҈ѯ {99T{D<Ur	@*nĖSS Ɩ~%`<MLu?* OOhOJױڍrc	@*{)'*)	a+  Ov#jPB=TSN:Un{XL  @5z@*3@9eS I ۖ]LY3C-	@[FG  GHmH=(@=T0#ڣg/Ω?W @ػ?`7Ͷb:C6#Ω?õ 
SjR/ H%Fhl<wG쵛{STAyn{ deڍrc= lA	@Z)&}aX.I
sCbTT{(A OrҩrW 	@ ؿXFl9U1:SN:Un{(A άa}M%4 =WޅPӋoNYTM @%kפ6~@F|Q ! Yxk8j@&_tC ٪5S=k254uj<wGSsL +H
s@jATUJ~VGO`V9.{99T{D<UPJ݈-? - OTAyn{@v#jkSN:Un{ 2
;`Mj:zUPv#jVzuWt;7   8= 
wߜҩr 
`D- Tvr~= 0#ڣg/Ω?W  ~! sw^{~95T{X Ty<52 H!FJ86b0xk7ɹ?C'R䩂6ȕbTT{ 1_eM];l{JR䩂ѻڍrc=  ˧O9T`+	@ ؾXFl9U1V4v{&<yꔓNj ʹH	 N r%_tSV:Un{ z  Wʦd	 ,0#ڣg/Ω?W>q@X_XUB?Ss|mcE[x-*qsjP dTy<=ͅ~^s T  YWmd먺~7Mbs^wN2
I
sCmLڍrc=@%]݃e=FbO  {JK
ڍrc=  SN:Un{ *W [" xbTTm)6ĕre/~uIm-@~q+	@2
`9eSgMz  3{ vl	Ԁ 3ًsj Tf+f  %i<wGSsL 3
S (d+ݑcFΚ=
+X=G̥KD % Z=1
S7J-Q_bTT{ d˧O9TW 	@ ؿq:Uj9~5
y)'*=L  @5z@&
`9eS  xͽ{TkC?8׶   P                                                                                                                                            z*~BeY٧-?WΒїBCiMF[]9ڟŒg6=|lU@5Ή{2|_%/KO>iMvy?%Om*q{>nJן.i  q| IO?m!Uw?.廟B~f[hXc>,0/d>~5Zعt]r>n}pY, Tf
Itaxֺ[zIIYS_bJf
Vz6y>n0es7W
  P,ؗӥ~~e߻;^Z}ݿ!?D!Ӆu}J|׷~,}Kq;!/m_!|1mj31BZMq.t-+u쟡5wǹSk\KPX (9ב~_cd9k܅tҟPϽ܏4D\/Y9Fi9l_*1
Vל99"  F    \QwR]kL&涗p/[{?^S=`~\M!ym+5Qsp^;ca)X      @5j|i~>0B{Ǘ7:ξ=r}]JGK7" j}wMDMKǏXXGl9f^T   F@~g&2pt_Z眿Xi[2|OY{xB8;x!߬]lÇkG Bú7yi9]Y/5{^',@ˆR,  = gw6bK/|C'ٽkھoa7!?m.Hhߺ{8j*}:Weig]|xibbm Z٠/Lҙnלw^z}%}5cԬԱM%gvVYjAy~}_:Ko%K}g1W)X  @5                                                                                                                                                          `=Ⱥ4u    IENDB`University of Illinois/NCSA
Open Source License

Copyright (c) 2019 yuko fuyutsuki.  All rights reserved.

Developed by: yuko fuyutsuki
              https://github.com/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 with
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:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution.
* Neither the names of yuko fuyutsuki nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission.

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
CONTRIBUTORS 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 WITH THE
SOFTWARE.name: Texter
prefix: Texter
main: tokyo\pmmp\Texter\Core
version: 3.4.6
api: 3.10.0 # pmmp/PocketMine-MP
mcpe-protocol:
  - 388 # 1.13.x
  - 389 # 1.14.x
  - 390 # 1.14.60
softdepend:
  - FormAPI # jojoe77777/FormAPI
virions:
  - libFormAPI # jojoe77777/libFormAPI
author: yuko_fuyutsuki
authors:
# SpecialThanks
- mfmfnek0
- onebone
- ogiwara
- Shootsta_ERU0531
- Toganon
- ShadowArt
- Marron0421
- No4NaMe # Russian translation
- SuperYYT # Chinese translation
- SuperAdam47 # Turkish translation
website: https://github.com/fuyutsuki/Texter

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 commands
    children:
      texter.command.txt:
        default: true
        description: permission that allows player to use /txt<img src="/assets/Texter.png" width="400px">  

[![GitHub license](https://img.shields.io/badge/license-UIUC/NCSA-blue.svg)](https://github.com/fuyutsuki/Texter/blob/master/LICENSE)
[![](https://poggit.pmmp.io/shield.state/Texter)](https://poggit.pmmp.io/p/Texter)
[![](https://poggit.pmmp.io/shield.api/Texter)](https://poggit.pmmp.io/p/Texter)  

[![](https://poggit.pmmp.io/shield.dl/Texter)](https://poggit.pmmp.io/p/Texter) / [![](https://poggit.pmmp.io/shield.dl.total/Texter)](https://poggit.pmmp.io/p/Texter)

[![PoggitCI Badge](https://poggit.pmmp.io/ci.badge/fuyutsuki/Texter/Texter)](https://poggit.pmmp.io/ci/fuyutsuki/Texter/Texter)

### Overview

You can set the plugin language by changing the `locale` in [config.yml](/resources/config.yml)  
also, supported languages are automatically displayed according to the locale of the client.

Select another language:
[日本語](./.github/readme/ja_jp.md),
[русский](./.github/readme/ru_ru.md),
[中文](./.github/readme/zh_cn.md),
[Türkçe](./.github/readme/tr_tr.md)

***

## Texter

Texter is plugin that displays and deletes FloatingTextPerticle supported to multi-world.  
Latest: ver **3.4.5**  

**This branch is under development. It may have many bugs.**  

### Supporting

- [x] Minecraft(Bedrock)
- [x] Multi-language (English, Japanese, Russian, Chinese, Turkish)
- [x] Multi-world display

### Download

* [Poggit](https://poggit.pmmp.io/p/Texter)

### Commands

#### General command

| \ |command|alias|
|:--:|:--:|:--:|
|Add text|`/txt add`|`/txt a`|
|Edit text|`/txt edit`|`/txt e`|
|Move text|`/txt move`|`/txt m`|
|Remove text|`/txt remove`|`/txt r`|
|Listup texts|`/txt list`|`/txt l`|
|Help|`/txt or /txt help`|`/txt ?`|

**Please use `#` for line breaks.**

### json notation

- uft.json
```json
{
  "LevelFolderName": {
    "TextName(Unique)": {
      "Xvec": 128,
      "Yvec": 90,
      "Zvec": 128,
      "TITLE": "Title",
      "TEXT": "Text(New line with #)"
    }
  }
}
```

- ft.json
```json
{
  "LevelFolderName": {
    "TextName1(Unique)": {
      "Xvec": 128,
      "Yvec": 90,
      "Zvec": 128,
      "TITLE": "Title",
      "TEXT": "Text(New line with #)",
      "OWNER": "Steve"
    }
  }
}
```
# Texter config file

# set language (en_us, ja_jp, ru_ru, tr_tr, zh_cn)
locale: en_us

# When set to true, update notification is displayed at startup
check.update: true

# If you want to disable the command, please set it to false
can.use.commands: true

# If you only want op to use the command, please set it to true
can.use.only.op: false

# To limit the number of characters, set it to that, if unnecessary, set it to -1
char: 50

# 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:
  # ex)
  # - world
  # - world-2
{
}
; Language file compatible with Minecraft: Bedrock Edition identifiers
;
; A message doesn't need to be there to be shown correctly on the client.
; Only messages shown in Texter itself need to be here.
; This language translated by @fuyutsuki
language.name=English
language.selected=Selected {%0} ({%1}) as the base language

on.load.commands.on=Commands was set to enabled
on.load.commands.off=Commands was set to disabled
on.load.version.dev=This version is under development. There may be many fatal bugs.
on.load.update.nothing=Latest version: v{%0} / No update needed
on.load.update.available.1=Latest version: v{%0} / Current version: v{%1}
on.load.update.available.2=Update is available
on.load.update.available.3=URL: {%0}
on.load.update.offline=Because there was a problem with the network, we could not confirm whether there was an update.
on.load.is.updater=There is an update of config.yml. Move what you are currently using to another directory and restart the server

on.enable.prepared=Uft: {%0} texts / Ft: {%1} texts have been generated
on.enable.message={%0} is Enabled

command.txt.description=Manipulate floating texts
command.txt.usage=/txt <a(dd)|e(dit)|m(ove)|r(emove)|l(ist)>
command.txt.usage.indent=Please insert §6\# §fif you want line break.
command.txt.add=/txt a(dd)
command.txt.add.success=FloatingText: {%0} created
command.txt.edit=/txt e(dit)
command.txt.edit.success=FloatingText: edited {%1} on {%0}
command.txt.move=/txt m(ove)
command.txt.move.success=FloatingText: moved {%0} to {%1}
command.txt.remove=/txt r(emove)
command.txt.remove.success=FloatingText: deleted {%0}

form.ftname=FloatingText name
form.ftname.unique=FloatingText name(unique)
form.title=Title
form.text=Text
form.add.description=Add a FloatingText with the specified name, title, text to the world.
form.edit.description=You can edit the title or text of FloatingText with the specified name.
form.edit.type=Parts to edit
form.edit.content=Edit contents
form.move.description=Move the FloatingText with the specified name to the position where you are at.
form.move.here=your position
form.remove.description=Delete the FloatingText with the specified name
form.list.description.1=Lists editable FloatingTexts within 10 blocks of straight line distance from yourself.\nIf not, nothing is displayed.
form.list.description.2=Specified FloatingText name: {%0}\nPlease select the operation to do for this.

error.on.enable.not.packaged=Please use the phar file downloaded from PoggitCI (https://poggit.pmmp.io/p/Texter).
error.on.enable.not.found.libformapi=Can't find the libFormAPI in the virions folder. Please download libFormAPI and install it correctly.
error.ftname.not.specified=A FloatingText name is not specified
error.ftname.exists=A FloatingText with the specified name: {%0} already exists.
error.ftname.not.exists=No FloatingText with the specified name: {%0} does not exist.
error.config.limit.char=The number of characters of FloatingText must be less than or equal to {%0}.
error.config.limit.feed=The number of line feeds of FloatingText must be {%0} times or less.
error.config.limit.world=World: {%0} is set to disable editing of FloatingText.
error.permission=You do not have the necessary permission to edit FloatingText.
error.console=Please run from inside the game
error.player=Please execute from the console
; Language file compatible with Minecraft: Bedrock Edition identifiers
;
; A message doesn't need to be there to be shown correctly on the client.
; Only messages shown in Texter itself need to be here.
; This language translated by @fuyutsuki
language.name=日本語
language.selected={%0} ({%1}) が言語に設定されました

on.load.commands.on=コマンドは使用可能に設定されました
on.load.commands.off=コマンドは使用不能に設定されました
on.load.version.dev=このバージョンは開発途中です。多くの致命的バグが存在する可能性があります
on.load.update.nothing=最新バージョン: v{%0} / アップデートは必要ありません
on.load.update.available.1=最新バージョン: v{%0} / 現在のバージョン: v{%1}
on.load.update.available.2=アップデートが利用可能です
on.load.update.available.3=URL: {%0}
on.load.update.offline=ネットワークに問題があるため、更新があるか確認できませんでした
on.load.is.updater=config.ymlの更新があります。現在使用しているものを他のディレクトリに移し、サーバーを再起動してください

on.enable.prepared=Uft: {%0} 個 / Ft: {%1} 個を生成しました
on.enable.message={%0} が有効になりました

command.txt.description=浮き文字を操作します
command.txt.usage=/txt <a(dd)|e(dit)|m(ove)|r(emove)|l(ist)>
command.txt.usage.indent=改行をしたい場合は §6\# §fを挿入してください
command.txt.add=/txt a(dd)
command.txt.add.success=浮き文字: {%0} を作成しました
command.txt.edit=/txt e(dit)
command.txt.edit.success=浮き文字: {%0} の {%1} を変更しました
command.txt.move=/txt m(ove)
command.txt.move.success=浮き文字: {%0} を {%1} に移動しました
command.txt.remove=/txt r(emove)
command.txt.remove.success=浮き文字: {%0} を削除しました

form.ftname=浮き文字名
form.ftname.unique=浮き文字名(一意)
form.title=タイトル
form.text=テキスト
form.add.description=指定した名前、タイトル、テキストを持つ浮き文字をワールドに追加します。
form.edit.description=指定した名前を持つ浮き文字のタイトルかテキストを編集できます。
form.edit.type=編集する部位
form.edit.content=編集内容
form.move.description=自分のいる位置に指定した名前の浮き文字を移動させます。
form.move.here=あなたの位置
form.remove.description=指定した名前の浮き文字を削除します
form.list.description.1=自分から直線距離10ブロック以内の編集可能な浮き文字をリスト表示します。\n存在しない場合は何も表示されません。
form.list.description.2=指定された浮き文字名: {%0}\nこの浮き文字に行う操作を選択して下さい。

error.on.enable.not.packaged=PoggitCI(https://poggit.pmmp.io/p/Texter)からダウンロードしたpharファイルをお使いください。
error.on.enable.not.found.libformapi=virionsフォルダにlibFormAPIが見つかりません。libFormAPIをダウンロードし、正しく導入してください。
error.ftname.not.specified=浮き文字の名前が指定されていません
error.ftname.exists=指定された名前: {%0} の浮き文字が既に存在します。
error.ftname.not.exists=指定された名前: {%0} の浮き文字は存在しません。
error.config.limit.char=浮き文字の文字数は {%0} 以下である必要があります。
error.config.limit.feed=浮き文字の改行数は {%0} 回以下である必要があります。
error.config.limit.world=ワールド: {%0} では浮き文字の編集が不可能に設定されています。
error.permission=浮き文字の編集に必要な権限がありません
error.console=ゲーム内から実行してください
error.player=コンソールから実行してください
; Language file compatible with Minecraft: Bedrock Edition identifiers
;
; A message doesn't need to be there to be shown correctly on the client.
; Only messages shown in Texter itself need to be here.
; This language translated by @No4NaMe
language.name=Russian
language.selected=Выбран {%0} ({%1}) в качестве базового языка

on.load.commands.on=Команды были включены
on.load.commands.off=Команды были отключены
on.load.version.dev=Эта версия находится в стадии разработки. Там может быть много смертельных ошибок.
on.load.update.nothing=Последняя версия: v {%0} / Обновление не требуется
on.load.update.available.1=Последняя версия: v {%0} / Текущая версия: v {%1}
on.load.update.available.2=Доступно обновление
on.load.update.available.3=URL: {%0}
on.load.update.offline=Поскольку возникла проблема с сетью, мы не смогли подтвердить, было ли обновление.
on.load.is.updater=Есть обновление config.yml. Переместите то, что вы сейчас используете, в другой каталог и перезапустите сервер.

on.enable.prepared=Uft: {%0} тексты / Ft: {%1} тексты были сгенерированы
on.enable.message={%0} Включен

command.txt.description=Управлять плавающими текстами
command.txt.usage=/txt <add|edit|move|remove|list>
command.txt.usage.indent=Пожалуйста, вставьте В§6\# В§, если вы хотите, чтобы был разрыв строки.
command.txt.add=/txt add
command.txt.add.success=Плавающий-Текст: {%0} создано
command.txt.edit=/txt edit
command.txt.edit.success=Плавающий-Текст: отредактировано {%1} из {%0}
command.txt.move=/txt m(ove)
command.txt.move.success=Плавающий-Текст: перенесен {%0} to {%1}
command.txt.remove=/txt r(emove)
command.txt.remove.success=Плавающий-Текст: Удален {%0}

form.ftname=Плавающее текстовое имя
form.ftname.unique=Плавающее текстовое имя(обязательно)
form.title=Заголовок
form.text=Описание
form.add.description=Добавьте плавающий-текст с указанным именем, заголовком, текстом в мир..
form.edit.description=Вы можете редактировать заголовок или плавающий-текст с указанным именем.
form.edit.type=Части для редактирования
form.edit.content=Изменить содержимое
form.move.description=Переместите Плавающий-текст с указанным именем в положение, в котором вы находитесь.
form.move.here=твоя позиция
form.remove.description=Удалить Плавающий-текст с указанным именем
form.list.description.1=Перечисляет редактируемые плавающие тексты в пределах 10 блоков на расстоянии прямой линии от вас.\nЕсли нет, то ничего не отображается.
form.list.description.2=Указанное имя Плавающего-текста: {%0}\nПожалуйста, выберите операцию для этого.

error.on.enable.not.packaged=Пожалуйста, используйте файл phar, загруженный с PoggitCI (https://poggit.pmmp.io/p/Texter).
error.on.enable.not.found.libformapi=Не могу найти libFormAPI в папке virions. Пожалуйста, скачайте libFormAPI и установите его правильно.
error.ftname.not.specified=Имя Плавающего-текста не указано
error.ftname.exists=Плавающий-текст с указанным именем: {%0} уже существует.
error.ftname.not.exists=Нет Плавающего-текста с указанным именем: {%0} не существует.
error.config.limit.char=Количество символов Плавающего-текста должно быть меньше или равно {%0}.
error.config.limit.feed=Количество переводов строки Плавающего-текста должно быть {%0} раз или меньше.
error.config.limit.world=Мир: {%0} отключает редактирование Плавающего-текста.
error.permission=У вас нет необходимых прав для редактирования Плавающего-текста.
error.console=Пожалуйста, используйте команды внутри игры
error.player=Пожалуйста, выполните с консоли; Language file compatible with Minecraft: Bedrock Edition identifiers
;
; A message doesn't need to be there to be shown correctly on the client.
; Only messages shown in Texter itself need to be here.
; Turkish language translated by @SuperAdam47
language.name=Türkçe
language.selected=Temel dil olarak {%0} ({%1}) seçildi

on.load.commands.on=Komutlar etkin olarak ayarlandı
on.load.commands.off=Komutlar devre dışı olarak ayarlandı
on.load.version.dev=Bu sürüm geliştirilme aşamasındadır. Bazı hatalar olabilir.
on.load.update.nothing=Güncel sürüm: v{%0} / Güncellemeye gerek yok
on.load.update.available.1=Güncel sürüm: v{%0} / Sizdeki sürüm: v{%1}
on.load.update.available.2=Güncelleme mevcut
on.load.update.available.3=URL: {%0}
on.load.update.offline=Ağda bir sorununuz var, güncelleme olup olmadığını doğrulayamadık.
on.load.is.updater=Config.yml dosyanızın güncellemesi var. Şu anda kullandığınız dosyayı başka bir dizine taşıyın ve sunucuyu yeniden başlatın

on.enable.prepared=Uft: {%0} metin / Ft: {%1} metinleri oluşturuldu
on.enable.message={%0} Etkin

command.txt.description=Uçan metni değiştirme
command.txt.usage=/txt <add | edit | move | remove | list>
command.txt.usage.indent=Lütfen satır sonu olmasını istiyorsanız§6 \ #§r ekleyin.
command.txt.add=/txt add
command.txt.add.success=Uçan Metin: {%0} oluşturuldu
command.txt.edit=/txt edit
command.txt.edit.success=UçanMetin: {%0} üzerinde {%1} düzenlendi
command.txt.move=/txt move
command.txt.move.success=Uçan Metin: {%0} {%1} klasörüne taşındı
command.txt.remove=/txt remove
command.txt.remove.success=Uçan Metin: {%0} silindi.

form.ftname=UçanMetin adı
form.ftname.unique=UçanMetin adı (benzersiz)
form.title=Başlık
form.text=Metin
form.add.description=Dünyaya belirtilen ad, başlık, metin ile bir UçanMetin ekleyin.
form.edit.description=UçanMetin'in başlığını veya metnini belirtilen adla düzenleyebilirsiniz.
form.edit.type=Düzenlenecek parçalar
form.edit.content=İçeriği düzenle
form.move.description=Belirtilen adla UçanMetin'i bulunduğunuz konuma taşıyın.
form.move.here=konumunuz
form.remove.description=UçanMetin öğesini belirtilen adla silin
form.list.description.1=Kendinizden düz çizgi mesafesinin 10 blok içersindeki düzenlenebilir UçanMetinler öğelerini listeler. \nAksi durumda hiçbir şey görüntülenmez.
form.list.description.2=Belirtilen Uçan Metin metni adı: {%0} \nLütfen bunun için yapılacak işlemi seçin.

error.on.enable.not.packaged=Lütfen PoggitCI'den indirilen phar dosyasını kullanın (https://poggit.pmmp.io/p/Texter).
error.on.enable.not.found.libformapi=libFormAPI virions klasöründe bulunamadı. Lütfen libFormAPI'yi indirin ve doğru şekilde kurun.
error.ftname.not.specified=Bir UçanMetin adı belirtilmedi
error.ftname.exists=Belirtilen ada sahip bir UçanMetin: {%0} zaten var.
error.ftname.not.exists=Belirtilen ada sahip UçanMetin yok: {%0} mevcut değil.
error.config.limit.char=UçanMetin öğesinin karakter sayısı {%0} veya daha az olmalıdır.
error.config.limit.feed=UçanMetin öğesinin satır besleme sayısı {%0} kez veya daha az olmalıdır.
error.config.limit.world=Dünya: {%0}, UçanMetin düzenlemesini devre dışı bırakmak üzere ayarlanmış.
error.permission=UçanMetin dosyasını düzenlemek için gerekli izniniz yok.
error.console=Lütfen oyunun içinden yürütün.
error.player=Lütfen konsoldan yürütün.
; 与Minecraft兼容的语言文件:Bedrock版本标识符
;
; 消息不需要在客户机上正确显示。
; 只有显示在文本本身的消息在这里。
; 该语言文件由@YYT提供
language.name=中文
language.selected=已选择 {%0} ({%1}) 作为默认语言

on.load.commands.on=指令被设置为启用
on.load.commands.off=指令被设置为禁用
on.load.version.dev=该版本处于开发中。 可能会有很多错误和漏洞。
on.load.update.nothing=最新版本： v{%0} / 无需更新
on.load.update.available.1=最新版本： v{%0} / 当前版本： v{%1}
on.load.update.available.2=有升级可用
on.load.update.available.3=URL: {%0}
on.load.update.offline=因为网络有问题，我们无法确认是否有更新。
on.load.is.updater=有配置文件的更新。将当前使用的内容移动到另一个目录并重新启动服务器。

on.enable.prepared=Uft: {%0} 文本 / Ft: {%1} 文本已经生成
on.enable.message={%0} 已启用

command.txt.description=操纵浮动文本
command.txt.usage=/txt <a(dd)|e(dit)|m(ove)|r(emove)|l(ist)>
command.txt.usage.indent=请输入 §6\# §f如果你想换行。
command.txt.add=/txt a(dd)
command.txt.add.success=FloatingText: {%0} 已创建
command.txt.edit=/txt e(dit)
command.txt.edit.success=FloatingText: 在 {%0} 上编辑 {%1}
command.txt.move=/txt m(ove)
command.txt.move.success=FloatingText: 将 {%0} 移动至 {%1}
command.txt.remove=/txt r(emove)
command.txt.remove.success=FloatingText: 删除 {%0}

form.ftname=浮动文本名称
form.ftname.unique=浮动文本名称(唯一)
form.title=标题
form.text=内容
form.add.description=向世界添加具有指定名称、标题和文本的浮动文本。
form.edit.description=您可以编辑具有指定名称的浮动文本的标题或文本。
form.edit.type=编辑部分
form.edit.content=编辑内容
form.move.description=将具有指定名称的浮动文本移动到当前位置。
form.move.here=你的位置
form.remove.description=删除具有指定名称的浮动文本
form.list.description.1=列出可编辑的浮动文本在10块直线距离内。如果没有，则不显示任何内容。
form.list.description.2=指定浮动文本名称名称: {%0}\n请为此选择要做的操作。

error.on.enable.not.packaged=请使用从PoggitCI下载的phar文件 (https://poggit.pmmp.io/p/Texter).
error.on.enable.not.found.libformapi=无法在病毒粒子文件夹中找到libFormAPI。请下载libFormAPI并正确安装。
error.ftname.not.specified=没有指定浮点文本名称
error.ftname.exists=具有指定名称的浮动文本: {%0} 已经存在。
error.ftname.not.exists=没有指定名称的浮动文本: {%0} 不存在。
error.config.limit.char=浮动文本的字符数必须小于或等于 {%0} 。
error.config.limit.feed=浮动文本的换行次数必须少于或等于 {%0} 次。
error.config.limit.world=世界: {%0} 设置为禁用浮动文本编辑。
error.permission=您没有编辑浮动文本的必要权限。
error.console=请从游戏中运行
error.player=请从控制台执行
{
  "world": {
    "TextName1": {
      "Xvec": 128,
      "Yvec": 90,
      "Zvec": 128,
      "TITLE": "Title",
      "TEXT": "Text(New line with #)"
    },
    "TextName2": {
      "Xvec": 128,
      "Yvec": 90,
      "Zvec": 130,
      "TITLE": "Title",
      "TEXT": "Text(New line with #)"
    },
    "TextName3": {
      "Xvec": 128,
      "Yvec": 90,
      "Zvec": 132,
      "TITLE": "Title",
      "TEXT": "Text(New line with #)"
    }
  }
}
<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\command\sub;

use pocketmine\Player;
use tokyo\pmmp\Texter\i18n\Lang;
use tokyo\pmmp\Texter\i18n\Language;

/**
 * Class TexterSubCommand
 * @package tokyo\pmmp\Texter\command\sub
 */
abstract class TexterSubCommand {

  /** @var Player */
  protected $player;
  /** @var Language */
  protected $lang;

  public function __construct(Player $player, string $default = "") {
    $this->player = $player;
    $this->lang = Lang::fromLocale($player->getLocale());
    $this->execute($default);
  }

  abstract public function execute(string $default = ""): void;
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\command\sub;

use tokyo\pmmp\Texter\libs\jojoe77777\FormAPI\CustomForm;
use pocketmine\level\Position;
use pocketmine\Player;
use pocketmine\utils\TextFormat;
use tokyo\pmmp\Texter\Core;
use tokyo\pmmp\Texter\data\ConfigData;
use tokyo\pmmp\Texter\text\FloatingText;
use tokyo\pmmp\Texter\TexterApi;

/**
 * Class TxtAdd
 * @package tokyo\pmmp\Texter\command\sub
 */
class TxtAdd extends TexterSubCommand {

  /** @var int response key */
  public const NAME = 1;
  public const TITLE = 3;
  public const TEXT = 4;

  public function execute(string $default = ""): void {
    $pluginDescription = Core::get()->getDescription();
    $description = $this->lang->translateString("form.add.description");
    $ftName = $this->lang->translateString("form.ftname.unique");
    $indent = $this->lang->translateString("command.txt.usage.indent");
    $title = $this->lang->translateString("form.title");
    $text = $this->lang->translateString("form.text");

    $custom = new CustomForm(function (Player $player, ?array $response) use ($pluginDescription) {
      if ($response !== null) {
        $level = $player->getLevel();
        if (!empty($response[self::NAME])) {
          $exists = TexterApi::getFtByLevel($level, $response[self::NAME]);
          if ($exists === null) {
            $title = $player->isOp() ? $response[self::TITLE] : TextFormat::clean($response[self::TITLE]);
            $text = $player->isOp() ? $response[self::TEXT] : TextFormat::clean($response[self::TEXT]);
            $ft = new FloatingText($response[self::NAME], Position::fromObject($player->add(0, 1, 0), $level), $title, $text, $player->getName());
            $cd = ConfigData::make();
            if ($cd->checkCharLimit($ft->getTextsForCheck(FloatingText::CHECK_CHAR))) {
              if ($cd->checkFeedLimit($ft->getTextsForCheck(FloatingText::CHECK_FEED))) {
                $ft->sendToLevel($level);
                TexterApi::registerText($ft);
                $message = $this->lang->translateString("command.txt.add.success", [
                  TextFormat::clean($response[self::NAME])
                ]);
                $player->sendMessage(TextFormat::GREEN . "[{$pluginDescription->getPrefix()}] $message");
              }else {
                $message = $this->lang->translateString("error.config.limit.feed", [
                  $cd->getFeedLimit()
                ]);
                $player->sendMessage(TextFormat::RED . "[{$pluginDescription->getPrefix()}] $message");
              }
            }else {
              $message = $this->lang->translateString("error.config.limit.char", [
                $cd->getCharLimit()
              ]);
              $player->sendMessage(TextFormat::RED . "[{$pluginDescription->getPrefix()}] $message");
            }
          }else {
            $message = $this->lang->translateString("error.ftname.exists", [
              $response[self::NAME]
            ]);
            $player->sendMessage(TextFormat::RED . "[{$pluginDescription->getPrefix()}] $message");
          }
        }else {
          $message = $this->lang->translateString("error.ftname.not.specified");
          $player->sendMessage(TextFormat::RED . "[{$pluginDescription->getPrefix()}] $message");
        }
      }
    });

    $custom->setTitle("[{$pluginDescription->getPrefix()}] /txt add");
    $custom->addLabel($description);
    $custom->addInput($ftName, $ftName);
    $custom->addLabel($indent);
    $custom->addInput($title, $title);
    $custom->addInput($text, $text);
    $this->player->sendForm($custom);
  }
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\command\sub;

use tokyo\pmmp\Texter\libs\jojoe77777\FormAPI\CustomForm;
use pocketmine\Player;
use pocketmine\utils\TextFormat;
use tokyo\pmmp\Texter\Core;
use tokyo\pmmp\Texter\data\ConfigData;
use tokyo\pmmp\Texter\data\FloatingTextData;
use tokyo\pmmp\Texter\text\Text;
use tokyo\pmmp\Texter\TexterApi;

/**
 * Class TxtEdit
 * @package tokyo\pmmp\Texter\command\sub
 */
class TxtEdit extends TexterSubCommand {

  /** @var int response key */
  public const FT_NAME = 1;
  public const TYPE = 2;
  public const TITLE = 0;
  public const TEXT = 1;
  public const CONTENT = 4;

  public function execute(string $default = ""): void {
    $pluginDescription = Core::get()->getDescription();
    $description = $this->lang->translateString("form.edit.description");
    $ftName = $this->lang->translateString("form.ftname");
    $type = $this->lang->translateString("form.edit.type");
    $title = $this->lang->translateString("form.title");
    $text = $this->lang->translateString("form.text");
    $tips = $this->lang->translateString("command.txt.usage.indent");
    $content = $this->lang->translateString("form.edit.content");

    $custom = new CustomForm(function (Player $player, ?array $response) use ($pluginDescription, $title, $text) {
      if ($response !== null) {
        $level = $player->getLevel();
        if (!empty($response[self::FT_NAME])) {
          $ft = TexterApi::getFtByLevel($level, $response[self::FT_NAME]);
          if ($ft !== null) {
            if ($ft->isOwner($player)) {
              $cd = ConfigData::make();
              switch ($response[self::TYPE]) {
                case self::TITLE:
                  $test = TextFormat::clean($response[self::CONTENT].$ft->getText());
                  if ($cd->checkCharLimit(str_replace("\n", "", $test))) {
                    if ($cd->checkFeedLimit($test)) {
                      $ft
                        ->setTitle($response[self::CONTENT])
                        ->sendToLevel($level, Text::SEND_TYPE_EDIT);
                      FloatingTextData::make()->saveFtChange($ft);
                      $message = $this->lang->translateString("command.txt.edit.success", [
                        $ft->getName(),
                        $title
                      ]);
                      $player->sendMessage(TextFormat::GREEN . "[{$pluginDescription->getPrefix()}] $message");
                    }
                  }
                  break;

                case self::TEXT:
                  $test = TextFormat::clean($ft->getTitle().$response[self::CONTENT]);
                  if ($cd->checkCharLimit(str_replace("\n", "", $test))) {
                    if ($cd->checkFeedLimit($test)) {
                      $ft
                        ->setText($response[self::CONTENT])
                        ->sendToLevel($level, Text::SEND_TYPE_EDIT);
                      FloatingTextData::make()->saveFtChange($ft);
                      $message = $this->lang->translateString("command.txt.edit.success", [
                        $ft->getName(),
                        $text
                      ]);
                      $player->sendMessage(TextFormat::GREEN . "[{$pluginDescription->getPrefix()}] $message");
                    }
                  }
                  break;
              }
            }else {
              $message = $this->lang->translateString("error.permission");
              $player->sendMessage(TextFormat::RED . "[{$pluginDescription->getPrefix()}] $message");
            }
          }else {
            $message = $this->lang->translateString("error.ftname.not.exists", [
              $response[self::FT_NAME]
            ]);
            $player->sendMessage(TextFormat::RED . "[{$pluginDescription->getPrefix()}] $message");
          }
        }else {
          $message = $this->lang->translateString("error.ftname.not.specified");
          $player->sendMessage(TextFormat::RED . "[{$pluginDescription->getPrefix()}] $message");
        }
      }
    });

    $custom->setTitle("[{$pluginDescription->getPrefix()}] /txt edit");
    $custom->addLabel($description);
    $custom->addInput($ftName, $ftName, $default);
    $custom->addDropdown($type, [$title, $text]);
    $custom->addLabel($tips);
    $custom->addInput($content, $content);
    $this->player->sendForm($custom);
  }
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\command\sub;

use tokyo\pmmp\Texter\libs\jojoe77777\FormAPI\SimpleForm;
use pocketmine\Player;
use tokyo\pmmp\Texter\Core;
use tokyo\pmmp\Texter\text\FloatingText;
use tokyo\pmmp\Texter\TexterApi;

/**
 * Class TxtList
 * @package tokyo\pmmp\Texter\command\sub
 */
class TxtList extends TexterSubCommand {

  /** @var int response key */
  public const EDIT = 0;
  public const MOVE = 1;
  public const REMOVE = 2;

  public function execute(string $default = ""): void {
    $pluginDescription = Core::get()->getDescription();
    $description = $this->lang->translateString("form.list.description.1");

    $fts = TexterApi::getFtsByLevel($this->player->getLevel());
    $search = [];
    foreach ($fts as $name => $ft) {
      /** @var FloatingText $ft */
      if ($this->player->distance($ft) <= 10 && $ft->isOwner($this->player)) {
        $search[] = $ft;
      }
    }

    $list1 = new SimpleForm(function (Player $player, ?int $key) use ($pluginDescription, $search) {
      if ($key !== null) {
        $target = $search[$key];
        $description = $this->lang->translateString("form.list.description.2", [
          $target->getName()
        ]);
        $list2 = new SimpleForm(function (Player $player, ?int $key) use ($target) {
          if ($key !== null) {
            switch ($key) {
              case self::EDIT:
                new TxtEdit($player, $target->getName());
                break;
              case self::MOVE:
                new TxtMove($player, $target->getName());
                break;
              case self::REMOVE:
                new TxtRemove($player, $target->getName());
                break;
            }
          }
        });

        $list2->setTitle("[{$pluginDescription->getPrefix()}] /txt list");
        $list2->setContent($description);
        $list2->addButton("edit");
        $list2->addButton("move");
        $list2->addButton("remove");
        $player->sendForm($list2);
      }
    });

    $list1->setTitle("[{$pluginDescription->getPrefix()}] /txt list");
    $list1->setContent($description);
    foreach ($search as $ft) $list1->addButton($ft->getName());
    $this->player->sendForm($list1);
  }
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\command\sub;

use tokyo\pmmp\Texter\libs\jojoe77777\FormAPI\CustomForm;
use pocketmine\level\Position;
use pocketmine\Player;
use pocketmine\utils\TextFormat;
use tokyo\pmmp\Texter\Core;
use tokyo\pmmp\Texter\data\FloatingTextData;
use tokyo\pmmp\Texter\text\Text;
use tokyo\pmmp\Texter\TexterApi;

/**
 * Class TxtMove
 * @package tokyo\pmmp\Texter\command\sub
 */
class TxtMove extends TexterSubCommand {

  /** @var int response key */
  public const FT_NAME = 1;

  public function execute(string $default = ""): void {
    $pluginDescription = Core::get()->getDescription();
    $description = $this->lang->translateString("form.move.description");
    $ftName = $this->lang->translateString("form.ftname");

    $custom = new CustomForm(function (Player $player, ?array $response) use ($pluginDescription) {
      if ($response !== null) {
        $level = $player->getLevel();
        if (!empty($response[self::FT_NAME])) {
          $ft = TexterApi::getFtByLevel($level, $response[self::FT_NAME]);
          if ($ft !== null) {
            if ($ft->isOwner($player)) {
              $ft
                ->setPosition(Position::fromObject($player->add(0, 2, 0), $level))
                ->sendToLevel($level, Text::SEND_TYPE_MOVE);
              FloatingTextData::make()->saveFtChange($ft);
              $message = $this->lang->translateString("command.txt.move.success", [
                $ft->getName(),
                $this->lang->translateString("form.move.here")// TODO: xyz specification(3.2.0~)
              ]);
              $player->sendMessage(TextFormat::GREEN . "[{$pluginDescription->getPrefix()}] $message");
            }else {
              $message = $this->lang->translateString("error.permission");
              $player->sendMessage(TextFormat::RED . "[{$pluginDescription->getPrefix()}] $message");
            }
          }else {
            $message = $this->lang->translateString("error.ftname.not.exists", [
              $response[self::FT_NAME]
            ]);
            $player->sendMessage(TextFormat::RED . "[{$pluginDescription->getPrefix()}] $message");
          }
        }else {
          $message = $this->lang->translateString("error.ftname.not.specified");
          $player->sendMessage(TextFormat::RED . "[{$pluginDescription->getPrefix()}] $message");
        }
      }
    });

    $custom->setTitle("[{$pluginDescription->getPrefix()}] /txt remove");
    $custom->addLabel($description);
    $custom->addInput($ftName, $ftName, $default);
    $this->player->sendForm($custom);
  }
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\command\sub;

use tokyo\pmmp\Texter\libs\jojoe77777\FormAPI\CustomForm;
use pocketmine\Player;
use pocketmine\utils\TextFormat;
use tokyo\pmmp\Texter\Core;
use tokyo\pmmp\Texter\text\Text;
use tokyo\pmmp\Texter\TexterApi;

/**
 * Class TxtRemove
 * @package tokyo\pmmp\Texter\command\sub
 */
class TxtRemove extends TexterSubCommand {

  /** @var int response key */
  public const FT_NAME = 1;

  public function execute(string $default = ""): void {
    $pluginDescription = Core::get()->getDescription();
    $description = $this->lang->translateString("form.remove.description");
    $ftName = $this->lang->translateString("form.ftname");

    $custom = new CustomForm(function (Player $player, ?array $response) use ($pluginDescription) {
      if ($response !== null) {
        $level = $player->getLevel();
        if (!empty($response[self::FT_NAME])) {
          $ft = TexterApi::getFtByLevel($level, $response[self::FT_NAME]);
          if ($ft !== null) {
            if ($ft->isOwner($player)) {
              $ft->sendToLevel($level, Text::SEND_TYPE_REMOVE);
              TexterApi::removeFtByLevel($level, $ft->getName());
              $message = $this->lang->translateString("command.txt.remove.success", [
                $ft->getName()
              ]);
              $player->sendMessage(TextFormat::GREEN . "[{$pluginDescription->getPrefix()}] $message");
            }else {
              $message = $this->lang->translateString("error.permission");
              $player->sendMessage(TextFormat::RED . "[{$pluginDescription->getPrefix()}] $message");
            }
          }else {
            $message = $this->lang->translateString("error.ftname.not.exists", [
              $response[self::FT_NAME]
            ]);
            $player->sendMessage(TextFormat::RED . "[{$pluginDescription->getPrefix()}] $message");
          }
        }else {
          $message = $this->lang->translateString("error.ftname.not.specified");
          $player->sendMessage(TextFormat::RED . "[{$pluginDescription->getPrefix()}] $message");
        }
      }
    });

    $custom->setTitle("[{$pluginDescription->getPrefix()}] /txt remove");
    $custom->addLabel($description);
    $custom->addInput($ftName, $ftName, $default);
    $this->player->sendForm($custom);
  }
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\command;

use pocketmine\command\Command;
use pocketmine\command\CommandSender;
use pocketmine\Player;
use pocketmine\utils\TextFormat;
use tokyo\pmmp\Texter\command\sub\TxtAdd;
use tokyo\pmmp\Texter\command\sub\TxtEdit;
use tokyo\pmmp\Texter\command\sub\TxtList;
use tokyo\pmmp\Texter\command\sub\TxtMove;
use tokyo\pmmp\Texter\command\sub\TxtRemove;
use tokyo\pmmp\Texter\Core;
use tokyo\pmmp\Texter\data\ConfigData;
use tokyo\pmmp\Texter\i18n\Lang;

/**
 * Class TxtCommand
 * @package tokyo\pmmp\Texter\command
 */
class TxtCommand extends Command {

  public function __construct() {
    $permission = ConfigData::make()->canUseOnlyOp() ? "texter.command.*" : "texter.command.txt";
    $this->setPermission($permission);
    $cl = Lang::fromConsole();
    $description = $cl->translateString("command.txt.description");
    $usage = $cl->translateString("command.txt.usage");
    parent::__construct("txt", $description, $usage);
  }

  public function execute(CommandSender $sender, string $commandLabel, array $args) {
    if (Core::get()->isDisabled() || !$this->testPermission($sender)) return false;
    if ($sender instanceof Player) {
      $pluginDescription = Core::get()->getDescription();
      $cd = ConfigData::make();
      $lang = Lang::fromLocale($sender->getLocale());
      if ($cd->checkWorldLimit($sender->getLevel()->getName())) {
        if (isset($args[0])) {
          switch ($args[0]) {
            case "add":
            case "a":
              new TxtAdd($sender);
              break;

            case "edit":
            case "e":
              new TxtEdit($sender);
              break;

            case "move":
            case "m":
              new TxtMove($sender);
              break;

            case "remove":
            case "r":
              new TxtRemove($sender);
              break;

            case "list":
            case "l":
              new TxtList($sender);
              break;

            default:
              $message = $lang->translateString("command.txt.usage");
              $sender->sendMessage("[{$pluginDescription->getPrefix()}] $message");
              break;
          }
        }else {
          $message = $lang->translateString("command.txt.usage");
          $sender->sendMessage("[{$pluginDescription->getPrefix()}] $message");
        }
      }else {
        $message = $lang->translateString("error.config.limit.world", [
          $sender->getLevel()->getName()
        ]);
        $sender->sendMessage(TextFormat::RED . "[{$pluginDescription->getPrefix()}] $message");
      }
    }else {
      $info = Lang::fromConsole()->translateString("error.console");
      Core::get()->getLogger()->info(TextFormat::RED.$info);
    }
    return true;
  }
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter;

use pocketmine\event\Listener;
use pocketmine\plugin\PluginBase;
use pocketmine\utils\TextFormat;
use pocketmine\utils\VersionString;
use tokyo\pmmp\Texter\command\TxtCommand;
use tokyo\pmmp\Texter\data\ConfigData;
use tokyo\pmmp\Texter\data\FloatingTextData;
use tokyo\pmmp\Texter\data\UnremovableFloatingTextData;
use tokyo\pmmp\Texter\i18n\Lang;
use tokyo\pmmp\Texter\task\CheckUpdateTask;
use tokyo\pmmp\Texter\task\PrepareTextsTask;
use function class_exists;

/**
 * Class Core
 * @package tokyo\pmmp\Texter
 */
class Core extends PluginBase implements Listener {

  /** @var Core */
  private static $core;
  /** @var bool */
  private static $isUpdater = false;

  public function onLoad() {
    self::$core = $this;
    $this
      ->loadResources()
      ->loadLanguage()
      ->registerCommands()
      ->prepareTexts()
      ->checkUpdate();
  }

  public function onEnable() {
    if ($this->checkPackaged()) {
      $listener = new EventListener;
      $this->getServer()->getPluginManager()->registerEvents($listener, $this);
    }else {
      $this->getServer()->getPluginManager()->disablePlugin($this);
    }
  }

  private function loadResources(): self {
    $dir = $this->getDataFolder();
    new ConfigData($this, $dir, "config.yml");
    new UnremovableFloatingTextData($this, $dir, "uft.json");
    new FloatingTextData($this, $dir, "ft.json");
    return $this;
  }

  private function loadLanguage(): self {
    new Lang($this);
    $cl = Lang::fromConsole();
    $message1 = $cl->translateString("language.selected", [
      $cl->getName(),
      $cl->getLang()
    ]);
    $this->getLogger()->info(TextFormat::GREEN . $message1);
    if (self::isUpdater()) {
      $message2 = $cl->translateString("on.load.is.updater");
      $this->getLogger()->notice($message2);
    }
    return $this;
  }

  private function registerCommands(): self {
    if ($canUse = ConfigData::make()->canUseCommands()) {
      $map = $this->getServer()->getCommandMap();
      $commands = [
        new TxtCommand
      ];
      $map->registerAll($this->getName(), $commands);
      $message = Lang::fromConsole()->translateString("on.load.commands.on");
    }else {
      $message = Lang::fromConsole()->translateString("on.load.commands.off");
    }
    $this->getLogger()->info(($canUse ? TextFormat::GREEN : TextFormat::RED) . $message);
    return $this;
  }

  private function prepareTexts(): self {
    $prepare = new PrepareTextsTask;
    $this->getScheduler()->scheduleDelayedRepeatingTask($prepare, 20, 1);
    return $this;
  }

  private function checkUpdate(): self {
    if (ConfigData::make()->checkUpdate()) {
      try {
        $this->getServer()->getAsyncPool()->submitTask(new CheckUpdateTask);
      } catch (\Exception $ex) {
        $this->getLogger()->warning($ex->getMessage());
      }
    }
    return $this;
  }

  public function compareVersion(bool $success, ?VersionString $new = null, string $url = "") {
    $cl = Lang::fromConsole();
    $logger = $this->getLogger();
    if ($success) {
      $current = new VersionString($this->getDescription()->getVersion());
      switch ($current->compare($new)) {
        case -1:// new: older
          $message = $cl->translateString("on.load.version.dev");
          $logger->warning($message);
          break;

        case 0:// same
          $message = $cl->translateString("on.load.update.nothing", [
            $current->getFullVersion()
          ]);
          $logger->notice($message);
          break;

        case 1:// new: newer
          $messages[] = $cl->translateString("on.load.update.available.1", [
            $new->getFullVersion(),
            $current->getFullVersion()
          ]);
          $messages[] = $cl->translateString("on.load.update.available.2");
          $messages[] = $cl->translateString("on.load.update.available.3", [
            $url
          ]);
          foreach ($messages as $message) $logger->notice($message);
      }
    }else {
      $message = $cl->translateString("on.load.update.offline");
      $logger->notice($message);
    }
  }

  private function checkPackaged(): bool {
    $cl = Lang::fromConsole();
    $logger = $this->getLogger();
    if ($this->isPhar()) {
      if (class_exists("\\tokyo\\pmmp\\Texter\\libs\\jojoe77777\\FormAPI\\FormAPI")) {
        return true;// PoggitCI
      }else {
        $message = $cl->translateString("error.on.enable.not.packaged");
        $logger->critical($message);
        return false;
      }
    }else {
      $plugins = $this->getServer()->getPluginManager()->getPlugins();
      if (isset($plugins["DEVirion"]) || isset($plugins["FormAPI"])) {
        if (class_exists("\\jojoe77777\\FormAPI\\FormAPI")) {
          return true;// developer
        }else {
          $message = $cl->translateString("error.on.enable.not.found.libformapi");
          $logger->critical($message);
          return false;
        }
      }else {
        $message = $cl->translateString("error.on.enable.not.packaged");
        $logger->critical($message);
        return false;
      }
    }
  }

  public static function isUpdater(): bool {
    return self::$isUpdater;
  }

  public static function setIsUpdater(bool $bool = true) {
    self::$isUpdater = $bool;
  }

  public static function get(): Core {
    return self::$core;
  }
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\data;

use pocketmine\plugin\PluginBase;
use pocketmine\utils\Config;
use tokyo\pmmp\Texter\Core;
use tokyo\pmmp\Texter\i18n\Lang;
use function strtolower;
use function mb_strlen;
use function mb_substr_count;
use function is_array;
use function array_flip;
use function is_string;

/**
 * Class ConfigData
 * @package tokyo\pmmp\Texter\data
 */
class ConfigData extends Config implements Data {

  /** @var ConfigData */
  private static $instance;

  public function __construct(PluginBase $plugin, string $path, string $file) {
    $plugin->saveResource($file);
    parent::__construct($path.$file, Config::YAML);
    self::$instance = $this;
    $this->checkIsUpdater();
  }

  private function checkIsUpdater() {
    Core::setIsUpdater(!$this->exists("can.use.only.op"));
  }

  public function getLocale(): string {
    return strtolower($this->get("locale", Lang::FALLBACK));
  }

  public function checkUpdate(): bool {
    return (bool) $this->get("check.update", true);
  }

  public function canUseCommands(): bool {
    return (bool) $this->get("can.use.commands", true);
  }

  public function canUseOnlyOp(): bool {
    return (bool) $this->get("can.use.only.op", false);
  }

  public function checkCharLimit(string $text): bool {
    $limit = $this->getCharLimit();
    if ($limit === -1) {
      return true;
    }else {
      $length = mb_strlen($text);
      return $limit >= $length;
    }
  }

  public function getCharLimit(): int {
    return (int) $this->get("char", -1);
  }

  public function checkFeedLimit(string $text): bool {
    $limit = $this->getFeedLimit();
    if ($limit === -1)
      return true;
    $feed = mb_substr_count($text, "#");
    return $limit >= $feed;
  }

  public function getFeedLimit(): int {
    return (int) $this->get("feed", -1);
  }

  public function checkWorldLimit(string $levelName): bool {
    if ($this->exists("world")) {
      $limited = $this->get("world", []);
      if (is_array($limited)) {
        $flip = array_flip($limited);
        return !isset($flip[$levelName]);
      }
      if (is_string($limited)) {
        return $limited !== $levelName;
      }
    }
    return true;// isn't limited
  }

  public static function make(): ConfigData {
    return self::$instance;
  }
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\data;

/**
 * Interface Data
 * @package tokyo\pmmp\Texter\data
 */
interface Data {

  public const KEY_NAME = "NAME";
  public const KEY_LEVEL = "LEVEL";
  public const KEY_X = "Xvec";
  public const KEY_Y = "Yvec";
  public const KEY_Z = "Zvec";
  public const KEY_TITLE = "TITLE";
  public const KEY_TEXT = "TEXT";

  public const JSON_OPTIONS = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;

  public static function make();
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\data;

use pocketmine\level\Level;
use pocketmine\plugin\Plugin;
use pocketmine\utils\Config;
use tokyo\pmmp\Texter\text\FloatingText;

/**
 * Class FloatingTextData
 * @package tokyo\pmmp\Texter\data
 */
class FloatingTextData extends Config implements Data {

  public const KEY_OWNER = "OWNER";

  /** @var FloatingTextData */
  private static $instance;

  public function __construct(Plugin $plugin, string $path, string $file) {
    $plugin->saveResource($file);
    parent::__construct($path.$file, Config::JSON);
    $this->enableJsonOption(Data::JSON_OPTIONS);
    self::$instance = $this;
  }

  public function saveFtChange(FloatingText $ft): bool {
    $levelName = $ft->level->getFolderName();
    $levelFts = $this->get($levelName, []);
    if (!empty($levelFts)) {
      $levelFts[$ft->getName()] = $ft->format();
      $this->set($levelName, $levelFts);
    }else {
      $this->set($levelName, [$ft->getName() => $ft->format()]);
    }
    $this->save();
    return true;
  }

  public function removeFtsByLevel(Level $level): bool {
    return $this->removeFtsByLevelName($level->getFolderName());
  }

  public function removeFtsByLevelName(string $levelName): bool {
    if ($bool = $this->exists($levelName)) {
      $this->remove($levelName);
      $this->save();
    }
    return $bool;
  }

  public function removeFtByLevel(Level $level, string $name): void {
    $this->removeFtByLevelName($level->getFolderName(), $name);
  }

  public function removeFtByLevelName(string $levelName, string $name): void {
    if ($bool = $this->exists($levelName)) {
      if ($bool) {
        $levelFts = $this->get($levelName);
        unset($levelFts[$name]);
        $this->set($levelName, $levelFts);
        $this->save();
      }
    }
  }

  public function getData(): array {
    $data = [];
    $fts = $this->getAll();
    foreach ($fts as $levelName => $texts) {
      foreach ($texts as $textName => $val) {
        $data[] = [
          Data::KEY_NAME => (string) $textName,
          Data::KEY_LEVEL => (string) $levelName,
          Data::KEY_X => (float) $val[Data::KEY_X],
          Data::KEY_Y => (float) $val[Data::KEY_Y],
          Data::KEY_Z => (float) $val[Data::KEY_Z],
          Data::KEY_TITLE => (string) $val[Data::KEY_TITLE],
          Data::KEY_TEXT => (string) $val[Data::KEY_TEXT],
          FloatingTextData::KEY_OWNER => (string) $val[FloatingTextData::KEY_OWNER]
        ];
      }
    }
    return $data;
  }

  /**
   * @return FloatingTextData
   */
  public static function make(): FloatingTextData {
    return self::$instance;
  }
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\data;

use pocketmine\plugin\Plugin;
use pocketmine\utils\Config;

/**
 * Class UnremovableFloatingTextData
 * @package tokyo\pmmp\Texter\data
 */
class UnremovableFloatingTextData extends Config implements Data {

  /** @var UnremovableFloatingTextData */
  private static $instance;

  public function __construct(Plugin $plugin, string $path, string $file) {
    $plugin->saveResource($file);
    parent::__construct($path.$file, Config::JSON);
    $this->enableJsonOption(Data::JSON_OPTIONS);
    self::$instance = $this;
  }

  public function getData(): array {
    $data = [];
    $ufts = $this->getAll();
    foreach ($ufts as $levelName => $texts) {
      foreach ($texts as $textName => $val) {
        $data[] = [
          Data::KEY_NAME => (string) $textName,
          Data::KEY_LEVEL => (string) $levelName,
          Data::KEY_X => (float) $val[Data::KEY_X],
          Data::KEY_Y => (float) $val[Data::KEY_Y],
          Data::KEY_Z => (float) $val[Data::KEY_Z],
          Data::KEY_TITLE => (string) $val[Data::KEY_TITLE],
          Data::KEY_TEXT => (string) $val[Data::KEY_TEXT]
        ];
      }
    }
    return $data;
  }

  /**
   * @return UnremovableFloatingTextData
   */
  public static function make(): UnremovableFloatingTextData {
    return self::$instance;
  }
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter;

use pocketmine\event\entity\EntityLevelChangeEvent;
use pocketmine\event\Listener;
use pocketmine\event\player\PlayerJoinEvent;
use pocketmine\event\server\DataPacketSendEvent;
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\Player;
use tokyo\pmmp\Texter\i18n\Lang;
use tokyo\pmmp\Texter\task\SendTextsTask;
use tokyo\pmmp\Texter\text\Text;

class EventListener implements Listener {

  public function onJoin(PlayerJoinEvent $ev): void {
    $p = $ev->getPlayer();
    $l = $p->getLevel();
    $add = new SendTextsTask($p, $l);
    Core::get()->getScheduler()->scheduleDelayedRepeatingTask($add, 20, 1);
  }

  public function onLevelChange(EntityLevelChangeEvent $ev): void {
    $ent = $ev->getEntity();
    if ($ent instanceof Player) {
      $from = $ev->getOrigin();
      $to = $ev->getTarget();
      $core = Core::get();
      $remove = new SendTextsTask($ent, $from, Text::SEND_TYPE_REMOVE);
      $core->getScheduler()->scheduleDelayedRepeatingTask($remove, 20, 1);
      $add = new SendTextsTask($ent, $to);
      $core->getScheduler()->scheduleDelayedRepeatingTask($add, 20, 1);
    }
  }

  public function onSendPacket(DataPacketSendEvent $ev): void {
    $pk = $ev->getPacket();
    if ($pk->pid() === ProtocolInfo::AVAILABLE_COMMANDS_PACKET) {
      /** @var AvailableCommandsPacket $pk */
      if (isset($pk->commandData["txt"])) {
        $p = $ev->getPlayer();
        $txt = $pk->commandData["txt"];
        $txt->commandDescription = Lang::fromLocale($p->getLocale())->translateString("command.txt.description");
      }
    }
  }
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\i18n;

use tokyo\pmmp\Texter\Core;
use tokyo\pmmp\Texter\data\ConfigData;
use function strtolower;

/**
 * Class Lang
 * @package tokyo\pmmp\Texter\language
 */
class Lang {

  /** @var string */
  public const DIR = "language";
  public const FALLBACK = "en_us";

  /** @var Lang */
  private static $instance;
  /** @var Language[] */
  private static $language;
  /** @var string */
  private static $consoleLang = self::FALLBACK;
  /** @var string[] */
  private static $available = [
    "en_us",
    "ja_jp",
    "ru_ru",
    "tr_tr",
    "zh_cn"
  ];

  public function __construct(Core $core) {
    self::$instance = $this;
    self::$consoleLang = ConfigData::make()->getLocale();
    foreach (self::$available as $lang) {
      $core->saveResource(Lang::DIR . DIRECTORY_SEPARATOR . $lang . ".ini", true);
      $this->register(new Language($lang));
    }
  }

  /**
   * @param Language $language
   * @return Lang
   */
  public function register(Language $language): self {
    self::$language[$language->getLang()] = $language;
    return self::$instance;
  }

  /**
   * @param string $lang
   * @return Language
   */
  public static function fromLocale(string $lang): Language {
    $lLang = strtolower($lang);
    if (isset(self::$language[$lLang])) {
      return self::$language[$lLang];
    }else {
      return self::$language[self::FALLBACK];
    }
  }

  /**
   * @return Language
   */
  public static function fromConsole(): Language {
    return self::fromLocale(self::$consoleLang);
  }

  /**
   * @return Lang
   */
  public static function get(): self {
    return self::$instance;
  }
}
<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\i18n;

use pocketmine\lang\BaseLang;
use tokyo\pmmp\Texter\Core;

/**
 * Class Language - simple wrapper for BaseLang
 * @package tokyo\pmmp\Texter\language
 */
class Language extends BaseLang {

  public function __construct(string $lang) {
    $path = Core::get()->getDataFolder().Lang::DIR.DIRECTORY_SEPARATOR;
    parent::__construct($lang, $path, Lang::FALLBACK);
  }
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\task;

use pocketmine\scheduler\AsyncTask;
use pocketmine\Server;
use pocketmine\utils\VersionString;
use tokyo\pmmp\Texter\Core;
use function curl_init;
use function curl_setopt_array;
use function curl_exec;
use function curl_errno;
use function curl_error;
use function curl_close;
use function json_decode;

/**
 * CheckUpdateTaskClass
 */
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 => "php_".PHP_VERSION,
      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){
    $core = Core::get();
    if ($core->isEnabled()) {
      $data = $this->getResult();
      if (isset($data[0])) {
        $ver = new VersionString($data[0]["name"]);
        $core->compareVersion(true, $ver, $data[0]["html_url"]);
      }else {
        $core->compareVersion(false);
      }
    }
  }
}
<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\task;

use pocketmine\level\Position;
use pocketmine\scheduler\Task;
use pocketmine\Server;
use pocketmine\utils\TextFormat;
use tokyo\pmmp\Texter\Core;
use tokyo\pmmp\Texter\data\Data;
use tokyo\pmmp\Texter\data\FloatingTextData;
use tokyo\pmmp\Texter\data\UnremovableFloatingTextData;
use tokyo\pmmp\Texter\i18n\Lang;
use tokyo\pmmp\Texter\text\FloatingText;
use tokyo\pmmp\Texter\text\UnremovableFloatingText;
use tokyo\pmmp\Texter\TexterApi;
use function count;

/**
 * Class PrepareTextsTask
 * @package tokyo\pmmp\Texter\task
 */
class PrepareTextsTask extends Task {

  /** @var Server */
  private $server;
  /** @var array */
  private $ufts;
  /** @var int */
  private $uftsCount = 0;
  /** @var int */
  private $uftsMax;
  /** @var array */
  private $fts;
  /** @var int */
  private $ftsCount = 0;
  /** @var int */
  private $ftsMax;

  public function __construct() {
    $this->server = Server::getInstance();
    $this->ufts = UnremovableFloatingTextData::make()->getData();
    $this->uftsMax = count($this->ufts);
    $this->fts = FloatingTextData::make()->getData();
    $this->ftsMax = count($this->fts);
  }

  public function onRun(int $tick) {
    if ($this->uftsCount === $this->uftsMax) {
      if ($this->ftsCount === $this->ftsMax) {
        $this->onSuccess();
      }else {
        $data = $this->fts[$this->ftsCount];
        $textName = $data[Data::KEY_NAME];
        $loaded = Server::getInstance()->isLevelLoaded($data[Data::KEY_LEVEL]);
        $canLoad = true;
        if (!$loaded) $canLoad = $this->server->loadLevel($data[Data::KEY_LEVEL]);
        if ($canLoad) {
          $level = $this->server->getLevelByName($data[Data::KEY_LEVEL]);
          if ($level !== null) {
            $x = $data[Data::KEY_X];
            $y = $data[Data::KEY_Y];
            $z = $data[Data::KEY_Z];
            $pos = new Position($x, $y, $z, $level);
            $title = $data[Data::KEY_TITLE];
            $text = $data[Data::KEY_TEXT];
            $owner = $data[FloatingTextData::KEY_OWNER];
            $ft = new FloatingText($textName, $pos, $title, $text, $owner);
            TexterApi::registerText($ft);
          }
        }
        ++$this->ftsCount;
      }
    }else {
      $data = $this->ufts[$this->uftsCount];
      $textName = $data[Data::KEY_NAME];
      $loaded = $this->server->isLevelLoaded($data[Data::KEY_LEVEL]);
      $canLoad = true;
      if (!$loaded) $canLoad = $this->server->loadLevel($data[Data::KEY_LEVEL]);
      if ($canLoad) {
        $level = $this->server->getLevelByName($data[Data::KEY_LEVEL]);
        if ($level !== null) {
          $x = $data[Data::KEY_X];
          $y = $data[Data::KEY_Y];
          $z = $data[Data::KEY_Z];
          $pos = new Position($x, $y, $z, $level);
          $title = $data[Data::KEY_TITLE];
          $text = $data[Data::KEY_TEXT];
          $uft = new UnremovableFloatingText($textName, $pos, $title, $text);
          TexterApi::registerText($uft);
        }
      }
      ++$this->uftsCount;
    }
  }

  private function onSuccess(): void {
    $plugin = $this->server->getPluginManager()->getPlugin("Texter");
    if ($plugin !== null && $plugin->isEnabled()) {
      $message = Lang::fromConsole()->translateString("on.enable.prepared", [
        count(TexterApi::getUfts(), COUNT_RECURSIVE) - count(TexterApi::getUfts()),
        count(TexterApi::getFts(), COUNT_RECURSIVE) - count(TexterApi::getFts())
      ]);
      $core = Core::get();
      $core->getLogger()->info(TextFormat::GREEN . $message);
      $core->getScheduler()->cancelTask($this->getTaskId());
    }
  }
}
<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\task;

use pocketmine\level\Level;
use pocketmine\Player;
use pocketmine\scheduler\Task;
use tokyo\pmmp\Texter\Core;
use tokyo\pmmp\Texter\text\Text;
use tokyo\pmmp\Texter\TexterApi;

/**
 * Class SendTextsTask
 * @package tokyo\pmmp\Texter\task
 */
class SendTextsTask extends Task {

  /** @var Player */
  private $target;
  /** @var int */
  private $type;

  /** @var array */
  private $ufts;
  /** @var int */
  private $uftsKey = 0;
  private $uftsKeyMax;
  /** @var array */
  private $fts;
  /** @var int */
  private $ftsKey = 0;
  private $ftsKeyMax;

  public function __construct(Player $target, Level $sendTo, int $type = Text::SEND_TYPE_ADD) {
    $this->target = $target;
    $this->type = $type;
    $this->ufts = array_values(TexterApi::getUftsByLevel($sendTo));
    $this->uftsKeyMax = count($this->ufts);
    $this->fts = array_values(TexterApi::getFtsByLevel($sendTo));
    $this->ftsKeyMax = count($this->fts);
  }

  public function onRun(int $currentTick) {
    if ($this->uftsKey === $this->uftsKeyMax) {
      if ($this->ftsKey === $this->ftsKeyMax) {
        $this->onSuccess();
      }else {
        $ft = $this->fts[$this->ftsKey];
        $ft->sendToPlayer($this->target, $this->type);
        ++$this->ftsKey;
      }
    }else {
      $uft = $this->ufts[$this->uftsKey];
      $uft->sendToPlayer($this->target, $this->type);
      ++$this->uftsKey;
    }
  }

  private function onSuccess(): void {
    Core::get()->getScheduler()->cancelTask($this->getTaskId());
  }
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\text;

use pocketmine\entity\Entity;
use pocketmine\entity\Skin;
use pocketmine\item\Item;
use pocketmine\level\Level;
use pocketmine\level\Position;
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\DataPacket;
use pocketmine\network\mcpe\protocol\MoveActorAbsolutePacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\RemoveActorPacket;
use pocketmine\network\mcpe\protocol\SetActorDataPacket;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
use pocketmine\Player;
use pocketmine\utils\TextFormat;
use pocketmine\utils\UUID;
use tokyo\pmmp\Texter\data\Data;
use tokyo\pmmp\Texter\data\FloatingTextData;
use function str_replace;
use function strtolower;
use function str_repeat;
use function sprintf;

/**
 * Class FloatingText
 * @package tokyo\pmmp\Texter\text
 */
class FloatingText extends Position implements Text {

  public const CHECK_CHAR = 0;
  public const CHECK_FEED = 1;

  /** @var string */
  protected $name;
  /** @var string */
  protected $title;
  /** @var string */
  protected $text;
  /** @var string */
  protected $owner;
  /** @var int */
  protected $eid;

  /** @var bool */
  protected $isInvisible = false;

  public function __construct(string $name, Position $pos, string $title = "", string $text = "", string $owner = "unknown", int $eid = 0) {
    $this
      ->setName($name)
      ->setPosition($pos)
      ->setTitle($title)
      ->setText($text)
      ->setOwner($owner)
      ->setEid($eid);
  }

  public function getName(): string {
    return $this->name;
  }

  public function setName(string $name): FloatingText {
    $this->name = $name;
    return $this;
  }

  public function getPosition(): Position {
    return $this->asPosition();
  }

  public function setPosition(Position $pos): FloatingText {
    parent::__construct($pos->x, $pos->y, $pos->z, $pos->level);
    return $this;
  }

  public function getTitle(): string {
    return str_replace("\n", "#", $this->title);
  }

  public function setTitle(string $title): FloatingText {
    $this->title = str_replace("#", "\n", $title);
    return $this;
  }

  public function getText(): string {
    return str_replace("\n", "#", $this->text);
  }

  public function setText(string $text): FloatingText {
    $this->text = str_replace("#", "\n", $text);
    return $this;
  }

  public function getIndentedTexts(bool $owned): string {
    $texts = "{$this->title}".TextFormat::RESET.TextFormat::WHITE."\n{$this->text}";
    return $texts . ($owned ? "\n".TextFormat::GRAY."[{$this->name}]" : "");
  }

  public function getTextsForCheck(int $mode = self::CHECK_CHAR): string {
    switch ($mode) {
      case self::CHECK_CHAR:
        $str = str_replace("\n", "", TextFormat::clean($this->title.$this->text));
        break;
      case self::CHECK_FEED:
        $str = TextFormat::clean($this->title.$this->text);
        break;
      default:
        throw new \InvalidArgumentException("The value of mode must be 0(FloatingText::CHECK_CHAR) to 1(FloatingText::CHECK_FEED)");
    }
    return $str;
  }

  public function isOwner(Player $player): bool {
    return ($player->isOp() || strtolower($player->getName()) === $this->owner) && !$this instanceof UnremovableFloatingText;
  }

  public function getOwner(): string {
    return $this->owner;
  }

  public function setOwner(string $owner): FloatingText {
    $this->owner = strtolower($owner);
    return $this;
  }

  public function getEid(): int {
    return $this->eid;
  }

  public function setEid(int $eid): FloatingText {
    $this->eid = $eid === 0 ? Entity::$entityCount++ : $eid;
    return $this;
  }

  public function isInvisible(): bool {
    return $this->isInvisible;
  }

  public function setInvisible(bool $value): FloatingText {
    $this->isInvisible = $value;
    return $this;
  }

  /**
   * @param int $type
   * @param bool $owned
   * @return DataPacket[]
   */
  public function asPackets(int $type = Text::SEND_TYPE_ADD, bool $owned = false): array {
    switch ($type) {
      #BLAME "MOJUNCROSOFT" on 1.13
      case Text::SEND_TYPE_ADD:
        $uuid = UUID::fromRandom();

        $apk = new PlayerListPacket;
        $apk->type = PlayerListPacket::TYPE_ADD;
        $apk->entries = [PlayerListEntry::createAdditionEntry(
          $uuid,
          $this->eid,
          $this->getIndentedTexts($owned),
          SkinAdapterSingleton::get()->toSkinData(new Skin(
            "Standard_Custom",
            str_repeat("\x00", 8192),
            "",
            "geometry.humanoid.custom"
          ))
        )];

        $pk = new AddPlayerPacket;
        $pk->username = $this->getIndentedTexts($owned);
        $pk->uuid = $uuid;
        $pk->entityRuntimeId = $this->eid;
        $pk->entityUniqueId = $this->eid;
        $pk->position = $this;
        $pk->item = Item::get(Item::AIR);
        $flags = 1 << Entity::DATA_FLAG_IMMOBILE;
        if ($this->isInvisible) {
          $flags |= 1 << Entity::DATA_FLAG_INVISIBLE;
        }
        $pk->metadata = [
          Entity::DATA_FLAGS => [Entity::DATA_TYPE_LONG, $flags],
          Entity::DATA_SCALE => [Entity::DATA_TYPE_FLOAT, 0]
        ];

        $rpk = new PlayerListPacket;
        $rpk->type = PlayerListPacket::TYPE_REMOVE;
        $rpk->entries = [PlayerListEntry::createRemovalEntry($uuid)];
        $pks = [$apk, $pk, $rpk];
        break;

      case Text::SEND_TYPE_EDIT:
        $pk = new SetActorDataPacket;
        $pk->entityRuntimeId = $this->eid;
        $pk->metadata = [
          Entity::DATA_NAMETAG => [Entity::DATA_NAMETAG, $this->getIndentedTexts($owned)]
        ];
        $pks = [$pk];
        break;

      case Text::SEND_TYPE_MOVE:
        $pk = new MoveActorAbsolutePacket;
        $pk->entityRuntimeId = $this->eid;
        $pk->flags = MoveActorAbsolutePacket::FLAG_TELEPORT;
        $pk->position = $this;
        $pk->xRot = 0;
        $pk->yRot = 0;
        $pk->zRot = 0;
        $pks = [$pk];
        break;

      case Text::SEND_TYPE_REMOVE:
        $pk = new RemoveActorPacket;
        $pk->entityUniqueId = $this->eid;
        $pks = [$pk];
        break;

      // for developers
      default:
        throw new \InvalidArgumentException("The type must be an integer value between 0 to 3");
        break;
    }
    return $pks;
  }

  public function sendToPlayer(Player $player, int $type = Text::SEND_TYPE_ADD): FloatingText {
    $pks = $this->asPackets($type, $this->isOwner($player));
    foreach ($pks as $pk) {
      $player->sendDataPacket($pk);
    }
    return $this;
  }

  public function sendToPlayers(array $players, int $type = Text::SEND_TYPE_ADD): FloatingText {
    foreach ($players as $player) {
      $this->sendToPlayer($player, $type);
    }
    return $this;
  }

  public function sendToLevel(Level $level, int $type = Text::SEND_TYPE_ADD): FloatingText {
    $this->sendToPlayers($level->getPlayers(), $type);
    return $this;
  }

  public function format(): array {
    return [
      Data::KEY_X => sprintf('%0.1f', $this->x),
      Data::KEY_Y => sprintf('%0.1f', $this->y),
      Data::KEY_Z => sprintf('%0.1f', $this->z),
      Data::KEY_TITLE => $this->title,
      Data::KEY_TEXT => $this->text,
      FloatingTextData::KEY_OWNER => $this->owner
    ];
  }

  public function __toString(): string {
    $p = $this->getPosition();
    return "FloatingText(name=\"{$this->name}\", pos=\"x:{$p->x};y:{$p->y};z:{$p->z};level:{$p->getLevel()->getFolderName()}\", title=\"{$this->title}\", text=\"{$this->text}\", owner=\"{$this->owner}\", eid=\"{$this->eid}\")";
  }
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\text;

use pocketmine\level\Level;
use pocketmine\Player;

/**
 * Interface Text
 * @package tokyo\pmmp\Texter\text
 */
interface Text {

  public const SEND_TYPE_ADD = 0;
  public const SEND_TYPE_EDIT = 1;
  public const SEND_TYPE_MOVE = 2;
  public const SEND_TYPE_REMOVE = 3;

  /**
   * @param Player $player
   * @param int $type
   * @return mixed
   */
  public function sendToPlayer(Player $player, int $type = Text::SEND_TYPE_ADD);

  /**
   * @param Player[] $players
   * @param int $type
   * @return mixed
   */
  public function sendToPlayers(array $players, int $type = Text::SEND_TYPE_ADD);

  /**
   * @param Level $level
   * @param int $type
   * @return mixed
   */
  public function sendToLevel(Level $level, int $type = Text::SEND_TYPE_ADD);

  /**
   * @return array[string key] = value
   */
  public function format(): array;

  public function __toString(): string;

}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\text;

use pocketmine\level\Position;
use pocketmine\Player;
use tokyo\pmmp\Texter\data\Data;
use function sprintf;

/**
 * Class UnremovableFloatingText
 * @package tokyo\pmmp\Texter\text
 */
class UnremovableFloatingText extends FloatingText implements Text {

  /** @var string */
  protected $owner = "uft.json";

  public function __construct(string $name, Position $pos, string $title = "", string $text = "", int $eid = 0) {
    parent::__construct($name, $pos, $title, $text, $this->owner, $eid);
  }

  public function sendToPlayer(Player $player, int $type = Text::SEND_TYPE_ADD): FloatingText {
    $pks = $this->asPackets($type);
    foreach ($pks as $pk) {
      $player->sendDataPacket($pk);
    }
    return $this;
  }

  public function format(): array {
    return [
      Data::KEY_X => sprintf('%0.1f', $this->x),
      Data::KEY_Y => sprintf('%0.1f', $this->y),
      Data::KEY_Z => sprintf('%0.1f', $this->z),
      Data::KEY_TITLE => $this->title,
      Data::KEY_TEXT => $this->text
    ];
  }

  public function __toString(): string {
    return "UnremovableFloatingText(name=\"{$this->text}\", pos=\"x:{$this->x};y:{$this->y};z:{$this->z};level:{$this->level->getFolderName()}\", title=\"{$this->title}\", text=\"{$this->text}\", eid=\"{$this->eid}\")";
  }
}<?php

/**
 * // English
 *
 * Texter, the display FloatingTextPerticle plugin for PocketMine-MP
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * This software is distributed under "NCSA license".
 * You should have received a copy of the NCSA license
 * along with this program.  If not, see
 * < https://opensource.org/licenses/NCSA >.
 *
 * ---------------------------------------------------------------------
 * // 日本語
 *
 * TexterはPocketMine-MP向けのFloatingTextPerticleを表示するプラグインです
 * Copyright (c) 2019-2020 yuko fuyutsuki < https://github.com/fuyutsuki >
 *
 * このソフトウェアは"NCSAライセンス"下で配布されています。
 * あなたはこのプログラムと共にNCSAライセンスのコピーを受け取ったはずです。
 * 受け取っていない場合、下記のURLからご覧ください。
 * < https://opensource.org/licenses/NCSA >
 */

declare(strict_types = 1);

namespace tokyo\pmmp\Texter;

use pocketmine\level\Level;
use pocketmine\Server;
use tokyo\pmmp\Texter\data\FloatingTextData;
use tokyo\pmmp\Texter\text\FloatingText;
use tokyo\pmmp\Texter\text\Text;
use tokyo\pmmp\Texter\text\UnremovableFloatingText;

/**
 * Class TexterApi
 * @package tokyo\pmmp\Texter
 */
class TexterApi {

  /** @var array */
  private static $ufts = [];
  /** @var array */
  private static $fts = [];

  private function __construct() {
    // DO NOT USE THIS METHOD!
  }

  /**
   * Register text in the Texter plugin and enable management using TexterApi
   * If you don't do this registration,
   * you need to manage FloatingText manually with the Text class.
   * @link tokyo\pmmp\Texter\text\{Text, FloatingText}
   * @param Text $text
   */
  public static function registerText(Text $text): void {
    switch (true) {
      case $text instanceof UnremovableFloatingText:
        self::$ufts[$text->getLevel()->getFolderName()][$text->getName()] = $text;
        break;

      case $text instanceof FloatingText:
        self::$fts[$text->getLevel()->getFolderName()][$text->getName()] = $text;
        FloatingTextData::make()->saveFtChange($text);
        break;
    }
  }

  /**
   * @return array[string FolderName][string TextName] = UnremovableFloatingText
   */
  public static function getUfts(): array {
    return self::$ufts;
  }

  /**
   * @param Level $level
   * @return array[string FolderName][string TextName] = UnremovableFloatingText
   */
  public static function getUftsByLevel(Level $level): array {
    return self::getUftsByLevelName($level->getFolderName());
  }

  /**
   * @param string $levelName
   * @return array[string FolderName][string TextName] = UnremovableFloatingText
   */
  public static function getUftsByLevelName(string $levelName): array {
    return self::$ufts[$levelName] ?? [];
  }

  /**
   * @param Level $level
   * @param string $name
   * @return null|UnremovableFloatingText
   */
  public static function getUftByLevel(Level $level, string $name): ?UnremovableFloatingText {
    $ufts = self::getUftsByLevel($level);
    return $ufts[$name] ?? null;
  }

  /**
   * @param string $levelName
   * @param string $name
   * @return null|UnremovableFloatingText
   */
  public static function getUftByLevelName(string $levelName, string $name): ?UnremovableFloatingText {
    $ufts = self::getUftsByLevelName($levelName);
    return $ufts[$name] ?? null;
  }

  /**
   * @return array[string FolderName][string TextName] = FloatingText
   */
  public static function getFts(): array {
    return self::$fts;
  }

  /**
   * @param Level $level
   * @return array[string FolderName][string TextName] = FloatingText
   */
  public static function getFtsByLevel(Level $level): array {
    return self::getFtsByLevelName($level->getFolderName());
  }

  /**
   * @param string $levelName
   * @return array[string FolderName][string TextName] = FloatingText
   */
  public static function getFtsByLevelName(string $levelName): array {
    return self::$fts[$levelName] ?? [];
  }

  /**
   * @param Level $level
   * @param string $name
   * @return null|FloatingText
   */
  public static function getFtByLevel(Level $level, string $name): ?FloatingText {
    $fts = self::getFtsByLevel($level);
    return $fts[$name] ?? null;
  }

  /**
   * @param string $levelName
   * @param string $name
   * @return null|FloatingText
   */
  public static function getFtByLevelName(string $levelName, string $name): ?FloatingText {
    $fts = self::getFtsByLevelName($levelName);
    return $fts[$name] ?? null;
  }

  /**
   * @param Level $level
   * @return bool
   */
  public static function removeFtsByLevel(Level $level): bool {
    $fts = self::getFtsByLevel($level);
    $onLevel = $fts[$level->getFolderName()] ?? [];
    if (!empty($onLevel)) {
      /** @var FloatingText $ft */
      foreach ($onLevel as $ft) {
        $ft->sendToLevel($level, Text::SEND_TYPE_REMOVE);
      }
      FloatingTextData::make()->removeFtsByLevel($level);
      unset(self::$fts[$level->getFolderName()]);
      return true;
    }
    return false;
  }

  /**
   * @param string $levelName
   * @return bool
   */
  public static function removeFtsByLevelName(string $levelName): bool {
    $level = Server::getInstance()->getLevelByName($levelName);
    return isset($level) ? self::removeFtsByLevel($level) : false;
  }

  /**
   * @param Level $level
   * @param string $name
   * @return bool
   */
  public static function removeFtByLevel(Level $level, string $name): bool {
    $ft = self::getFtByLevel($level, $name);
    if (isset($ft)) {
      $ft->sendToLevel($level, Text::SEND_TYPE_REMOVE);
      FloatingTextData::make()->removeFtByLevel($level, $name);
      unset(self::$fts[$level->getFolderName()][$name]);
      return true;
    }
    return false;
  }

  /**
   * @param string $levelName
   * @param string $name
   * @return bool
   */
  public static function removeFtByLevelName(string $levelName, string $name): bool {
    $level = Server::getInstance()->getLevelByName($levelName);
    return isset($level) ? self::removeFtByLevel($level, $name) : false;
  }
}<?php

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\libs\jojoe77777\FormAPI;

class CustomForm extends Form {

    private $labelMap = [];

    /**
     * @param callable|null $callable
     */
    public function __construct(?callable $callable) {
        parent::__construct($callable);
        $this->data["type"] = "custom_form";
        $this->data["title"] = "";
        $this->data["content"] = [];
    }

    public function processData(&$data) : void {
        if(is_array($data)) {
            $new = [];
            foreach ($data as $i => $v) {
                $new[$this->labelMap[$i]] = $v;
            }
            $data = $new;
        }
    }

    /**
     * @param string $title
     */
    public function setTitle(string $title) : void {
        $this->data["title"] = $title;
    }

    /**
     * @return string
     */
    public function getTitle() : string {
        return $this->data["title"];
    }

    /**
     * @param string $text
     * @param string|null $label
     */
    public function addLabel(string $text, ?string $label = null) : void {
        $this->addContent(["type" => "label", "text" => $text]);
        $this->labelMap[] = $label ?? count($this->labelMap);
    }

    /**
     * @param string $text
     * @param bool|null $default
     * @param string|null $label
     */
    public function addToggle(string $text, bool $default = null, ?string $label = null) : void {
        $content = ["type" => "toggle", "text" => $text];
        if($default !== null) {
            $content["default"] = $default;
        }
        $this->addContent($content);
        $this->labelMap[] = $label ?? count($this->labelMap);
    }

    /**
     * @param string $text
     * @param int $min
     * @param int $max
     * @param int $step
     * @param int $default
     * @param string|null $label
     */
    public function addSlider(string $text, int $min, int $max, int $step = -1, int $default = -1, ?string $label = null) : void {
        $content = ["type" => "slider", "text" => $text, "min" => $min, "max" => $max];
        if($step !== -1) {
            $content["step"] = $step;
        }
        if($default !== -1) {
            $content["default"] = $default;
        }
        $this->addContent($content);
        $this->labelMap[] = $label ?? count($this->labelMap);
    }

    /**
     * @param string $text
     * @param array $steps
     * @param int $defaultIndex
     * @param string|null $label
     */
    public function addStepSlider(string $text, array $steps, int $defaultIndex = -1, ?string $label = null) : void {
        $content = ["type" => "step_slider", "text" => $text, "steps" => $steps];
        if($defaultIndex !== -1) {
            $content["default"] = $defaultIndex;
        }
        $this->addContent($content);
        $this->labelMap[] = $label ?? count($this->labelMap);
    }

    /**
     * @param string $text
     * @param array $options
     * @param int $default
     * @param string|null $label
     */
    public function addDropdown(string $text, array $options, int $default = null, ?string $label = null) : void {
        $this->addContent(["type" => "dropdown", "text" => $text, "options" => $options, "default" => $default]);
        $this->labelMap[] = $label ?? count($this->labelMap);
    }

    /**
     * @param string $text
     * @param string $placeholder
     * @param string $default
     * @param string|null $label
     */
    public function addInput(string $text, string $placeholder = "", string $default = null, ?string $label = null) : void {
        $this->addContent(["type" => "input", "text" => $text, "placeholder" => $placeholder, "default" => $default]);
        $this->labelMap[] = $label ?? count($this->labelMap);
    }

    /**
     * @param array $content
     */
    private function addContent(array $content) : void {
        $this->data["content"][] = $content;
    }

}
<?php

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\libs\jojoe77777\FormAPI;

use pocketmine\form\Form as IForm;
use pocketmine\Player;

abstract class Form implements IForm{

    /** @var array */
    protected $data = [];
    /** @var callable|null */
    private $callable;

    /**
     * @param callable|null $callable
     */
    public function __construct(?callable $callable) {
        $this->callable = $callable;
    }

    /**
     * @deprecated
     * @see Player::sendForm()
     *
     * @param Player $player
     */
    public function sendToPlayer(Player $player) : void {
        $player->sendForm($this);
    }

    public function getCallable() : ?callable {
        return $this->callable;
    }

    public function setCallable(?callable $callable) {
        $this->callable = $callable;
    }

    public function handleResponse(Player $player, $data) : void {
        $this->processData($data);
        $callable = $this->getCallable();
        if($callable !== null) {
            $callable($player, $data);
        }
    }

    public function processData(&$data) : void {
    }

    public function jsonSerialize(){
        return $this->data;
    }
}
<?php

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\libs\jojoe77777\FormAPI;

use pocketmine\plugin\PluginBase;

class FormAPI extends PluginBase{

    /**
     * @deprecated
     *
     * @param callable|null $function
     * @return CustomForm
     */
    public function createCustomForm(?callable $function = null) : CustomForm {
        return new CustomForm($function);
    }

    /**
     * @deprecated
     *
     * @param callable|null $function
     * @return SimpleForm
     */
    public function createSimpleForm(?callable $function = null) : SimpleForm {
        return new SimpleForm($function);
    }

    /**
     * @deprecated
     *
     * @param callable|null $function
     * @return ModalForm
     */
    public function createModalForm(?callable $function = null) : ModalForm {
        return new ModalForm($function);
    }
}
<?php

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\libs\jojoe77777\FormAPI;

class ModalForm extends Form {

    /** @var string */
    private $content = "";

    /**
     * @param callable|null $callable
     */
    public function __construct(?callable $callable) {
        parent::__construct($callable);
        $this->data["type"] = "modal";
        $this->data["title"] = "";
        $this->data["content"] = $this->content;
        $this->data["button1"] = "";
        $this->data["button2"] = "";
    }

    /**
     * @param string $title
     */
    public function setTitle(string $title) : void {
        $this->data["title"] = $title;
    }

    /**
     * @return string
     */
    public function getTitle() : string {
        return $this->data["title"];
    }

    /**
     * @return string
     */
    public function getContent() : string {
        return $this->data["content"];
    }

    /**
     * @param string $content
     */
    public function setContent(string $content) : void {
        $this->data["content"] = $content;
    }

    /**
     * @param string $text
     */
    public function setButton1(string $text) : void {
        $this->data["button1"] = $text;
    }

    /**
     * @return string
     */
    public function getButton1() : string {
        return $this->data["button1"];
    }

    /**
     * @param string $text
     */
    public function setButton2(string $text) : void {
        $this->data["button2"] = $text;
    }

    /**
     * @return string
     */
    public function getButton2() : string {
        return $this->data["button2"];
    }
}
<?php

declare(strict_types = 1);

namespace tokyo\pmmp\Texter\libs\jojoe77777\FormAPI;

class SimpleForm extends Form {

    const IMAGE_TYPE_PATH = 0;
    const IMAGE_TYPE_URL = 1;

    /** @var string */
    private $content = "";

    private $labelMap = [];

    /**
     * @param callable|null $callable
     */
    public function __construct(?callable $callable) {
        parent::__construct($callable);
        $this->data["type"] = "form";
        $this->data["title"] = "";
        $this->data["content"] = $this->content;
    }

    public function processData(&$data) : void {
        $data = $this->labelMap[$data] ?? null;
    }

    /**
     * @param string $title
     */
    public function setTitle(string $title) : void {
        $this->data["title"] = $title;
    }

    /**
     * @return string
     */
    public function getTitle() : string {
        return $this->data["title"];
    }

    /**
     * @return string
     */
    public function getContent() : string {
        return $this->data["content"];
    }

    /**
     * @param string $content
     */
    public function setContent(string $content) : void {
        $this->data["content"] = $content;
    }

    /**
     * @param string $text
     * @param int $imageType
     * @param string $imagePath
     * @param string $label
     */
    public function addButton(string $text, int $imageType = -1, string $imagePath = "", ?string $label = null) : void {
        $content = ["text" => $text];
        if($imageType !== -1) {
            $content["image"]["type"] = $imageType === 0 ? "path" : "url";
            $content["image"]["data"] = $imagePath;
        }
        $this->data["buttons"][] = $content;
        $this->labelMap[] = $label ?? count($this->labelMap);
    }

}
{"tokyo\\pmmp\\Texter\\libs\\jojoe77777\\FormAPI":{"name":"libFormAPI","author":"jojoe77777","api":"3.2.0","version":"1.3.0","antigen":"jojoe77777\\FormAPI","build":{"builder":"PoggitCI\/1.0-beta\/beta poggit-virion\/1.0","builderName":"poggit","buildTime":"2019-03-30T11:00:43+00:00","poggitBuildId":57511,"buildClass":"Dev","projectId":4250,"projectBuildNumber":2,"fromCommit":"eb857f01f06c39de4dac32043ca03632672575f5","poggitResourceId":54590}}}ݵǓ+#+p;   GBMB