├── .poggit.yml
├── LICENSE
├── MIE_Converter
├── README.md
├── icon.png
├── plugin.yml
└── src
│ └── FaigerSYS
│ └── MIE_Converter
│ ├── MIE_Converter.php
│ └── MapImageUtils.php
├── MIE_Protector
├── README.md
├── icon.png
├── plugin.yml
└── src
│ └── FaigerSYS
│ └── MIE_Protector
│ └── MIE_Protector.php
├── MapImageEngine
├── README.md
├── icon.png
├── plugin.yml
├── resources
│ ├── instructions
│ │ ├── ell.txt
│ │ ├── eng.txt
│ │ ├── kor.txt
│ │ └── rus.txt
│ └── strings
│ │ ├── ell.ini
│ │ ├── eng.ini
│ │ ├── kor.ini
│ │ └── rus.ini
└── src
│ └── FaigerSYS
│ └── MapImageEngine
│ ├── MapImageEngine.php
│ ├── TranslateStrings.php
│ ├── command
│ └── MapImageEngineCommand.php
│ ├── item
│ └── FilledMap.php
│ ├── packet
│ └── CustomClientboundMapItemDataPacket.php
│ └── storage
│ ├── ImageStorage.php
│ ├── MapImage.php
│ ├── MapImageChunk.php
│ └── OldFormatConverter.php
└── README.md
/.poggit.yml:
--------------------------------------------------------------------------------
1 | --- # Poggit-CI Manifest. Open the CI at https://poggit.pmmp.io/ci/FaigerSYS/MapImageEngine
2 | branches:
3 | - master
4 | projects:
5 | MapImageEngine:
6 | path: MapImageEngine/
7 | icon: /MapImageEngine/icon.png
8 | MIE_Protector:
9 | path: MIE_Protector/
10 | icon: /MIE_Protector/icon.png
11 | MIE_Converter:
12 | path: MIE_Converter/
13 | icon: /MIE_Converter/icon.png
14 | ...
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/MIE_Converter/README.md:
--------------------------------------------------------------------------------
1 | # MIE_Converter 
2 |
3 | This plugin used as offline converter for your images. You can find additional information in the MapImageEngine's instructions
--------------------------------------------------------------------------------
/MIE_Converter/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaigerSYS/MapImageEngine/bd83f15fd782b2c3b177a2fcb473862bca57b59f/MIE_Converter/icon.png
--------------------------------------------------------------------------------
/MIE_Converter/plugin.yml:
--------------------------------------------------------------------------------
1 | name: MIE_Converter
2 | main: FaigerSYS\MIE_Converter\MIE_Converter
3 | version: "1.0.2"
4 | api: ["3.0.0", "4.0.0"]
5 | load: POSTWORLD
6 | author: FaigerSYS
7 | description: "Addon for MapImageEngine to convert images to .mie format"
8 |
9 | commands:
10 | mieconvert:
11 | description: "Convert image to .mie format"
12 | permission: "mapimageengine.convert"
13 |
14 | permissions:
15 | mapimageengine.convert:
16 | default: op
17 | description: "Allows command usage (/mieconvert)"
18 |
--------------------------------------------------------------------------------
/MIE_Converter/src/FaigerSYS/MIE_Converter/MIE_Converter.php:
--------------------------------------------------------------------------------
1 | getLogger()->info(CLR::GOLD . 'MIE_Converter enabling...');
17 |
18 | if (!extension_loaded('gd')) {
19 | $this->getLogger()->warning('Your PHP binary does not contains the GD library required for image parsing');
20 | $this->getLogger()->warning('Install GD, or use the online converter (website provided in the MapImageEngine\'s instructions)');
21 |
22 | $this->getServer()->getPluginManager()->disablePlugin($this);
23 | return;
24 | }
25 |
26 | @mkdir($path = $this->getDataFolder());
27 | @mkdir($path . 'to_convert');
28 | @mkdir($path . 'converted');
29 |
30 | $this->getLogger()->info(CLR::GOLD . 'MIE_Converter enabled! MIEI version: ' . MapImageUtils::CURRENT_VERSION);
31 | }
32 |
33 | public function onCommand(CommandSender $sender, Command $command, string $label, array $args) : bool {
34 | $x = array_shift($args);
35 | $y = array_shift($args);
36 | $path = implode(' ', $args);
37 |
38 | if (!is_numeric($x) || !is_numeric($y) || !strlen($path)) {
39 | $sender->sendMessage(self::MSG_PREFIX . 'Usage: /' . $label . ' ');
40 | $sender->sendMessage(CLR::GRAY . 'Notice: place image into the "to_convert" folder. Image name = full file name');
41 | return true;
42 | }
43 |
44 | $x = (int) $x;
45 | $y = (int) $y;
46 |
47 | if ($x < 0 || $y < 0) {
48 | $sender->sendMessage(self::MSG_PREFIX . 'The count of blocks must be greater than 0!');
49 | return true;
50 | }
51 |
52 | if ($path[0] !== '/') {
53 | $path = $this->getDataFolder() . 'to_convert/' . $path;
54 | }
55 |
56 | $data = @file_get_contents($path);
57 | if ($data === false) {
58 | $sender->sendMessage(self::MSG_PREFIX . 'File not found!');
59 | return true;
60 | }
61 |
62 | $image = @imagecreatefromstring($data);
63 | if (!is_resource($image)) {
64 | $sender->sendMessage(self::MSG_PREFIX . 'File is not an image, or image has unsupported by your GD library format! Convert image to supported format (e.g. PNG) and try again, or use online converter');
65 | return true;
66 | }
67 |
68 | $sender->sendMessage(self::MSG_PREFIX . 'Converting image...');
69 |
70 | $data = MapImageUtils::generateImageData($image, $x, $y, 7);
71 | imagedestroy($image);
72 |
73 | $path = $this->getDataFolder() . 'converted/' . pathinfo($path)['filename'] . '_' . $x . 'x' . $y . '.miei';
74 | file_put_contents($path, $data);
75 |
76 | $sender->sendMessage(self::MSG_PREFIX . 'Done! Image location: ' . CLR::WHITE . $path);
77 | return true;
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/MIE_Converter/src/FaigerSYS/MIE_Converter/MapImageUtils.php:
--------------------------------------------------------------------------------
1 | put('MIEI');
30 | $data->putInt(self::CURRENT_VERSION);
31 | $data->putByte((int) ($compression_level > 0));
32 |
33 | $buffer = new BinaryStream();
34 | $buffer->put(UUID::fromRandom()->toBinary());
35 | $buffer->putInt($blocks_width);
36 | $buffer->putInt($blocks_height);
37 |
38 | for ($y_b = 0; $y_b < $blocks_height; $y_b++) {
39 | for ($x_b = 0; $x_b < $blocks_width; $x_b++) {
40 | $buffer->putInt($chunk_width);
41 | $buffer->putInt($chunk_height);
42 |
43 | for ($y = 0; $y < $chunk_width; $y++) {
44 | for ($x = 0; $x < $chunk_height; $x++) {
45 | $color = imagecolorsforindex($image, imagecolorat($image, $x + ($x_b * $chunk_width), $y + ($y_b * $chunk_height)));
46 | $color = chr($color['red']) . chr($color['green']) . chr($color['blue']) . chr($color['alpha'] === 0 ? 255 : ~$color['alpha'] << 1 & 0xff);
47 |
48 | $buffer->put($color);
49 | }
50 | }
51 | }
52 | }
53 |
54 | imagedestroy($image);
55 |
56 | $buffer = $buffer->buffer;
57 | if ($compression_level > 0) {
58 | $buffer = zlib_encode($buffer, ZLIB_ENCODING_DEFLATE, min($compression_level, 9));
59 | }
60 | $data->put($buffer);
61 |
62 | return $data->buffer;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/MIE_Protector/README.md:
--------------------------------------------------------------------------------
1 | # MIE_Protector 
2 |
3 | This plugin used as protection for MapImageEngine images (protection from rotation and destruction)
4 | To bypass protection, player must have **mapimageengine.bypassprotect** permission
--------------------------------------------------------------------------------
/MIE_Protector/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaigerSYS/MapImageEngine/bd83f15fd782b2c3b177a2fcb473862bca57b59f/MIE_Protector/icon.png
--------------------------------------------------------------------------------
/MIE_Protector/plugin.yml:
--------------------------------------------------------------------------------
1 | name: MIE_Protector
2 | main: FaigerSYS\MIE_Protector\MIE_Protector
3 | version: "1.0.4"
4 | api: ["3.0.0", "4.0.0"]
5 | load: POSTWORLD
6 | author: FaigerSYS
7 | description: "Addon for MapImageEngine to protect the image (frame) from rotation and destruction"
8 |
9 | permissions:
10 | mapimageengine.bypassprotect:
11 | default: op
12 | description: "Bypass image protection"
13 |
--------------------------------------------------------------------------------
/MIE_Protector/src/FaigerSYS/MIE_Protector/MIE_Protector.php:
--------------------------------------------------------------------------------
1 | getLogger()->info(CLR::GOLD . 'MIE_Protector enabling...');
18 |
19 | $this->getServer()->getPluginManager()->registerEvents($this, $this);
20 |
21 | $this->getLogger()->info(CLR::GOLD . 'MIE_Protector enabled!');
22 | }
23 |
24 | /**
25 | * @priority LOW
26 | * @ignoreCancelled
27 | */
28 | public function onClick(PlayerInteractEvent $e) {
29 | $block = $e->getBlock();
30 | $frame = $block->getLevel()->getTile($block);
31 | if ($frame instanceof ItemFrame && $frame->getItem() instanceof FilledMap && !$e->getPlayer()->hasPermission('mapimageengine.bypassprotect')) {
32 | $e->setCancelled(true);
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/MapImageEngine/README.md:
--------------------------------------------------------------------------------
1 | # MapImageEngine [](https://poggit.pmmp.io/p/MapImageEngine)
2 |
3 | Plugin that allows you to add full-color images to your server!
4 | Instructions: [click here](https://github.com/FaigerSYS/MapImageEngine/tree/master/MapImageEngine/resources/instructions)!
--------------------------------------------------------------------------------
/MapImageEngine/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaigerSYS/MapImageEngine/bd83f15fd782b2c3b177a2fcb473862bca57b59f/MapImageEngine/icon.png
--------------------------------------------------------------------------------
/MapImageEngine/plugin.yml:
--------------------------------------------------------------------------------
1 | name: MapImageEngine
2 | main: FaigerSYS\MapImageEngine\MapImageEngine
3 | version: "1.1.3"
4 | api: ["3.0.0"]
5 | load: POSTWORLD
6 | author: FaigerSYS
7 | description: "Image engine for MC:BE servers"
8 |
9 | permissions:
10 | mapimageengine:
11 | default: op
12 | description: "Allows command usage (/mapimageengine)"
13 |
--------------------------------------------------------------------------------
/MapImageEngine/resources/instructions/ell.txt:
--------------------------------------------------------------------------------
1 | ; Translated by nikoskon
2 |
3 | Αυτό το plugin δημιουργήθηκε απο τον FaigerSYS
4 | Ιστοσελίδα Github: https://github.com/FaigerSYS/MapImageEngine
5 |
6 | Αν βρήκατε ένα σφάλμα, ενημερώστε μας! Υποβάλετε νέα βλάβη στο GitHub
7 |
8 | Πώς να φτιάξετε τη δική σας εικόνα:
9 | 1. Μετατρέψτε οποιαδήποτε μια εικόνα ('png', 'jpg' κα.)...
10 | - Χρησιμοποιώντας το MIE_Converter plugin: https://github.com/FaigerSYS/MapImageEngine/tree/master/MIE_Converter
11 | - Χρησιμοποιώντας μετατροπέα στο διαδίκτυο (αν το plugin δεν λειτουργεί): https://faigersys.github.io/mie-converter
12 | 1.1. Δεν χρειάζεται να αλλάξετε το μέγεθος της εικόνας σε 128x128 . Ο ίδιος ο μετατροπέας θα αλλάξει το μέγεθος!
13 | 1.2. Δεν χρειάζεται να κομματιάσετε την εικόνα. Απλά επιλέξτε το μέγεθος που θέλετε σε κύβους!
14 | 2. Η μετατρεπόμενη εικόνα μεταφέρεται στο φάκελο 'images' του plugin
15 | 3. Εγινε! Τώρα μπορείτε να χρησιμοποιήσετε αυτήν την εικόνα
16 |
17 | Πώς να εγκαταστήσετε την εικόνα:
18 | 1. Εισάγετε '/mie list' για να δείτε τη λίστα με τις διαθέσιμες εικόνες
19 | 2. Επιλέξτε μία από αυτές και εισάγετε '/mie place <όνομα εικόνας>'
20 | 2.1. Μπορείτε να χρησιμοποιήσετε σημαίες για εύκολη εγκατάσταση εικόνων (Εισάγετε την εντολή ('/mie place') για να πάρετε μια πλήρη περιγραφή)
21 | 2.2. Για να φύγετε τη λειτουργία τοποθέτησης εισάγετε την εντολή '/mie exit'
22 | 3. Ακολουθήστε τις οδηγίες και η εικόνα θα εγκατασταθεί!
23 |
24 | Για να δείτε την πλήρη λίστα των εντολών, πληκτρολογήστε '/mie'
25 |
26 | Καλή τύχη!
27 |
--------------------------------------------------------------------------------
/MapImageEngine/resources/instructions/eng.txt:
--------------------------------------------------------------------------------
1 | This plugin was made by FaigerSYS
2 | Github page: https://github.com/FaigerSYS/MapImageEngine
3 |
4 | If you found a bug, please let me know! Submit new issue on GitHub
5 |
6 | How to make own image:
7 | 1. Convert any image ('png', 'jpg' etc.)...
8 | - Using MIE_Converter plugin: https://github.com/FaigerSYS/MapImageEngine/tree/master/MIE_Converter
9 | - Using online converter (if the plugin does not work): https://faigersys.github.io/mie-converter
10 | 1.1. You don't need to change image size to 128x128. Converter itself will change the size
11 | 1.2. You don't need to chunk image. Just select required size in blocks
12 | 2. Move converted image to folder 'images' of this plugin
13 | 3. Done! Now you can use this image
14 |
15 | How to install image:
16 | 1. Enter '/mie list' to view the list of available images
17 | 2. Choose one of them and enter '/mie place '
18 | 2.1. You can use flags for easy installation of images (enter command without arguments ('/mie place') to get a full description)
19 | 2.2. To leave the placement mode enter '/mie exit'
20 | 3. Follow the instructions and the picture will be installed
21 |
22 | To view the full list of commands, enter '/mie'
23 |
24 | Good luck!
25 |
--------------------------------------------------------------------------------
/MapImageEngine/resources/instructions/kor.txt:
--------------------------------------------------------------------------------
1 | 이 플러그인은 FaigerSYS가 작성했습니다
2 | GitHub 페이지: https://github.com/FaigerSYS/MapImageEngine
3 |
4 | 버그를 발견하면 제가 알 수 있게 해주세요! GitHub에 새 이슈를 제출하세요
5 |
6 | 자신의 이미지 제작 방법:
7 | 1. 이미지 파일을 변환하세요('png', 'jpg' 기타 등등)...
8 | - MIE_Convert 플러그인 사용: https://github.com/FaigerSYS/MapImageEngine/tree/master/MIE_Converter
9 | - 온라인 변환기 사용(플러그인 미작동 시): https://faigersys.github.io/mie-converter
10 | 1.1 이미지 크기는 128x128로 변경하지 않아도 됩니다. 변환기가 크기를 변경합니다
11 | 1.2 이미지는 나누지 않아도 됩니다. 블록으로 필요한 크기를 선택하기만 하면 됩니다
12 | 2. 변환된 이미지 파일을 이 플러그인의 'images' 폴더에 넣어주세요
13 | 3. 완료! 이제 이 이미지를 사용할 수 있습니다
14 |
15 | 이미지 설치 방법:
16 | 1.'/mie list'를 사용해 사용할 수 있는 이미지 파일 목록을 찾아주세요
17 | 2. 이 중 하나를 선택해서 '/mie place <이미지 이름>'을 입력해주세요
18 | 2.1 이미지의 쉬운 설치를 위해 플래그를 사용할 수 있습니다(전체 설명을 보려면 ('/mie place')를 인수 없이 입력하세요)
19 | 2.2 배치 모드를 마치려면 '/mie exit'를 입력하세요
20 | 3. 설명을 따르면 이미지가 설치됩니다
21 |
22 | 명령어의 모든 목록을 보려면 '/mie'를 입력해주세요
23 |
24 | 행운을 빕니다!
25 |
--------------------------------------------------------------------------------
/MapImageEngine/resources/instructions/rus.txt:
--------------------------------------------------------------------------------
1 | Этот плагин создан FaigerSYS
2 |
3 | Если вы заметили какой-либо баг - сразу сообщайте
4 |
5 | Как сделать свою картинку:
6 | 1. Конвертируйте любую картинку ('png', 'jpg' и т.д.)...
7 | - Используя плагин MIE_Converter: https://github.com/FaigerSYS/MapImageEngine/tree/master/MIE_Converter
8 | - Используя онлайн-конвертер (если плагин не сработал): https://faigersys.github.io/mie-converter
9 | 1.1. Не обязательно изменять размер картинки именно 128x128 . Сайт всё сделает сам!
10 | 1.2. Не обязательно резать картинку на несколько частей. В конвертере уже предоставлены средства для этого
11 | 2. Конвертинованную картинку переместите в папку 'images' этого плагина
12 | 3. Готово! Теперь эту картинку можно использовать в плагине
13 |
14 | Как установить картинку:
15 | 1. Введите '/mie list' для получения списка доступных картинок
16 | 2. Выберите одну из них и введите команду '/mie place <название>'
17 | 2.1. Для удобства установки картинки вы можете использовать флаги (введите команду без аргументов ('/mie place') для их полного описания)
18 | 2.2. Для выхода из режима установки картинки введите '/mie exit'
19 | 3. Следуйте инструкции что вы увидите после введения команды и картинка будет установлена
20 |
21 | Полный список команд доступен по команде '/mie'
22 |
23 | Приятного использования!
24 |
--------------------------------------------------------------------------------
/MapImageEngine/resources/strings/ell.ini:
--------------------------------------------------------------------------------
1 | ; Translated by nikoskon
2 |
3 | plugin-loader.loading="Ενεργοποίηση MapImageEngine..."
4 | plugin-loader.reloading="Επαναφόρτωση MapImageEngine..."
5 | plugin-loader.info-instruction="Ειδοποίηση #1: Bρείτε τις οδηγίες στον φάκελο του plugin"
6 | plugin-loader.info-long-loading="Ειδοποίηση #2: μετά την εγκατάσταση νέων εικόνων, η ενεργοποίηση μπορεί να διαρκέσει περισσότερο από το συνηθισμένο, αλλά μετά θα είναι ως συνήθως"
7 | plugin-loader.loaded="Το MapImageEngine ενεργοποιήθηκε!"
8 | plugin-loader.reloaded="Το MapImageEngine επαναφορτώθηκε!"
9 |
10 | image-loader.prefix="Η εικόνα \"%s\": "
11 | image-loader.err-name-exists="ίδιο όνομα έχει ήδη καταχωριστεί"
12 | image-loader.err-image-exists="η εικόνα υπάρχει ήδη"
13 | image-loader.err-corrupted="το αρχείο είναι κατεστραμμένο"
14 | ; image-loader.err-unsupported-api="Το API δεν είναι συμβατό. Ενημερώστε την εικόνα σας"
15 |
16 | command.desc="Εντολή MapImageEngine"
17 | command.desc.list="λάβετε μια λίστα με τις διαθέσιμες εικόνες"
18 | command.desc.place="μπείτε σε λειτουργία τοποθέτησης εικόνας"
19 | command.desc.exit="βγείται απο λειτουργία τοποθέτησης εικόνας"
20 |
21 | command.usage="Χρήση: "
22 |
23 | command.in-game="Εκτελέστε αυτήν την εντολή στο παιχνίδι"
24 |
25 | command.list="Διαθέσιμες εικόνες: "
26 | command.list.no-images="Δεν έχετε ακόμα εικόνες"
27 |
28 | command.place.usage="<όνομα εικόνας> [σημαίες]"
29 | command.place.usage.flags="Διαθέσιμες σημαίες: "
30 | command.place.usage.flags.pretty="Εμφάνιση τετραγώνων για βολικό προσανατολισμό"
31 | command.place.usage.flags.auto="Αυτόματη λειτουργία (τοποθετήστε μια εικόνα σε δύο κλικ)"
32 | command.place.not-found="Η εικόνα \"%s\" δεν βρέθηκε!"
33 | command.place.placing="Τοποθέτιση εικόνας!"
34 | command.place.click-top-left="Κάντε κλικ στην επάνω αριστερή γωνία ..."
35 | command.place.click-bottom-right="Κάντε κλικ στην κάτω δεξιά γωνία ..."
36 | command.place.placing-info="Η τοποθέτηση ξεκινά από την πάνω αριστερή γωνία (x * y)"
37 | command.place.click="Κάντε κλικ στο πλαίσιο (item frame) (%d * %d)"
38 | command.place.not-frame="Αυτό δεν είναι ένα πλαίσιο (item frame)!"
39 | command.place.width-not-match="Το πλάτος δεν ταιριάζει με το πλάτος της εικόνας!"
40 | command.place.height-not-match="Το ύψος δεν ταιριάζει με το ύψος της εικόνας!"
41 | command.place.invalid-upper-corner="Η επάνω γωνία δεν μπορεί να είναι πιο χαμηλά από την κάτω γωνία!"
42 | command.place.no-frames="Πρέπει να τοποθετήσετε τα πλαίσια (item frames) στην επιλεγμένη περιοχή! Τοποθετήστε όλα τα πλαίσια και δοκιμάστε ξανά"
43 | command.place.not-flat="Η επιλεγμένη περιοχή πρέπει να είναι επίπεδη!"
44 | command.place.success="Η εικόνα τοποθετήθηκε με επιτυχία ;)"
45 |
46 | command.exit="Φύγατε τη λειτουργία τοποθέτησης"
47 | command.exit.not-allowed="Πρέπει να εισάγετε τον τρόπο τοποθέτησης πριν φύγετε"
48 |
--------------------------------------------------------------------------------
/MapImageEngine/resources/strings/eng.ini:
--------------------------------------------------------------------------------
1 | plugin-loader.loading="MapImageEngine enabling..."
2 | plugin-loader.reloading="MapImageEngine reloading..."
3 | plugin-loader.info-instruction="Notice #1: find the instructions in the plugin folder"
4 | plugin-loader.info-long-loading="Notice #2: after installing new images or updating plugin enabling may take longer, but then it will be as usual"
5 | plugin-loader.info-1.1-update="Notice #3: after installing MapImageEngine v1.1 you must place all your previously placed images again"
6 | plugin-loader.loaded="MapImageEngine enabled!"
7 | plugin-loader.reloaded="MapImageEngine reloaded!"
8 |
9 | image-loader.prefix="Image \"%s\": "
10 | image-loader.success="image loaded"
11 | image-loader.err-name-exists="similar name already registered"
12 | image-loader.err-image-exists="image already exists"
13 | image-loader.err-corrupted="file corrupted"
14 | image-loader.err-unsupported-api="API not supported. Please update your plugin to the latest version (and convert image again if didn't help)"
15 | image-loader.converted="image was converted to new format! You can find the original in the 'images/old_images' folder"
16 | image-loader.not-converted="image cannot be converted to new format! Convert the original image again using the online converter or MIE_Converter plugin"
17 | image-loader.cache-not-supported="It seems our custom packet for caching support is incompatible with your server software. Loading images may be slower than usual"
18 |
19 | command.desc="MapImageEngine command"
20 | command.desc.list="get list of available images"
21 | command.desc.place="enter placement mode"
22 | command.desc.exit="leave placement mode"
23 |
24 | command.usage="Usage: "
25 |
26 | command.in-game="Run this command in-game"
27 |
28 | command.list="Available images: "
29 | command.list.no-images="You do not have any images yet"
30 |
31 | command.place.usage=" [flags]"
32 | command.place.usage.flags="Available flags: "
33 | command.place.usage.flags.pretty="Show squares for convenient orientation"
34 | command.place.usage.flags.auto="Automatic mode (place an image in two clicks)"
35 | command.place.not-found="Image \"%s\" not found!"
36 | command.place.placing="Placing image!"
37 | command.place.click-top-left="Click on the top left corner..."
38 | command.place.click-bottom-right="Click on the bottom right corner..."
39 | command.place.placing-info="Placement starts from the top left corner (x * y)"
40 | command.place.click="Click on the frame (%d * %d)"
41 | command.place.not-frame="This is not a frame!"
42 | command.place.width-not-match="Width does not match the width of the image!"
43 | command.place.height-not-match="Height does not match the height of the image!"
44 | command.place.invalid-upper-corner="The upper corner cannot be lower than the bottom corner!"
45 | command.place.no-frames="You must place the frames in the selected area! Place all frames and try again"
46 | command.place.not-flat="The selected area must be flat!"
47 | command.place.success="Image succesfully placed ;)"
48 |
49 | command.exit="You left the placement mode"
50 | command.exit.not-allowed="You must enter placement mode before leaving it"
51 |
--------------------------------------------------------------------------------
/MapImageEngine/resources/strings/kor.ini:
--------------------------------------------------------------------------------
1 | plugin-loader.loading="MapImageEngine 활성화 중..."
2 | plugin-loader.reloading="MapImageEngine 새로 고침 중..."
3 | plugin-loader.info-instruction="알림 #1: 플러그인 폴더에서 사용 방법을 확인하세요"
4 | plugin-loader.info-long-loading="알림 #2: 새로운 이미지를 설치하거나 플러그인을 업데이트하면 활성화하는 데 오래 걸릴 수 있으나, 그다음부터는 평소처럼 활성화될 것입니다"
5 | plugin-loader.info-1.1-update="알림 #3: MapImageEngine v1.1을 설치한 후 이전에 배치한 모든 이미지를 다시 배치해야 합니다"
6 | plugin-loader.loaded="MapImageEngine이 활성화되었습니다!"
7 | plugin-loader.reloaded="MapImageEngine이 새로 고침 되었습니다!"
8 |
9 | image-loader.prefix="이미지 \"%s\": "
10 | image-loader.success="이미지가 로드되었습니다"
11 | image-loader.err-name-exists="비슷한 이름이 이미 존재합니다"
12 | image-loader.err-image-exists="이미지가 이미 존재합니다"
13 | image-loader.err-corrupted="파일이 손상되었습니다"
14 | image-loader.err-unsupported-api="API가 지원되지 않습니다. 플러그인을 최신 버전으로 업데이트 해주세요 (그리고 도움이 되지 않았다면 이미지를 다시 변환하세요)"
15 | image-loader.converted="이미지가 새로운 형식으로 변환되었습니다! 'images/old_images' 폴더에서 원본을 찾을 수 있습니다"
16 | image-loader.not-converted="이미지가 새로운 형식으로 변환될 수 없습니다! 온라인 변환기나 MIE_Converter 플러그인을 사용해 이미지를 다시 변환하세요"
17 |
18 | command.desc="MapImageEngine 명령어"
19 | command.desc.list="사용할 수 있는 이미지의 목록을 가져옵니다"
20 | command.desc.place="배치 모드로 전환합니다"
21 | command.desc.exit="배치 모드를 종료합니다"
22 |
23 | command.usage="사용법: "
24 |
25 | command.in-game="게임 안에서 실행해야 합니다"
26 |
27 | command.list="사용할 수 있는 이미지: "
28 | command.list.no-images="사용할 수 있는 이미지가 없습니다"
29 |
30 | command.place.usage="<이미지 이름> [플래그]"
31 | command.place.usage.flags="사용할 수 있는 플래그: "
32 | command.place.usage.flags.pretty="간편한 방향을 위해 사각형을 보여줍니다"
33 | command.place.usage.flags.auto="자동 모드(두번 클릭해 이미지를 배치합니다)"
34 | command.place.not-found="이미지 \"%s\"이(가) 존재하지 않습니다!"
35 | command.place.placing="이미지 배치 중!"
36 | command.place.click-top-left="왼쪽 상단 구석을 선택하세요..."
37 | command.place.click-bottom-right="오른쪽 하단 구석을 선택하세요..."
38 | command.place.placing-info="배치는 왼쪽 상단 구석에서부터 시작합니다(x * y)"
39 | command.place.click="액자를 클릭하세요(%d * %d)"
40 | command.place.not-frame="액자가 아닙니다!"
41 | command.place.width-not-match="너비가 이미지의 너비와 맞지 않습니다!"
42 | command.place.height-not-match="높이가 이미지의 높이와 맞지 않습니다!"
43 | command.place.invalid-upper-corner="위에 있는 구석이 아래에 있는 구석보다 낮을 수 없습니다!"
44 | command.place.no-frames="선택된 영역에 액자를 배치해야 합니다! 모든 액자를 배치하고 다시 시도하세요"
45 | command.place.not-flat="선택된 지역은 평평해야 합니다!"
46 | command.place.success="이미지가 성공적으로 배치되었습니다 ;)"
47 |
48 | command.exit="배치 모드를 나갔습니다"
49 | command.exit.not-allowed="배치 모드를 나가기 전에 이미 배치 모드에 진입해야 합니다"
50 |
--------------------------------------------------------------------------------
/MapImageEngine/resources/strings/rus.ini:
--------------------------------------------------------------------------------
1 | plugin-loader.loading="MapImageEngine загружается..."
2 | plugin-loader.reloading="MapImageEngine перезагружается..."
3 | plugin-loader.info-instruction="Напоминание #1: вы можете найти инструкцию в папке плагина"
4 | plugin-loader.info-long-loading="Напоминание #2: После добавления новых картинок или обновления плагина загрузка может идти дольше, но потом будет как обычно"
5 | plugin-loader.info-1.1-update="Напоминание #3: после установки MapImageEngine v1.1 вы должны снова установить на рамки все ранее установленные картинки"
6 | plugin-loader.loaded="MapImageEngine загружен!"
7 | plugin-loader.reloaded="MapImageEngine перезагружен!"
8 |
9 | image-loader.prefix="Картинка \"%s\": "
10 | image-loader.success="картинка загружена"
11 | image-loader.err-name-exists="подобное имя уже было зарегестрировано"
12 | image-loader.err-image-exists="такая картинка уже существует"
13 | image-loader.err-corrupted="файл поврежден"
14 | image-loader.err-unsupported-api="версия API не поддерживается. Обновите плагин до последней версии (и конвертируйте картинку снова если не помогло)"
15 | image-loader.converted="картинка была конвертирована в новый формат! Вы можете найти старую в папке './images/old_images'"
16 | image-loader.not-converted="картинка не была конвертирована в новый формат! Конвертируйте её оригинал снова используя онлайн-конвертер или плагин"
17 |
18 | command.desc="Команда MapImageEngine"
19 | command.desc.list="получить список доступных картинок"
20 | command.desc.place="ввойти в режим установки картинки"
21 | command.desc.exit="выйти с режима установки картинки"
22 |
23 | command.usage="Использование: "
24 |
25 | command.in-game="Выполните эту команду в игре"
26 |
27 | command.list="Доступные картинки: "
28 | command.list.no-images="У вас ещё нету каких-либо картинок"
29 |
30 | command.place.usage="<название> [флаги]"
31 | command.place.usage.flags="Доступные флаги: "
32 | command.place.usage.flags.pretty=" показывать квадраты для удобной ориентации по картинке"
33 | command.place.usage.flags.auto=" автоматический режим (установка картинки в два клика)"
34 | command.place.not-found="Картинка \"%s\" не найдена!"
35 | command.place.placing="Идёт установка изображения!"
36 | command.place.click-top-left="Нажмите на левый верхний угол..."
37 | command.place.click-bottom-right="Нажмите на правый нижний угол..."
38 | command.place.placing-info="Установка начинается с левого верхнего угла (x * y)"
39 | command.place.click="Нажмите на рамку (%d * %d)"
40 | command.place.not-frame="Это не рамка!"
41 | command.place.width-not-match="Ширина не совпадает с высотой картинки!"
42 | command.place.height-not-match="Высота не совпадает с высотой картинки!"
43 | command.place.invalid-upper-corner="Верхний угол не может быть ниже нижнего угла!"
44 | command.place.no-frames="Вы должны установить в выбранной области рамки! Установите все и попробуйте ещё раз"
45 | command.place.not-flat="Выбранная область должна быть плоской!"
46 | command.place.success="Картинка успешно установлена ;)"
47 |
48 | command.exit="Вы вышли из режима установки"
49 | command.exit.not-allowed="Вы должны быть в режиме установки чтобы выйти с него"
50 |
--------------------------------------------------------------------------------
/MapImageEngine/src/FaigerSYS/MapImageEngine/MapImageEngine.php:
--------------------------------------------------------------------------------
1 | getLogger()->info(CLR::GOLD . TS::translate($is_reload ? 'plugin-loader.reloading' : 'plugin-loader.loading'));
50 | $this->getLogger()->info(CLR::AQUA . TS::translate('plugin-loader.info-instruction'));
51 | $this->getLogger()->info(CLR::AQUA . TS::translate('plugin-loader.info-long-loading'));
52 | $this->getLogger()->info(CLR::AQUA . TS::translate('plugin-loader.info-1.1-update'));
53 |
54 | if ($is_reload) {
55 | $this->storage = $old_plugin->storage;
56 | }
57 |
58 | $this->getServer()->getPluginManager()->registerEvents($this, $this);
59 |
60 | @mkdir($path = $this->getDataFolder());
61 |
62 | @mkdir($dir = $path . 'instructions/');
63 | foreach (scandir($r_dir = $this->getFile() . '/resources/instructions/') as $file) {
64 | if ($file[0] !== '.') {
65 | copy($r_dir . $file, $dir . $file);
66 | }
67 | }
68 |
69 | @mkdir($path . 'images');
70 | @mkdir($path . 'images/old_files');
71 | @mkdir($path . 'cache');
72 |
73 | if (self::$is_custom_pk_suppoted === null) {
74 | self::$is_custom_pk_suppoted = CustomClientboundMapItemDataPacket::checkCompatiblity();
75 | }
76 |
77 | $this->loadImages($is_reload);
78 |
79 | $this->getServer()->getCommandMap()->register('mapimageengine', new MapImageEngineCommand());
80 |
81 | ItemFactory::registerItem(new FilledMap(), true);
82 |
83 | $this->getLogger()->info(CLR::GOLD . TS::translate($is_reload ? 'plugin-loader.reloaded' : 'plugin-loader.loaded'));
84 | }
85 |
86 | private function loadImages(bool $is_reload = false) {
87 | $path = $this->getDataFolder() . 'images/';
88 | $storage = $this->storage ?? new ImageStorage;
89 |
90 | $files = array_filter(
91 | scandir($path),
92 | function ($file) use ($path) {
93 | return is_file($path . $file) && substr($file, -5, 5) === '.miei';
94 | }
95 | );
96 |
97 | $old_files_path = $path . 'old_files/';
98 | $old_files = array_filter(
99 | scandir($path),
100 | function ($file) use ($path) {
101 | return is_file($path . $file) && substr($file, -4, 4) === '.mie';
102 | }
103 | );
104 | foreach ($old_files as $old_file) {
105 | $new_data = OldFormatConverter::tryConvert(file_get_contents($path . $old_file));
106 | if ($new_data !== null) {
107 | $this->getLogger()->notice(TS::translate('image-loader.prefix', $old_file) . TS::translate('image-loader.converted'));
108 |
109 | $basename = pathinfo($old_file, PATHINFO_BASENAME);
110 | $new_path = $old_files_path . $basename;
111 | $i = 0;
112 | while (file_exists($new_path)) {
113 | $new_path = $old_files_path . $basename . '.' . ++$i;
114 | }
115 | rename($path . $old_file, $new_path);
116 |
117 | $filename = pathinfo($old_file, PATHINFO_FILENAME);
118 | $extension = '.miei';
119 | $new_file = $filename . $extension;
120 | $i = 0;
121 | while (file_exists($path . $new_file)) {
122 | $new_file = $filename . '_' . ++$i . $extension;
123 | }
124 | file_put_contents($path . $new_file, $new_data);
125 |
126 | unset($new_data);
127 |
128 | $files[] = $new_file;
129 | } else {
130 | $this->getLogger()->warning(TS::translate('image-loader.prefix', $old_file) . TS::translate('image-loader.not-converted'));
131 | }
132 | }
133 |
134 | if (!self::isCustomPacketSupported()) {
135 | $this->getLogger()->warning(TS::translate('image-loader.cache-not-supported'));
136 | }
137 |
138 | foreach ($files as $file) {
139 | $image = MapImage::fromBinary(file_get_contents($path . $file), $state);
140 | if ($image !== null) {
141 | $name = substr($file, 0, -5);
142 | $state = $storage->registerImage($image, true, $name);
143 | switch ($state) {
144 | case ImageStorage::R_OK:
145 | $this->getLogger()->info(CLR::GREEN . TS::translate('image-loader.prefix', $file) . TS::translate('image-loader.success'));
146 | break;
147 |
148 | case ImageStorage::R_UUID_EXISTS:
149 | !$is_reload && $this->getLogger()->info(TS::translate('image-loader.prefix', $file) . TS::translate('image-loader.err-image-exists'));
150 | break;
151 |
152 | case ImageStorage::R_NAME_EXISTS:
153 | case ImageStorage::R_INVALID_NAME:
154 | $this->getLogger()->warning(TS::translate('image-loader.prefix', $file) . TS::translate('image-loader.err-name-exists'));
155 | break;
156 | }
157 | } else {
158 | switch ($state) {
159 | case MapImage::R_CORRUPTED:
160 | $this->getLogger()->warning(TS::translate('image-loader.prefix', $file) . TS::translate('image-loader.err-corrupted'));
161 | break;
162 |
163 | case MapImage::R_UNSUPPORTED_API:
164 | $this->getLogger()->warning(TS::translate('image-loader.prefix', $file) . TS::translate('image-loader.err-unsupported-api'));
165 | break;
166 | }
167 | }
168 | }
169 |
170 | $this->storage = $storage;
171 | }
172 |
173 | public function getImageStorage() : ImageStorage {
174 | return $this->storage;
175 | }
176 |
177 | /**
178 | * @ignoreCancelled true
179 | */
180 | public function onRequest(DataPacketReceiveEvent $e) {
181 | if ($e->getPacket() instanceof MapInfoRequestPacket) {
182 | $pk = $this->getImageStorage()->getCachedPacket($e->getPacket()->mapId);
183 | if ($pk !== null) {
184 | $e->getPlayer()->dataPacket($pk);
185 | }
186 | $e->setCancelled(true);
187 | }
188 | }
189 |
190 | /**
191 | * @priority LOW
192 | */
193 | public function onChunkLoad(ChunkLoadEvent $e) {
194 | foreach ($e->getChunk()->getTiles() as $frame) {
195 | if ($frame instanceof ItemFrame) {
196 | $item = $frame->getItem();
197 | if ($item instanceof FilledMap) {
198 | $frame->setItem($item);
199 | }
200 | }
201 | }
202 | }
203 |
204 | public static function getInstance() : MapImageEngine {
205 | return self::$instance;
206 | }
207 |
208 | public static function isCustomPacketSupported() : bool {
209 | return self::$is_custom_pk_suppoted;
210 | }
211 |
212 | }
213 |
--------------------------------------------------------------------------------
/MapImageEngine/src/FaigerSYS/MapImageEngine/TranslateStrings.php:
--------------------------------------------------------------------------------
1 | getLanguage()->getLang();
15 | $owner = MapImageEngine::getInstance();
16 |
17 | $default_strings = parse_ini_string(stream_get_contents($owner->getResource('strings/' . self::DEFAULT_LANG . '.ini')));
18 | if ($strings = $owner->getResource('strings/' . $lang . '.ini')) {
19 | $strings = parse_ini_string(stream_get_contents($strings)) + $default_strings;
20 | } else {
21 | $strings = $default_strings;
22 | }
23 |
24 | self::$strings = $strings;
25 | }
26 |
27 | public static function translate(string $str, ...$args) {
28 | return sprintf(self::$strings[$str] ?? $str, ...$args);
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/MapImageEngine/src/FaigerSYS/MapImageEngine/command/MapImageEngineCommand.php:
--------------------------------------------------------------------------------
1 | getPlugin()->getServer()->getPluginManager()->registerEvents($this, $this->getPlugin());
36 |
37 | parent::__construct('mapimageengine', TS::translate('command.desc'), null, ['mie']);
38 | $this->setPermission('mapimageengine');
39 | }
40 |
41 | public function execute(CommandSender $sender, string $label, array $args) {
42 | if (!$this->testPermission($sender)) {
43 | return;
44 | }
45 |
46 | if (!$sender instanceof Player) {
47 | $sender->sendMessage(self::MSG_PREFIX . TS::translate('command.in-game'));
48 | return;
49 | }
50 |
51 | $cmd = array_shift($args);
52 | switch ($cmd) {
53 | case 'list':
54 | $list = $this->getPlugin()->getImageStorage()->getNamedImages();
55 | if (empty($list)) {
56 | $sender->sendMessage(self::MSG_PREFIX . TS::translate('command.list.no-images'));
57 | } else {
58 | $new_list = [];
59 | foreach ($list as $name => $image) {
60 | $w = $image->getBlocksWidth();
61 | $h = $image->getBlocksHeight();
62 |
63 | $new_list[] = $name . CLR::RESET . ' ' . CLR::AQUA . '(' . CLR::DARK_GREEN . $w . CLR::AQUA . 'x'. CLR::DARK_GREEN . $h . CLR::AQUA . ')';
64 | }
65 |
66 | $list = CLR::WHITE . CLR::ITALIC . implode(CLR::GRAY . ', ' . CLR::WHITE . CLR::ITALIC, $new_list) . CLR::GRAY;
67 | $sender->sendMessage(self::MSG_PREFIX . TS::translate('command.list') . $list);
68 | }
69 | break;
70 |
71 | case 'place':
72 | $image_name = (string) array_shift($args);
73 | if (!strlen($image_name)) {
74 | $sender->sendMessage(self::MSG_PREFIX . TS::translate('command.usage') . '/' . $label . ' place ' . TS::translate('command.place.usage'));
75 | $sender->sendMessage(CLR::GRAY . TS::translate('command.place.usage.flags'));
76 | $sender->sendMessage(CLR::GRAY . ' pretty - ' . TS::translate('command.place.usage.flags.pretty'));
77 | $sender->sendMessage(CLR::GRAY . ' auto - ' . TS::translate('command.place.usage.flags.auto'));
78 | } else {
79 | $image = $this->getPlugin()->getImageStorage()->getImageByName($image_name);
80 | if (!$image) {
81 | $sender->sendMessage(self::MSG_PREFIX . TS::translate('command.place.not-found', $image_name));
82 | } else {
83 | $this->cache[$sender->getName()] = [
84 | 'image_hash' => $image->getHashedUUID(),
85 | 'pretty' => in_array('pretty', $args),
86 | 'auto' => in_array('auto', $args),
87 | 'placed' => 0,
88 | 'x_count' => $image->getBlocksWidth(),
89 | 'y_count' => $image->getBlocksHeight()
90 | ];
91 |
92 | $this->processPlaceMessage($sender);
93 | }
94 | }
95 | break;
96 |
97 | case 'exit':
98 | $name = $sender->getName();
99 | if (isset($this->cache[$name])) {
100 | unset($this->cache[$name]);
101 | $sender->sendMessage(self::MSG_PREFIX . TS::translate('command.exit'));
102 | } else {
103 | $sender->sendMessage(self::MSG_PREFIX . TS::translate('command.exit.not-allowed'));
104 | }
105 | break;
106 |
107 | default:
108 | $sender->sendMessage(self::MSG_PREFIX . TS::translate('command.usage'));
109 | $sender->sendMessage(CLR::GRAY . ' /' . $label . ' list - ' . TS::translate('command.desc.list'));
110 | $sender->sendMessage(CLR::GRAY . ' /' . $label . ' place - ' . TS::translate('command.desc.place'));
111 | $sender->sendMessage(CLR::GRAY . ' /' . $label . ' exit - ' . TS::translate('command.desc.exit'));
112 | }
113 | }
114 |
115 | /**
116 | * @priority LOW
117 | * @ignoreCancelled true
118 | */
119 | public function onTouch(PlayerInteractEvent $e) {
120 | if ($e->getAction() !== PlayerInteractEvent::RIGHT_CLICK_BLOCK) {
121 | return;
122 | }
123 |
124 | $player = $e->getPlayer();
125 | $name = $player->getName();
126 |
127 | if (isset($this->cache[$name])) {
128 | $block = $e->getBlock();
129 | $level = $block->getLevel();
130 |
131 | $frame = $level->getTile($block);
132 | if (!($frame instanceof ItemFrame)) {
133 | $player->sendMessage(self::MSG_PREFIX . TS::translate('command.place.not-frame'));
134 | } else {
135 | $data = &$this->cache[$name];
136 |
137 | if ($data['auto']) {
138 | if (!isset($data['p1'])) {
139 | $data['p1'] = $block;
140 | $this->processPlaceMessage($player);
141 | } else {
142 | $p1 = $data['p1'];
143 | $p2 = $block;
144 |
145 | $x1 = $p1->getX();
146 | $y1 = $p1->getY();
147 | $z1 = $p1->getZ();
148 | $x2 = $p2->getX();
149 | $y2 = $p2->getY();
150 | $z2 = $p2->getZ();
151 |
152 | if ($y1 < $y2) {
153 | $player->sendMessage(self::MSG_PREFIX . TS::translate('command.place.invalid-upper-corner'));
154 | } else if ($y1 - $y2 + 1 !== $data['y_count']) {
155 | $player->sendMessage(self::MSG_PREFIX . TS::translate('command.place.height-not-match'));
156 | } else {
157 | $x = $x1;
158 | $z = $z1;
159 | $a = null;
160 | if ($x1 === $x2) {
161 | $a = &$z;
162 | $from = $z1;
163 | $to = $z2;
164 | } else if ($z1 === $z2) {
165 | $a = &$x;
166 | $from = $x1;
167 | $to = $x2;
168 | } else {
169 | $player->sendMessage(self::MSG_PREFIX . TS::translate('command.place.not-flat'));
170 | }
171 |
172 | if (abs($to - $from) + 1 !== $data['x_count']) {
173 | $player->sendMessage(self::MSG_PREFIX . TS::translate('command.place.width-not-match'));
174 | } else if ($a !== null) {
175 | $x_b = -1;
176 | for ($a = $from; $from < $to ? $a <= $to : $a >= $to; $from < $to ? $a++ : $a--) {
177 | $y_b = -1;
178 | $x_b++;
179 | for ($y = $y1; $y >= $y2; $y--) {
180 | $y_b++;
181 |
182 | $frame = $level->getTile(new Vector3($x, $y, $z));
183 | if (!($frame instanceof ItemFrame)) {
184 | $player->sendMessage(self::MSG_PREFIX . TS::translate('command.place.no-frames'));
185 | break 2;
186 | }
187 |
188 | $map = new FilledMap();
189 | $map->setImageData($data['image_hash'], $x_b, $y_b);
190 |
191 | $frame->setItem($map);
192 | }
193 | }
194 | $player->sendMessage(self::MSG_PREFIX . TS::translate('command.place.success'));
195 | }
196 | }
197 |
198 | unset($this->cache[$name]);
199 | }
200 | } else {
201 | $x = $data['placed'] % $data['x_count'];
202 | $y = floor($data['placed'] / $data['x_count']);
203 |
204 | $map = new FilledMap();
205 | $map->setImageData($data['image_hash'], $x, $y);
206 |
207 | $frame->setItem($map);
208 |
209 | if (++$data['placed'] === ($data['x_count'] * $data['y_count'])) {
210 | $player->sendMessage(self::MSG_PREFIX . TS::translate('command.place.success'));
211 | unset($this->cache[$name]);
212 | } else {
213 | $this->processPlaceMessage($player);
214 | }
215 | }
216 | }
217 |
218 | $e->setCancelled(true);
219 | }
220 | }
221 |
222 | public function onQuit(PlayerQuitEvent $e) {
223 | unset($this->cache[$e->getPlayer()->getName()]);
224 | }
225 |
226 | private function processPlaceMessage(Player $player) {
227 | $name = $player->getName();
228 | $data = &$this->cache[$name];
229 |
230 | $player->sendMessage('');
231 | $player->sendMessage(self::MSG_PREFIX . TS::translate('command.place.placing'));
232 |
233 | if ($data['auto']) {
234 | if (!isset($data['p1'])) {
235 | $player->sendMessage(CLR::GRAY . TS::translate('command.place.click-top-left'));
236 | } else {
237 | $player->sendMessage(CLR::GRAY . TS::translate('command.place.click-bottom-right'));
238 | }
239 | } else {
240 | $player->sendMessage(CLR::GRAY . TS::translate('command.place.placing-info'));
241 |
242 | $x = (int) $data['placed'] % $data['x_count'];
243 | $y = (int) ($data['placed'] / $data['x_count']);
244 |
245 | if ($data['pretty']) {
246 | $block = "\xe2\xac\x9b";
247 |
248 | for ($y_b = 0; $y_b < $data['y_count']; $y_b++) {
249 | $line = CLR::WHITE;
250 | for ($x_b = 0; $x_b < $data['x_count']; $x_b++) {
251 | $line .= ($x_b === $x && $y_b === $y) ? CLR::GREEN . $block . CLR::WHITE : $block;
252 | }
253 |
254 | $player->sendMessage($line);
255 | }
256 | }
257 |
258 | $player->sendMessage(CLR::GRAY . TS::translate('command.place.click', $x + 1, $y + 1));
259 | }
260 | }
261 |
262 | public function getPlugin() : Plugin {
263 | return MapImageEngine::getInstance();
264 | }
265 |
266 | }
267 |
--------------------------------------------------------------------------------
/MapImageEngine/src/FaigerSYS/MapImageEngine/item/FilledMap.php:
--------------------------------------------------------------------------------
1 | updateMapData();
23 |
24 | return $this;
25 | }
26 |
27 | public function setNamedTag(CompoundTag $tag) : Item {
28 | parent::setNamedTag($tag);
29 | $this->updateMapData();
30 |
31 | return $this;
32 | }
33 |
34 | protected function updateMapData() {
35 | $plugin = MapImageEngine::getInstance();
36 | if (!$plugin) {
37 | return;
38 | }
39 |
40 | $mie_data = $this->getImageData();
41 | if (!is_array($mie_data)) {
42 | return;
43 | }
44 |
45 | $map_id = 0;
46 |
47 | $api = $mie_data['api'] ?? -1;;
48 | if (in_array($api, self::SUPPORTED_MAP_API)) {
49 | $image = $plugin->getImageStorage()->getImage($mie_data['image_hash']);
50 | if ($image) {
51 | $chunk = $image->getChunk($mie_data['x_block'], $mie_data['y_block']);
52 | if ($chunk) {
53 | $map_id = $chunk->getMapId();
54 | }
55 | }
56 | }
57 |
58 | $tag = $this->getNamedTag();
59 | $tag->setLong('map_uuid', $map_id, true);
60 | parent::setNamedTag($tag);
61 | }
62 |
63 | public function setImageData(string $image_hash, int $block_x, int $block_y) {
64 | $tag = $this->getNamedTag();
65 | $tag->setString('mie_data', json_encode([
66 | 'api' => self::CURRENT_MAP_API,
67 | 'image_hash' => $image_hash,
68 | 'x_block' => $block_x,
69 | 'y_block' => $block_y
70 | ]));
71 | parent::setNamedTag($tag);
72 |
73 | $this->updateMapData();
74 | }
75 |
76 | public function getImageData() {
77 | $tag = $this->getNamedTag();
78 | if ($tag->hasTag('mie_data', StringTag::class)) {
79 | return json_decode($tag->getString('mie_data'), true);
80 | }
81 | }
82 |
83 | public function getImageHash() {
84 | return $this->getImageData()['image_hash'] ?? null;
85 | }
86 |
87 | public function getImageChunkX() {
88 | return $this->getImageData()['x_block'] ?? null;
89 | }
90 |
91 | public function getImageChunkY() {
92 | return $this->getImageData()['y_block'] ?? null;
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/MapImageEngine/src/FaigerSYS/MapImageEngine/packet/CustomClientboundMapItemDataPacket.php:
--------------------------------------------------------------------------------
1 | mapId = $this->getEntityUniqueId();
80 | $this->type = $this->getUnsignedVarInt();
81 | $this->dimensionId = $this->getByte();
82 | $this->isLocked = $this->getBool();
83 |
84 | if (($this->type & 0x08) !== 0) {
85 | $count = $this->getUnsignedVarInt();
86 | for ($i = 0; $i < $count; ++$i) {
87 | $this->eids[] = $this->getEntityUniqueId();
88 | }
89 | }
90 |
91 | if (($this->type & (0x08 | self::BITFLAG_DECORATION_UPDATE | self::BITFLAG_TEXTURE_UPDATE)) !== 0) {
92 | $this->scale = $this->getByte();
93 | }
94 |
95 | if (($this->type & self::BITFLAG_DECORATION_UPDATE) !== 0) {
96 | for ($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i) {
97 | $object = new MapTrackedObject();
98 | $object->type = $this->getLInt();
99 | if ($object->type === MapTrackedObject::TYPE_BLOCK) {
100 | $this->getBlockPosition($object->x, $object->y, $object->z);
101 | } elseif ($object->type === MapTrackedObject::TYPE_ENTITY) {
102 | $object->entityUniqueId = $this->getEntityUniqueId();
103 | } else {
104 | throw new \UnexpectedValueException("Unknown map object type");
105 | }
106 | $this->trackedEntities[] = $object;
107 | }
108 |
109 | for ($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i) {
110 | $this->decorations[$i]["rot"] = $this->getByte();
111 | $this->decorations[$i]["img"] = $this->getByte();
112 | $this->decorations[$i]["xOffset"] = $this->getByte();
113 | $this->decorations[$i]["yOffset"] = $this->getByte();
114 | $this->decorations[$i]["label"] = $this->getString();
115 |
116 | $this->decorations[$i]["color"] = $this->getUnsignedVarInt();
117 | }
118 | }
119 |
120 | if (($this->type & self::BITFLAG_TEXTURE_UPDATE) !== 0) {
121 | $this->width = $this->getVarInt();
122 | $this->height = $this->getVarInt();
123 | $this->xOffset = $this->getVarInt();
124 | $this->yOffset = $this->getVarInt();
125 |
126 | $count = $this->getUnsignedVarInt();
127 | assert($count === $this->width * $this->height);
128 |
129 | $this->colors = $this->get($count);
130 | }
131 | }
132 |
133 | protected function encodePayload() : void {
134 | $this->putEntityUniqueId($this->mapId);
135 |
136 | $type = 0;
137 | if (($eidsCount = count($this->eids)) > 0) {
138 | $type |= 0x08;
139 | }
140 | if (($decorationCount = count($this->decorations)) > 0) {
141 | $type |= self::BITFLAG_DECORATION_UPDATE;
142 | }
143 | if (!empty($this->colors)) {
144 | $type |= self::BITFLAG_TEXTURE_UPDATE;
145 | }
146 |
147 | $this->putUnsignedVarInt($type);
148 | $this->putByte($this->dimensionId);
149 | $this->putBool($this->isLocked);
150 |
151 | if (($type & 0x08) !== 0) {
152 | $this->putUnsignedVarInt($eidsCount);
153 | foreach ($this->eids as $eid) {
154 | $this->putEntityUniqueId($eid);
155 | }
156 | }
157 |
158 | if (($type & (0x08 | self::BITFLAG_TEXTURE_UPDATE | self::BITFLAG_DECORATION_UPDATE)) !== 0) {
159 | $this->putByte($this->scale);
160 | }
161 |
162 | if (($type & self::BITFLAG_DECORATION_UPDATE) !== 0) {
163 | $this->putUnsignedVarInt(count($this->trackedEntities));
164 | foreach ($this->trackedEntities as $object) {
165 | $this->putLInt($object->type);
166 | if ($object->type === MapTrackedObject::TYPE_BLOCK) {
167 | $this->putBlockPosition($object->x, $object->y, $object->z);
168 | } elseif ($object->type === MapTrackedObject::TYPE_ENTITY) {
169 | $this->putEntityUniqueId($object->entityUniqueId);
170 | } else {
171 | throw new \UnexpectedValueException("Unknown map object type");
172 | }
173 | }
174 |
175 | $this->putUnsignedVarInt($decorationCount);
176 | foreach ($this->decorations as $decoration) {
177 | $this->putByte($decoration["rot"]);
178 | $this->putByte($decoration["img"]);
179 | $this->putByte($decoration["xOffset"]);
180 | $this->putByte($decoration["yOffset"]);
181 | $this->putString($decoration["label"]);
182 |
183 | $this->putUnsignedVarInt($decoration["color"]);
184 | }
185 | }
186 |
187 | if (($type & self::BITFLAG_TEXTURE_UPDATE) !== 0) {
188 | $this->putVarInt($this->width);
189 | $this->putVarInt($this->height);
190 | $this->putVarInt($this->xOffset);
191 | $this->putVarInt($this->yOffset);
192 |
193 | $this->putUnsignedVarInt($this->width * $this->height);
194 |
195 | $this->put($this->colors);
196 | }
197 | }
198 |
199 | public function handle(NetworkSession $session) : bool {
200 | return true;
201 | }
202 |
203 | public static function prepareColors(array $colors, int $width, int $height) {
204 | $buffer = new BinaryStream;
205 | for ($y = 0; $y < $height; $y++) {
206 | for ($x = 0; $x < $width; $x++) {
207 | $buffer->putUnsignedVarInt($colors[$y][$x]);
208 | }
209 | }
210 | return $buffer->buffer;
211 | }
212 |
213 | public static function checkCompatiblity() : bool {
214 | $original = new ClientboundMapItemDataPacket();
215 | $custom = new CustomClientboundMapItemDataPacket();
216 |
217 | $original->mapId = $custom->mapId = 1;
218 | $original->dimensionId = $custom->dimensionId = DimensionIds::OVERWORLD;
219 | $original->eids = $custom->eids = [];
220 | $original->scale = $custom->scale = 0;
221 | $original->trackedEntities = $custom->trackedEntities = [];
222 | $original->decorations = $custom->decorations = [];
223 | $original->width = $custom->width = 128;
224 | $original->height = $custom->height = 128;
225 | $original->xOffset = $custom->xOffset = 0;
226 | $original->yOffset = $custom->yOffset = 0;
227 |
228 | $color = new Color(0xff, 0xee, 0xdd);
229 | $original->colors = array_fill(0, 128, array_fill(0, 128, $color));
230 | $custom->colors = str_repeat(Binary::writeUnsignedVarInt($color->toABGR()), 128 * 128);
231 |
232 | $original->encode();
233 | $custom->encode();
234 | return $original->buffer === $custom->buffer;
235 | }
236 |
237 | }
238 |
--------------------------------------------------------------------------------
/MapImageEngine/src/FaigerSYS/MapImageEngine/storage/ImageStorage.php:
--------------------------------------------------------------------------------
1 | images[$spl_hash])) {
42 | return self::R_ALREADY_REGISTERED;
43 | }
44 |
45 | $hash = $image->getHashedUUID();
46 | if (isset($this->hashes[$hash])) {
47 | return self::R_UUID_EXISTS;
48 | }
49 |
50 | if ($name !== null) {
51 | $name = strtr($name, ' ', '_');
52 | if (!strlen($name)) {
53 | return self::R_INVALID_NAME;
54 | }
55 | if (isset($this->names[$name])) {
56 | return self::R_NAME_EXISTS;
57 | }
58 | $this->names[$name] = $hash;
59 | }
60 |
61 | $this->images[$spl_hash] = $image;
62 | $this->hashes[$hash] = $spl_hash;
63 |
64 | if ($cache_packets) {
65 | $this->regeneratePacketsCache($image);
66 | }
67 |
68 | return self::R_OK;
69 | }
70 |
71 | /**
72 | * Unregisters image
73 | *
74 | * @param MapImage $image
75 | */
76 | public function unregisterImage(MapImage $image) {
77 | $spl_hash = spl_object_hash($image);
78 | if (!isset($this->images[$spl_hash])) {
79 | return;
80 | }
81 | $hash = $image->getHashedUUID();
82 |
83 | $this->removePacketsCache($image);
84 |
85 | foreach ($this->names as $name => $o_spl_hash) {
86 | if ($spl_hash === $o_spl_hash) {
87 | unset($this->names[$name]);
88 | }
89 | }
90 | foreach ($this->names as $name => $o_spl_hash) {
91 | if ($spl_hash === $o_spl_hash) {
92 | unset($this->names[$name]);
93 | }
94 | }
95 | unset($this->hashes[$hash]);
96 | unset($this->images[$spl_hash]);
97 | }
98 |
99 | /**
100 | * Regenerates map image packets cache
101 | *
102 | * @param MapImage $image
103 | * @param int $chunk_x
104 | * @param int $chunk_y
105 | */
106 | public function regeneratePacketsCache(MapImage $image = null, int $chunk_x = null, int $chunk_y = null) {
107 | if ($image === null) {
108 | $this->cache = [];
109 | foreach ($this->images as $image) {
110 | $this->regeneratePacketsCache($image);
111 | }
112 | } else {
113 | if (!isset($this->images[spl_object_hash($image)])) {
114 | return;
115 | }
116 |
117 | if ($chunk_x === null && $chunk_y === null) {
118 | foreach ($image->getChunks() as $chunks) {
119 | foreach ($chunks as $chunk) {
120 | $pk = new BatchPacket();
121 | $pk->setCompressionLevel(7);
122 | if (MapImageEngine::isCustomPacketSupported()) {
123 | $pk->addPacket($chunk->generateCustomMapImagePacket());
124 | } else {
125 | $pk->addPacket($chunk->generateMapImagePacket());
126 | }
127 | $pk->encode();
128 | $this->packet_cache[$chunk->getMapId()] = $pk;
129 | }
130 | }
131 | } else {
132 | $chunk = $image->getChunk($chunk_x, $chunk_y);
133 | if ($chunk !== null) {
134 | $pk = new BatchPacket();
135 | $pk->setCompressionLevel(7);
136 | if (MapImageEngine::isCustomPacketSupported()) {
137 | $pk->addPacket($chunk->generateCustomMapImagePacket());
138 | } else {
139 | $pk->addPacket($chunk->generateMapImagePacket());
140 | }
141 | $pk->encode();
142 | $this->packet_cache[$chunk->getMapId()] = $pk;
143 | }
144 | }
145 | }
146 | }
147 |
148 | /**
149 | * Removes map image packets from cache
150 | *
151 | * @param MapImage $image
152 | * @param int $chunk_x
153 | * @param int $chunk_y
154 | */
155 | public function removePacketsCache(MapImage $image, int $chunk_x = null, int $chunk_y = null) {
156 | if (!isset($this->images[spl_object_hash($image)])) {
157 | return;
158 | }
159 |
160 | if ($chunk_x === null && $chunk_y === null) {
161 | foreach ($image->getChunks() as $chunks) {
162 | foreach ($chunks as $chunk) {
163 | unset($this->packet_cache[$chunk->getMapId()]);
164 | }
165 | }
166 | } else {
167 | $chunk = $image->getChunk($chunk_x, $chunk_y);
168 | if ($chunk !== null) {
169 | unset($this->packet_cache[$chunk->getMapId()]);
170 | }
171 | }
172 | }
173 |
174 | /**
175 | * Removes map image packet with specified map ID
176 | *
177 | * @param int $map_id
178 | */
179 | public function removePacketCache(int $map_id) {
180 | unset($this->packet_cache[$map_id]);
181 | }
182 |
183 | /**
184 | * Returns map image with specified UUID hash
185 | *
186 | * @param string $uuid_hash
187 | *
188 | * @return MapImage|null
189 | */
190 | public function getImage(string $uuid_hash) {
191 | return $this->images[$this->hashes[$uuid_hash] ?? null] ?? null;
192 | }
193 |
194 | /**
195 | * Returns map image with specified name
196 | *
197 | * @param string $name
198 | *
199 | * @return MapImage|null
200 | */
201 | public function getImageByName(string $name) {
202 | return $this->getImage($this->names[strtr($name, ' ', '_')] ?? '');
203 | }
204 |
205 | /**
206 | * Returns all of map images
207 | *
208 | * @return MapImage[]
209 | */
210 | public function getImages() : array {
211 | return $this->images;
212 | }
213 |
214 | /**
215 | * Returns all of map images that have name
216 | *
217 | * @return MapImage[]
218 | */
219 | public function getNamedImages() : array {
220 | return array_map(
221 | function ($hash) {
222 | return $this->getImage($hash);
223 | },
224 | $this->names
225 | );
226 | }
227 |
228 | /**
229 | * Returns cached batched map image packet
230 | *
231 | * @param int $map_id
232 | *
233 | * @return BatchPacket
234 | */
235 | public function getCachedPacket(int $map_id) {
236 | if (isset($this->packet_cache[$map_id])) {
237 | return clone $this->packet_cache[$map_id];
238 | }
239 | }
240 |
241 | }
242 |
--------------------------------------------------------------------------------
/MapImageEngine/src/FaigerSYS/MapImageEngine/storage/MapImage.php:
--------------------------------------------------------------------------------
1 | blocks_width = $blocks_width;
61 | $this->blocks_height = $blocks_height;
62 | $this->uuid = $uuid ?? UUID::fromRandom();
63 | $this->default_chunk_width = $default_chunk_width;
64 | $this->default_chunk_height = $default_chunk_height;
65 | $this->setChunks($chunks);
66 | }
67 |
68 | /**
69 | * Returns image blocks width
70 | *
71 | * @return int
72 | */
73 | public function getBlocksWidth() : int {
74 | return $this->blocks_width;
75 | }
76 |
77 | /**
78 | * Returns image blocks height
79 | *
80 | * @return int
81 | */
82 | public function getBlocksHeight() : int {
83 | return $this->blocks_height;
84 | }
85 |
86 | /**
87 | * Sets image blocks width
88 | *
89 | * @param int $blocks_width
90 | */
91 | public function setBlocksWidth(int $blocks_width) {
92 | if ($blocks_width < 0) {
93 | throw new \InvalidArgumentException('Blocks width must be greater than 0');
94 | }
95 | $this->blocks_width = $blocks_width;
96 | $this->checkChunks();
97 | }
98 |
99 | /**
100 | * Sets image blocks height
101 | *
102 | * @param int $blocks_width
103 | */
104 | public function setBlocksHeight(int $blocks_height) {
105 | if ($blocks_height < 0) {
106 | throw new \InvalidArgumentException('Blocks height must be greater than 0');
107 | }
108 | $this->blocks_height = $blocks_height;
109 | $this->checkChunks();
110 | }
111 |
112 | /**
113 | * Returns the image chunk at specified position
114 | *
115 | * @param int $block_x
116 | * @param int $block_y
117 | *
118 | * @return MapImageChunk|null
119 | */
120 | public function getChunk(int $block_x, int $block_y) {
121 | return $this->chunks[$block_y][$block_x] ?? null;
122 | }
123 |
124 | /**
125 | * Returns all image chunks
126 | *
127 | * @return MapImageChunk|null
128 | */
129 | public function getChunks() : array {
130 | return $this->chunks;
131 | }
132 |
133 | /**
134 | * Sets the image chunk at specified position
135 | *
136 | * @param int $block_x
137 | * @param int $block_y
138 | * @param MapImageChunk $chunk
139 | */
140 | public function setChunk(int $block_x, int $block_y, MapImageChunk $chunk) {
141 | if ($block_x < 0 || $block_y < 0) {
142 | throw new \InvalidArgumentException('Block X/Y must be greater than 0');
143 | }
144 | if ($block_x >= $this->blocks_width) {
145 | throw new \InvalidArgumentException('Block X cannot be greater than width');
146 | }
147 | if ($block_y >= $this->blocks_height) {
148 | throw new \InvalidArgumentException('Block Y cannot be greater than height');
149 | }
150 |
151 | $this->chunks[$block_y][$block_x] = $chunk;
152 | }
153 |
154 | /**
155 | * Rewrites all image chunks
156 | *
157 | * @param MapImageChunk[][] $chunks
158 | */
159 | public function setChunks(array $chunks) {
160 | $this->chunks = $chunks;
161 | $this->checkChunks();
162 | }
163 |
164 | /**
165 | * Generates bathed packet for all of image chunks
166 | *
167 | * @param int $compression_level
168 | *
169 | * @return BatchPacket
170 | */
171 | public function generateBatchedMapImagesPacket(int $compression_level = 6) {
172 | $pk = new BatchPacket();
173 | $pk->setCompressionLevel($compression_level);
174 | foreach ($this->chunks as $chunk) {
175 | $pk->addPacket($chunk->generateMapImagePacket());
176 | }
177 | return $pk;
178 | }
179 |
180 | /**
181 | * Generates bathed packet for all of image chunks
182 | *
183 | * @param int $compression_level
184 | *
185 | * @return BatchPacket
186 | */
187 | public function generateBatchedCustomMapImagesPacket(int $compression_level = 6) {
188 | $pk = new BatchPacket();
189 | $pk->setCompressionLevel($compression_level);
190 | foreach ($this->chunks as $chunk) {
191 | $pk->addPacket($chunk->generateCustomMapImagePacket());
192 | }
193 | return $pk;
194 | }
195 |
196 | /**
197 | * Returns the image UUID
198 | *
199 | * @return UUID
200 | */
201 | public function getUUID() : UUID {
202 | return $this->uuid;
203 | }
204 |
205 | /**
206 | * Returns the image UUID hash
207 | *
208 | * @return string
209 | */
210 | public function getHashedUUID() : string {
211 | return hash('sha1', $this->uuid->toBinary());
212 | }
213 |
214 | /**
215 | * Creates new MapImage object from MIE image binary
216 | *
217 | * @param stirng $buffer
218 | * @param int &$state
219 | *
220 | * @return MapImage|null
221 | */
222 | public static function fromBinary(string $buffer, &$state = null) {
223 | try {
224 | $buffer = new BinaryStream($buffer);
225 |
226 | $header = $buffer->get(4);
227 | if ($header !== 'MIEI') {
228 | $state = self::R_CORRUPTED;
229 | return;
230 | }
231 |
232 | $api = $buffer->getInt();
233 | if (!in_array($api, self::SUPPORTED_VERSIONS)) {
234 | $state = self::R_UNSUPPORTED_VERSIONS;
235 | return;
236 | }
237 |
238 | $is_compressed = $buffer->getByte();
239 | if ($is_compressed) {
240 | $buffer = $buffer->get(true);
241 | $buffer = @zlib_decode($buffer);
242 | if ($buffer === false) {
243 | $state = self::R_CORRUPTED;
244 | return;
245 | }
246 |
247 | $buffer = new BinaryStream($buffer);
248 | }
249 |
250 | $uuid = UUID::fromBinary($buffer->get(16), 4);
251 |
252 | $blocks_width = $buffer->getInt();
253 | $blocks_height = $buffer->getInt();
254 |
255 | $chunks = [];
256 | for ($block_y = 0; $block_y < $blocks_height; $block_y++) {
257 | for ($block_x = 0; $block_x < $blocks_width; $block_x++) {
258 | $chunk_width = $buffer->getInt();
259 | $chunk_height = $buffer->getInt();
260 | $chunk_data = $buffer->get($chunk_width * $chunk_height * 4);
261 |
262 | $chunks[$block_y][$block_x] = new MapImageChunk($chunk_width, $chunk_height, $chunk_data);
263 | }
264 | }
265 |
266 | $state = self::R_OK;
267 | return new MapImage($blocks_width, $blocks_height, $chunks, $uuid);
268 | } catch (\Throwable $e) {
269 | $state = self::R_CORRUPTED;
270 | }
271 | }
272 |
273 | private function checkChunks() {
274 | $chunks = $this->chunks;
275 | $this->chunks = [];
276 | for ($y = 0; $y < $this->blocks_height; $y++) {
277 | for ($x = 0; $x < $this->blocks_width; $x++) {
278 | $this->chunks[$y][$x] = ($chunks[$y][$x] ?? null) instanceof MapImageChunk ? $chunks[$y][$x] : MapImageChunk::generateImageChunk($this->default_chunk_width, $this->default_chunk_height);
279 | }
280 | }
281 | }
282 |
283 | public function __clone() {
284 | for ($y = 0; $y < $this->blocks_height; $y++) {
285 | for ($x = 0; $x < $this->blocks_width; $x++) {
286 | $this->chunks[$y][$x] = clone $this->chunks[$y][$x];
287 | }
288 | }
289 | $this->uuid = UUID::fromRandom();
290 | }
291 |
292 | }
293 |
--------------------------------------------------------------------------------
/MapImageEngine/src/FaigerSYS/MapImageEngine/storage/MapImageChunk.php:
--------------------------------------------------------------------------------
1 | width = $width;
48 | $this->height = $height;
49 | $this->map_id = $map_id ?? Entity::$entityCount++;
50 | $this->data = new BinaryStream($data);
51 | }
52 |
53 | /**
54 | * Returns map image chunk map ID
55 | *
56 | * @return int
57 | */
58 | public function getMapId() : int {
59 | return $this->map_id;
60 | }
61 |
62 | /**
63 | * Sets map image chunk map ID
64 | *
65 | * @return int
66 | */
67 | public function setMapId(int $map_id) {
68 | $this->map_id = $map_id;
69 | }
70 |
71 | /**
72 | * Returns map image chunk width
73 | *
74 | * @return int
75 | */
76 | public function getWidth() : int {
77 | return $this->width;
78 | }
79 |
80 | /**
81 | * Returns map image chunk height
82 | *
83 | * @return int
84 | */
85 | public function getHeight() : int {
86 | return $this->height;
87 | }
88 |
89 | /**
90 | * Returns RGBA color at specified position
91 | *
92 | * @return int
93 | */
94 | public function getRGBA(int $x, int $y) : int {
95 | $this->data->offset = $this->getStartOffset($x, $y);
96 | return (int) $this->data->getInt();
97 | }
98 |
99 | /**
100 | * Sets RBGA color at specified position
101 | *
102 | * @param int $x
103 | * @param int $y
104 | * @param int $color
105 | */
106 | public function setRGBA(int $x, int $y, int $color) {
107 | $pos = $this->getStartOffset($x, $y);
108 | $this->data->buffer[$pos++] = chr($color & 0xff);
109 | $this->data->buffer[$pos++] = chr($color >> 8 & 0xff);
110 | $this->data->buffer[$pos++] = chr($color >> 16 & 0xff);
111 | $this->data->buffer[$pos] = chr($color >> 24 & 0xff);
112 | }
113 |
114 | /**
115 | * Returns ABGR color at specified position
116 | *
117 | * @return int
118 | */
119 | public function getABGR(int $x, int $y) : int {
120 | $this->data->offset = $this->getStartOffset($x, $y);
121 | return (int) $this->data->getLInt() & 0xffffffff;
122 | }
123 |
124 | /**
125 | * Sets ABGR color at specified position
126 | *
127 | * @param int $x
128 | * @param int $y
129 | * @param int $color
130 | */
131 | public function setABGR(int $x, int $y, int $color) {
132 | $pos = $this->getStartOffset($x, $y);
133 | $this->data->buffer[$pos++] = chr($color >> 24 & 0xff);
134 | $this->data->buffer[$pos++] = chr($color >> 16 & 0xff);
135 | $this->data->buffer[$pos++] = chr($color >> 8 & 0xff);
136 | $this->data->buffer[$pos] = chr($color & 0xff);
137 | }
138 |
139 | /**
140 | * Returns array of Color objects
141 | *
142 | * @return array
143 | */
144 | public function toArrayColor() : array {
145 | $colors = [];
146 | $this->data->offset = 0;
147 | for ($y = 0; $y < $this->height; $y++) {
148 | for ($x = 0; $x < $this->width; $x++) {
149 | $color = $this->data->getInt();
150 | $colors[$y][$x] = new Color($color >> 24 & 0xff, $color >> 16 & 0xff, $color >> 8 & 0xff, $color & 0xff);
151 | }
152 | }
153 | return $colors;
154 | }
155 |
156 | /**
157 | * Returns RGBA colors array
158 | *
159 | * @return array
160 | */
161 | public function toArrayRGBA() : array {
162 | $colors = [];
163 | $this->data->offset = 0;
164 | for ($y = 0; $y < $this->height; $y++) {
165 | for ($x = 0; $x < $this->width; $x++) {
166 | $colors[$y][$x] = (int) $this->data->getInt();
167 | }
168 | }
169 |
170 | return $colors;
171 | }
172 |
173 | /**
174 | * Returns pretty RGBA colors array
175 | *
176 | * @return array
177 | */
178 | public function toArrayPrettyRGBA() : array {
179 | $colors = [];
180 | $this->data->offset = 0;
181 | for ($y = 0; $y < $this->height; $y++) {
182 | for ($x = 0; $x < $this->width; $x++) {
183 | $colors[$y][$x] = [
184 | 'r' => $this->data->getByte(),
185 | 'g' => $this->data->getByte(),
186 | 'b' => $this->data->getByte(),
187 | 'a' => $this->data->getByte()
188 | ];
189 | }
190 | }
191 |
192 | return $colors;
193 | }
194 |
195 | /**
196 | * Returns ABGR colors array
197 | *
198 | * @return array
199 | */
200 | public function toArrayABGR() : array {
201 | $colors = [];
202 | $this->data->offset = 0;
203 | for ($y = 0; $y < $this->height; $y++) {
204 | for ($x = 0; $x < $this->width; $x++) {
205 | $colors[$y][$x] = $this->data->getLInt() & 0xffffffff;
206 | }
207 | }
208 |
209 | return $colors;
210 | }
211 |
212 | /**
213 | * Returns RGBA colors binary
214 | *
215 | * @return string
216 | */
217 | public function toBinaryRGBA() : string {
218 | return $this->data->buffer;
219 | }
220 |
221 | /**
222 | * Generates map image packet
223 | *
224 | * @param int $compression_level
225 | * @param int $map_id
226 | * @param bool $use_cache
227 | *
228 | * @return ClientboundMapItemDataPacket
229 | */
230 | public function generateMapImagePacket(int $map_id = null) {
231 | $pk = new ClientboundMapItemDataPacket;
232 | $pk->mapId = $map_id ?? $this->map_id;
233 | $pk->scale = 0;
234 | $pk->width = $this->width;
235 | $pk->height = $this->height;
236 | $pk->colors = $this->toArrayColor();
237 | return $pk;
238 | }
239 |
240 | /**
241 | * Generates custom map image packet
242 | *
243 | * @param int $compression_level
244 | * @param int $map_id
245 | * @param bool $use_cache
246 | *
247 | * @return CustomClientboundMapItemDataPacket
248 | */
249 | public function generateCustomMapImagePacket(int $map_id = null, bool $use_cache = true) {
250 | $pk = new CustomClientboundMapItemDataPacket;
251 | $pk->mapId = $map_id ?? $this->map_id;
252 | $pk->scale = 0;
253 | $pk->width = $this->width;
254 | $pk->height = $this->height;
255 |
256 | $colors = null;
257 | $generate_cache = false;
258 |
259 | if ($use_cache) {
260 | $cache_hash = hash('md5', $this->width . '.' . $this->height . '.' . hash('md5', $this->data->buffer));
261 | $cache_path = MapImageEngine::getInstance()->getDataFolder() . 'cache/' . $cache_hash;
262 | $generate_cache = true;
263 | if (file_exists($cache_path) && is_file($cache_path)) {
264 | $cache_buffer = new BinaryStream(file_get_contents($cache_path));
265 |
266 | $cache_api = $cache_buffer->getInt();
267 | if ($cache_api === self::CACHE_API) {
268 | $colors = $cache_buffer->get(true);
269 | $generate_cache = false;
270 | }
271 | }
272 | }
273 |
274 | if ($colors === null) {
275 | $colors = CustomClientboundMapItemDataPacket::prepareColors($this->toArrayABGR(), $this->width, $this->height);
276 | }
277 |
278 | if ($generate_cache) {
279 | $cache_buffer = new BinaryStream;
280 | $cache_buffer->putInt(self::CACHE_API);
281 | $cache_buffer->put($colors);
282 |
283 | file_put_contents($cache_path, $cache_buffer->buffer);
284 | }
285 |
286 | $pk->colors = $colors;
287 |
288 | return $pk;
289 | }
290 |
291 | /**
292 | * Creates a new map image chunk from the RGBA color array
293 | *
294 | * @param int $width
295 | * @param int $height
296 | * @param array $colors
297 | *
298 | * @return MapImageChunk
299 | */
300 | public static function fromArrayRGBA(int $width, int $height, array $colors) {
301 | if ($width < 0 || $height < 0) {
302 | throw new \InvalidArgumentException('Width/height must be greater than 0');
303 | }
304 |
305 | $data = new BinaryStream;
306 |
307 | for ($y = 0; $y < $height; $y++) {
308 | for ($x = 0; $x < $width; $x++) {
309 | if (!is_int($colors[$y][$x] ?? null)) {
310 | throw new \InvalidArgumentException('Color is corrupted on [X: ' . $x . ', Y: ' . $y . ']');
311 | }
312 |
313 | $data->putInt($colors[$y][$x]);
314 | }
315 | }
316 |
317 | return new MapImageChunk($width, $height, $data->buffer);
318 | }
319 |
320 | /**
321 | * Creates a new map image chunk from the ABGR colors array
322 | *
323 | * @param int $width
324 | * @param int $height
325 | * @param array $colors
326 | *
327 | * @return MapImageChunk
328 | */
329 | public static function fromArrayABGR(int $width, int $height, array $colors) {
330 | if ($width < 0 || $height < 0) {
331 | throw new \InvalidArgumentException('Width/height must be greater than 0');
332 | }
333 |
334 | $data = new BinaryStream;
335 |
336 | for ($y = 0; $y < $height; $y++) {
337 | for ($x = 0; $x < $width; $x++) {
338 | if (!is_int($colors[$y][$x] ?? null)) {
339 | throw new \InvalidArgumentException('Color is corrupted on [X: ' . $x . ', Y: ' . $y . ']');
340 | }
341 |
342 | $data->putLInt($colors[$y][$x]);
343 | }
344 | }
345 |
346 | return new MapImageChunk($width, $height, $data->buffer);
347 | }
348 |
349 |
350 | /**
351 | * Creates a new map image chunk with the specified color
352 | *
353 | * @param int $width
354 | * @param int $height
355 | * @param int $fill_color
356 | *
357 | * @return MapImageChunk
358 | */
359 | public static function generateImageChunk(int $width, int $height, int $fill_color = 0) {
360 | if ($width < 0 || $height < 0) {
361 | throw new \InvalidArgumentException('Width/height must be greater than 0');
362 | }
363 |
364 | return new MapImageChunk($width, $height, str_repeat(Binary::writeInt($fill_color), $width * $height));
365 | }
366 |
367 | private function getStartOffset(int $x, int $y) : int {
368 | if ($x < 0 || $y < 0) {
369 | throw new \InvalidArgumentException('X/Y must be greater than 0');
370 | }
371 | if ($x >= $this->width) {
372 | throw new \InvalidArgumentException('X cannot be greater than width');
373 | }
374 | if ($y >= $this->height) {
375 | throw new \InvalidArgumentException('Y cannot be greater than height');
376 | }
377 |
378 | return ($y * $this->width) + $x;
379 | }
380 |
381 | public function __clone() {
382 | $this->map_id = Entity::$entityCount++;
383 | }
384 |
385 | }
386 |
--------------------------------------------------------------------------------
/MapImageEngine/src/FaigerSYS/MapImageEngine/storage/OldFormatConverter.php:
--------------------------------------------------------------------------------
1 | put('MIEI');
15 | $buffer->putInt(MapImage::CURRENT_VERSION);
16 | $buffer->putByte(0);
17 | $buffer->put(UUID::fromRandom()->toBinary());
18 |
19 | $image = @gzinflate($data);
20 | if ($image) {
21 | $image = json_decode($image, true);
22 | if (!is_array($image)) {
23 | return;
24 | }
25 |
26 | $b_height = count($image['blocks']);
27 | $b_width = count($image['blocks'][0]);
28 |
29 | $buffer->putInt($b_width);
30 | $buffer->putInt($b_height);
31 |
32 | for ($b_y = 0; $b_y < $b_height; $b_y++) {
33 | for ($b_x = 0; $b_x < $b_width; $b_x++) {
34 | $chunk = json_decode(gzinflate(base64_decode($image['blocks'][$b_y][$b_x])));
35 | if (!is_array($chunk)) {
36 | return;
37 | }
38 |
39 | $chunk = MapImageChunk::fromArrayABGR(128, 128, $chunk)->toBinaryRGBA();
40 |
41 | $buffer->putInt(128);
42 | $buffer->putInt(128);
43 | $buffer->put($chunk);
44 | }
45 | }
46 | } else {
47 | return;
48 | }
49 |
50 | return $buffer->buffer;
51 | } catch (\Throwable $e) {
52 | return;
53 | }
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### This repository contains some plugins:
2 |
3 | Plugin|Description
4 | ------|-----------
5 | [MapImageEngine](https://github.com/FaigerSYS/MapImageEngine/tree/master/MapImageEngine)|Image engine for MCPE
6 | MIE_Animations|Animated images (not soon)
7 | [MIE_Protector](https://github.com/FaigerSYS/MapImageEngine/tree/master/MIE_Protector)|Addon for MapImageEngine to protect the image (frame) from rotation and destruction
8 | [MIE_Converter](https://github.com/FaigerSYS/MapImageEngine/tree/master/MIE_Converter)|Addon for MapImageEngine to convert images to .mie format
9 |
10 | I can not translate the plugin into all languages. I can also have grammatical mistakes in English, so any "language" contribution is welcomed :)
11 |
12 | ## Best users
13 | |#|User|Shox's|
14 | |---|---|---|
15 | |1|Encritary|500|
16 | |2|Yexeed|125|
17 |
--------------------------------------------------------------------------------