├── .idea ├── .gitignore ├── Commando.iml ├── misc.xml ├── modules.xml ├── php.xml └── vcs.xml ├── .poggit.yml ├── LICENSE ├── README.md ├── commando.png ├── composer.json ├── composer.lock ├── docs └── MIGRATION.md ├── src └── CortexPE │ └── Commando │ ├── BaseCommand.php │ ├── BaseSubCommand.php │ ├── IRunnable.php │ ├── PacketHooker.php │ ├── args │ ├── BaseArgument.php │ ├── BlockPositionArgument.php │ ├── BooleanArgument.php │ ├── FloatArgument.php │ ├── IntegerArgument.php │ ├── RawStringArgument.php │ ├── StringEnumArgument.php │ ├── TargetPlayerArgument.php │ ├── TextArgument.php │ └── Vector3Argument.php │ ├── constraint │ ├── BaseConstraint.php │ ├── ConsoleRequiredConstraint.php │ └── InGameRequiredConstraint.php │ ├── exception │ ├── ArgumentOrderException.php │ ├── CommandoException.php │ ├── HookAlreadyRegistered.php │ └── InvalidErrorCode.php │ ├── store │ └── SoftEnumStore.php │ └── traits │ ├── ArgumentableTrait.php │ └── IArgumentable.php └── virion.yml /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/Commando.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/php.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.poggit.yml: -------------------------------------------------------------------------------- 1 | --- # Poggit-CI Manifest. Open the CI at https://poggit.pmmp.io/ci/Paroxity/Commando 2 | build-by-default: true 3 | branches: 4 | - master 5 | projects: 6 | Commando: 7 | model: virion 8 | type: library 9 | libs: 10 | - src: Muqsit/SimplePacketHandler/SimplePacketHandler 11 | version: ^0.1.4 12 | ... 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Commando

2 |
3 | 4 | A PocketMine-MP Virion for easier implementation of dynamic commands, including support for Minecraft: Bedrock Edition argument listing aimed for both the end users and the plugin developers.
5 | 6 | Refer to the [official documentation](https://github.com/CortexPE/Commando/blob/master/README.md) for instructions on how to use this virion library. 7 | 8 | - Poggit CI [https://poggit.pmmp.io/ci/Paroxity/Commando](https://poggit.pmmp.io/ci/Paroxity/Commando/Commando) 9 | - Packagist [https://packagist.org/packages/paroxity/commando](https://packagist.org/packages/paroxity/commando) 10 | 11 | ## Differences 12 | - Support infinite depth subcommands (used in [PiggyFactions](https://github.com/DaPigGuy/PiggyFactions)) 13 | - Improve error handling for command arguments 14 | - And other miscellaneous bug fixes 15 | 16 | We will try to keep this fork up-to-date with the upstream changes. 17 | 18 | ----- 19 | **This framework was made with :heart: by CortexPE, modified by Paroxity. Enjoy!~ :3** 20 | -------------------------------------------------------------------------------- /commando.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Paroxity/Commando/625f33888132cfadc570be33001488d10e84f0d0/commando.png -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paroxity/commando", 3 | "description": "A Command Framework virion for PocketMine-MP ", 4 | "type": "library", 5 | "license": "LGPL-3.0-only", 6 | "require": { 7 | "pocketmine/pocketmine-mp": "^5.0", 8 | "muqsit/simple-packet-handler": "^0.1.4" 9 | }, 10 | "autoload": { 11 | "psr-4": {"CortexPE\\Commando\\": ["src/CortexPE/Commando/"]} 12 | }, 13 | "extra": { 14 | "virion": { 15 | "spec": "3.0", 16 | "namespace-root": "CortexPE\\Commando" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "c4ceaa9d387a5d82fcf1c21d70331fb0", 8 | "packages": [ 9 | { 10 | "name": "adhocore/json-comment", 11 | "version": "1.2.1", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/adhocore/php-json-comment.git", 15 | "reference": "651023f9fe52e9efa2198cbaf6e481d1968e2377" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/adhocore/php-json-comment/zipball/651023f9fe52e9efa2198cbaf6e481d1968e2377", 20 | "reference": "651023f9fe52e9efa2198cbaf6e481d1968e2377", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "ext-ctype": "*", 25 | "php": ">=7.0" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "^6.5 || ^7.5 || ^8.5" 29 | }, 30 | "type": "library", 31 | "autoload": { 32 | "psr-4": { 33 | "Ahc\\Json\\": "src/" 34 | } 35 | }, 36 | "notification-url": "https://packagist.org/downloads/", 37 | "license": [ 38 | "MIT" 39 | ], 40 | "authors": [ 41 | { 42 | "name": "Jitendra Adhikari", 43 | "email": "jiten.adhikary@gmail.com" 44 | } 45 | ], 46 | "description": "Lightweight JSON comment stripper library for PHP", 47 | "keywords": [ 48 | "comment", 49 | "json", 50 | "strip-comment" 51 | ], 52 | "support": { 53 | "issues": "https://github.com/adhocore/php-json-comment/issues", 54 | "source": "https://github.com/adhocore/php-json-comment/tree/1.2.1" 55 | }, 56 | "funding": [ 57 | { 58 | "url": "https://paypal.me/ji10", 59 | "type": "custom" 60 | }, 61 | { 62 | "url": "https://github.com/adhocore", 63 | "type": "github" 64 | } 65 | ], 66 | "time": "2022-10-02T11:22:07+00:00" 67 | }, 68 | { 69 | "name": "brick/math", 70 | "version": "0.12.1", 71 | "source": { 72 | "type": "git", 73 | "url": "https://github.com/brick/math.git", 74 | "reference": "f510c0a40911935b77b86859eb5223d58d660df1" 75 | }, 76 | "dist": { 77 | "type": "zip", 78 | "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", 79 | "reference": "f510c0a40911935b77b86859eb5223d58d660df1", 80 | "shasum": "" 81 | }, 82 | "require": { 83 | "php": "^8.1" 84 | }, 85 | "require-dev": { 86 | "php-coveralls/php-coveralls": "^2.2", 87 | "phpunit/phpunit": "^10.1", 88 | "vimeo/psalm": "5.16.0" 89 | }, 90 | "type": "library", 91 | "autoload": { 92 | "psr-4": { 93 | "Brick\\Math\\": "src/" 94 | } 95 | }, 96 | "notification-url": "https://packagist.org/downloads/", 97 | "license": [ 98 | "MIT" 99 | ], 100 | "description": "Arbitrary-precision arithmetic library", 101 | "keywords": [ 102 | "Arbitrary-precision", 103 | "BigInteger", 104 | "BigRational", 105 | "arithmetic", 106 | "bigdecimal", 107 | "bignum", 108 | "bignumber", 109 | "brick", 110 | "decimal", 111 | "integer", 112 | "math", 113 | "mathematics", 114 | "rational" 115 | ], 116 | "support": { 117 | "issues": "https://github.com/brick/math/issues", 118 | "source": "https://github.com/brick/math/tree/0.12.1" 119 | }, 120 | "funding": [ 121 | { 122 | "url": "https://github.com/BenMorel", 123 | "type": "github" 124 | } 125 | ], 126 | "time": "2023-11-29T23:19:16+00:00" 127 | }, 128 | { 129 | "name": "muqsit/simple-packet-handler", 130 | "version": "0.1.4", 131 | "source": { 132 | "type": "git", 133 | "url": "https://github.com/Muqsit/SimplePacketHandler.git", 134 | "reference": "8121eca3f21cb9912c3ac8406a11f70cf105c905" 135 | }, 136 | "dist": { 137 | "type": "zip", 138 | "url": "https://api.github.com/repos/Muqsit/SimplePacketHandler/zipball/8121eca3f21cb9912c3ac8406a11f70cf105c905", 139 | "reference": "8121eca3f21cb9912c3ac8406a11f70cf105c905", 140 | "shasum": "" 141 | }, 142 | "require": { 143 | "pocketmine/pocketmine-mp": "^5.0.0" 144 | }, 145 | "type": "library", 146 | "extra": { 147 | "virion": { 148 | "spec": "3.0", 149 | "namespace-root": "muqsit\\simplepackethandler" 150 | } 151 | }, 152 | "autoload": { 153 | "classmap": [ 154 | "src" 155 | ] 156 | }, 157 | "notification-url": "https://packagist.org/downloads/", 158 | "license": [ 159 | "GPL-3.0" 160 | ], 161 | "authors": [ 162 | { 163 | "name": "Muqsit", 164 | "email": "hackhack05@gmail.com" 165 | } 166 | ], 167 | "description": "Handle specific data packets (virion for PMMP API 4.0.0)", 168 | "support": { 169 | "issues": "https://github.com/Muqsit/SimplePacketHandler/issues", 170 | "source": "https://github.com/Muqsit/SimplePacketHandler/tree/0.1.4" 171 | }, 172 | "time": "2024-06-14T08:39:49+00:00" 173 | }, 174 | { 175 | "name": "pocketmine/bedrock-block-upgrade-schema", 176 | "version": "4.2.0", 177 | "source": { 178 | "type": "git", 179 | "url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git", 180 | "reference": "8a327197b3b462fa282f40f76b070ffe585a25d2" 181 | }, 182 | "dist": { 183 | "type": "zip", 184 | "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/8a327197b3b462fa282f40f76b070ffe585a25d2", 185 | "reference": "8a327197b3b462fa282f40f76b070ffe585a25d2", 186 | "shasum": "" 187 | }, 188 | "type": "library", 189 | "notification-url": "https://packagist.org/downloads/", 190 | "license": [ 191 | "CC0-1.0" 192 | ], 193 | "description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves", 194 | "support": { 195 | "issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues", 196 | "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/4.2.0" 197 | }, 198 | "time": "2024-06-13T17:28:26+00:00" 199 | }, 200 | { 201 | "name": "pocketmine/bedrock-data", 202 | "version": "2.11.0+bedrock-1.21.0", 203 | "source": { 204 | "type": "git", 205 | "url": "https://github.com/pmmp/BedrockData.git", 206 | "reference": "cae40bde98081b388c4d3ab59d45b8d1cf5d8761" 207 | }, 208 | "dist": { 209 | "type": "zip", 210 | "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/cae40bde98081b388c4d3ab59d45b8d1cf5d8761", 211 | "reference": "cae40bde98081b388c4d3ab59d45b8d1cf5d8761", 212 | "shasum": "" 213 | }, 214 | "type": "library", 215 | "notification-url": "https://packagist.org/downloads/", 216 | "license": [ 217 | "CC0-1.0" 218 | ], 219 | "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP", 220 | "support": { 221 | "issues": "https://github.com/pmmp/BedrockData/issues", 222 | "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.0" 223 | }, 224 | "time": "2024-06-13T17:17:55+00:00" 225 | }, 226 | { 227 | "name": "pocketmine/bedrock-item-upgrade-schema", 228 | "version": "1.10.0", 229 | "source": { 230 | "type": "git", 231 | "url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git", 232 | "reference": "b4687afa19f91eacebd46c40d487f4cc515be504" 233 | }, 234 | "dist": { 235 | "type": "zip", 236 | "url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/b4687afa19f91eacebd46c40d487f4cc515be504", 237 | "reference": "b4687afa19f91eacebd46c40d487f4cc515be504", 238 | "shasum": "" 239 | }, 240 | "type": "library", 241 | "notification-url": "https://packagist.org/downloads/", 242 | "license": [ 243 | "CC0-1.0" 244 | ], 245 | "description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves", 246 | "support": { 247 | "issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues", 248 | "source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.10.0" 249 | }, 250 | "time": "2024-05-15T15:15:55+00:00" 251 | }, 252 | { 253 | "name": "pocketmine/bedrock-protocol", 254 | "version": "31.0.0+bedrock-1.21.0", 255 | "source": { 256 | "type": "git", 257 | "url": "https://github.com/pmmp/BedrockProtocol.git", 258 | "reference": "972373b6b8068963649f0a95163424eb5b645e29" 259 | }, 260 | "dist": { 261 | "type": "zip", 262 | "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/972373b6b8068963649f0a95163424eb5b645e29", 263 | "reference": "972373b6b8068963649f0a95163424eb5b645e29", 264 | "shasum": "" 265 | }, 266 | "require": { 267 | "ext-json": "*", 268 | "php": "^8.1", 269 | "pocketmine/binaryutils": "^0.2.0", 270 | "pocketmine/color": "^0.2.0 || ^0.3.0", 271 | "pocketmine/math": "^0.3.0 || ^0.4.0 || ^1.0.0", 272 | "pocketmine/nbt": "^1.0.0", 273 | "ramsey/uuid": "^4.1" 274 | }, 275 | "require-dev": { 276 | "phpstan/phpstan": "1.11.2", 277 | "phpstan/phpstan-phpunit": "^1.0.0", 278 | "phpstan/phpstan-strict-rules": "^1.0.0", 279 | "phpunit/phpunit": "^9.5 || ^10.0" 280 | }, 281 | "type": "library", 282 | "autoload": { 283 | "psr-4": { 284 | "pocketmine\\network\\mcpe\\protocol\\": "src/" 285 | } 286 | }, 287 | "notification-url": "https://packagist.org/downloads/", 288 | "license": [ 289 | "LGPL-3.0" 290 | ], 291 | "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", 292 | "support": { 293 | "issues": "https://github.com/pmmp/BedrockProtocol/issues", 294 | "source": "https://github.com/pmmp/BedrockProtocol/tree/31.0.0+bedrock-1.21.0" 295 | }, 296 | "time": "2024-06-13T17:34:14+00:00" 297 | }, 298 | { 299 | "name": "pocketmine/binaryutils", 300 | "version": "0.2.6", 301 | "source": { 302 | "type": "git", 303 | "url": "https://github.com/pmmp/BinaryUtils.git", 304 | "reference": "ccfc1899b859d45814ea3592e20ebec4cb731c84" 305 | }, 306 | "dist": { 307 | "type": "zip", 308 | "url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/ccfc1899b859d45814ea3592e20ebec4cb731c84", 309 | "reference": "ccfc1899b859d45814ea3592e20ebec4cb731c84", 310 | "shasum": "" 311 | }, 312 | "require": { 313 | "php": "^7.4 || ^8.0", 314 | "php-64bit": "*" 315 | }, 316 | "require-dev": { 317 | "phpstan/extension-installer": "^1.0", 318 | "phpstan/phpstan": "~1.10.3", 319 | "phpstan/phpstan-phpunit": "^1.0", 320 | "phpstan/phpstan-strict-rules": "^1.0.0", 321 | "phpunit/phpunit": "^9.5 || ^10.0 || ^11.0" 322 | }, 323 | "type": "library", 324 | "autoload": { 325 | "psr-4": { 326 | "pocketmine\\utils\\": "src/" 327 | } 328 | }, 329 | "notification-url": "https://packagist.org/downloads/", 330 | "license": [ 331 | "LGPL-3.0" 332 | ], 333 | "description": "Classes and methods for conveniently handling binary data", 334 | "support": { 335 | "issues": "https://github.com/pmmp/BinaryUtils/issues", 336 | "source": "https://github.com/pmmp/BinaryUtils/tree/0.2.6" 337 | }, 338 | "time": "2024-03-04T15:04:17+00:00" 339 | }, 340 | { 341 | "name": "pocketmine/callback-validator", 342 | "version": "1.0.3", 343 | "source": { 344 | "type": "git", 345 | "url": "https://github.com/pmmp/CallbackValidator.git", 346 | "reference": "64787469766bcaa7e5885242e85c23c25e8c55a2" 347 | }, 348 | "dist": { 349 | "type": "zip", 350 | "url": "https://api.github.com/repos/pmmp/CallbackValidator/zipball/64787469766bcaa7e5885242e85c23c25e8c55a2", 351 | "reference": "64787469766bcaa7e5885242e85c23c25e8c55a2", 352 | "shasum": "" 353 | }, 354 | "require": { 355 | "ext-reflection": "*", 356 | "php": "^7.1 || ^8.0" 357 | }, 358 | "replace": { 359 | "daverandom/callback-validator": "*" 360 | }, 361 | "require-dev": { 362 | "phpstan/extension-installer": "^1.0", 363 | "phpstan/phpstan": "0.12.59", 364 | "phpstan/phpstan-strict-rules": "^0.12.4", 365 | "phpunit/phpunit": "^7.5 || ^8.5 || ^9.0" 366 | }, 367 | "type": "library", 368 | "autoload": { 369 | "psr-4": { 370 | "DaveRandom\\CallbackValidator\\": "src/" 371 | } 372 | }, 373 | "notification-url": "https://packagist.org/downloads/", 374 | "license": [ 375 | "MIT" 376 | ], 377 | "authors": [ 378 | { 379 | "name": "Chris Wright", 380 | "email": "cw@daverandom.com" 381 | } 382 | ], 383 | "description": "Fork of daverandom/callback-validator - Tools for validating callback signatures", 384 | "support": { 385 | "issues": "https://github.com/pmmp/CallbackValidator/issues", 386 | "source": "https://github.com/pmmp/CallbackValidator/tree/1.0.3" 387 | }, 388 | "time": "2020-12-11T01:45:37+00:00" 389 | }, 390 | { 391 | "name": "pocketmine/color", 392 | "version": "0.3.1", 393 | "source": { 394 | "type": "git", 395 | "url": "https://github.com/pmmp/Color.git", 396 | "reference": "a0421f1e9e0b0c619300fb92d593283378f6a5e1" 397 | }, 398 | "dist": { 399 | "type": "zip", 400 | "url": "https://api.github.com/repos/pmmp/Color/zipball/a0421f1e9e0b0c619300fb92d593283378f6a5e1", 401 | "reference": "a0421f1e9e0b0c619300fb92d593283378f6a5e1", 402 | "shasum": "" 403 | }, 404 | "require": { 405 | "php": "^8.0" 406 | }, 407 | "require-dev": { 408 | "phpstan/phpstan": "1.10.3", 409 | "phpstan/phpstan-strict-rules": "^1.2.0" 410 | }, 411 | "type": "library", 412 | "autoload": { 413 | "psr-4": { 414 | "pocketmine\\color\\": "src/" 415 | } 416 | }, 417 | "notification-url": "https://packagist.org/downloads/", 418 | "license": [ 419 | "LGPL-3.0" 420 | ], 421 | "description": "Color handling library used by PocketMine-MP and related projects", 422 | "support": { 423 | "issues": "https://github.com/pmmp/Color/issues", 424 | "source": "https://github.com/pmmp/Color/tree/0.3.1" 425 | }, 426 | "time": "2023-04-10T11:38:05+00:00" 427 | }, 428 | { 429 | "name": "pocketmine/errorhandler", 430 | "version": "0.7.0", 431 | "source": { 432 | "type": "git", 433 | "url": "https://github.com/pmmp/ErrorHandler.git", 434 | "reference": "cae94884368a74ece5294b9ff7fef18732dcd921" 435 | }, 436 | "dist": { 437 | "type": "zip", 438 | "url": "https://api.github.com/repos/pmmp/ErrorHandler/zipball/cae94884368a74ece5294b9ff7fef18732dcd921", 439 | "reference": "cae94884368a74ece5294b9ff7fef18732dcd921", 440 | "shasum": "" 441 | }, 442 | "require": { 443 | "php": "^8.0" 444 | }, 445 | "require-dev": { 446 | "phpstan/phpstan": "~1.10.3", 447 | "phpstan/phpstan-strict-rules": "^1.0", 448 | "phpunit/phpunit": "^9.5 || ^10.0 || ^11.0" 449 | }, 450 | "type": "library", 451 | "autoload": { 452 | "psr-4": { 453 | "pocketmine\\errorhandler\\": "src/" 454 | } 455 | }, 456 | "notification-url": "https://packagist.org/downloads/", 457 | "license": [ 458 | "LGPL-3.0" 459 | ], 460 | "description": "Utilities to handle nasty PHP E_* errors in a usable way", 461 | "support": { 462 | "issues": "https://github.com/pmmp/ErrorHandler/issues", 463 | "source": "https://github.com/pmmp/ErrorHandler/tree/0.7.0" 464 | }, 465 | "time": "2024-04-02T18:29:54+00:00" 466 | }, 467 | { 468 | "name": "pocketmine/locale-data", 469 | "version": "2.19.6", 470 | "source": { 471 | "type": "git", 472 | "url": "https://github.com/pmmp/Language.git", 473 | "reference": "93e473e20e7f4515ecf45c5ef0f9155b9247a86e" 474 | }, 475 | "dist": { 476 | "type": "zip", 477 | "url": "https://api.github.com/repos/pmmp/Language/zipball/93e473e20e7f4515ecf45c5ef0f9155b9247a86e", 478 | "reference": "93e473e20e7f4515ecf45c5ef0f9155b9247a86e", 479 | "shasum": "" 480 | }, 481 | "type": "library", 482 | "notification-url": "https://packagist.org/downloads/", 483 | "description": "Language resources used by PocketMine-MP", 484 | "support": { 485 | "issues": "https://github.com/pmmp/Language/issues", 486 | "source": "https://github.com/pmmp/Language/tree/2.19.6" 487 | }, 488 | "time": "2023-08-08T16:53:23+00:00" 489 | }, 490 | { 491 | "name": "pocketmine/log", 492 | "version": "0.4.0", 493 | "source": { 494 | "type": "git", 495 | "url": "https://github.com/pmmp/Log.git", 496 | "reference": "e6c912c0f9055c81d23108ec2d179b96f404c043" 497 | }, 498 | "dist": { 499 | "type": "zip", 500 | "url": "https://api.github.com/repos/pmmp/Log/zipball/e6c912c0f9055c81d23108ec2d179b96f404c043", 501 | "reference": "e6c912c0f9055c81d23108ec2d179b96f404c043", 502 | "shasum": "" 503 | }, 504 | "require": { 505 | "php": "^7.4 || ^8.0" 506 | }, 507 | "conflict": { 508 | "pocketmine/spl": "<0.4" 509 | }, 510 | "require-dev": { 511 | "phpstan/phpstan": "0.12.88", 512 | "phpstan/phpstan-strict-rules": "^0.12.2" 513 | }, 514 | "type": "library", 515 | "autoload": { 516 | "classmap": [ 517 | "./src" 518 | ] 519 | }, 520 | "notification-url": "https://packagist.org/downloads/", 521 | "license": [ 522 | "LGPL-3.0" 523 | ], 524 | "description": "Logging components used by PocketMine-MP and related projects", 525 | "support": { 526 | "issues": "https://github.com/pmmp/Log/issues", 527 | "source": "https://github.com/pmmp/Log/tree/0.4.0" 528 | }, 529 | "time": "2021-06-18T19:08:09+00:00" 530 | }, 531 | { 532 | "name": "pocketmine/math", 533 | "version": "1.0.0", 534 | "source": { 535 | "type": "git", 536 | "url": "https://github.com/pmmp/Math.git", 537 | "reference": "dc132d93595b32e9f210d78b3c8d43c662a5edbf" 538 | }, 539 | "dist": { 540 | "type": "zip", 541 | "url": "https://api.github.com/repos/pmmp/Math/zipball/dc132d93595b32e9f210d78b3c8d43c662a5edbf", 542 | "reference": "dc132d93595b32e9f210d78b3c8d43c662a5edbf", 543 | "shasum": "" 544 | }, 545 | "require": { 546 | "php": "^8.0", 547 | "php-64bit": "*" 548 | }, 549 | "require-dev": { 550 | "phpstan/extension-installer": "^1.0", 551 | "phpstan/phpstan": "~1.10.3", 552 | "phpstan/phpstan-strict-rules": "^1.0", 553 | "phpunit/phpunit": "^8.5 || ^9.5" 554 | }, 555 | "type": "library", 556 | "autoload": { 557 | "psr-4": { 558 | "pocketmine\\math\\": "src/" 559 | } 560 | }, 561 | "notification-url": "https://packagist.org/downloads/", 562 | "license": [ 563 | "LGPL-3.0" 564 | ], 565 | "description": "PHP library containing math related code used in PocketMine-MP", 566 | "support": { 567 | "issues": "https://github.com/pmmp/Math/issues", 568 | "source": "https://github.com/pmmp/Math/tree/1.0.0" 569 | }, 570 | "time": "2023-08-03T12:56:33+00:00" 571 | }, 572 | { 573 | "name": "pocketmine/nbt", 574 | "version": "1.0.0", 575 | "source": { 576 | "type": "git", 577 | "url": "https://github.com/pmmp/NBT.git", 578 | "reference": "20540271cb59e04672cb163dca73366f207974f1" 579 | }, 580 | "dist": { 581 | "type": "zip", 582 | "url": "https://api.github.com/repos/pmmp/NBT/zipball/20540271cb59e04672cb163dca73366f207974f1", 583 | "reference": "20540271cb59e04672cb163dca73366f207974f1", 584 | "shasum": "" 585 | }, 586 | "require": { 587 | "php": "^7.4 || ^8.0", 588 | "php-64bit": "*", 589 | "pocketmine/binaryutils": "^0.2.0" 590 | }, 591 | "require-dev": { 592 | "phpstan/extension-installer": "^1.0", 593 | "phpstan/phpstan": "1.10.25", 594 | "phpstan/phpstan-strict-rules": "^1.0", 595 | "phpunit/phpunit": "^9.5" 596 | }, 597 | "type": "library", 598 | "autoload": { 599 | "psr-4": { 600 | "pocketmine\\nbt\\": "src/" 601 | } 602 | }, 603 | "notification-url": "https://packagist.org/downloads/", 604 | "license": [ 605 | "LGPL-3.0" 606 | ], 607 | "description": "PHP library for working with Named Binary Tags", 608 | "support": { 609 | "issues": "https://github.com/pmmp/NBT/issues", 610 | "source": "https://github.com/pmmp/NBT/tree/1.0.0" 611 | }, 612 | "time": "2023-07-14T13:01:49+00:00" 613 | }, 614 | { 615 | "name": "pocketmine/netresearch-jsonmapper", 616 | "version": "v4.4.999", 617 | "source": { 618 | "type": "git", 619 | "url": "https://github.com/pmmp/netresearch-jsonmapper.git", 620 | "reference": "9a6610033d56e358e86a3e4fd5f87063c7318833" 621 | }, 622 | "dist": { 623 | "type": "zip", 624 | "url": "https://api.github.com/repos/pmmp/netresearch-jsonmapper/zipball/9a6610033d56e358e86a3e4fd5f87063c7318833", 625 | "reference": "9a6610033d56e358e86a3e4fd5f87063c7318833", 626 | "shasum": "" 627 | }, 628 | "require": { 629 | "ext-json": "*", 630 | "ext-pcre": "*", 631 | "ext-reflection": "*", 632 | "ext-spl": "*", 633 | "php": ">=7.1" 634 | }, 635 | "replace": { 636 | "netresearch/jsonmapper": "~4.2.0" 637 | }, 638 | "require-dev": { 639 | "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", 640 | "squizlabs/php_codesniffer": "~3.5" 641 | }, 642 | "type": "library", 643 | "autoload": { 644 | "psr-0": { 645 | "JsonMapper": "src/" 646 | } 647 | }, 648 | "notification-url": "https://packagist.org/downloads/", 649 | "license": [ 650 | "OSL-3.0" 651 | ], 652 | "authors": [ 653 | { 654 | "name": "Christian Weiske", 655 | "email": "cweiske@cweiske.de", 656 | "homepage": "http://github.com/cweiske/jsonmapper/", 657 | "role": "Developer" 658 | } 659 | ], 660 | "description": "Fork of netresearch/jsonmapper with security fixes needed by pocketmine/pocketmine-mp", 661 | "support": { 662 | "email": "cweiske@cweiske.de", 663 | "issues": "https://github.com/cweiske/jsonmapper/issues", 664 | "source": "https://github.com/pmmp/netresearch-jsonmapper/tree/v4.4.999" 665 | }, 666 | "time": "2024-02-23T13:17:01+00:00" 667 | }, 668 | { 669 | "name": "pocketmine/pocketmine-mp", 670 | "version": "5.16.0", 671 | "source": { 672 | "type": "git", 673 | "url": "https://github.com/pmmp/PocketMine-MP.git", 674 | "reference": "22a1549998ef0e4668ed48838900ad84258fe04a" 675 | }, 676 | "dist": { 677 | "type": "zip", 678 | "url": "https://api.github.com/repos/pmmp/PocketMine-MP/zipball/22a1549998ef0e4668ed48838900ad84258fe04a", 679 | "reference": "22a1549998ef0e4668ed48838900ad84258fe04a", 680 | "shasum": "" 681 | }, 682 | "require": { 683 | "adhocore/json-comment": "~1.2.0", 684 | "composer-runtime-api": "^2.0", 685 | "ext-chunkutils2": "^0.3.1", 686 | "ext-crypto": "^0.3.1", 687 | "ext-ctype": "*", 688 | "ext-curl": "*", 689 | "ext-date": "*", 690 | "ext-gmp": "*", 691 | "ext-hash": "*", 692 | "ext-igbinary": "^3.0.1", 693 | "ext-json": "*", 694 | "ext-leveldb": "^0.2.1 || ^0.3.0", 695 | "ext-mbstring": "*", 696 | "ext-morton": "^0.1.0", 697 | "ext-openssl": "*", 698 | "ext-pcre": "*", 699 | "ext-phar": "*", 700 | "ext-pmmpthread": "^6.1.0", 701 | "ext-reflection": "*", 702 | "ext-simplexml": "*", 703 | "ext-sockets": "*", 704 | "ext-spl": "*", 705 | "ext-yaml": ">=2.0.0", 706 | "ext-zip": "*", 707 | "ext-zlib": ">=1.2.11", 708 | "php": "^8.1", 709 | "php-64bit": "*", 710 | "pocketmine/bedrock-block-upgrade-schema": "~4.2.0+bedrock-1.21.0", 711 | "pocketmine/bedrock-data": "~2.11.0+bedrock-1.21.0", 712 | "pocketmine/bedrock-item-upgrade-schema": "~1.10.0+bedrock-1.21.0", 713 | "pocketmine/bedrock-protocol": "~31.0.0+bedrock-1.21.0", 714 | "pocketmine/binaryutils": "^0.2.1", 715 | "pocketmine/callback-validator": "^1.0.2", 716 | "pocketmine/color": "^0.3.0", 717 | "pocketmine/errorhandler": "^0.7.0", 718 | "pocketmine/locale-data": "~2.19.0", 719 | "pocketmine/log": "^0.4.0", 720 | "pocketmine/math": "~1.0.0", 721 | "pocketmine/nbt": "~1.0.0", 722 | "pocketmine/netresearch-jsonmapper": "~v4.4.999", 723 | "pocketmine/raklib": "~1.1.0", 724 | "pocketmine/raklib-ipc": "~1.0.0", 725 | "pocketmine/snooze": "^0.5.0", 726 | "ramsey/uuid": "~4.7.0", 727 | "symfony/filesystem": "~6.4.0" 728 | }, 729 | "require-dev": { 730 | "phpstan/phpstan": "1.11.2", 731 | "phpstan/phpstan-phpunit": "^1.1.0", 732 | "phpstan/phpstan-strict-rules": "^1.2.0", 733 | "phpunit/phpunit": "~10.3.0 || ~10.2.0 || ~10.1.0" 734 | }, 735 | "type": "project", 736 | "autoload": { 737 | "files": [ 738 | "src/CoreConstants.php" 739 | ], 740 | "psr-4": { 741 | "pocketmine\\": "src/" 742 | } 743 | }, 744 | "notification-url": "https://packagist.org/downloads/", 745 | "license": [ 746 | "LGPL-3.0" 747 | ], 748 | "description": "A server software for Minecraft: Bedrock Edition written in PHP", 749 | "homepage": "https://pmmp.io", 750 | "support": { 751 | "issues": "https://github.com/pmmp/PocketMine-MP/issues", 752 | "source": "https://github.com/pmmp/PocketMine-MP/tree/5.16.0" 753 | }, 754 | "funding": [ 755 | { 756 | "url": "https://github.com/pmmp/PocketMine-MP#donate", 757 | "type": "custom" 758 | }, 759 | { 760 | "url": "https://www.patreon.com/pocketminemp", 761 | "type": "patreon" 762 | } 763 | ], 764 | "time": "2024-06-13T17:55:18+00:00" 765 | }, 766 | { 767 | "name": "pocketmine/raklib", 768 | "version": "1.1.1", 769 | "source": { 770 | "type": "git", 771 | "url": "https://github.com/pmmp/RakLib.git", 772 | "reference": "be2783be516bf6e2872ff5c81fb9048596617b97" 773 | }, 774 | "dist": { 775 | "type": "zip", 776 | "url": "https://api.github.com/repos/pmmp/RakLib/zipball/be2783be516bf6e2872ff5c81fb9048596617b97", 777 | "reference": "be2783be516bf6e2872ff5c81fb9048596617b97", 778 | "shasum": "" 779 | }, 780 | "require": { 781 | "ext-sockets": "*", 782 | "php": "^8.1", 783 | "php-64bit": "*", 784 | "php-ipv6": "*", 785 | "pocketmine/binaryutils": "^0.2.0", 786 | "pocketmine/log": "^0.3.0 || ^0.4.0" 787 | }, 788 | "require-dev": { 789 | "phpstan/phpstan": "1.10.1", 790 | "phpstan/phpstan-strict-rules": "^1.0" 791 | }, 792 | "type": "library", 793 | "autoload": { 794 | "psr-4": { 795 | "raklib\\": "src/" 796 | } 797 | }, 798 | "notification-url": "https://packagist.org/downloads/", 799 | "license": [ 800 | "GPL-3.0" 801 | ], 802 | "description": "A RakNet server implementation written in PHP", 803 | "support": { 804 | "issues": "https://github.com/pmmp/RakLib/issues", 805 | "source": "https://github.com/pmmp/RakLib/tree/1.1.1" 806 | }, 807 | "time": "2024-03-04T14:02:14+00:00" 808 | }, 809 | { 810 | "name": "pocketmine/raklib-ipc", 811 | "version": "1.0.1", 812 | "source": { 813 | "type": "git", 814 | "url": "https://github.com/pmmp/RakLibIpc.git", 815 | "reference": "ce632ef2c6743e71eddb5dc329c49af6555f90bc" 816 | }, 817 | "dist": { 818 | "type": "zip", 819 | "url": "https://api.github.com/repos/pmmp/RakLibIpc/zipball/ce632ef2c6743e71eddb5dc329c49af6555f90bc", 820 | "reference": "ce632ef2c6743e71eddb5dc329c49af6555f90bc", 821 | "shasum": "" 822 | }, 823 | "require": { 824 | "php": "^8.0", 825 | "php-64bit": "*", 826 | "pocketmine/binaryutils": "^0.2.0", 827 | "pocketmine/raklib": "^0.15.0 || ^1.0.0" 828 | }, 829 | "require-dev": { 830 | "phpstan/phpstan": "1.10.1", 831 | "phpstan/phpstan-strict-rules": "^1.0.0" 832 | }, 833 | "type": "library", 834 | "autoload": { 835 | "psr-4": { 836 | "raklib\\server\\ipc\\": "src/" 837 | } 838 | }, 839 | "notification-url": "https://packagist.org/downloads/", 840 | "license": [ 841 | "GPL-3.0" 842 | ], 843 | "description": "Channel-based protocols for inter-thread/inter-process communication with RakLib", 844 | "support": { 845 | "issues": "https://github.com/pmmp/RakLibIpc/issues", 846 | "source": "https://github.com/pmmp/RakLibIpc/tree/1.0.1" 847 | }, 848 | "time": "2024-03-01T15:55:05+00:00" 849 | }, 850 | { 851 | "name": "pocketmine/snooze", 852 | "version": "0.5.0", 853 | "source": { 854 | "type": "git", 855 | "url": "https://github.com/pmmp/Snooze.git", 856 | "reference": "a86d9ee60ce44755d166d3c7ba4b8b8be8360915" 857 | }, 858 | "dist": { 859 | "type": "zip", 860 | "url": "https://api.github.com/repos/pmmp/Snooze/zipball/a86d9ee60ce44755d166d3c7ba4b8b8be8360915", 861 | "reference": "a86d9ee60ce44755d166d3c7ba4b8b8be8360915", 862 | "shasum": "" 863 | }, 864 | "require": { 865 | "ext-pmmpthread": "^6.0", 866 | "php-64bit": "^8.1" 867 | }, 868 | "require-dev": { 869 | "phpstan/extension-installer": "^1.0", 870 | "phpstan/phpstan": "1.10.3", 871 | "phpstan/phpstan-strict-rules": "^1.0" 872 | }, 873 | "type": "library", 874 | "autoload": { 875 | "psr-4": { 876 | "pocketmine\\snooze\\": "src/" 877 | } 878 | }, 879 | "notification-url": "https://packagist.org/downloads/", 880 | "license": [ 881 | "LGPL-3.0" 882 | ], 883 | "description": "Thread notification management library for code using the pthreads extension", 884 | "support": { 885 | "issues": "https://github.com/pmmp/Snooze/issues", 886 | "source": "https://github.com/pmmp/Snooze/tree/0.5.0" 887 | }, 888 | "time": "2023-05-22T23:43:01+00:00" 889 | }, 890 | { 891 | "name": "ramsey/collection", 892 | "version": "2.0.0", 893 | "source": { 894 | "type": "git", 895 | "url": "https://github.com/ramsey/collection.git", 896 | "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" 897 | }, 898 | "dist": { 899 | "type": "zip", 900 | "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", 901 | "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", 902 | "shasum": "" 903 | }, 904 | "require": { 905 | "php": "^8.1" 906 | }, 907 | "require-dev": { 908 | "captainhook/plugin-composer": "^5.3", 909 | "ergebnis/composer-normalize": "^2.28.3", 910 | "fakerphp/faker": "^1.21", 911 | "hamcrest/hamcrest-php": "^2.0", 912 | "jangregor/phpstan-prophecy": "^1.0", 913 | "mockery/mockery": "^1.5", 914 | "php-parallel-lint/php-console-highlighter": "^1.0", 915 | "php-parallel-lint/php-parallel-lint": "^1.3", 916 | "phpcsstandards/phpcsutils": "^1.0.0-rc1", 917 | "phpspec/prophecy-phpunit": "^2.0", 918 | "phpstan/extension-installer": "^1.2", 919 | "phpstan/phpstan": "^1.9", 920 | "phpstan/phpstan-mockery": "^1.1", 921 | "phpstan/phpstan-phpunit": "^1.3", 922 | "phpunit/phpunit": "^9.5", 923 | "psalm/plugin-mockery": "^1.1", 924 | "psalm/plugin-phpunit": "^0.18.4", 925 | "ramsey/coding-standard": "^2.0.3", 926 | "ramsey/conventional-commits": "^1.3", 927 | "vimeo/psalm": "^5.4" 928 | }, 929 | "type": "library", 930 | "extra": { 931 | "captainhook": { 932 | "force-install": true 933 | }, 934 | "ramsey/conventional-commits": { 935 | "configFile": "conventional-commits.json" 936 | } 937 | }, 938 | "autoload": { 939 | "psr-4": { 940 | "Ramsey\\Collection\\": "src/" 941 | } 942 | }, 943 | "notification-url": "https://packagist.org/downloads/", 944 | "license": [ 945 | "MIT" 946 | ], 947 | "authors": [ 948 | { 949 | "name": "Ben Ramsey", 950 | "email": "ben@benramsey.com", 951 | "homepage": "https://benramsey.com" 952 | } 953 | ], 954 | "description": "A PHP library for representing and manipulating collections.", 955 | "keywords": [ 956 | "array", 957 | "collection", 958 | "hash", 959 | "map", 960 | "queue", 961 | "set" 962 | ], 963 | "support": { 964 | "issues": "https://github.com/ramsey/collection/issues", 965 | "source": "https://github.com/ramsey/collection/tree/2.0.0" 966 | }, 967 | "funding": [ 968 | { 969 | "url": "https://github.com/ramsey", 970 | "type": "github" 971 | }, 972 | { 973 | "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", 974 | "type": "tidelift" 975 | } 976 | ], 977 | "time": "2022-12-31T21:50:55+00:00" 978 | }, 979 | { 980 | "name": "ramsey/uuid", 981 | "version": "4.7.6", 982 | "source": { 983 | "type": "git", 984 | "url": "https://github.com/ramsey/uuid.git", 985 | "reference": "91039bc1faa45ba123c4328958e620d382ec7088" 986 | }, 987 | "dist": { 988 | "type": "zip", 989 | "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", 990 | "reference": "91039bc1faa45ba123c4328958e620d382ec7088", 991 | "shasum": "" 992 | }, 993 | "require": { 994 | "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", 995 | "ext-json": "*", 996 | "php": "^8.0", 997 | "ramsey/collection": "^1.2 || ^2.0" 998 | }, 999 | "replace": { 1000 | "rhumsaa/uuid": "self.version" 1001 | }, 1002 | "require-dev": { 1003 | "captainhook/captainhook": "^5.10", 1004 | "captainhook/plugin-composer": "^5.3", 1005 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", 1006 | "doctrine/annotations": "^1.8", 1007 | "ergebnis/composer-normalize": "^2.15", 1008 | "mockery/mockery": "^1.3", 1009 | "paragonie/random-lib": "^2", 1010 | "php-mock/php-mock": "^2.2", 1011 | "php-mock/php-mock-mockery": "^1.3", 1012 | "php-parallel-lint/php-parallel-lint": "^1.1", 1013 | "phpbench/phpbench": "^1.0", 1014 | "phpstan/extension-installer": "^1.1", 1015 | "phpstan/phpstan": "^1.8", 1016 | "phpstan/phpstan-mockery": "^1.1", 1017 | "phpstan/phpstan-phpunit": "^1.1", 1018 | "phpunit/phpunit": "^8.5 || ^9", 1019 | "ramsey/composer-repl": "^1.4", 1020 | "slevomat/coding-standard": "^8.4", 1021 | "squizlabs/php_codesniffer": "^3.5", 1022 | "vimeo/psalm": "^4.9" 1023 | }, 1024 | "suggest": { 1025 | "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", 1026 | "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", 1027 | "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", 1028 | "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", 1029 | "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." 1030 | }, 1031 | "type": "library", 1032 | "extra": { 1033 | "captainhook": { 1034 | "force-install": true 1035 | } 1036 | }, 1037 | "autoload": { 1038 | "files": [ 1039 | "src/functions.php" 1040 | ], 1041 | "psr-4": { 1042 | "Ramsey\\Uuid\\": "src/" 1043 | } 1044 | }, 1045 | "notification-url": "https://packagist.org/downloads/", 1046 | "license": [ 1047 | "MIT" 1048 | ], 1049 | "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", 1050 | "keywords": [ 1051 | "guid", 1052 | "identifier", 1053 | "uuid" 1054 | ], 1055 | "support": { 1056 | "issues": "https://github.com/ramsey/uuid/issues", 1057 | "source": "https://github.com/ramsey/uuid/tree/4.7.6" 1058 | }, 1059 | "funding": [ 1060 | { 1061 | "url": "https://github.com/ramsey", 1062 | "type": "github" 1063 | }, 1064 | { 1065 | "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", 1066 | "type": "tidelift" 1067 | } 1068 | ], 1069 | "time": "2024-04-27T21:32:50+00:00" 1070 | }, 1071 | { 1072 | "name": "symfony/filesystem", 1073 | "version": "v6.4.8", 1074 | "source": { 1075 | "type": "git", 1076 | "url": "https://github.com/symfony/filesystem.git", 1077 | "reference": "4d37529150e7081c51b3c5d5718c55a04a9503f3" 1078 | }, 1079 | "dist": { 1080 | "type": "zip", 1081 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/4d37529150e7081c51b3c5d5718c55a04a9503f3", 1082 | "reference": "4d37529150e7081c51b3c5d5718c55a04a9503f3", 1083 | "shasum": "" 1084 | }, 1085 | "require": { 1086 | "php": ">=8.1", 1087 | "symfony/polyfill-ctype": "~1.8", 1088 | "symfony/polyfill-mbstring": "~1.8" 1089 | }, 1090 | "require-dev": { 1091 | "symfony/process": "^5.4|^6.4|^7.0" 1092 | }, 1093 | "type": "library", 1094 | "autoload": { 1095 | "psr-4": { 1096 | "Symfony\\Component\\Filesystem\\": "" 1097 | }, 1098 | "exclude-from-classmap": [ 1099 | "/Tests/" 1100 | ] 1101 | }, 1102 | "notification-url": "https://packagist.org/downloads/", 1103 | "license": [ 1104 | "MIT" 1105 | ], 1106 | "authors": [ 1107 | { 1108 | "name": "Fabien Potencier", 1109 | "email": "fabien@symfony.com" 1110 | }, 1111 | { 1112 | "name": "Symfony Community", 1113 | "homepage": "https://symfony.com/contributors" 1114 | } 1115 | ], 1116 | "description": "Provides basic utilities for the filesystem", 1117 | "homepage": "https://symfony.com", 1118 | "support": { 1119 | "source": "https://github.com/symfony/filesystem/tree/v6.4.8" 1120 | }, 1121 | "funding": [ 1122 | { 1123 | "url": "https://symfony.com/sponsor", 1124 | "type": "custom" 1125 | }, 1126 | { 1127 | "url": "https://github.com/fabpot", 1128 | "type": "github" 1129 | }, 1130 | { 1131 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1132 | "type": "tidelift" 1133 | } 1134 | ], 1135 | "time": "2024-05-31T14:49:08+00:00" 1136 | }, 1137 | { 1138 | "name": "symfony/polyfill-ctype", 1139 | "version": "v1.29.0", 1140 | "source": { 1141 | "type": "git", 1142 | "url": "https://github.com/symfony/polyfill-ctype.git", 1143 | "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" 1144 | }, 1145 | "dist": { 1146 | "type": "zip", 1147 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", 1148 | "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", 1149 | "shasum": "" 1150 | }, 1151 | "require": { 1152 | "php": ">=7.1" 1153 | }, 1154 | "provide": { 1155 | "ext-ctype": "*" 1156 | }, 1157 | "suggest": { 1158 | "ext-ctype": "For best performance" 1159 | }, 1160 | "type": "library", 1161 | "extra": { 1162 | "thanks": { 1163 | "name": "symfony/polyfill", 1164 | "url": "https://github.com/symfony/polyfill" 1165 | } 1166 | }, 1167 | "autoload": { 1168 | "files": [ 1169 | "bootstrap.php" 1170 | ], 1171 | "psr-4": { 1172 | "Symfony\\Polyfill\\Ctype\\": "" 1173 | } 1174 | }, 1175 | "notification-url": "https://packagist.org/downloads/", 1176 | "license": [ 1177 | "MIT" 1178 | ], 1179 | "authors": [ 1180 | { 1181 | "name": "Gert de Pagter", 1182 | "email": "BackEndTea@gmail.com" 1183 | }, 1184 | { 1185 | "name": "Symfony Community", 1186 | "homepage": "https://symfony.com/contributors" 1187 | } 1188 | ], 1189 | "description": "Symfony polyfill for ctype functions", 1190 | "homepage": "https://symfony.com", 1191 | "keywords": [ 1192 | "compatibility", 1193 | "ctype", 1194 | "polyfill", 1195 | "portable" 1196 | ], 1197 | "support": { 1198 | "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" 1199 | }, 1200 | "funding": [ 1201 | { 1202 | "url": "https://symfony.com/sponsor", 1203 | "type": "custom" 1204 | }, 1205 | { 1206 | "url": "https://github.com/fabpot", 1207 | "type": "github" 1208 | }, 1209 | { 1210 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1211 | "type": "tidelift" 1212 | } 1213 | ], 1214 | "time": "2024-01-29T20:11:03+00:00" 1215 | }, 1216 | { 1217 | "name": "symfony/polyfill-mbstring", 1218 | "version": "v1.29.0", 1219 | "source": { 1220 | "type": "git", 1221 | "url": "https://github.com/symfony/polyfill-mbstring.git", 1222 | "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" 1223 | }, 1224 | "dist": { 1225 | "type": "zip", 1226 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", 1227 | "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", 1228 | "shasum": "" 1229 | }, 1230 | "require": { 1231 | "php": ">=7.1" 1232 | }, 1233 | "provide": { 1234 | "ext-mbstring": "*" 1235 | }, 1236 | "suggest": { 1237 | "ext-mbstring": "For best performance" 1238 | }, 1239 | "type": "library", 1240 | "extra": { 1241 | "thanks": { 1242 | "name": "symfony/polyfill", 1243 | "url": "https://github.com/symfony/polyfill" 1244 | } 1245 | }, 1246 | "autoload": { 1247 | "files": [ 1248 | "bootstrap.php" 1249 | ], 1250 | "psr-4": { 1251 | "Symfony\\Polyfill\\Mbstring\\": "" 1252 | } 1253 | }, 1254 | "notification-url": "https://packagist.org/downloads/", 1255 | "license": [ 1256 | "MIT" 1257 | ], 1258 | "authors": [ 1259 | { 1260 | "name": "Nicolas Grekas", 1261 | "email": "p@tchwork.com" 1262 | }, 1263 | { 1264 | "name": "Symfony Community", 1265 | "homepage": "https://symfony.com/contributors" 1266 | } 1267 | ], 1268 | "description": "Symfony polyfill for the Mbstring extension", 1269 | "homepage": "https://symfony.com", 1270 | "keywords": [ 1271 | "compatibility", 1272 | "mbstring", 1273 | "polyfill", 1274 | "portable", 1275 | "shim" 1276 | ], 1277 | "support": { 1278 | "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" 1279 | }, 1280 | "funding": [ 1281 | { 1282 | "url": "https://symfony.com/sponsor", 1283 | "type": "custom" 1284 | }, 1285 | { 1286 | "url": "https://github.com/fabpot", 1287 | "type": "github" 1288 | }, 1289 | { 1290 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1291 | "type": "tidelift" 1292 | } 1293 | ], 1294 | "time": "2024-01-29T20:11:03+00:00" 1295 | } 1296 | ], 1297 | "packages-dev": [], 1298 | "aliases": [], 1299 | "minimum-stability": "stable", 1300 | "stability-flags": [], 1301 | "prefer-stable": false, 1302 | "prefer-lowest": false, 1303 | "platform": [], 1304 | "platform-dev": [], 1305 | "plugin-api-version": "2.6.0" 1306 | } 1307 | -------------------------------------------------------------------------------- /docs/MIGRATION.md: -------------------------------------------------------------------------------- 1 | ### Migration for versions 1.x.x -> 2.x.x 2 | 3 | As the implementation of the `PluginIdentifiableCommand` is now being enforced by Poggit-CI, we needed to comply and include it with the virion itself (being a base framework, to reduce boilerplate code) 4 | 5 | To migrate your plugins, if you have overridden the constructor (`__construct()`) of any class that's extending `BaseCommand`, you will need to update the function's signature as it now includes the `Plugin` context before anything else. 6 | 7 | ```php 8 | public function __construct( 9 | Plugin $plugin, 10 | string $name, 11 | string $description = "", 12 | array $aliases = [] 13 | ) 14 | ``` 15 | 16 | You will need to change the parent call (`parent::__construct($name, $description, $aliases)`) to include the plugin instance: 17 | ```php 18 | parent::__construct($plugin, $name, $description, null, $aliases); 19 | ``` 20 | 21 | Along with these changes, you will need to include the `Plugin` context with your class' constructor. 22 | 23 | Because of those signature differences, you will need to update the part where your plugin registers a command that uses Commando. As an example: 24 | ```php 25 | // Example code from 1.x.x README.md 26 | $this->getServer()->getCommandMap()->register("myplugin", new MyCommand("greet", "Make the server greet you!")); 27 | // Will have to be migrated to: 28 | $this->getServer()->getCommandMap()->register("myplugin", new MyCommand($this, "greet", "Make the server greet you!")); 29 | ``` 30 | 31 | Assuming you're calling `CommandMap->register()` directly from the plugin itself. -------------------------------------------------------------------------------- /src/CortexPE/Commando/BaseCommand.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando; 31 | 32 | use CortexPE\Commando\constraint\BaseConstraint; 33 | use CortexPE\Commando\exception\InvalidErrorCode; 34 | use CortexPE\Commando\traits\ArgumentableTrait; 35 | use CortexPE\Commando\traits\IArgumentable; 36 | use InvalidArgumentException; 37 | use pocketmine\command\Command; 38 | use pocketmine\command\CommandSender; 39 | use pocketmine\lang\Translatable; 40 | use pocketmine\plugin\Plugin; 41 | use pocketmine\plugin\PluginOwned; 42 | use pocketmine\utils\TextFormat; 43 | use function array_shift; 44 | use function array_unique; 45 | use function array_unshift; 46 | use function count; 47 | use function dechex; 48 | use function str_replace; 49 | 50 | abstract class BaseCommand extends Command implements IArgumentable, IRunnable, PluginOwned { 51 | use ArgumentableTrait; 52 | 53 | public const ERR_INVALID_ARG_VALUE = 0x01; 54 | public const ERR_TOO_MANY_ARGUMENTS = 0x02; 55 | public const ERR_INSUFFICIENT_ARGUMENTS = 0x03; 56 | public const ERR_NO_ARGUMENTS = 0x04; 57 | public const ERR_INVALID_ARGUMENTS = 0x05; 58 | 59 | /** @var string[] */ 60 | protected $errorMessages = [ 61 | self::ERR_INVALID_ARG_VALUE => TextFormat::RED . "Invalid value '{value}' for argument #{position}. Expecting: {expected}.", 62 | self::ERR_TOO_MANY_ARGUMENTS => TextFormat::RED . "Too many arguments given.", 63 | self::ERR_INSUFFICIENT_ARGUMENTS => TextFormat::RED . "Insufficient number of arguments given.", 64 | self::ERR_NO_ARGUMENTS => TextFormat::RED . "No arguments are required for this command.", 65 | self::ERR_INVALID_ARGUMENTS => TextFormat::RED . "Invalid arguments supplied.", 66 | ]; 67 | 68 | /** @var CommandSender */ 69 | protected CommandSender $currentSender; 70 | 71 | /** @var BaseSubCommand[] */ 72 | private array $subCommands = []; 73 | 74 | /** @var BaseConstraint[] */ 75 | private array $constraints = []; 76 | 77 | /** @var Plugin */ 78 | protected Plugin $plugin; 79 | 80 | public function __construct( 81 | Plugin $plugin, 82 | string $name, 83 | Translatable|string $description = "", 84 | array $aliases = [] 85 | ) { 86 | $this->plugin = $plugin; 87 | parent::__construct($name, $description, null, $aliases); 88 | 89 | $this->prepare(); 90 | 91 | $this->usageMessage = $this->generateUsageMessage(); 92 | } 93 | 94 | public function getOwningPlugin(): Plugin { 95 | return $this->plugin; 96 | } 97 | 98 | final public function execute(CommandSender $sender, string $commandLabel, array $args){ 99 | $this->currentSender = $sender; 100 | if(!$this->testPermission($sender)){ 101 | return; 102 | } 103 | /** @var BaseCommand|BaseSubCommand $cmd */ 104 | $cmd = $this; 105 | $passArgs = []; 106 | if(count($args) > 0){ 107 | if(isset($this->subCommands[($label = $args[0])])){ 108 | array_shift($args); 109 | $this->subCommands[$label]->execute($sender, $label, $args); 110 | return; 111 | } 112 | 113 | $passArgs = $this->attemptArgumentParsing($cmd, $args); 114 | } elseif($this->hasRequiredArguments()){ 115 | $this->sendError(self::ERR_INSUFFICIENT_ARGUMENTS); 116 | return; 117 | } 118 | if($passArgs !== null) { 119 | foreach ($cmd->getConstraints() as $constraint){ 120 | if(!$constraint->test($sender, $commandLabel, $passArgs)){ 121 | $constraint->onFailure($sender, $commandLabel, $passArgs); 122 | return; 123 | } 124 | } 125 | $cmd->onRun($sender, $commandLabel, $passArgs); 126 | } 127 | } 128 | 129 | /** 130 | * @param ArgumentableTrait $ctx 131 | * @param array $args 132 | * 133 | * @return array|null 134 | */ 135 | private function attemptArgumentParsing($ctx, array $args): ?array { 136 | $dat = $ctx->parseArguments($args, $this->currentSender); 137 | if(!empty(($errors = $dat["errors"]))) { 138 | foreach($errors as $error) { 139 | $this->sendError($error["code"], $error["data"]); 140 | } 141 | 142 | return null; 143 | } 144 | 145 | return $dat["arguments"]; 146 | } 147 | 148 | abstract public function onRun(CommandSender $sender, string $aliasUsed, array $args): void; 149 | 150 | protected function sendUsage(): void { 151 | $this->currentSender->sendMessage(TextFormat::RED . "Usage: " . $this->getUsage()); 152 | } 153 | 154 | public function sendError(int $errorCode, array $args = []): void { 155 | $str = (string)$this->errorMessages[$errorCode]; 156 | foreach($args as $item => $value) { 157 | $str = str_replace('{' . $item . '}', (string) $value, $str); 158 | } 159 | $this->currentSender->sendMessage($str); 160 | $this->sendUsage(); 161 | } 162 | 163 | public function setErrorFormat(int $errorCode, string $format): void { 164 | if(!isset($this->errorMessages[$errorCode])) { 165 | throw new InvalidErrorCode("Invalid error code 0x" . dechex($errorCode)); 166 | } 167 | $this->errorMessages[$errorCode] = $format; 168 | } 169 | 170 | public function setErrorFormats(array $errorFormats): void { 171 | foreach($errorFormats as $errorCode => $format) { 172 | $this->setErrorFormat($errorCode, $format); 173 | } 174 | } 175 | 176 | public function registerSubCommand(BaseSubCommand $subCommand): void { 177 | $keys = $subCommand->getAliases(); 178 | array_unshift($keys, $subCommand->getName()); 179 | $keys = array_unique($keys); 180 | foreach($keys as $key) { 181 | if(!isset($this->subCommands[$key])) { 182 | $subCommand->setParent($this); 183 | $this->subCommands[$key] = $subCommand; 184 | } else { 185 | throw new InvalidArgumentException("SubCommand with same name / alias for '$key' already exists"); 186 | } 187 | } 188 | } 189 | 190 | /** 191 | * @return BaseSubCommand[] 192 | */ 193 | public function getSubCommands(): array { 194 | return $this->subCommands; 195 | } 196 | 197 | public function addConstraint(BaseConstraint $constraint) : void { 198 | $this->constraints[] = $constraint; 199 | } 200 | 201 | /** 202 | * @return BaseConstraint[] 203 | */ 204 | public function getConstraints(): array { 205 | return $this->constraints; 206 | } 207 | 208 | public function getUsageMessage(): string { 209 | return $this->getUsage(); 210 | } 211 | 212 | public function setCurrentSender(CommandSender $sender): void{ 213 | $this->currentSender = $sender; 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/CortexPE/Commando/BaseSubCommand.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando; 31 | 32 | use pocketmine\command\CommandSender; 33 | use pocketmine\plugin\PluginBase; 34 | use function trim; 35 | 36 | abstract class BaseSubCommand extends BaseCommand{ 37 | /** @var BaseCommand */ 38 | protected BaseCommand $parent; 39 | 40 | public function __construct(PluginBase $plugin, string $name, string $description = "", array $aliases = []){ 41 | parent::__construct($plugin, $name, $description, $aliases); 42 | 43 | $this->usageMessage = ""; 44 | } 45 | 46 | public function getParent(): BaseCommand { 47 | return $this->parent; 48 | } 49 | 50 | /** 51 | * @param BaseCommand $parent 52 | * 53 | * @internal Used to pass the parent context from the parent command 54 | */ 55 | public function setParent(BaseCommand $parent): void { 56 | $this->parent = $parent; 57 | } 58 | 59 | public function getUsage(): string{ 60 | if(empty($this->usageMessage)){ 61 | $parent = $this->parent; 62 | $parentNames = ""; 63 | 64 | while($parent instanceof BaseSubCommand) { 65 | $parentNames = $parent->getName() . $parentNames; 66 | $parent = $parent->getParent(); 67 | } 68 | 69 | if($parent instanceof BaseCommand){ 70 | $parentNames = $parent->getName() . " " . $parentNames; 71 | } 72 | 73 | $this->usageMessage = $this->generateUsageMessage(trim($parentNames)); 74 | } 75 | 76 | return $this->usageMessage; 77 | } 78 | 79 | public function testPermissionSilent(CommandSender $sender, ?string $permission = null): bool { 80 | if($permission === null && count($this->getPermissions()) === 0) { 81 | return true; 82 | } 83 | return parent::testPermissionSilent($sender, $permission); 84 | } 85 | } -------------------------------------------------------------------------------- /src/CortexPE/Commando/IRunnable.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando; 31 | 32 | 33 | use CortexPE\Commando\constraint\BaseConstraint; 34 | 35 | /** 36 | * Interface IRunnable 37 | * 38 | * An interface which is declares the minimum required information 39 | * to get background information for a command and/or a sub-command 40 | * 41 | * @package CortexPE\Commando 42 | */ 43 | interface IRunnable { 44 | public function getName(): string; 45 | 46 | /** 47 | * @return string[] 48 | */ 49 | public function getAliases(): array; 50 | 51 | public function getUsageMessage():string; 52 | 53 | /** 54 | * @return string[] 55 | */ 56 | public function getPermissions(): array; 57 | 58 | /** 59 | * @return BaseConstraint[] 60 | */ 61 | public function getConstraints():array; 62 | } -------------------------------------------------------------------------------- /src/CortexPE/Commando/PacketHooker.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando; 31 | 32 | use CortexPE\Commando\exception\HookAlreadyRegistered; 33 | use CortexPE\Commando\store\SoftEnumStore; 34 | use CortexPE\Commando\traits\IArgumentable; 35 | use muqsit\simplepackethandler\SimplePacketHandler; 36 | use pocketmine\command\CommandSender; 37 | use pocketmine\event\EventPriority; 38 | use pocketmine\event\Listener; 39 | use pocketmine\network\mcpe\NetworkSession; 40 | use pocketmine\network\mcpe\protocol\AvailableCommandsPacket; 41 | use pocketmine\network\mcpe\protocol\types\command\CommandEnum; 42 | use pocketmine\network\mcpe\protocol\types\command\CommandOverload; 43 | use pocketmine\network\mcpe\protocol\types\command\CommandParameter; 44 | use pocketmine\plugin\Plugin; 45 | use pocketmine\Server; 46 | use ReflectionClass; 47 | use function array_unshift; 48 | use function count; 49 | 50 | class PacketHooker implements Listener { 51 | /** @var bool */ 52 | private static bool $isRegistered = false; 53 | 54 | /** @var bool */ 55 | private static bool $isIntercepting = false; 56 | 57 | public static function isRegistered(): bool { 58 | return self::$isRegistered; 59 | } 60 | 61 | public static function register(Plugin $registrant): void { 62 | if(self::$isRegistered) { 63 | throw new HookAlreadyRegistered("Event listener is already registered by another plugin."); 64 | } 65 | $interceptor = SimplePacketHandler::createInterceptor($registrant, EventPriority::HIGHEST, false); 66 | $interceptor->interceptOutgoing(function(AvailableCommandsPacket $pk, NetworkSession $target) : bool{ 67 | if(self::$isIntercepting)return true; 68 | $p = $target->getPlayer(); 69 | foreach($pk->commandData as $commandName => $commandData) { 70 | $cmd = Server::getInstance()->getCommandMap()->getCommand($commandName); 71 | if($cmd instanceof BaseCommand) { 72 | foreach($cmd->getConstraints() as $constraint){ 73 | if(!$constraint->isVisibleTo($p)){ 74 | continue 2; 75 | } 76 | } 77 | $pk->commandData[$commandName]->overloads = self::generateOverloads($p, $cmd); 78 | } 79 | } 80 | $pk->softEnums = SoftEnumStore::getEnums(); 81 | self::$isIntercepting = true; 82 | $target->sendDataPacket($pk); 83 | self::$isIntercepting = false; 84 | return false; 85 | }); 86 | 87 | self::$isRegistered = true; 88 | } 89 | 90 | /** 91 | * @param CommandSender $cs 92 | * @param BaseCommand $command 93 | * 94 | * @return CommandOverload[] 95 | */ 96 | private static function generateOverloads(CommandSender $cs, BaseCommand $command): array { 97 | $overloads = []; 98 | 99 | foreach($command->getSubCommands() as $label => $subCommand) { 100 | if(!$subCommand->testPermissionSilent($cs) || $subCommand->getName() !== $label){ // hide aliases 101 | continue; 102 | } 103 | foreach($subCommand->getConstraints() as $constraint){ 104 | if(!$constraint->isVisibleTo($cs)){ 105 | continue 2; 106 | } 107 | } 108 | $scParam = new CommandParameter(); 109 | $scParam->paramName = $label; 110 | $scParam->paramType = AvailableCommandsPacket::ARG_FLAG_VALID | AvailableCommandsPacket::ARG_FLAG_ENUM; 111 | $scParam->isOptional = false; 112 | $scParam->enum = new CommandEnum($label, [$label]); 113 | 114 | $overloadList = self::generateOverloads($cs, $subCommand); 115 | if(!empty($overloadList)){ 116 | foreach($overloadList as $overload) { 117 | $overloads[] = new CommandOverload(false, [$scParam, ...$overload->getParameters()]); 118 | } 119 | } else { 120 | $overloads[] = new CommandOverload(false, [$scParam]); 121 | } 122 | } 123 | 124 | foreach(self::generateOverloadList($command) as $overload) { 125 | $overloads[] = $overload; 126 | } 127 | 128 | return $overloads; 129 | } 130 | 131 | /** 132 | * @param IArgumentable $argumentable 133 | * 134 | * @return CommandOverload[] 135 | */ 136 | private static function generateOverloadList(IArgumentable $argumentable): array { 137 | $input = $argumentable->getArgumentList(); 138 | $combinations = []; 139 | $outputLength = array_product(array_map("count", $input)); 140 | $indexes = []; 141 | foreach($input as $k => $charList){ 142 | $indexes[$k] = 0; 143 | } 144 | do { 145 | /** @var CommandParameter[] $set */ 146 | $set = []; 147 | foreach($indexes as $k => $index){ 148 | $param = $set[$k] = clone $input[$k][$index]->getNetworkParameterData(); 149 | 150 | if (isset($param->enum) && $param->enum instanceof CommandEnum) { 151 | $refClass = new ReflectionClass(CommandEnum::class); 152 | $refProp = $refClass->getProperty("enumName"); 153 | $refProp->setAccessible(true); 154 | $refProp->setValue($param->enum, "enum#" . spl_object_id($param->enum)); 155 | } 156 | } 157 | $combinations[] = new CommandOverload(false, $set); 158 | 159 | foreach($indexes as $k => $v){ 160 | $indexes[$k]++; 161 | $lim = count($input[$k]); 162 | if($indexes[$k] >= $lim){ 163 | $indexes[$k] = 0; 164 | continue; 165 | } 166 | break; 167 | } 168 | } while(count($combinations) !== $outputLength); 169 | 170 | return $combinations; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/CortexPE/Commando/args/BaseArgument.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\args; 31 | 32 | 33 | use pocketmine\command\CommandSender; 34 | use pocketmine\network\mcpe\protocol\AvailableCommandsPacket; 35 | use pocketmine\network\mcpe\protocol\types\command\CommandParameter; 36 | 37 | abstract class BaseArgument { 38 | /** @var string */ 39 | protected string $name; 40 | /** @var bool */ 41 | protected bool $optional = false; 42 | /** @var CommandParameter */ 43 | protected CommandParameter $parameterData; 44 | 45 | public function __construct(string $name, bool $optional = false) { 46 | $this->name = $name; 47 | $this->optional = $optional; 48 | 49 | $this->parameterData = new CommandParameter(); 50 | $this->parameterData->paramName = $name; 51 | $this->parameterData->paramType = AvailableCommandsPacket::ARG_FLAG_VALID; 52 | $this->parameterData->paramType |= $this->getNetworkType(); 53 | $this->parameterData->isOptional = $this->isOptional(); 54 | } 55 | 56 | abstract public function getNetworkType(): int; 57 | 58 | /** 59 | * @param string $testString 60 | * @param CommandSender $sender 61 | * 62 | * @return bool 63 | */ 64 | abstract public function canParse(string $testString, CommandSender $sender): bool; 65 | 66 | /** 67 | * @param string $argument 68 | * @param CommandSender $sender 69 | * 70 | * @return mixed 71 | */ 72 | abstract public function parse(string $argument, CommandSender $sender) : mixed; 73 | 74 | /** 75 | * @return string 76 | */ 77 | public function getName(): string { 78 | return $this->name; 79 | } 80 | 81 | /** 82 | * @return bool 83 | */ 84 | public function isOptional(): bool { 85 | return $this->optional; 86 | } 87 | 88 | /** 89 | * Returns how much command arguments 90 | * it takes to build the full argument 91 | * 92 | * @return int 93 | */ 94 | public function getSpanLength(): int { 95 | return 1; 96 | } 97 | 98 | abstract public function getTypeName(): string; 99 | 100 | public function getNetworkParameterData():CommandParameter { 101 | return $this->parameterData; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/CortexPE/Commando/args/BlockPositionArgument.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\args; 31 | 32 | 33 | use pocketmine\command\CommandSender; 34 | use pocketmine\math\Vector3; 35 | use function preg_match; 36 | 37 | class BlockPositionArgument extends Vector3Argument { 38 | public function isValidCoordinate(string $coordinate, bool $locatable): bool { 39 | return (bool)preg_match("/^(?:" . ($locatable ? "(?:~-|~\+)?" : "") . "-?\d+)" . ($locatable ? "|~" : "") . "$/", $coordinate); 40 | } 41 | 42 | public function parse(string $argument, CommandSender $sender) : Vector3{ 43 | $v = parent::parse($argument, $sender); 44 | 45 | return $v->floor(); 46 | } 47 | } -------------------------------------------------------------------------------- /src/CortexPE/Commando/args/BooleanArgument.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\args; 31 | 32 | 33 | use pocketmine\command\CommandSender; 34 | 35 | class BooleanArgument extends StringEnumArgument { 36 | 37 | protected const VALUES = [ 38 | "true" => true, 39 | "false" => false, 40 | ]; 41 | 42 | public function getTypeName(): string { 43 | return "bool"; 44 | } 45 | 46 | public function getEnumName(): string { 47 | return "bool"; 48 | } 49 | 50 | public function parse(string $argument, CommandSender $sender): bool{ 51 | return (bool) $this->getValue($argument); 52 | } 53 | } -------------------------------------------------------------------------------- /src/CortexPE/Commando/args/FloatArgument.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\args; 31 | 32 | 33 | use pocketmine\command\CommandSender; 34 | use pocketmine\network\mcpe\protocol\AvailableCommandsPacket; 35 | use function preg_match; 36 | 37 | class FloatArgument extends BaseArgument { 38 | public function getNetworkType(): int { 39 | return AvailableCommandsPacket::ARG_TYPE_FLOAT; 40 | } 41 | 42 | public function getTypeName(): string { 43 | return "decimal"; 44 | } 45 | 46 | public function canParse(string $testString, CommandSender $sender): bool { 47 | return (bool)preg_match("/^-?(?:\d+|\d*\.\d+)$/", $testString); 48 | } 49 | 50 | public function parse(string $argument, CommandSender $sender) : float{ 51 | return (float) $argument; 52 | } 53 | } -------------------------------------------------------------------------------- /src/CortexPE/Commando/args/IntegerArgument.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\args; 31 | 32 | 33 | use pocketmine\command\CommandSender; 34 | use pocketmine\network\mcpe\protocol\AvailableCommandsPacket; 35 | use function preg_match; 36 | 37 | class IntegerArgument extends BaseArgument { 38 | public function getNetworkType(): int { 39 | return AvailableCommandsPacket::ARG_TYPE_INT; 40 | } 41 | 42 | public function getTypeName(): string { 43 | return "int"; 44 | } 45 | 46 | public function canParse(string $testString, CommandSender $sender): bool { 47 | return (bool)preg_match("/^-?(?:\d+)$/", $testString); 48 | } 49 | 50 | public function parse(string $argument, CommandSender $sender) : int{ 51 | return (int) $argument; 52 | } 53 | } -------------------------------------------------------------------------------- /src/CortexPE/Commando/args/RawStringArgument.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\args; 31 | 32 | use pocketmine\command\CommandSender; 33 | use pocketmine\network\mcpe\protocol\AvailableCommandsPacket; 34 | 35 | class RawStringArgument extends BaseArgument { 36 | public function getNetworkType(): int { 37 | return AvailableCommandsPacket::ARG_TYPE_STRING; 38 | } 39 | 40 | public function getTypeName(): string { 41 | return "string"; 42 | } 43 | 44 | public function canParse(string $testString, CommandSender $sender): bool { 45 | return true; 46 | } 47 | 48 | public function parse(string $argument, CommandSender $sender) : string{ 49 | return $argument; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/CortexPE/Commando/args/StringEnumArgument.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\args; 31 | 32 | 33 | use pocketmine\command\CommandSender; 34 | use pocketmine\network\mcpe\protocol\types\command\CommandEnum; 35 | use function array_keys; 36 | use function array_map; 37 | use function implode; 38 | use function preg_match; 39 | use function strtolower; 40 | 41 | abstract class StringEnumArgument extends BaseArgument { 42 | 43 | protected const VALUES = []; 44 | 45 | public function __construct(string $name, bool $optional = false) { 46 | parent::__construct($name, $optional); 47 | 48 | $this->parameterData->enum = new CommandEnum($this->getEnumName(), $this->getEnumValues()); 49 | } 50 | 51 | public function getNetworkType(): int { 52 | // this will be disregarded by PM anyways because this will be considered as a string enum 53 | return -1; 54 | } 55 | 56 | public function canParse(string $testString, CommandSender $sender): bool { 57 | return (bool)preg_match( 58 | "/^(" . implode("|", array_map("\\strtolower", $this->getEnumValues())) . ")$/iu", 59 | $testString 60 | ); 61 | } 62 | 63 | public abstract function getEnumName(): string; 64 | 65 | public function getValue(string $string) { 66 | return static::VALUES[strtolower($string)]; 67 | } 68 | 69 | public function getEnumValues(): array { 70 | return array_keys(static::VALUES); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/CortexPE/Commando/args/TargetPlayerArgument.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\args; 31 | 32 | 33 | use pocketmine\command\CommandSender; 34 | use pocketmine\network\mcpe\protocol\AvailableCommandsPacket; 35 | 36 | class TextArgument extends RawStringArgument { 37 | public function getNetworkType(): int { 38 | return AvailableCommandsPacket::ARG_TYPE_RAWTEXT; 39 | } 40 | 41 | public function getTypeName(): string { 42 | return "text"; 43 | } 44 | 45 | public function getSpanLength(): int { 46 | return PHP_INT_MAX; 47 | } 48 | public function canParse(string $testString, CommandSender $sender): bool { 49 | return $testString !== ""; 50 | } 51 | } -------------------------------------------------------------------------------- /src/CortexPE/Commando/args/Vector3Argument.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\args; 31 | 32 | 33 | use pocketmine\command\CommandSender; 34 | use pocketmine\entity\Entity; 35 | use pocketmine\math\Vector3; 36 | use pocketmine\network\mcpe\protocol\AvailableCommandsPacket; 37 | use function count; 38 | use function explode; 39 | use function preg_match; 40 | use function substr; 41 | 42 | class Vector3Argument extends BaseArgument { 43 | public function getNetworkType(): int { 44 | return AvailableCommandsPacket::ARG_TYPE_POSITION; 45 | } 46 | 47 | public function getTypeName(): string { 48 | return "x y z"; 49 | } 50 | 51 | public function canParse(string $testString, CommandSender $sender): bool { 52 | $coords = explode(" ", $testString); 53 | if(count($coords) === 3) { 54 | foreach($coords as $coord) { 55 | if(!$this->isValidCoordinate($coord, $sender instanceof Vector3)) { 56 | return false; 57 | } 58 | } 59 | 60 | return true; 61 | } 62 | 63 | return false; 64 | } 65 | 66 | public function isValidCoordinate(string $coordinate, bool $locatable): bool { 67 | return (bool)preg_match("/^(?:" . ($locatable ? "(\\?:~-|~\+)?" : "") . "-?(?:\d+|\d*\.\d+))" . ($locatable ? "|~" : "") . "$/", $coordinate); 68 | } 69 | 70 | public function parse(string $argument, CommandSender $sender) : Vector3{ 71 | $coords = explode(" ", $argument); 72 | $vals = []; 73 | foreach($coords as $k => $coord){ 74 | $offset = 0; 75 | // if it's locatable and starts with ~- or ~+ 76 | if($sender instanceof Entity && preg_match("/^(?:~-|~\+)|~/", $coord)){ 77 | // this will work with -n, +n and "" due to typecast later 78 | $offset = substr($coord, 1); 79 | 80 | // replace base coordinate with actual entity coordinates 81 | $position = $sender->getPosition(); 82 | $coord = match ($k) { 83 | 0 => $position->x, 84 | 1 => $position->y, 85 | 2 => $position->z, 86 | }; 87 | } 88 | $vals[] = (float)$coord + (float)$offset; 89 | } 90 | return new Vector3(...$vals); 91 | } 92 | 93 | public function getSpanLength(): int { 94 | return 3; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/CortexPE/Commando/constraint/BaseConstraint.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\constraint; 31 | 32 | 33 | use CortexPE\Commando\IRunnable; 34 | use pocketmine\command\CommandSender; 35 | 36 | abstract class BaseConstraint { 37 | /** @var IRunnable */ 38 | protected IRunnable $context; 39 | 40 | /** 41 | * BaseConstraint constructor. 42 | * 43 | * "Context" is required so that this new-constraint-system doesn't hinder getting command info 44 | * 45 | * @param IRunnable $context 46 | */ 47 | public function __construct(IRunnable $context) { 48 | $this->context = $context; 49 | } 50 | 51 | /** 52 | * @return IRunnable 53 | */ 54 | public function getContext(): IRunnable { 55 | return $this->context; 56 | } 57 | 58 | abstract public function test(CommandSender $sender, string $aliasUsed, array $args): bool; 59 | 60 | abstract public function onFailure(CommandSender $sender, string $aliasUsed, array $args): void; 61 | 62 | abstract public function isVisibleTo(CommandSender $sender): bool; 63 | } -------------------------------------------------------------------------------- /src/CortexPE/Commando/constraint/ConsoleRequiredConstraint.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\constraint; 31 | 32 | 33 | use pocketmine\command\CommandSender; 34 | use pocketmine\player\Player; 35 | use pocketmine\utils\TextFormat; 36 | 37 | class ConsoleRequiredConstraint extends BaseConstraint { 38 | 39 | public function test(CommandSender $sender, string $aliasUsed, array $args): bool { 40 | return $this->isVisibleTo($sender); 41 | } 42 | 43 | public function onFailure(CommandSender $sender, string $aliasUsed, array $args): void { 44 | $sender->sendMessage(TextFormat::RED . "This command must be executed from a server console."); // f*ck off grammar police 45 | } 46 | 47 | public function isVisibleTo(CommandSender $sender): bool { 48 | return !($sender instanceof Player); 49 | } 50 | } -------------------------------------------------------------------------------- /src/CortexPE/Commando/constraint/InGameRequiredConstraint.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\constraint; 31 | 32 | 33 | use pocketmine\command\CommandSender; 34 | use pocketmine\player\Player; 35 | use pocketmine\utils\TextFormat; 36 | 37 | class InGameRequiredConstraint extends BaseConstraint { 38 | 39 | public function test(CommandSender $sender, string $aliasUsed, array $args): bool { 40 | return $this->isVisibleTo($sender); 41 | } 42 | 43 | public function onFailure(CommandSender $sender, string $aliasUsed, array $args): void { 44 | $sender->sendMessage(TextFormat::RED . "This command must be executed in-game."); // f*ck off grammar police 45 | } 46 | 47 | public function isVisibleTo(CommandSender $sender): bool { 48 | return $sender instanceof Player; 49 | } 50 | } -------------------------------------------------------------------------------- /src/CortexPE/Commando/exception/ArgumentOrderException.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\exception; 31 | 32 | 33 | class ArgumentOrderException extends CommandoException { 34 | 35 | } -------------------------------------------------------------------------------- /src/CortexPE/Commando/exception/CommandoException.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\exception; 31 | 32 | 33 | use Exception; 34 | 35 | class CommandoException extends Exception { 36 | 37 | } -------------------------------------------------------------------------------- /src/CortexPE/Commando/exception/HookAlreadyRegistered.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\exception; 31 | 32 | 33 | class HookAlreadyRegistered extends CommandoException { 34 | 35 | } -------------------------------------------------------------------------------- /src/CortexPE/Commando/exception/InvalidErrorCode.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\exception; 31 | 32 | 33 | class InvalidErrorCode extends CommandoException { 34 | 35 | } -------------------------------------------------------------------------------- /src/CortexPE/Commando/store/SoftEnumStore.php: -------------------------------------------------------------------------------- 1 | getName()] = $enum; 31 | self::broadcastSoftEnum($enum, UpdateSoftEnumPacket::TYPE_ADD); 32 | } 33 | 34 | public static function updateEnum(string $enumName, array $values):void { 35 | if(self::getEnumByName($enumName) === null){ 36 | throw new CommandoException("Unknown enum named " . $enumName); 37 | } 38 | self::$enums[$enumName] = $enum = new CommandEnum($enumName, $values); 39 | self::broadcastSoftEnum($enum, UpdateSoftEnumPacket::TYPE_SET); 40 | } 41 | 42 | public static function removeEnum(string $enumName):void { 43 | if(($enum = self::getEnumByName($enumName)) === null){ 44 | throw new CommandoException("Unknown enum named " . $enumName); 45 | } 46 | unset(static::$enums[$enumName]); 47 | self::broadcastSoftEnum($enum, UpdateSoftEnumPacket::TYPE_REMOVE); 48 | } 49 | 50 | public static function broadcastSoftEnum(CommandEnum $enum, int $type):void { 51 | $pk = new UpdateSoftEnumPacket(); 52 | $pk->enumName = $enum->getName(); 53 | $pk->values = $enum->getValues(); 54 | $pk->type = $type; 55 | self::broadcastPacket($pk); 56 | } 57 | 58 | private static function broadcastPacket(ClientboundPacket $pk):void { 59 | $sv = Server::getInstance(); 60 | NetworkBroadcastUtils::broadcastPackets($sv->getOnlinePlayers(), [$pk]); 61 | } 62 | } -------------------------------------------------------------------------------- /src/CortexPE/Commando/traits/ArgumentableTrait.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\traits; 31 | 32 | 33 | use CortexPE\Commando\args\BaseArgument; 34 | use CortexPE\Commando\args\TextArgument; 35 | use CortexPE\Commando\BaseCommand; 36 | use CortexPE\Commando\exception\ArgumentOrderException; 37 | use pocketmine\command\CommandSender; 38 | use pocketmine\utils\TextFormat; 39 | use function array_slice; 40 | use function count; 41 | use function implode; 42 | use function is_array; 43 | use function rtrim; 44 | use function trim; 45 | use function usort; 46 | 47 | trait ArgumentableTrait{ 48 | /** @var BaseArgument[][] */ 49 | private array $argumentList = []; // [argumentPosition => [...possible BaseArgument(s)]] 50 | /** @var bool[] */ 51 | private array $requiredArgumentCount = []; 52 | 53 | /** 54 | * This is where all the arguments, permissions, sub-commands, etc would be registered 55 | */ 56 | abstract protected function prepare() : void; 57 | 58 | /** 59 | * @param int $position 60 | * @param BaseArgument $argument 61 | * 62 | * @throws ArgumentOrderException 63 | */ 64 | public function registerArgument(int $position, BaseArgument $argument): void { 65 | if($position < 0) { 66 | throw new ArgumentOrderException("You cannot register arguments at negative positions"); 67 | } 68 | if($position > 0 && !isset($this->argumentList[$position - 1])) { 69 | throw new ArgumentOrderException("There were no arguments before $position"); 70 | } 71 | foreach($this->argumentList[$position - 1] ?? [] as $arg) { 72 | if($arg instanceof TextArgument) { 73 | throw new ArgumentOrderException("No other arguments can be registered after a TextArgument"); 74 | } 75 | if($arg->isOptional() && !$argument->isOptional()){ 76 | throw new ArgumentOrderException("You cannot register a required argument after an optional argument"); 77 | } 78 | } 79 | $this->argumentList[$position][] = $argument; 80 | if(!$argument->isOptional()) { 81 | $this->requiredArgumentCount[$position] = true; 82 | } 83 | } 84 | 85 | public function parseArguments(array $rawArgs, CommandSender $sender): array { 86 | $return = [ 87 | "arguments" => [], 88 | "errors" => [] 89 | ]; 90 | // try parsing arguments 91 | $required = count($this->requiredArgumentCount); 92 | if(!$this->hasArguments() && count($rawArgs) > 0) { // doesnt take args but sender gives args anyways 93 | $return["errors"][] = [ 94 | "code" => BaseCommand::ERR_NO_ARGUMENTS, 95 | "data" => [] 96 | ]; 97 | } 98 | $offset = 0; 99 | $argOffset = 0; 100 | if(count($rawArgs) > 0) { 101 | foreach($this->argumentList as $pos => $possibleArguments) { 102 | // try the one that spans more first... before the others 103 | usort($possibleArguments, function (BaseArgument $a): int { 104 | if($a->getSpanLength() === PHP_INT_MAX) { // if it takes unlimited arguments, pull it down 105 | return 1; 106 | } 107 | 108 | return -1; 109 | }); 110 | $parsed = false; 111 | $optional = true; 112 | foreach($possibleArguments as $argument) { 113 | $arg = trim(implode(" ", array_slice($rawArgs, $offset, ($len = $argument->getSpanLength())))); 114 | if(!$argument->isOptional()) { 115 | $optional = false; 116 | } 117 | if($arg !== "" && $argument->canParse($arg, $sender)) { 118 | $k = $argument->getName(); 119 | $result = (clone $argument)->parse($arg, $sender); 120 | if(isset($return["arguments"][$k]) && !is_array($return["arguments"][$k])) { 121 | $old = $return["arguments"][$k]; 122 | unset($return["arguments"][$k]); 123 | $return["arguments"][$k] = [$old]; 124 | $return["arguments"][$k][] = $result; 125 | } else { 126 | $return["arguments"][$k] = $result; 127 | } 128 | if(!$optional) { 129 | $required--; 130 | } 131 | $offset += $len; 132 | ++$argOffset; 133 | $parsed = true; 134 | break; 135 | } 136 | if($offset > count($rawArgs)) { 137 | break; // we've reached the end of the argument list the user passed 138 | } 139 | } 140 | if(!$parsed && !($optional && empty($arg))) { // we tried every other possible argument type, none was satisfied 141 | $expectedArgs = $this->argumentList[$argOffset]; 142 | $expected = ""; 143 | foreach($expectedArgs as $expectedArg){ 144 | $expected .= $expectedArg->getTypeName() . "|"; 145 | } 146 | 147 | $return["errors"][] = [ 148 | "code" => BaseCommand::ERR_INVALID_ARG_VALUE, 149 | "data" => [ 150 | "value" => $rawArgs[$offset] ?? "", 151 | "position" => $pos + 1, 152 | "expected" => rtrim($expected, "|") 153 | ] 154 | ]; 155 | 156 | return $return; // let's break it here. 157 | } 158 | } 159 | } 160 | if($offset < count($rawArgs)) { // this means that the arguments our user sent is more than the needed amount 161 | $return["errors"][] = [ 162 | "code" => BaseCommand::ERR_TOO_MANY_ARGUMENTS, 163 | "data" => [] 164 | ]; 165 | } 166 | if($required > 0) {// We still have more unfilled required arguments 167 | $return["errors"][] = [ 168 | "code" => BaseCommand::ERR_INSUFFICIENT_ARGUMENTS, 169 | "data" => [] 170 | ]; 171 | } 172 | 173 | // up to my testing this occurs when BaseCommand::ERR_NO_ARGUMENTS and BaseCommand::ERR_TOO_MANY_ARGUMENTS are given as errors 174 | // this only (as far as my testing) happens when command accepts arguments (e.g. a subcommand) but the user supplied invalid argument 175 | // also the error code remains as shown due to the way they are passed 176 | // have a better way? pr please :) 177 | if( 178 | count($return["errors"]) === 2 && 179 | $return["errors"][0]["code"] === BaseCommand::ERR_NO_ARGUMENTS && 180 | $return["errors"][1]["code"] === BaseCommand::ERR_TOO_MANY_ARGUMENTS 181 | ){ 182 | unset($return["errors"]); 183 | 184 | $return["errors"][] = [ 185 | "code" => BaseCommand::ERR_INVALID_ARGUMENTS, 186 | "data" => [] 187 | ]; 188 | } 189 | 190 | return $return; 191 | } 192 | 193 | public function generateUsageMessage(string $parent = ""): string { 194 | $name = $parent . (empty($parent) ? "" : " ") . $this->getName(); 195 | $msg = TextFormat::RED . "/" . $name; 196 | $args = []; 197 | foreach($this->argumentList as $arguments){ 198 | $hasOptional = false; 199 | $names = []; 200 | foreach($arguments as $argument){ 201 | $names[] = $argument->getName() . ":" . $argument->getTypeName(); 202 | if($argument->isOptional()){ 203 | $hasOptional = true; 204 | } 205 | } 206 | $names = implode("|", $names); 207 | if($hasOptional){ 208 | $args[] = "[" . $names . "]"; 209 | } else { 210 | $args[] = "<" . $names . ">"; 211 | } 212 | } 213 | $msg .= ((empty($args)) ? "" : " ") . implode(TextFormat::RED . " ", $args) . ": " . $this->getDescription(); 214 | foreach($this->subCommands as $label => $subCommand){ 215 | if($label === $subCommand->getName()){ 216 | $msg .= "\n - " . $subCommand->generateUsageMessage($name); 217 | } 218 | } 219 | 220 | return trim($msg); 221 | } 222 | 223 | public function hasArguments(): bool { 224 | return !empty($this->argumentList); 225 | } 226 | 227 | public function hasRequiredArguments(): bool { 228 | foreach($this->argumentList as $arguments) { 229 | foreach($arguments as $argument) { 230 | if(!$argument->isOptional()) { 231 | return true; 232 | } 233 | } 234 | } 235 | 236 | return false; 237 | } 238 | 239 | /** 240 | * @return BaseArgument[][] 241 | */ 242 | public function getArgumentList(): array { 243 | return $this->argumentList; 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/CortexPE/Commando/traits/IArgumentable.php: -------------------------------------------------------------------------------- 1 | . 24 | * 25 | * Written by @CortexPE 26 | * 27 | */ 28 | declare(strict_types=1); 29 | 30 | namespace CortexPE\Commando\traits; 31 | 32 | 33 | use CortexPE\Commando\args\BaseArgument; 34 | use pocketmine\command\CommandSender; 35 | 36 | interface IArgumentable { 37 | public function generateUsageMessage(string $parent = ""): string; 38 | public function hasArguments(): bool; 39 | 40 | /** 41 | * @return BaseArgument[][] 42 | */ 43 | public function getArgumentList(): array; 44 | public function parseArguments(array $rawArgs, CommandSender $sender): array; 45 | public function registerArgument(int $position, BaseArgument $argument): void; 46 | } -------------------------------------------------------------------------------- /virion.yml: -------------------------------------------------------------------------------- 1 | name: Commando 2 | antigen: CortexPE\Commando 3 | api: 5.3.0 4 | version: 3.2.1 5 | author: CortexPE 6 | authors: [ParoxityTeam] 7 | --------------------------------------------------------------------------------