├── .editorconfig
├── .github
└── workflows
│ └── tutorial.yml
├── .gitignore
├── .poggit.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── assets
└── icon.png
├── composer.json
├── composer.lock
├── src
└── DiamondStrider1
│ └── Remark
│ ├── Async
│ ├── Thenable.php
│ └── UnhandledAsyncException.php
│ ├── Command
│ ├── Arg
│ │ ├── Arg.php
│ │ ├── ArgumentStack.php
│ │ ├── ExtractionFailed.php
│ │ ├── SetParameterTrait.php
│ │ ├── bool_arg.php
│ │ ├── command_arg.php
│ │ ├── enum.php
│ │ ├── float_arg.php
│ │ ├── int_arg.php
│ │ ├── json_arg.php
│ │ ├── player_arg.php
│ │ ├── remaining.php
│ │ ├── sender.php
│ │ ├── text.php
│ │ └── vector_arg.php
│ ├── BoundCommand.php
│ ├── Cmd.php
│ ├── CmdConfig.php
│ ├── CommandContext.php
│ ├── CommandHintListener.php
│ ├── Guard
│ │ ├── Guard.php
│ │ └── permission.php
│ ├── HandlerMethod.php
│ ├── HandlerMethodTree.php
│ └── OverloadMap.php
│ ├── Form
│ ├── CustomFormElement
│ │ ├── CustomFormElement.php
│ │ ├── Dropdown.php
│ │ ├── Input.php
│ │ ├── Label.php
│ │ ├── Slider.php
│ │ ├── StepSlider.php
│ │ └── Toggle.php
│ ├── CustomFormResultTrait.php
│ ├── Forms.php
│ ├── InternalCustomForm.php
│ ├── InternalMenuForm.php
│ ├── InternalModalForm.php
│ └── MenuFormElement
│ │ ├── MenuFormButton.php
│ │ └── MenuFormImage.php
│ ├── Remark.php
│ └── Types
│ └── RelativeVector3.php
├── tools
├── php-cs-fixer
│ ├── .php-cs-fixer.php
│ ├── composer.json
│ └── composer.lock
└── phpstan
│ ├── composer.json
│ ├── composer.lock
│ └── phpstan.neon.dist
├── tutorial
├── README.md
├── book.toml
└── src
│ ├── SUMMARY.md
│ ├── in-depth
│ ├── command_args.md
│ ├── command_guards.md
│ ├── commands.md
│ ├── custom_form_elements.md
│ ├── forms.md
│ ├── index.md
│ └── installing.md
│ ├── introduction.md
│ └── quick-guide
│ ├── command_handling.md
│ ├── forms.md
│ └── index.md
└── virion.yml
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 4
6 | end_of_line = lf
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 | charset = utf-8
10 |
--------------------------------------------------------------------------------
/.github/workflows/tutorial.yml:
--------------------------------------------------------------------------------
1 | name: The Remark Tutorial
2 | on:
3 | push:
4 | branches:
5 | - main
6 | jobs:
7 | deploy:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v2
11 | with:
12 | fetch-depth: 0
13 | - name: Install mdBook
14 | run: |
15 | mkdir mdbook
16 | curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.14/mdbook-v0.4.14-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
17 | echo `pwd`/mdbook >> $GITHUB_PATH
18 | - name: Deploy GitHub Pages
19 | run: |
20 | git diff --exit-code HEAD~1...HEAD -- tutorial && exit 0
21 | cd tutorial
22 | mdbook build
23 | git worktree add gh-pages gh-pages
24 | git config user.name "tutorial-workflow[bot]"
25 | git config user.email ""
26 | cd gh-pages
27 | # Delete the ref to avoid keeping history.
28 | git update-ref -d refs/heads/gh-pages
29 | rm -rf *
30 | mv ../book/* .
31 | git add .
32 | git commit -m "Deploy $GITHUB_SHA to gh-pages"
33 | git push --force
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/vendor/
2 |
3 | /.php-cs-fixer.cache
4 |
5 | /tools/phpstan/phpstan.neon
6 |
7 | /tutorial/book
8 |
--------------------------------------------------------------------------------
/.poggit.yml:
--------------------------------------------------------------------------------
1 | build-by-default: true
2 | branches:
3 | - main
4 | projects:
5 | Remark:
6 | path: ""
7 | model: virion
8 | type: library
9 | lint:
10 | phpstan: false
11 | libs:
12 | - src: sof3/await-generator/await-generator
13 | version: ^3.3.0
14 | epitope: .random
15 | ...
16 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # v1.2.2
2 | * Increased EventPriority of the AvailableCommandsPacket listener to HIGH.
3 |
4 | # v1.2.1
5 | * Fixed potentially crashing bug in SetParameterTrait.php
6 | * Fixed Composer settings so Arg's with underscores play nicely with static analyzers.
7 |
8 | # v1.2.0
9 | * Allow `Arg`'s to be optional by making the parameter's type accept null
10 |
11 | # v1.1.2
12 | * Fix bug that treated non-HandlerMethods as HandlerMethods in `Remark::command()`.
13 |
14 | # v1.1.1
15 | * `permission()` Guard errors if any passed permission does not exist.
16 | * Fixed bug that caused an error when a player closed out of a Custom Form.
17 | * Enum names assigned to subcommand parameters are prefixed to reduce the likelihood of conflicting with the `enum()` Arg.
18 |
19 | # v1.1.0
20 | * Added await-generator as a dependency.
21 | * Added args: bool_arg, command_arg, float_arg, int_arg, vector_arg.
22 | * `string[] CmdConfig->permissions` changed to `?string CmdConfig->permission`
23 |
24 | # v1.0.0
25 | * Command Handling and Forms implemented.
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright © 2022 DiamondStrider1
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Remark - Easy and Asynchronous Commands and Forms
6 | * [Quick Guide](https://swift-strider.github.io/Remark/quick-guide/index.html) - Learn Remark by building a plugin.
7 | * [Install](#install) - Add Remark as a library to your plugin.
8 | * [Example](https://github.com/Swift-Strider/ExampleRemarkPlugin) - View an example plugin using Remark.
9 |
10 | # Benefits
11 | * `TAB`-completion for commands
12 | * Command argument validation
13 | * Type-safe commands and forms
14 | * Asynchronous Forms API
15 | * Optional AwaitGenerator
16 |
17 | # Install
18 | ```sh
19 | composer require diamondstrider1/remark ^1.1.0
20 | ```
21 |
22 | Add Remark as a library in `.poggit.yml`.
23 | ```yml
24 | projects:
25 | ExampleRemarkPlugin:
26 | path: ""
27 | libs:
28 | - src: Swift-Strider/Remark/Remark
29 | version: ^1.1.0
30 | epitope: .random
31 | ```
32 |
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Swift-Strider/Remark/fef59d259d67e2a5e10860226e9c9fd54db0283c/assets/icon.png
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://getcomposer.org/schema.json",
3 | "name": "diamondstrider1/remark",
4 | "description": "UI virion (library) for PocketMine-MP plugins",
5 | "version": "1.2.2",
6 | "type": "library",
7 | "license": "MIT",
8 | "authors": [
9 | {
10 | "name": "DiamondStrider1",
11 | "email": "62265561+Swift-Strider@users.noreply.github.com"
12 | }
13 | ],
14 | "minimum-stability": "stable",
15 | "require": {
16 | "pocketmine/pocketmine-mp": "^4.0.0",
17 | "sof3/await-generator": "^3.3.0"
18 | },
19 | "autoload": {
20 | "psr-4": {
21 | "DiamondStrider1\\Remark\\": "src/DiamondStrider1/Remark/"
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/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": "4d05d3730805524c30549c5b6bd85ee5",
8 | "packages": [
9 | {
10 | "name": "adhocore/json-comment",
11 | "version": "1.1.2",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/adhocore/php-json-comment.git",
15 | "reference": "fc2f76979f0a44a5f5bc2a2b600d0762fe0e78e7"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/adhocore/php-json-comment/zipball/fc2f76979f0a44a5f5bc2a2b600d0762fe0e78e7",
20 | "reference": "fc2f76979f0a44a5f5bc2a2b600d0762fe0e78e7",
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.1.2"
55 | },
56 | "funding": [
57 | {
58 | "url": "https://paypal.me/ji10",
59 | "type": "custom"
60 | }
61 | ],
62 | "time": "2021-04-09T03:06:06+00:00"
63 | },
64 | {
65 | "name": "brick/math",
66 | "version": "0.9.3",
67 | "source": {
68 | "type": "git",
69 | "url": "https://github.com/brick/math.git",
70 | "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae"
71 | },
72 | "dist": {
73 | "type": "zip",
74 | "url": "https://api.github.com/repos/brick/math/zipball/ca57d18f028f84f777b2168cd1911b0dee2343ae",
75 | "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae",
76 | "shasum": ""
77 | },
78 | "require": {
79 | "ext-json": "*",
80 | "php": "^7.1 || ^8.0"
81 | },
82 | "require-dev": {
83 | "php-coveralls/php-coveralls": "^2.2",
84 | "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.0",
85 | "vimeo/psalm": "4.9.2"
86 | },
87 | "type": "library",
88 | "autoload": {
89 | "psr-4": {
90 | "Brick\\Math\\": "src/"
91 | }
92 | },
93 | "notification-url": "https://packagist.org/downloads/",
94 | "license": [
95 | "MIT"
96 | ],
97 | "description": "Arbitrary-precision arithmetic library",
98 | "keywords": [
99 | "Arbitrary-precision",
100 | "BigInteger",
101 | "BigRational",
102 | "arithmetic",
103 | "bigdecimal",
104 | "bignum",
105 | "brick",
106 | "math"
107 | ],
108 | "support": {
109 | "issues": "https://github.com/brick/math/issues",
110 | "source": "https://github.com/brick/math/tree/0.9.3"
111 | },
112 | "funding": [
113 | {
114 | "url": "https://github.com/BenMorel",
115 | "type": "github"
116 | },
117 | {
118 | "url": "https://tidelift.com/funding/github/packagist/brick/math",
119 | "type": "tidelift"
120 | }
121 | ],
122 | "time": "2021-08-15T20:50:18+00:00"
123 | },
124 | {
125 | "name": "fgrosse/phpasn1",
126 | "version": "v2.4.0",
127 | "source": {
128 | "type": "git",
129 | "url": "https://github.com/fgrosse/PHPASN1.git",
130 | "reference": "eef488991d53e58e60c9554b09b1201ca5ba9296"
131 | },
132 | "dist": {
133 | "type": "zip",
134 | "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/eef488991d53e58e60c9554b09b1201ca5ba9296",
135 | "reference": "eef488991d53e58e60c9554b09b1201ca5ba9296",
136 | "shasum": ""
137 | },
138 | "require": {
139 | "php": "~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0"
140 | },
141 | "require-dev": {
142 | "php-coveralls/php-coveralls": "~2.0",
143 | "phpunit/phpunit": "^6.3 || ^7.0 || ^8.0"
144 | },
145 | "suggest": {
146 | "ext-bcmath": "BCmath is the fallback extension for big integer calculations",
147 | "ext-curl": "For loading OID information from the web if they have not bee defined statically",
148 | "ext-gmp": "GMP is the preferred extension for big integer calculations",
149 | "phpseclib/bcmath_compat": "BCmath polyfill for servers where neither GMP nor BCmath is available"
150 | },
151 | "type": "library",
152 | "extra": {
153 | "branch-alias": {
154 | "dev-master": "2.0.x-dev"
155 | }
156 | },
157 | "autoload": {
158 | "psr-4": {
159 | "FG\\": "lib/"
160 | }
161 | },
162 | "notification-url": "https://packagist.org/downloads/",
163 | "license": [
164 | "MIT"
165 | ],
166 | "authors": [
167 | {
168 | "name": "Friedrich Große",
169 | "email": "friedrich.grosse@gmail.com",
170 | "homepage": "https://github.com/FGrosse",
171 | "role": "Author"
172 | },
173 | {
174 | "name": "All contributors",
175 | "homepage": "https://github.com/FGrosse/PHPASN1/contributors"
176 | }
177 | ],
178 | "description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.",
179 | "homepage": "https://github.com/FGrosse/PHPASN1",
180 | "keywords": [
181 | "DER",
182 | "asn.1",
183 | "asn1",
184 | "ber",
185 | "binary",
186 | "decoding",
187 | "encoding",
188 | "x.509",
189 | "x.690",
190 | "x509",
191 | "x690"
192 | ],
193 | "support": {
194 | "issues": "https://github.com/fgrosse/PHPASN1/issues",
195 | "source": "https://github.com/fgrosse/PHPASN1/tree/v2.4.0"
196 | },
197 | "time": "2021-12-11T12:41:06+00:00"
198 | },
199 | {
200 | "name": "netresearch/jsonmapper",
201 | "version": "v4.0.0",
202 | "source": {
203 | "type": "git",
204 | "url": "https://github.com/cweiske/jsonmapper.git",
205 | "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d"
206 | },
207 | "dist": {
208 | "type": "zip",
209 | "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d",
210 | "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d",
211 | "shasum": ""
212 | },
213 | "require": {
214 | "ext-json": "*",
215 | "ext-pcre": "*",
216 | "ext-reflection": "*",
217 | "ext-spl": "*",
218 | "php": ">=7.1"
219 | },
220 | "require-dev": {
221 | "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0",
222 | "squizlabs/php_codesniffer": "~3.5"
223 | },
224 | "type": "library",
225 | "autoload": {
226 | "psr-0": {
227 | "JsonMapper": "src/"
228 | }
229 | },
230 | "notification-url": "https://packagist.org/downloads/",
231 | "license": [
232 | "OSL-3.0"
233 | ],
234 | "authors": [
235 | {
236 | "name": "Christian Weiske",
237 | "email": "cweiske@cweiske.de",
238 | "homepage": "http://github.com/cweiske/jsonmapper/",
239 | "role": "Developer"
240 | }
241 | ],
242 | "description": "Map nested JSON structures onto PHP classes",
243 | "support": {
244 | "email": "cweiske@cweiske.de",
245 | "issues": "https://github.com/cweiske/jsonmapper/issues",
246 | "source": "https://github.com/cweiske/jsonmapper/tree/v4.0.0"
247 | },
248 | "time": "2020-12-01T19:48:11+00:00"
249 | },
250 | {
251 | "name": "pocketmine/bedrock-data",
252 | "version": "1.7.0+bedrock-1.18.30",
253 | "source": {
254 | "type": "git",
255 | "url": "https://github.com/pmmp/BedrockData.git",
256 | "reference": "c8f323ff0cbdb36a5d95e7e4a23969f562445be0"
257 | },
258 | "dist": {
259 | "type": "zip",
260 | "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/c8f323ff0cbdb36a5d95e7e4a23969f562445be0",
261 | "reference": "c8f323ff0cbdb36a5d95e7e4a23969f562445be0",
262 | "shasum": ""
263 | },
264 | "type": "library",
265 | "notification-url": "https://packagist.org/downloads/",
266 | "license": [
267 | "CC0-1.0"
268 | ],
269 | "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
270 | "support": {
271 | "issues": "https://github.com/pmmp/BedrockData/issues",
272 | "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.18.30"
273 | },
274 | "time": "2022-04-20T12:40:59+00:00"
275 | },
276 | {
277 | "name": "pocketmine/bedrock-protocol",
278 | "version": "7.3.1+bedrock-1.18.0",
279 | "source": {
280 | "type": "git",
281 | "url": "https://github.com/pmmp/BedrockProtocol.git",
282 | "reference": "c2667453b03ca08a8c54cd89a1fd45cb29196aeb"
283 | },
284 | "dist": {
285 | "type": "zip",
286 | "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c2667453b03ca08a8c54cd89a1fd45cb29196aeb",
287 | "reference": "c2667453b03ca08a8c54cd89a1fd45cb29196aeb",
288 | "shasum": ""
289 | },
290 | "require": {
291 | "ext-json": "*",
292 | "netresearch/jsonmapper": "^4.0",
293 | "php": "^8.0",
294 | "pocketmine/binaryutils": "^0.2.0",
295 | "pocketmine/color": "^0.2.0",
296 | "pocketmine/math": "^0.3.0 || ^0.4.0",
297 | "pocketmine/nbt": "^0.3.0",
298 | "ramsey/uuid": "^4.1"
299 | },
300 | "require-dev": {
301 | "phpstan/phpstan": "1.4.2",
302 | "phpstan/phpstan-phpunit": "^1.0.0",
303 | "phpstan/phpstan-strict-rules": "^1.0.0",
304 | "phpunit/phpunit": "^9.5"
305 | },
306 | "type": "library",
307 | "autoload": {
308 | "psr-4": {
309 | "pocketmine\\network\\mcpe\\protocol\\": "src/"
310 | }
311 | },
312 | "notification-url": "https://packagist.org/downloads/",
313 | "license": [
314 | "LGPL-3.0"
315 | ],
316 | "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
317 | "support": {
318 | "issues": "https://github.com/pmmp/BedrockProtocol/issues",
319 | "source": "https://github.com/pmmp/BedrockProtocol/tree/7.3.1+bedrock-1.18.0"
320 | },
321 | "time": "2022-01-26T21:14:23+00:00"
322 | },
323 | {
324 | "name": "pocketmine/binaryutils",
325 | "version": "0.2.4",
326 | "source": {
327 | "type": "git",
328 | "url": "https://github.com/pmmp/BinaryUtils.git",
329 | "reference": "5ac7eea91afbad8dc498f5ce34ce6297d5e6ea9a"
330 | },
331 | "dist": {
332 | "type": "zip",
333 | "url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/5ac7eea91afbad8dc498f5ce34ce6297d5e6ea9a",
334 | "reference": "5ac7eea91afbad8dc498f5ce34ce6297d5e6ea9a",
335 | "shasum": ""
336 | },
337 | "require": {
338 | "php": "^7.4 || ^8.0",
339 | "php-64bit": "*"
340 | },
341 | "require-dev": {
342 | "phpstan/extension-installer": "^1.0",
343 | "phpstan/phpstan": "1.3.0",
344 | "phpstan/phpstan-phpunit": "^1.0",
345 | "phpstan/phpstan-strict-rules": "^1.0.0",
346 | "phpunit/phpunit": "^9.5"
347 | },
348 | "type": "library",
349 | "autoload": {
350 | "psr-4": {
351 | "pocketmine\\utils\\": "src/"
352 | }
353 | },
354 | "notification-url": "https://packagist.org/downloads/",
355 | "license": [
356 | "LGPL-3.0"
357 | ],
358 | "description": "Classes and methods for conveniently handling binary data",
359 | "support": {
360 | "issues": "https://github.com/pmmp/BinaryUtils/issues",
361 | "source": "https://github.com/pmmp/BinaryUtils/tree/0.2.4"
362 | },
363 | "time": "2022-01-12T18:06:33+00:00"
364 | },
365 | {
366 | "name": "pocketmine/callback-validator",
367 | "version": "1.0.3",
368 | "source": {
369 | "type": "git",
370 | "url": "https://github.com/pmmp/CallbackValidator.git",
371 | "reference": "64787469766bcaa7e5885242e85c23c25e8c55a2"
372 | },
373 | "dist": {
374 | "type": "zip",
375 | "url": "https://api.github.com/repos/pmmp/CallbackValidator/zipball/64787469766bcaa7e5885242e85c23c25e8c55a2",
376 | "reference": "64787469766bcaa7e5885242e85c23c25e8c55a2",
377 | "shasum": ""
378 | },
379 | "require": {
380 | "ext-reflection": "*",
381 | "php": "^7.1 || ^8.0"
382 | },
383 | "replace": {
384 | "daverandom/callback-validator": "*"
385 | },
386 | "require-dev": {
387 | "phpstan/extension-installer": "^1.0",
388 | "phpstan/phpstan": "0.12.59",
389 | "phpstan/phpstan-strict-rules": "^0.12.4",
390 | "phpunit/phpunit": "^7.5 || ^8.5 || ^9.0"
391 | },
392 | "type": "library",
393 | "autoload": {
394 | "psr-4": {
395 | "DaveRandom\\CallbackValidator\\": "src/"
396 | }
397 | },
398 | "notification-url": "https://packagist.org/downloads/",
399 | "license": [
400 | "MIT"
401 | ],
402 | "authors": [
403 | {
404 | "name": "Chris Wright",
405 | "email": "cw@daverandom.com"
406 | }
407 | ],
408 | "description": "Fork of daverandom/callback-validator - Tools for validating callback signatures",
409 | "support": {
410 | "issues": "https://github.com/pmmp/CallbackValidator/issues",
411 | "source": "https://github.com/pmmp/CallbackValidator/tree/1.0.3"
412 | },
413 | "time": "2020-12-11T01:45:37+00:00"
414 | },
415 | {
416 | "name": "pocketmine/classloader",
417 | "version": "0.2.0",
418 | "source": {
419 | "type": "git",
420 | "url": "https://github.com/pmmp/ClassLoader.git",
421 | "reference": "49ea303993efdfb39cd302e2156d50aa78209e78"
422 | },
423 | "dist": {
424 | "type": "zip",
425 | "url": "https://api.github.com/repos/pmmp/ClassLoader/zipball/49ea303993efdfb39cd302e2156d50aa78209e78",
426 | "reference": "49ea303993efdfb39cd302e2156d50aa78209e78",
427 | "shasum": ""
428 | },
429 | "require": {
430 | "ext-pthreads": "~3.2.0 || ^4.0",
431 | "ext-reflection": "*",
432 | "php": "^8.0"
433 | },
434 | "conflict": {
435 | "pocketmine/spl": "<0.4"
436 | },
437 | "require-dev": {
438 | "phpstan/extension-installer": "^1.0",
439 | "phpstan/phpstan": "0.12.99",
440 | "phpstan/phpstan-strict-rules": "^0.12.4",
441 | "phpunit/phpunit": "^9.5"
442 | },
443 | "type": "library",
444 | "autoload": {
445 | "classmap": [
446 | "./src"
447 | ]
448 | },
449 | "notification-url": "https://packagist.org/downloads/",
450 | "license": [
451 | "LGPL-3.0"
452 | ],
453 | "description": "Ad-hoc autoloading components used by PocketMine-MP",
454 | "support": {
455 | "issues": "https://github.com/pmmp/ClassLoader/issues",
456 | "source": "https://github.com/pmmp/ClassLoader/tree/0.2.0"
457 | },
458 | "time": "2021-11-01T20:17:27+00:00"
459 | },
460 | {
461 | "name": "pocketmine/color",
462 | "version": "0.2.0",
463 | "source": {
464 | "type": "git",
465 | "url": "https://github.com/pmmp/Color.git",
466 | "reference": "09be6ea6d76f2e33d6813c39d29c22c46c17e1d2"
467 | },
468 | "dist": {
469 | "type": "zip",
470 | "url": "https://api.github.com/repos/pmmp/Color/zipball/09be6ea6d76f2e33d6813c39d29c22c46c17e1d2",
471 | "reference": "09be6ea6d76f2e33d6813c39d29c22c46c17e1d2",
472 | "shasum": ""
473 | },
474 | "require": {
475 | "php": "^7.2 || ^8.0"
476 | },
477 | "require-dev": {
478 | "phpstan/phpstan": "0.12.59",
479 | "phpstan/phpstan-strict-rules": "^0.12.2"
480 | },
481 | "type": "library",
482 | "autoload": {
483 | "psr-4": {
484 | "pocketmine\\color\\": "src/"
485 | }
486 | },
487 | "notification-url": "https://packagist.org/downloads/",
488 | "license": [
489 | "LGPL-3.0"
490 | ],
491 | "description": "Color handling library used by PocketMine-MP and related projects",
492 | "support": {
493 | "issues": "https://github.com/pmmp/Color/issues",
494 | "source": "https://github.com/pmmp/Color/tree/0.2.0"
495 | },
496 | "time": "2020-12-11T01:24:32+00:00"
497 | },
498 | {
499 | "name": "pocketmine/errorhandler",
500 | "version": "0.3.0",
501 | "source": {
502 | "type": "git",
503 | "url": "https://github.com/pmmp/ErrorHandler.git",
504 | "reference": "ec742b209e8056bbe855069c4eff94c9734ea19b"
505 | },
506 | "dist": {
507 | "type": "zip",
508 | "url": "https://api.github.com/repos/pmmp/ErrorHandler/zipball/ec742b209e8056bbe855069c4eff94c9734ea19b",
509 | "reference": "ec742b209e8056bbe855069c4eff94c9734ea19b",
510 | "shasum": ""
511 | },
512 | "require": {
513 | "php": "^7.2 || ^8.0"
514 | },
515 | "require-dev": {
516 | "phpstan/phpstan": "0.12.75",
517 | "phpstan/phpstan-strict-rules": "^0.12.2"
518 | },
519 | "type": "library",
520 | "autoload": {
521 | "psr-4": {
522 | "pocketmine\\errorhandler\\": "src/"
523 | }
524 | },
525 | "notification-url": "https://packagist.org/downloads/",
526 | "license": [
527 | "LGPL-3.0"
528 | ],
529 | "description": "Utilities to handle nasty PHP E_* errors in a usable way",
530 | "support": {
531 | "issues": "https://github.com/pmmp/ErrorHandler/issues",
532 | "source": "https://github.com/pmmp/ErrorHandler/tree/0.3.0"
533 | },
534 | "time": "2021-02-12T18:56:22+00:00"
535 | },
536 | {
537 | "name": "pocketmine/locale-data",
538 | "version": "2.8.3",
539 | "source": {
540 | "type": "git",
541 | "url": "https://github.com/pmmp/Language.git",
542 | "reference": "113c115a3b8976917eb22b74dccab464831b6483"
543 | },
544 | "dist": {
545 | "type": "zip",
546 | "url": "https://api.github.com/repos/pmmp/Language/zipball/113c115a3b8976917eb22b74dccab464831b6483",
547 | "reference": "113c115a3b8976917eb22b74dccab464831b6483",
548 | "shasum": ""
549 | },
550 | "type": "library",
551 | "notification-url": "https://packagist.org/downloads/",
552 | "description": "Language resources used by PocketMine-MP",
553 | "support": {
554 | "issues": "https://github.com/pmmp/Language/issues",
555 | "source": "https://github.com/pmmp/Language/tree/2.8.3"
556 | },
557 | "time": "2022-05-11T13:51:37+00:00"
558 | },
559 | {
560 | "name": "pocketmine/log",
561 | "version": "0.4.0",
562 | "source": {
563 | "type": "git",
564 | "url": "https://github.com/pmmp/Log.git",
565 | "reference": "e6c912c0f9055c81d23108ec2d179b96f404c043"
566 | },
567 | "dist": {
568 | "type": "zip",
569 | "url": "https://api.github.com/repos/pmmp/Log/zipball/e6c912c0f9055c81d23108ec2d179b96f404c043",
570 | "reference": "e6c912c0f9055c81d23108ec2d179b96f404c043",
571 | "shasum": ""
572 | },
573 | "require": {
574 | "php": "^7.4 || ^8.0"
575 | },
576 | "conflict": {
577 | "pocketmine/spl": "<0.4"
578 | },
579 | "require-dev": {
580 | "phpstan/phpstan": "0.12.88",
581 | "phpstan/phpstan-strict-rules": "^0.12.2"
582 | },
583 | "type": "library",
584 | "autoload": {
585 | "classmap": [
586 | "./src"
587 | ]
588 | },
589 | "notification-url": "https://packagist.org/downloads/",
590 | "license": [
591 | "LGPL-3.0"
592 | ],
593 | "description": "Logging components used by PocketMine-MP and related projects",
594 | "support": {
595 | "issues": "https://github.com/pmmp/Log/issues",
596 | "source": "https://github.com/pmmp/Log/tree/0.4.0"
597 | },
598 | "time": "2021-06-18T19:08:09+00:00"
599 | },
600 | {
601 | "name": "pocketmine/log-pthreads",
602 | "version": "0.4.0",
603 | "source": {
604 | "type": "git",
605 | "url": "https://github.com/pmmp/LogPthreads.git",
606 | "reference": "61f709e8cf36bcc24e4efe02acded680a1ce23cd"
607 | },
608 | "dist": {
609 | "type": "zip",
610 | "url": "https://api.github.com/repos/pmmp/LogPthreads/zipball/61f709e8cf36bcc24e4efe02acded680a1ce23cd",
611 | "reference": "61f709e8cf36bcc24e4efe02acded680a1ce23cd",
612 | "shasum": ""
613 | },
614 | "require": {
615 | "ext-pthreads": "~3.2.0 || ^4.0",
616 | "php": "^7.4 || ^8.0",
617 | "pocketmine/log": "^0.4.0"
618 | },
619 | "conflict": {
620 | "pocketmine/spl": "<0.4"
621 | },
622 | "require-dev": {
623 | "phpstan/extension-installer": "^1.0",
624 | "phpstan/phpstan": "0.12.88",
625 | "phpstan/phpstan-strict-rules": "^0.12.4"
626 | },
627 | "type": "library",
628 | "autoload": {
629 | "classmap": [
630 | "./src"
631 | ]
632 | },
633 | "notification-url": "https://packagist.org/downloads/",
634 | "license": [
635 | "LGPL-3.0"
636 | ],
637 | "description": "Logging components specialized for pthreads used by PocketMine-MP and related projects",
638 | "support": {
639 | "issues": "https://github.com/pmmp/LogPthreads/issues",
640 | "source": "https://github.com/pmmp/LogPthreads/tree/0.4.0"
641 | },
642 | "time": "2021-11-01T21:42:09+00:00"
643 | },
644 | {
645 | "name": "pocketmine/math",
646 | "version": "0.4.2",
647 | "source": {
648 | "type": "git",
649 | "url": "https://github.com/pmmp/Math.git",
650 | "reference": "aacc3759a508a69dfa5bc4dfa770ab733c5c94bf"
651 | },
652 | "dist": {
653 | "type": "zip",
654 | "url": "https://api.github.com/repos/pmmp/Math/zipball/aacc3759a508a69dfa5bc4dfa770ab733c5c94bf",
655 | "reference": "aacc3759a508a69dfa5bc4dfa770ab733c5c94bf",
656 | "shasum": ""
657 | },
658 | "require": {
659 | "php": "^8.0",
660 | "php-64bit": "*"
661 | },
662 | "require-dev": {
663 | "phpstan/extension-installer": "^1.0",
664 | "phpstan/phpstan": "1.2.0",
665 | "phpstan/phpstan-strict-rules": "^1.0",
666 | "phpunit/phpunit": "^8.5 || ^9.5"
667 | },
668 | "type": "library",
669 | "autoload": {
670 | "psr-4": {
671 | "pocketmine\\math\\": "src/"
672 | }
673 | },
674 | "notification-url": "https://packagist.org/downloads/",
675 | "license": [
676 | "LGPL-3.0"
677 | ],
678 | "description": "PHP library containing math related code used in PocketMine-MP",
679 | "support": {
680 | "issues": "https://github.com/pmmp/Math/issues",
681 | "source": "https://github.com/pmmp/Math/tree/0.4.2"
682 | },
683 | "time": "2021-12-05T01:15:17+00:00"
684 | },
685 | {
686 | "name": "pocketmine/nbt",
687 | "version": "0.3.2",
688 | "source": {
689 | "type": "git",
690 | "url": "https://github.com/pmmp/NBT.git",
691 | "reference": "3e0d9ef6b6c5fb45e3745a121296e75631b3eefe"
692 | },
693 | "dist": {
694 | "type": "zip",
695 | "url": "https://api.github.com/repos/pmmp/NBT/zipball/3e0d9ef6b6c5fb45e3745a121296e75631b3eefe",
696 | "reference": "3e0d9ef6b6c5fb45e3745a121296e75631b3eefe",
697 | "shasum": ""
698 | },
699 | "require": {
700 | "php": "^7.4 || ^8.0",
701 | "php-64bit": "*",
702 | "pocketmine/binaryutils": "^0.2.0"
703 | },
704 | "require-dev": {
705 | "phpstan/extension-installer": "^1.0",
706 | "phpstan/phpstan": "1.2.0",
707 | "phpstan/phpstan-strict-rules": "^1.0",
708 | "phpunit/phpunit": "^9.5"
709 | },
710 | "type": "library",
711 | "autoload": {
712 | "psr-4": {
713 | "pocketmine\\nbt\\": "src/"
714 | }
715 | },
716 | "notification-url": "https://packagist.org/downloads/",
717 | "license": [
718 | "LGPL-3.0"
719 | ],
720 | "description": "PHP library for working with Named Binary Tags",
721 | "support": {
722 | "issues": "https://github.com/pmmp/NBT/issues",
723 | "source": "https://github.com/pmmp/NBT/tree/0.3.2"
724 | },
725 | "time": "2021-12-16T01:02:37+00:00"
726 | },
727 | {
728 | "name": "pocketmine/pocketmine-mp",
729 | "version": "4.0.0",
730 | "source": {
731 | "type": "git",
732 | "url": "https://github.com/pmmp/PocketMine-MP.git",
733 | "reference": "468faa464b2bc5c97f23fafbb71ea61035f6f218"
734 | },
735 | "dist": {
736 | "type": "zip",
737 | "url": "https://api.github.com/repos/pmmp/PocketMine-MP/zipball/468faa464b2bc5c97f23fafbb71ea61035f6f218",
738 | "reference": "468faa464b2bc5c97f23fafbb71ea61035f6f218",
739 | "shasum": ""
740 | },
741 | "require": {
742 | "adhocore/json-comment": "^1.1",
743 | "composer-runtime-api": "^2.0",
744 | "ext-chunkutils2": "^0.3.1",
745 | "ext-crypto": "^0.3.1",
746 | "ext-ctype": "*",
747 | "ext-curl": "*",
748 | "ext-date": "*",
749 | "ext-gmp": "*",
750 | "ext-hash": "*",
751 | "ext-igbinary": "^3.0.1",
752 | "ext-json": "*",
753 | "ext-leveldb": "^0.2.1 || ^0.3.0",
754 | "ext-mbstring": "*",
755 | "ext-morton": "^0.1.0",
756 | "ext-openssl": "*",
757 | "ext-pcre": "*",
758 | "ext-phar": "*",
759 | "ext-pthreads": "^4.0",
760 | "ext-reflection": "*",
761 | "ext-simplexml": "*",
762 | "ext-sockets": "*",
763 | "ext-spl": "*",
764 | "ext-yaml": ">=2.0.0",
765 | "ext-zip": "*",
766 | "ext-zlib": ">=1.2.11",
767 | "fgrosse/phpasn1": "^2.3",
768 | "netresearch/jsonmapper": "^4.0",
769 | "php": "^8.0",
770 | "php-64bit": "*",
771 | "pocketmine/bedrock-data": "^1.5.0+bedrock-1.18.0",
772 | "pocketmine/bedrock-protocol": "^7.0.0+bedrock-1.18.0",
773 | "pocketmine/binaryutils": "^0.2.1",
774 | "pocketmine/callback-validator": "^1.0.2",
775 | "pocketmine/classloader": "^0.2.0",
776 | "pocketmine/color": "^0.2.0",
777 | "pocketmine/errorhandler": "^0.3.0",
778 | "pocketmine/locale-data": "^2.0.16",
779 | "pocketmine/log": "^0.4.0",
780 | "pocketmine/log-pthreads": "^0.4.0",
781 | "pocketmine/math": "^0.4.0",
782 | "pocketmine/nbt": "^0.3.0",
783 | "pocketmine/raklib": "^0.14.2",
784 | "pocketmine/raklib-ipc": "^0.1.0",
785 | "pocketmine/snooze": "^0.3.0",
786 | "ramsey/uuid": "^4.1",
787 | "webmozart/path-util": "^2.3"
788 | },
789 | "require-dev": {
790 | "phpstan/phpstan": "1.2.0",
791 | "phpstan/phpstan-phpunit": "^1.0.0",
792 | "phpstan/phpstan-strict-rules": "^1.0.0",
793 | "phpunit/phpunit": "^9.2"
794 | },
795 | "type": "project",
796 | "autoload": {
797 | "files": [
798 | "src/CoreConstants.php"
799 | ],
800 | "psr-4": {
801 | "pocketmine\\": "src/"
802 | }
803 | },
804 | "notification-url": "https://packagist.org/downloads/",
805 | "license": [
806 | "LGPL-3.0"
807 | ],
808 | "description": "A server software for Minecraft: Bedrock Edition written in PHP",
809 | "homepage": "https://pmmp.io",
810 | "support": {
811 | "issues": "https://github.com/pmmp/PocketMine-MP/issues",
812 | "source": "https://github.com/pmmp/PocketMine-MP/tree/4.0.0"
813 | },
814 | "funding": [
815 | {
816 | "url": "https://github.com/pmmp/PocketMine-MP#donate",
817 | "type": "custom"
818 | },
819 | {
820 | "url": "https://www.patreon.com/pocketminemp",
821 | "type": "patreon"
822 | }
823 | ],
824 | "time": "2021-12-01T22:33:52+00:00"
825 | },
826 | {
827 | "name": "pocketmine/raklib",
828 | "version": "0.14.4",
829 | "source": {
830 | "type": "git",
831 | "url": "https://github.com/pmmp/RakLib.git",
832 | "reference": "1ea8e3b95a1b6bf785dc27d76578657be4185f42"
833 | },
834 | "dist": {
835 | "type": "zip",
836 | "url": "https://api.github.com/repos/pmmp/RakLib/zipball/1ea8e3b95a1b6bf785dc27d76578657be4185f42",
837 | "reference": "1ea8e3b95a1b6bf785dc27d76578657be4185f42",
838 | "shasum": ""
839 | },
840 | "require": {
841 | "ext-sockets": "*",
842 | "php": "^8.0",
843 | "php-64bit": "*",
844 | "php-ipv6": "*",
845 | "pocketmine/binaryutils": "^0.2.0",
846 | "pocketmine/log": "^0.3.0 || ^0.4.0"
847 | },
848 | "require-dev": {
849 | "phpstan/phpstan": "1.5.4",
850 | "phpstan/phpstan-strict-rules": "^1.0"
851 | },
852 | "type": "library",
853 | "autoload": {
854 | "psr-4": {
855 | "raklib\\": "src/"
856 | }
857 | },
858 | "notification-url": "https://packagist.org/downloads/",
859 | "license": [
860 | "GPL-3.0"
861 | ],
862 | "description": "A RakNet server implementation written in PHP",
863 | "support": {
864 | "issues": "https://github.com/pmmp/RakLib/issues",
865 | "source": "https://github.com/pmmp/RakLib/tree/0.14.4"
866 | },
867 | "time": "2022-04-17T18:42:17+00:00"
868 | },
869 | {
870 | "name": "pocketmine/raklib-ipc",
871 | "version": "0.1.1",
872 | "source": {
873 | "type": "git",
874 | "url": "https://github.com/pmmp/RakLibIpc.git",
875 | "reference": "922a6444b0c6c7daaa5aa5a832107e1ec4738aed"
876 | },
877 | "dist": {
878 | "type": "zip",
879 | "url": "https://api.github.com/repos/pmmp/RakLibIpc/zipball/922a6444b0c6c7daaa5aa5a832107e1ec4738aed",
880 | "reference": "922a6444b0c6c7daaa5aa5a832107e1ec4738aed",
881 | "shasum": ""
882 | },
883 | "require": {
884 | "php": "^7.4 || ^8.0",
885 | "php-64bit": "*",
886 | "pocketmine/binaryutils": "^0.2.0",
887 | "pocketmine/raklib": "^0.13.1 || ^0.14.0"
888 | },
889 | "require-dev": {
890 | "phpstan/phpstan": "0.12.81",
891 | "phpstan/phpstan-strict-rules": "^0.12.2"
892 | },
893 | "type": "library",
894 | "autoload": {
895 | "psr-4": {
896 | "raklib\\server\\ipc\\": "src/"
897 | }
898 | },
899 | "notification-url": "https://packagist.org/downloads/",
900 | "license": [
901 | "GPL-3.0"
902 | ],
903 | "description": "Channel-based protocols for inter-thread/inter-process communication with RakLib",
904 | "support": {
905 | "issues": "https://github.com/pmmp/RakLibIpc/issues",
906 | "source": "https://github.com/pmmp/RakLibIpc/tree/0.1.1"
907 | },
908 | "time": "2021-09-22T17:01:12+00:00"
909 | },
910 | {
911 | "name": "pocketmine/snooze",
912 | "version": "0.3.1",
913 | "source": {
914 | "type": "git",
915 | "url": "https://github.com/pmmp/Snooze.git",
916 | "reference": "0ac8fc2a781c419a1f64ebca4d5835028f59e29b"
917 | },
918 | "dist": {
919 | "type": "zip",
920 | "url": "https://api.github.com/repos/pmmp/Snooze/zipball/0ac8fc2a781c419a1f64ebca4d5835028f59e29b",
921 | "reference": "0ac8fc2a781c419a1f64ebca4d5835028f59e29b",
922 | "shasum": ""
923 | },
924 | "require": {
925 | "ext-pthreads": "~3.2.0 || ^4.0",
926 | "php-64bit": "^7.3 || ^8.0"
927 | },
928 | "require-dev": {
929 | "phpstan/extension-installer": "^1.0",
930 | "phpstan/phpstan": "0.12.99",
931 | "phpstan/phpstan-strict-rules": "^0.12.4"
932 | },
933 | "type": "library",
934 | "autoload": {
935 | "psr-4": {
936 | "pocketmine\\snooze\\": "src/"
937 | }
938 | },
939 | "notification-url": "https://packagist.org/downloads/",
940 | "license": [
941 | "LGPL-3.0"
942 | ],
943 | "description": "Thread notification management library for code using the pthreads extension",
944 | "support": {
945 | "issues": "https://github.com/pmmp/Snooze/issues",
946 | "source": "https://github.com/pmmp/Snooze/tree/0.3.1"
947 | },
948 | "time": "2021-11-01T20:50:08+00:00"
949 | },
950 | {
951 | "name": "ramsey/collection",
952 | "version": "1.2.2",
953 | "source": {
954 | "type": "git",
955 | "url": "https://github.com/ramsey/collection.git",
956 | "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a"
957 | },
958 | "dist": {
959 | "type": "zip",
960 | "url": "https://api.github.com/repos/ramsey/collection/zipball/cccc74ee5e328031b15640b51056ee8d3bb66c0a",
961 | "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a",
962 | "shasum": ""
963 | },
964 | "require": {
965 | "php": "^7.3 || ^8",
966 | "symfony/polyfill-php81": "^1.23"
967 | },
968 | "require-dev": {
969 | "captainhook/captainhook": "^5.3",
970 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
971 | "ergebnis/composer-normalize": "^2.6",
972 | "fakerphp/faker": "^1.5",
973 | "hamcrest/hamcrest-php": "^2",
974 | "jangregor/phpstan-prophecy": "^0.8",
975 | "mockery/mockery": "^1.3",
976 | "phpspec/prophecy-phpunit": "^2.0",
977 | "phpstan/extension-installer": "^1",
978 | "phpstan/phpstan": "^0.12.32",
979 | "phpstan/phpstan-mockery": "^0.12.5",
980 | "phpstan/phpstan-phpunit": "^0.12.11",
981 | "phpunit/phpunit": "^8.5 || ^9",
982 | "psy/psysh": "^0.10.4",
983 | "slevomat/coding-standard": "^6.3",
984 | "squizlabs/php_codesniffer": "^3.5",
985 | "vimeo/psalm": "^4.4"
986 | },
987 | "type": "library",
988 | "autoload": {
989 | "psr-4": {
990 | "Ramsey\\Collection\\": "src/"
991 | }
992 | },
993 | "notification-url": "https://packagist.org/downloads/",
994 | "license": [
995 | "MIT"
996 | ],
997 | "authors": [
998 | {
999 | "name": "Ben Ramsey",
1000 | "email": "ben@benramsey.com",
1001 | "homepage": "https://benramsey.com"
1002 | }
1003 | ],
1004 | "description": "A PHP library for representing and manipulating collections.",
1005 | "keywords": [
1006 | "array",
1007 | "collection",
1008 | "hash",
1009 | "map",
1010 | "queue",
1011 | "set"
1012 | ],
1013 | "support": {
1014 | "issues": "https://github.com/ramsey/collection/issues",
1015 | "source": "https://github.com/ramsey/collection/tree/1.2.2"
1016 | },
1017 | "funding": [
1018 | {
1019 | "url": "https://github.com/ramsey",
1020 | "type": "github"
1021 | },
1022 | {
1023 | "url": "https://tidelift.com/funding/github/packagist/ramsey/collection",
1024 | "type": "tidelift"
1025 | }
1026 | ],
1027 | "time": "2021-10-10T03:01:02+00:00"
1028 | },
1029 | {
1030 | "name": "ramsey/uuid",
1031 | "version": "4.3.1",
1032 | "source": {
1033 | "type": "git",
1034 | "url": "https://github.com/ramsey/uuid.git",
1035 | "reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28"
1036 | },
1037 | "dist": {
1038 | "type": "zip",
1039 | "url": "https://api.github.com/repos/ramsey/uuid/zipball/8505afd4fea63b81a85d3b7b53ac3cb8dc347c28",
1040 | "reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28",
1041 | "shasum": ""
1042 | },
1043 | "require": {
1044 | "brick/math": "^0.8 || ^0.9",
1045 | "ext-ctype": "*",
1046 | "ext-json": "*",
1047 | "php": "^8.0",
1048 | "ramsey/collection": "^1.0"
1049 | },
1050 | "replace": {
1051 | "rhumsaa/uuid": "self.version"
1052 | },
1053 | "require-dev": {
1054 | "captainhook/captainhook": "^5.10",
1055 | "captainhook/plugin-composer": "^5.3",
1056 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
1057 | "doctrine/annotations": "^1.8",
1058 | "ergebnis/composer-normalize": "^2.15",
1059 | "mockery/mockery": "^1.3",
1060 | "moontoast/math": "^1.1",
1061 | "paragonie/random-lib": "^2",
1062 | "php-mock/php-mock": "^2.2",
1063 | "php-mock/php-mock-mockery": "^1.3",
1064 | "php-parallel-lint/php-parallel-lint": "^1.1",
1065 | "phpbench/phpbench": "^1.0",
1066 | "phpstan/extension-installer": "^1.0",
1067 | "phpstan/phpstan": "^0.12",
1068 | "phpstan/phpstan-mockery": "^0.12",
1069 | "phpstan/phpstan-phpunit": "^0.12",
1070 | "phpunit/phpunit": "^8.5 || ^9",
1071 | "slevomat/coding-standard": "^7.0",
1072 | "squizlabs/php_codesniffer": "^3.5",
1073 | "vimeo/psalm": "^4.9"
1074 | },
1075 | "suggest": {
1076 | "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
1077 | "ext-ctype": "Enables faster processing of character classification using ctype functions.",
1078 | "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
1079 | "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
1080 | "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
1081 | "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
1082 | },
1083 | "type": "library",
1084 | "extra": {
1085 | "captainhook": {
1086 | "force-install": true
1087 | }
1088 | },
1089 | "autoload": {
1090 | "files": [
1091 | "src/functions.php"
1092 | ],
1093 | "psr-4": {
1094 | "Ramsey\\Uuid\\": "src/"
1095 | }
1096 | },
1097 | "notification-url": "https://packagist.org/downloads/",
1098 | "license": [
1099 | "MIT"
1100 | ],
1101 | "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
1102 | "keywords": [
1103 | "guid",
1104 | "identifier",
1105 | "uuid"
1106 | ],
1107 | "support": {
1108 | "issues": "https://github.com/ramsey/uuid/issues",
1109 | "source": "https://github.com/ramsey/uuid/tree/4.3.1"
1110 | },
1111 | "funding": [
1112 | {
1113 | "url": "https://github.com/ramsey",
1114 | "type": "github"
1115 | },
1116 | {
1117 | "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
1118 | "type": "tidelift"
1119 | }
1120 | ],
1121 | "time": "2022-03-27T21:42:02+00:00"
1122 | },
1123 | {
1124 | "name": "sof3/await-generator",
1125 | "version": "3.3.0",
1126 | "source": {
1127 | "type": "git",
1128 | "url": "https://github.com/SOF3/await-generator.git",
1129 | "reference": "6cfe07c2b50dd069854ccb7719397f42ead9e946"
1130 | },
1131 | "dist": {
1132 | "type": "zip",
1133 | "url": "https://api.github.com/repos/SOF3/await-generator/zipball/6cfe07c2b50dd069854ccb7719397f42ead9e946",
1134 | "reference": "6cfe07c2b50dd069854ccb7719397f42ead9e946",
1135 | "shasum": ""
1136 | },
1137 | "require": {
1138 | "php": "^7.3 || ^8.0"
1139 | },
1140 | "require-dev": {
1141 | "composer/package-versions-deprecated": "1.11.99.1",
1142 | "infection/infection": "^0.18.2 || ^0.20.2",
1143 | "phpstan/phpstan": "^0.12.84",
1144 | "phpunit/phpunit": "^9"
1145 | },
1146 | "type": "library",
1147 | "autoload": {
1148 | "psr-0": {
1149 | "SOFe\\AwaitGenerator\\": "await-generator/src/"
1150 | }
1151 | },
1152 | "notification-url": "https://packagist.org/downloads/",
1153 | "license": [
1154 | "apache-2.0"
1155 | ],
1156 | "authors": [
1157 | {
1158 | "name": "SOFe",
1159 | "email": "sofe2038@gmail.com"
1160 | }
1161 | ],
1162 | "description": "Use async/await in PHP using generators",
1163 | "support": {
1164 | "issues": "https://github.com/SOF3/await-generator/issues",
1165 | "source": "https://github.com/SOF3/await-generator/tree/3.3.0"
1166 | },
1167 | "time": "2022-03-13T02:52:08+00:00"
1168 | },
1169 | {
1170 | "name": "symfony/polyfill-ctype",
1171 | "version": "v1.25.0",
1172 | "source": {
1173 | "type": "git",
1174 | "url": "https://github.com/symfony/polyfill-ctype.git",
1175 | "reference": "30885182c981ab175d4d034db0f6f469898070ab"
1176 | },
1177 | "dist": {
1178 | "type": "zip",
1179 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
1180 | "reference": "30885182c981ab175d4d034db0f6f469898070ab",
1181 | "shasum": ""
1182 | },
1183 | "require": {
1184 | "php": ">=7.1"
1185 | },
1186 | "provide": {
1187 | "ext-ctype": "*"
1188 | },
1189 | "suggest": {
1190 | "ext-ctype": "For best performance"
1191 | },
1192 | "type": "library",
1193 | "extra": {
1194 | "branch-alias": {
1195 | "dev-main": "1.23-dev"
1196 | },
1197 | "thanks": {
1198 | "name": "symfony/polyfill",
1199 | "url": "https://github.com/symfony/polyfill"
1200 | }
1201 | },
1202 | "autoload": {
1203 | "files": [
1204 | "bootstrap.php"
1205 | ],
1206 | "psr-4": {
1207 | "Symfony\\Polyfill\\Ctype\\": ""
1208 | }
1209 | },
1210 | "notification-url": "https://packagist.org/downloads/",
1211 | "license": [
1212 | "MIT"
1213 | ],
1214 | "authors": [
1215 | {
1216 | "name": "Gert de Pagter",
1217 | "email": "BackEndTea@gmail.com"
1218 | },
1219 | {
1220 | "name": "Symfony Community",
1221 | "homepage": "https://symfony.com/contributors"
1222 | }
1223 | ],
1224 | "description": "Symfony polyfill for ctype functions",
1225 | "homepage": "https://symfony.com",
1226 | "keywords": [
1227 | "compatibility",
1228 | "ctype",
1229 | "polyfill",
1230 | "portable"
1231 | ],
1232 | "support": {
1233 | "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0"
1234 | },
1235 | "funding": [
1236 | {
1237 | "url": "https://symfony.com/sponsor",
1238 | "type": "custom"
1239 | },
1240 | {
1241 | "url": "https://github.com/fabpot",
1242 | "type": "github"
1243 | },
1244 | {
1245 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1246 | "type": "tidelift"
1247 | }
1248 | ],
1249 | "time": "2021-10-20T20:35:02+00:00"
1250 | },
1251 | {
1252 | "name": "symfony/polyfill-php81",
1253 | "version": "v1.25.0",
1254 | "source": {
1255 | "type": "git",
1256 | "url": "https://github.com/symfony/polyfill-php81.git",
1257 | "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f"
1258 | },
1259 | "dist": {
1260 | "type": "zip",
1261 | "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/5de4ba2d41b15f9bd0e19b2ab9674135813ec98f",
1262 | "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f",
1263 | "shasum": ""
1264 | },
1265 | "require": {
1266 | "php": ">=7.1"
1267 | },
1268 | "type": "library",
1269 | "extra": {
1270 | "branch-alias": {
1271 | "dev-main": "1.23-dev"
1272 | },
1273 | "thanks": {
1274 | "name": "symfony/polyfill",
1275 | "url": "https://github.com/symfony/polyfill"
1276 | }
1277 | },
1278 | "autoload": {
1279 | "files": [
1280 | "bootstrap.php"
1281 | ],
1282 | "psr-4": {
1283 | "Symfony\\Polyfill\\Php81\\": ""
1284 | },
1285 | "classmap": [
1286 | "Resources/stubs"
1287 | ]
1288 | },
1289 | "notification-url": "https://packagist.org/downloads/",
1290 | "license": [
1291 | "MIT"
1292 | ],
1293 | "authors": [
1294 | {
1295 | "name": "Nicolas Grekas",
1296 | "email": "p@tchwork.com"
1297 | },
1298 | {
1299 | "name": "Symfony Community",
1300 | "homepage": "https://symfony.com/contributors"
1301 | }
1302 | ],
1303 | "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
1304 | "homepage": "https://symfony.com",
1305 | "keywords": [
1306 | "compatibility",
1307 | "polyfill",
1308 | "portable",
1309 | "shim"
1310 | ],
1311 | "support": {
1312 | "source": "https://github.com/symfony/polyfill-php81/tree/v1.25.0"
1313 | },
1314 | "funding": [
1315 | {
1316 | "url": "https://symfony.com/sponsor",
1317 | "type": "custom"
1318 | },
1319 | {
1320 | "url": "https://github.com/fabpot",
1321 | "type": "github"
1322 | },
1323 | {
1324 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1325 | "type": "tidelift"
1326 | }
1327 | ],
1328 | "time": "2021-09-13T13:58:11+00:00"
1329 | },
1330 | {
1331 | "name": "webmozart/assert",
1332 | "version": "1.10.0",
1333 | "source": {
1334 | "type": "git",
1335 | "url": "https://github.com/webmozarts/assert.git",
1336 | "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25"
1337 | },
1338 | "dist": {
1339 | "type": "zip",
1340 | "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25",
1341 | "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25",
1342 | "shasum": ""
1343 | },
1344 | "require": {
1345 | "php": "^7.2 || ^8.0",
1346 | "symfony/polyfill-ctype": "^1.8"
1347 | },
1348 | "conflict": {
1349 | "phpstan/phpstan": "<0.12.20",
1350 | "vimeo/psalm": "<4.6.1 || 4.6.2"
1351 | },
1352 | "require-dev": {
1353 | "phpunit/phpunit": "^8.5.13"
1354 | },
1355 | "type": "library",
1356 | "extra": {
1357 | "branch-alias": {
1358 | "dev-master": "1.10-dev"
1359 | }
1360 | },
1361 | "autoload": {
1362 | "psr-4": {
1363 | "Webmozart\\Assert\\": "src/"
1364 | }
1365 | },
1366 | "notification-url": "https://packagist.org/downloads/",
1367 | "license": [
1368 | "MIT"
1369 | ],
1370 | "authors": [
1371 | {
1372 | "name": "Bernhard Schussek",
1373 | "email": "bschussek@gmail.com"
1374 | }
1375 | ],
1376 | "description": "Assertions to validate method input/output with nice error messages.",
1377 | "keywords": [
1378 | "assert",
1379 | "check",
1380 | "validate"
1381 | ],
1382 | "support": {
1383 | "issues": "https://github.com/webmozarts/assert/issues",
1384 | "source": "https://github.com/webmozarts/assert/tree/1.10.0"
1385 | },
1386 | "time": "2021-03-09T10:59:23+00:00"
1387 | },
1388 | {
1389 | "name": "webmozart/path-util",
1390 | "version": "2.3.0",
1391 | "source": {
1392 | "type": "git",
1393 | "url": "https://github.com/webmozart/path-util.git",
1394 | "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725"
1395 | },
1396 | "dist": {
1397 | "type": "zip",
1398 | "url": "https://api.github.com/repos/webmozart/path-util/zipball/d939f7edc24c9a1bb9c0dee5cb05d8e859490725",
1399 | "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725",
1400 | "shasum": ""
1401 | },
1402 | "require": {
1403 | "php": ">=5.3.3",
1404 | "webmozart/assert": "~1.0"
1405 | },
1406 | "require-dev": {
1407 | "phpunit/phpunit": "^4.6",
1408 | "sebastian/version": "^1.0.1"
1409 | },
1410 | "type": "library",
1411 | "extra": {
1412 | "branch-alias": {
1413 | "dev-master": "2.3-dev"
1414 | }
1415 | },
1416 | "autoload": {
1417 | "psr-4": {
1418 | "Webmozart\\PathUtil\\": "src/"
1419 | }
1420 | },
1421 | "notification-url": "https://packagist.org/downloads/",
1422 | "license": [
1423 | "MIT"
1424 | ],
1425 | "authors": [
1426 | {
1427 | "name": "Bernhard Schussek",
1428 | "email": "bschussek@gmail.com"
1429 | }
1430 | ],
1431 | "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.",
1432 | "support": {
1433 | "issues": "https://github.com/webmozart/path-util/issues",
1434 | "source": "https://github.com/webmozart/path-util/tree/2.3.0"
1435 | },
1436 | "abandoned": "symfony/filesystem",
1437 | "time": "2015-12-17T08:42:14+00:00"
1438 | }
1439 | ],
1440 | "packages-dev": [],
1441 | "aliases": [],
1442 | "minimum-stability": "stable",
1443 | "stability-flags": [],
1444 | "prefer-stable": false,
1445 | "prefer-lowest": false,
1446 | "platform": [],
1447 | "platform-dev": [],
1448 | "plugin-api-version": "2.3.0"
1449 | }
1450 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Async/Thenable.php:
--------------------------------------------------------------------------------
1 | state) {
54 | $this->onResolve[] = $onResolve;
55 | if (null !== $onReject) {
56 | $this->onReject[] = $onReject;
57 | }
58 | } elseif (self::RESOLVED === $this->state) {
59 | $onResolve($this->value);
60 | } elseif (null !== $onReject) {
61 | $onReject($this->exception);
62 | }
63 | }
64 |
65 | /**
66 | * Creates a new Thenable using a Closure like a JavaScript Promise.
67 | *
68 | * Like in JavaScript, the $closure will be called immediately, and
69 | * the returned Thenable will start in a PENDING state.
70 | *
71 | * @phpstan-template V
72 | * @phpstan-template T of Throwable
73 | * @phpstan-param Closure(Closure(V): void, Closure(T): void): void $closure
74 | * @phpstan-return Thenable
75 | */
76 | public static function promise(Closure $closure): self
77 | {
78 | /** @phpstan-var Thenable $self */
79 | $self = new self();
80 | $closure(
81 | function (mixed $value) use ($self): void {
82 | if (self::PENDING !== $self->state) {
83 | $ending = match ($self->state) {
84 | self::RESOLVED => 'resolved!',
85 | self::REJECTED => 'rejected!',
86 | };
87 | throw new LogicException('Attempt to resolve a promise that has already been '.$ending);
88 | }
89 |
90 | $self->value = $value;
91 | $self->state = self::RESOLVED;
92 | foreach ($self->onResolve as $onResolve) {
93 | $onResolve($value);
94 | }
95 | },
96 | function (Throwable $exception) use ($self): void {
97 | if (self::PENDING !== $self->state) {
98 | $ending = match ($self->state) {
99 | self::RESOLVED => 'resolved!',
100 | self::REJECTED => 'rejected!',
101 | };
102 | throw new LogicException('Attempt to resolve a promise that has already been '.$ending);
103 | }
104 |
105 | $self->exception = $exception;
106 | $self->state = self::REJECTED;
107 | if (0 === count($self->onReject)) {
108 | throw new UnhandledAsyncException("An asynchronous exception wasn't caught!", previous: $exception);
109 | }
110 | foreach ($self->onReject as $onReject) {
111 | $onReject($exception);
112 | }
113 | }
114 | );
115 |
116 | return $self;
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Async/UnhandledAsyncException.php:
--------------------------------------------------------------------------------
1 | .
36 | *
37 | * @param string $name the name of the parameter
38 | *
39 | * @return ?string null if not applicable
40 | */
41 | public function toUsageComponent(string $name): ?string;
42 |
43 | /**
44 | * Create a CommandParameter, registering any command enums if needed.
45 | *
46 | * @param string $name the name of the parameter
47 | *
48 | * @return ?CommandParameter null if not applicable
49 | */
50 | public function toCommandParameter(string $name): ?CommandParameter;
51 | }
52 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Arg/ArgumentStack.php:
--------------------------------------------------------------------------------
1 | args[$this->cursor + $lookahead] ?? null;
39 | }
40 |
41 | /**
42 | * Peek at the current element or a future element
43 | * without modifying the stack. On failure, throws an
44 | * ExtractionFailed exception.
45 | *
46 | * @param int $lookahead a positive number (including zero)
47 | *
48 | * @throws ExtractionFailed
49 | */
50 | public function peek(string|Translatable $failMessage, int $lookahead = 0): string
51 | {
52 | if ($lookahead < 0) {
53 | throw new InvalidArgumentException('The lookahead must be greater than one!');
54 | }
55 |
56 | return $this->args[$this->cursor + $lookahead] ??
57 | throw new ExtractionFailed($failMessage);
58 | }
59 |
60 | /**
61 | * Get the current element and
62 | * advance to the next element in the stack.
63 | * On failure, throws an ExtractionFailed exception.
64 | *
65 | * @throws ExtractionFailed
66 | */
67 | public function tryPop(): ?string
68 | {
69 | return $this->args[$this->cursor++] ?? null;
70 | }
71 |
72 | /**
73 | * Get the current element and
74 | * advance to the next element in the stack.
75 | * On failure, throws an ExtractionFailed exception.
76 | *
77 | * @throws ExtractionFailed
78 | */
79 | public function pop(string|Translatable $failMessage): string
80 | {
81 | return $this->args[$this->cursor++] ??
82 | throw new ExtractionFailed($failMessage);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Arg/ExtractionFailed.php:
--------------------------------------------------------------------------------
1 | getText());
15 | }
16 |
17 | public function getTranslatable(): string|Translatable
18 | {
19 | return $this->translatable;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Arg/SetParameterTrait.php:
--------------------------------------------------------------------------------
1 | optional = $parameter->getType()?->allowsNull() ?? false;
18 | $this->parameter = $parameter;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Arg/bool_arg.php:
--------------------------------------------------------------------------------
1 |
20 | */
21 | #[Attribute(
22 | Attribute::IS_REPEATABLE |
23 | Attribute::TARGET_METHOD
24 | )]
25 | final class bool_arg implements Arg
26 | {
27 | use SetParameterTrait;
28 |
29 | public function extract(CommandContext $context, ArgumentStack $args): ?bool
30 | {
31 | $component = $this->toUsageComponent($this->parameter->getName());
32 | if ($this->optional) {
33 | $value = $args->tryPop();
34 | if (null === $value) {
35 | return null;
36 | }
37 | } else {
38 | $value = $args->pop("Required argument $component");
39 | }
40 |
41 | return match ($value) {
42 | 'true', 'on', 'yes' => true,
43 | 'false', 'off', 'no' => false,
44 | default => throw new ExtractionFailed("$value does not satisfy $component"),
45 | };
46 | }
47 |
48 | public function toUsageComponent(string $name): ?string
49 | {
50 | if ($this->optional) {
51 | return "[$name: true|false]";
52 | } else {
53 | return "<$name: true|false>";
54 | }
55 | }
56 |
57 | public function toCommandParameter(string $name): ?CommandParameter
58 | {
59 | return CommandParameter::enum($name, new CommandEnum('bool', ['true', 'false']), 0, $this->optional);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Arg/command_arg.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | #[Attribute(
19 | Attribute::IS_REPEATABLE |
20 | Attribute::TARGET_METHOD
21 | )]
22 | final class command_arg implements Arg
23 | {
24 | use SetParameterTrait;
25 |
26 | public function extract(CommandContext $context, ArgumentStack $args): ?string
27 | {
28 | if ($this->optional) {
29 | return $args->tryPop();
30 | } else {
31 | $component = $this->toUsageComponent($this->parameter->getName());
32 |
33 | return $args->pop("Required argument $component");
34 | }
35 | }
36 |
37 | public function toUsageComponent(string $name): ?string
38 | {
39 | if ($this->optional) {
40 | return "[$name: command]";
41 | } else {
42 | return "<$name: command>";
43 | }
44 | }
45 |
46 | public function toCommandParameter(string $name): ?CommandParameter
47 | {
48 | return CommandParameter::standard($name, ACP::ARG_TYPE_COMMAND, 0, $this->optional);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Arg/enum.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | #[Attribute(
18 | Attribute::IS_REPEATABLE |
19 | Attribute::TARGET_METHOD
20 | )]
21 | final class enum implements Arg
22 | {
23 | use SetParameterTrait;
24 | /** @var string[] */
25 | private array $choices;
26 | /** @var bool[] */
27 | private array $choiceSet;
28 |
29 | public function __construct(
30 | private string $name,
31 | string $choice,
32 | string ...$otherChoices,
33 | ) {
34 | $this->choices = [$choice, ...$otherChoices];
35 | $this->choiceSet = [];
36 | foreach ($this->choices as $c) {
37 | $this->choiceSet[$c] = true;
38 | }
39 | }
40 |
41 | public function extract(CommandContext $context, ArgumentStack $args): ?string
42 | {
43 | $component = $this->toUsageComponent($this->parameter->getName());
44 | if ($this->optional) {
45 | $choice = $args->tryPop();
46 | if (null === $choice) {
47 | return null;
48 | }
49 | } else {
50 | $choice = $args->pop("Required argument $component");
51 | }
52 | if (isset($this->choiceSet[$choice])) {
53 | return $choice;
54 | }
55 | throw new ExtractionFailed("$choice does not satisfy $component");
56 | }
57 |
58 | public function toUsageComponent(string $name): ?string
59 | {
60 | $choicesString = implode('|', $this->choices);
61 |
62 | if ($this->optional) {
63 | return "[$name: $choicesString]";
64 | } else {
65 | return "<$name: $choicesString>";
66 | }
67 | }
68 |
69 | public function toCommandParameter(string $name): ?CommandParameter
70 | {
71 | return CommandParameter::enum(
72 | $name,
73 | new CommandEnum($this->name, $this->choices),
74 | 0,
75 | );
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Arg/float_arg.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | #[Attribute(
18 | Attribute::IS_REPEATABLE |
19 | Attribute::TARGET_METHOD
20 | )]
21 | final class float_arg implements Arg
22 | {
23 | use SetParameterTrait;
24 |
25 | public function extract(CommandContext $context, ArgumentStack $args): ?float
26 | {
27 | $component = $this->toUsageComponent($this->parameter->getName());
28 | if ($this->optional) {
29 | $value = $args->tryPop();
30 | if (null === $value) {
31 | return null;
32 | }
33 | } else {
34 | $value = $args->pop("Required argument $component");
35 | }
36 | if (is_numeric($value)) {
37 | return (float) $value;
38 | }
39 | throw new ExtractionFailed("$value does not satisfy $component");
40 | }
41 |
42 | public function toUsageComponent(string $name): ?string
43 | {
44 | if ($this->optional) {
45 | return "[$name: float]";
46 | } else {
47 | return "<$name: float>";
48 | }
49 | }
50 |
51 | public function toCommandParameter(string $name): ?CommandParameter
52 | {
53 | return CommandParameter::standard($name, ACP::ARG_TYPE_FLOAT, 0, $this->optional);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Arg/int_arg.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | #[Attribute(
18 | Attribute::IS_REPEATABLE |
19 | Attribute::TARGET_METHOD
20 | )]
21 | final class int_arg implements Arg
22 | {
23 | use SetParameterTrait;
24 |
25 | public function extract(CommandContext $context, ArgumentStack $args): ?int
26 | {
27 | $component = $this->toUsageComponent($this->parameter->getName());
28 | if ($this->optional) {
29 | $value = $args->tryPop();
30 | if (null === $value) {
31 | return null;
32 | }
33 | } else {
34 | $value = $args->pop("Required argument $component");
35 | }
36 | if (is_numeric($value)) {
37 | return (int) $value;
38 | }
39 | throw new ExtractionFailed("$value does not satisfy $component");
40 | }
41 |
42 | public function toUsageComponent(string $name): ?string
43 | {
44 | if ($this->optional) {
45 | return "[$name: int]";
46 | } else {
47 | return "<$name: int>";
48 | }
49 | }
50 |
51 | public function toCommandParameter(string $name): ?CommandParameter
52 | {
53 | return CommandParameter::standard($name, ACP::ARG_TYPE_INT, 0, $this->optional);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Arg/json_arg.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | #[Attribute(
19 | Attribute::IS_REPEATABLE |
20 | Attribute::TARGET_METHOD
21 | )]
22 | final class json_arg implements Arg
23 | {
24 | use SetParameterTrait;
25 |
26 | public function extract(CommandContext $context, ArgumentStack $args): ?string
27 | {
28 | if ($this->optional) {
29 | return $args->tryPop();
30 | } else {
31 | $component = $this->toUsageComponent($this->parameter->getName());
32 |
33 | return $args->pop("Required argument $component");
34 | }
35 | }
36 |
37 | public function toUsageComponent(string $name): ?string
38 | {
39 | if ($this->optional) {
40 | return "[$name: json]";
41 | } else {
42 | return "<$name: json>";
43 | }
44 | }
45 |
46 | public function toCommandParameter(string $name): ?CommandParameter
47 | {
48 | return CommandParameter::standard($name, ACP::ARG_TYPE_JSON, 0, $this->optional);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Arg/player_arg.php:
--------------------------------------------------------------------------------
1 |
21 | */
22 | #[Attribute(
23 | Attribute::IS_REPEATABLE |
24 | Attribute::TARGET_METHOD
25 | )]
26 | final class player_arg implements Arg
27 | {
28 | use SetParameterTrait;
29 |
30 | /**
31 | * @param bool $exact whether to not match by prefix
32 | */
33 | public function __construct(
34 | private bool $exact = false,
35 | ) {
36 | }
37 |
38 | public function extract(CommandContext $context, ArgumentStack $args): ?PmPlayer
39 | {
40 | $component = $this->toUsageComponent($this->parameter->getName());
41 | if ($this->optional) {
42 | $name = $args->tryPop();
43 | if (null === $name) {
44 | return null;
45 | }
46 | } else {
47 | $name = $args->pop("Required argument $component");
48 | }
49 | if ($this->exact) {
50 | $player = Server::getInstance()->getPlayerExact($name);
51 | } else {
52 | $player = Server::getInstance()->getPlayerByPrefix($name);
53 | }
54 |
55 | return $player ?? throw new ExtractionFailed(KnownTranslationFactory::commands_generic_player_notFound());
56 | }
57 |
58 | public function toUsageComponent(string $name): ?string
59 | {
60 | if ($this->optional) {
61 | return "[$name: target]";
62 | } else {
63 | return "<$name: target>";
64 | }
65 | }
66 |
67 | public function toCommandParameter(string $name): ?CommandParameter
68 | {
69 | return CommandParameter::standard($name, ACP::ARG_TYPE_TARGET, 0, $this->optional);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Arg/remaining.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | #[Attribute(
18 | Attribute::IS_REPEATABLE |
19 | Attribute::TARGET_METHOD
20 | )]
21 | final class remaining implements Arg
22 | {
23 | use SetParameterTrait;
24 |
25 | /**
26 | * @return string[]
27 | */
28 | public function extract(CommandContext $context, ArgumentStack $args): array
29 | {
30 | $collected = [];
31 | while (true) {
32 | $popped = $args->tryPop();
33 | if (null === $popped) {
34 | break;
35 | }
36 | $collected[] = $popped;
37 | }
38 |
39 | return $collected;
40 | }
41 |
42 | public function toUsageComponent(string $name): ?string
43 | {
44 | return "[$name: text]";
45 | }
46 |
47 | public function toCommandParameter(string $name): ?CommandParameter
48 | {
49 | return CommandParameter::standard($name, ACP::ARG_TYPE_RAWTEXT, 0, true);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Arg/sender.php:
--------------------------------------------------------------------------------
1 |
21 | */
22 | #[Attribute(
23 | Attribute::IS_REPEATABLE |
24 | Attribute::TARGET_METHOD
25 | )]
26 | final class sender implements Arg
27 | {
28 | use SetParameterTrait;
29 | private bool $player;
30 |
31 | public function setParameter(ReflectionParameter $parameter): void
32 | {
33 | $type = $parameter->getType();
34 | if (
35 | !$type instanceof ReflectionNamedType ||
36 | (CommandSender::class !== $type->getName() &&
37 | Player::class !== $type->getName()
38 | )
39 | ) {
40 | $name = $parameter->getName();
41 | throw new InvalidArgumentException("The parameter ($name) corresponding to `sender()` Arg must have a type of either CommandSender or Player!");
42 | }
43 |
44 | $this->player = Player::class === $type->getName();
45 | $this->parameter = $parameter;
46 | }
47 |
48 | public function extract(CommandContext $context, ArgumentStack $args): CommandSender
49 | {
50 | $sender = $context->sender();
51 | if ($this->player && !$sender instanceof Player) {
52 | throw new ExtractionFailed('You must be a player to run this command!');
53 | }
54 |
55 | return $sender;
56 | }
57 |
58 | public function toUsageComponent(string $name): ?string
59 | {
60 | return null;
61 | }
62 |
63 | public function toCommandParameter(string $name): ?CommandParameter
64 | {
65 | return null;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Arg/text.php:
--------------------------------------------------------------------------------
1 |
19 | */
20 | #[Attribute(
21 | Attribute::IS_REPEATABLE |
22 | Attribute::TARGET_METHOD
23 | )]
24 | final class text implements Arg
25 | {
26 | use SetParameterTrait;
27 |
28 | /**
29 | * @param int $count number of arguments to match
30 | * @param bool $require whether to fail when less
31 | * than $count arguments remain
32 | */
33 | public function __construct(
34 | private int $count = 1,
35 | private bool $require = true,
36 | ) {
37 | if ($count <= 0) {
38 | throw new InvalidArgumentException('Count must greater than zero!');
39 | }
40 | }
41 |
42 | public function setParameter(ReflectionParameter $parameter): void
43 | {
44 | $type = $parameter->getType();
45 | if ($type instanceof ReflectionNamedType) {
46 | if (
47 | 1 === $this->count && $this->require &&
48 | ('string' !== $type->getName() || $type->allowsNull())
49 | ) {
50 | $name = $parameter->getName();
51 | throw new InvalidArgumentException("The corresponding parameter ($name) to `text()` must have the type `string` (hint: `?string` is invalid)!");
52 | } elseif (
53 | 1 === $this->count && !$this->require &&
54 | ('string' !== $type->getName() || !$type->allowsNull())
55 | ) {
56 | $name = $parameter->getName();
57 | throw new InvalidArgumentException("The corresponding parameter ($name) to `text()` must have the type `?string` (hint: `string` is invalid)!");
58 | } elseif (
59 | 1 !== $this->count &&
60 | ('array' !== $type->getName() || $type->allowsNull())
61 | ) {
62 | $name = $parameter->getName();
63 | throw new InvalidArgumentException("The corresponding parameter ($name) to `text()` must have the type `array` (hint: `?array` is invalid)!");
64 | }
65 | }
66 | $this->parameter = $parameter;
67 | }
68 |
69 | /**
70 | * @return string|string[]|null
71 | */
72 | public function extract(CommandContext $context, ArgumentStack $args): null|string|array
73 | {
74 | if (1 === $this->count && $this->require) {
75 | $component = $this->toUsageComponent($this->parameter->getName());
76 |
77 | return $args->pop("Required argument $component");
78 | } elseif (1 === $this->count) {
79 | return $args->tryPop();
80 | }
81 |
82 | if ($this->require) {
83 | $collected = [];
84 | while (count($collected) < $this->count) {
85 | $component = $this->toUsageComponent($this->parameter->getName());
86 | $collected[] = $args->pop("$component is not satisfied");
87 | }
88 |
89 | return $collected;
90 | } else {
91 | $collected = [];
92 | while (count($collected) < $this->count) {
93 | $popped = $args->tryPop();
94 | if (null === $popped) {
95 | break;
96 | }
97 | $collected[] = $popped;
98 | }
99 |
100 | return $collected;
101 | }
102 | }
103 |
104 | public function toUsageComponent(string $name): ?string
105 | {
106 | if ($this->require) {
107 | return "<$name: text>";
108 | } else {
109 | return "[$name: text]";
110 | }
111 | }
112 |
113 | public function toCommandParameter(string $name): ?CommandParameter
114 | {
115 | return CommandParameter::standard($name, ACP::ARG_TYPE_RAWTEXT, 0, !$this->require);
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Arg/vector_arg.php:
--------------------------------------------------------------------------------
1 |
22 | */
23 | #[Attribute(
24 | Attribute::IS_REPEATABLE |
25 | Attribute::TARGET_METHOD
26 | )]
27 | final class vector_arg implements Arg
28 | {
29 | use SetParameterTrait;
30 |
31 | private bool $optional;
32 | private bool $relative;
33 |
34 | public function setParameter(ReflectionParameter $parameter): void
35 | {
36 | $type = $parameter->getType();
37 | if (
38 | !$type instanceof ReflectionNamedType ||
39 | (Vector3::class !== $type->getName() &&
40 | RelativeVector3::class !== $type->getName()
41 | )
42 | ) {
43 | $name = $parameter->getName();
44 | throw new InvalidArgumentException("The parameter ($name) corresponding to `sender()` Arg must have a type of either CommandSender or Player!");
45 | }
46 |
47 | $this->optional = $type->allowsNull();
48 | $this->relative = RelativeVector3::class === $type->getName();
49 | $this->parameter = $parameter;
50 | }
51 |
52 | public function extract(CommandContext $context, ArgumentStack $args): Vector3|RelativeVector3|null
53 | {
54 | $component = $this->toUsageComponent($this->parameter->getName());
55 | if ($this->optional) {
56 | $x = $args->tryPop();
57 | $y = $args->tryPop();
58 | $z = $args->tryPop();
59 | if (null === $x || null === $y || null === $z) {
60 | return null;
61 | }
62 | } else {
63 | $x = $args->pop("Required argument $component");
64 | $y = $args->pop("Required argument $component");
65 | $z = $args->pop("Required argument $component");
66 | }
67 |
68 | if ($this->relative) {
69 | [$vX, $oX] = $this->extractRelativeNumber($x);
70 | [$vY, $oY] = $this->extractRelativeNumber($y);
71 | [$vZ, $oZ] = $this->extractRelativeNumber($z);
72 |
73 | return new RelativeVector3($vX, $vY, $vZ, $oX, $oY, $oZ);
74 | }
75 |
76 | $vX = $this->extractNumber($x);
77 | $vY = $this->extractNumber($y);
78 | $vZ = $this->extractNumber($z);
79 |
80 | return new Vector3($vX, $vY, $vZ);
81 | }
82 |
83 | private function extractNumber(string $numeric, ?string $arg = null): float
84 | {
85 | if (!is_numeric($numeric)) {
86 | $arg ??= $numeric;
87 | $component = $this->toUsageComponent($this->parameter->getName());
88 | throw new ExtractionFailed("$arg does not satisfy $component");
89 | }
90 |
91 | return (float) $numeric;
92 | }
93 |
94 | /**
95 | * @phpstan-return array{float, bool}
96 | */
97 | private function extractRelativeNumber(string $raw): array
98 | {
99 | $isOffset = false;
100 | $numeric = $raw;
101 | if (str_starts_with($raw, '~')) {
102 | $isOffset = true;
103 | if ('~' === $raw) {
104 | return [0, true];
105 | }
106 | $numeric = substr($raw, 1, strlen($raw) - 1);
107 | }
108 |
109 | return [$this->extractNumber($numeric, $raw), $isOffset];
110 | }
111 |
112 | public function toUsageComponent(string $name): ?string
113 | {
114 | if ($this->optional) {
115 | return "<$name: x y z>";
116 | } else {
117 | return "[$name: x y z]";
118 | }
119 | }
120 |
121 | public function toCommandParameter(string $name): ?CommandParameter
122 | {
123 | return CommandParameter::standard($name, ACP::ARG_TYPE_POSITION, 0, $this->optional);
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/BoundCommand.php:
--------------------------------------------------------------------------------
1 | setPermission($permission);
38 | $this->map = new HandlerMethodTree();
39 | $this->overloads = new OverloadMap($name);
40 | }
41 |
42 | /**
43 | * @param string[] $subNames
44 | */
45 | public function attach(array $subNames, HandlerMethod $handlerMethod): void
46 | {
47 | $this->map->add($subNames, $handlerMethod);
48 | $this->overloads->add($subNames, $handlerMethod);
49 | $this->setUsage($this->overloads->getUsage());
50 | }
51 |
52 | public function execute(CommandSender $sender, string $commandLabel, array $args): void
53 | {
54 | $newArgs = [];
55 | $handlerMethod = $this->map->getMostNested($args, $newArgs);
56 | if (null === $handlerMethod) {
57 | $message = $sender->getLanguage()->translate(
58 | KnownTranslationFactory::commands_generic_usage($this->getUsage())
59 | );
60 | $sender->sendMessage(TF::RED.$message);
61 |
62 | return;
63 | }
64 | $handlerMethod->invoke(new CommandContext($sender, $newArgs));
65 | }
66 |
67 | /**
68 | * @return CommandParameter[][]
69 | */
70 | public function getOverloads(): array
71 | {
72 | return $this->overloads->getOverloads();
73 | }
74 |
75 | public function getOwningPlugin(): Plugin
76 | {
77 | return $this->plugin;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Cmd.php:
--------------------------------------------------------------------------------
1 | subNames = $subNames;
26 | }
27 |
28 | public function name(): string
29 | {
30 | return $this->name;
31 | }
32 |
33 | /**
34 | * @return string[]
35 | */
36 | public function subNames(): array
37 | {
38 | return $this->subNames;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/CmdConfig.php:
--------------------------------------------------------------------------------
1 | name;
33 | }
34 |
35 | public function description(): string
36 | {
37 | return $this->description;
38 | }
39 |
40 | /**
41 | * @return string[]
42 | */
43 | public function aliases(): array
44 | {
45 | return $this->aliases;
46 | }
47 |
48 | public function permission(): ?string
49 | {
50 | return $this->permission;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/CommandContext.php:
--------------------------------------------------------------------------------
1 | sender;
27 | }
28 |
29 | /**
30 | * @return string[]
31 | */
32 | public function args(): array
33 | {
34 | return $this->args;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/CommandHintListener.php:
--------------------------------------------------------------------------------
1 | getPackets() as $pk) {
20 | if (!$pk instanceof AvailableCommandsPacket) {
21 | continue;
22 | }
23 |
24 | foreach (array_keys($pk->commandData) as $name) {
25 | $command = Server::getInstance()->getCommandMap()->getCommand($name);
26 | if (!$command instanceof BoundCommand) {
27 | continue;
28 | }
29 |
30 | $pk->commandData[$name]->overloads = $command->getOverloads();
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/Guard/Guard.php:
--------------------------------------------------------------------------------
1 | permissions = [$permission, ...$otherPermissions];
25 |
26 | foreach ($this->permissions as $perm) {
27 | if (null === PermissionManager::getInstance()->getPermission($perm)) {
28 | throw new InvalidArgumentException("Cannot use non-existing permission \"$perm\"");
29 | }
30 | }
31 | }
32 |
33 | public function passes(CommandContext $context): null|string|Translatable
34 | {
35 | foreach ($this->permissions as $permission) {
36 | if (!$context->sender()->hasPermission($permission)) {
37 | return KnownTranslationFactory::commands_generic_permission();
38 | }
39 | }
40 |
41 | return null;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/HandlerMethod.php:
--------------------------------------------------------------------------------
1 | guards as $guard) {
39 | if (null !== ($message = $guard->passes($context))) {
40 | if ($message instanceof Translatable) {
41 | $message = $context->sender()->getLanguage()->translate($message);
42 | }
43 | $context->sender()->sendMessage(TF::RED.$message);
44 |
45 | return;
46 | }
47 | }
48 |
49 | $stack = new ArgumentStack($context->args());
50 | $parameters = [];
51 | try {
52 | foreach ($this->args as $arg) {
53 | $parameters[] = $arg->extract($context, $stack);
54 | }
55 | } catch (ExtractionFailed $e) {
56 | $message = $e->getTranslatable();
57 | if ($message instanceof Translatable) {
58 | $message = $context->sender()->getLanguage()->translate($message);
59 | }
60 | $context->sender()->sendMessage(TF::RED.$message);
61 |
62 | return;
63 | }
64 |
65 | $generator = $this->method->invokeArgs($this->handler, $parameters);
66 | if ($generator instanceof Generator) {
67 | Await::g2c($generator);
68 | }
69 | }
70 |
71 | /**
72 | * @return Arg[] parameter name => Arg
73 | * @phpstan-ignore-next-line generics
74 | */
75 | public function getArgs(): array
76 | {
77 | $argCount = count($this->args);
78 | $params = $this->method->getParameters();
79 |
80 | $args = [];
81 | for ($index = 0; $index < $argCount; ++$index) {
82 | $args[$params[$index]->getName()] = $this->args[$index];
83 | }
84 |
85 | return $args;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/HandlerMethodTree.php:
--------------------------------------------------------------------------------
1 | root = $handlerMethod;
24 |
25 | return;
26 | }
27 |
28 | $name = array_shift($subNames);
29 | if (!isset($this->children[$name])) {
30 | $this->children[$name] = new self();
31 | }
32 | $this->children[$name]->add($subNames, $handlerMethod);
33 | }
34 |
35 | /**
36 | * @param string[] $subNames
37 | * @param string[] $unusedNames
38 | */
39 | public function getMostNested(array $subNames, array &$unusedNames): ?HandlerMethod
40 | {
41 | if (0 === count($subNames)) {
42 | $unusedNames = [];
43 |
44 | return $this->root;
45 | }
46 |
47 | $name = array_shift($subNames);
48 | $child = $this->children[$name] ?? null;
49 | $fetched = null === $child ? null : $child->getMostNested($subNames, $unusedNames);
50 |
51 | if (null === $fetched && null !== $this->root) {
52 | $unusedNames = [$name, ...$subNames];
53 |
54 | return $this->root;
55 | }
56 |
57 | return $fetched;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Command/OverloadMap.php:
--------------------------------------------------------------------------------
1 | usage)) {
30 | $this->usage .= ' OR ';
31 | }
32 | $this->usage .= "/$this->name ";
33 | $subNamesString = implode(' ', $subNames);
34 | if (strlen($subNamesString) > 0) {
35 | $this->usage .= "$subNamesString ";
36 | }
37 |
38 | $overload = [];
39 | foreach ($subNames as $index => $subName) {
40 | $overload[] = CommandParameter::enum(
41 | $subName,
42 | new CommandEnum("param-$index-$subName", [$subName]),
43 | CommandParameter::FLAG_FORCE_COLLAPSE_ENUM,
44 | );
45 | }
46 |
47 | foreach ($handlerMethod->getArgs() as $name => $arg) {
48 | $usageComponent = $arg->toUsageComponent($name);
49 | if (null !== $usageComponent) {
50 | $this->usage .= "$usageComponent ";
51 | }
52 | $parameter = $arg->toCommandParameter($name);
53 | if (null !== $parameter) {
54 | $overload[] = $parameter;
55 | }
56 | }
57 | $this->usage = rtrim($this->usage);
58 | $this->overloads[] = $overload;
59 | }
60 |
61 | /**
62 | * @return CommandParameter[][]
63 | */
64 | public function getOverloads(): array
65 | {
66 | return $this->overloads;
67 | }
68 |
69 | public function getUsage(): string
70 | {
71 | return $this->usage;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Form/CustomFormElement/CustomFormElement.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | #[Attribute(Attribute::TARGET_PROPERTY)]
14 | final class Dropdown implements CustomFormElement
15 | {
16 | /**
17 | * @param array $options
18 | * @param bool $allowDefault whether the player may skip filling
19 | * out a dropdown when its default
20 | * value is set out of range (ex: -1)
21 | */
22 | public function __construct(
23 | private string $text,
24 | private array $options,
25 | private int $defaultOption = 0,
26 | private bool $allowDefault = true,
27 | ) {
28 | }
29 |
30 | public function extract(mixed $data): int
31 | {
32 | if (
33 | (!is_int($data) || !isset($this->options[$data])) &&
34 | !($data === $this->defaultOption && $this->allowDefault)
35 | ) {
36 | throw new FormValidationException('Invalid response to Dropdown element!');
37 | }
38 |
39 | return $data;
40 | }
41 |
42 | public function jsonSerialize(): mixed
43 | {
44 | return [
45 | 'type' => 'dropdown',
46 | 'text' => $this->text,
47 | 'options' => $this->options,
48 | 'default' => $this->defaultOption,
49 | ];
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Form/CustomFormElement/Input.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | #[Attribute(Attribute::TARGET_PROPERTY)]
14 | final class Input implements CustomFormElement
15 | {
16 | public function __construct(
17 | private string $text,
18 | private string $placeholder = '',
19 | private string $default = '',
20 | ) {
21 | }
22 |
23 | public function extract(mixed $data): string
24 | {
25 | if (!is_string($data)) {
26 | throw new FormValidationException('Invalid response to Input element!');
27 | }
28 |
29 | return $data;
30 | }
31 |
32 | public function jsonSerialize(): mixed
33 | {
34 | return [
35 | 'type' => 'input',
36 | 'text' => $this->text,
37 | 'placeholder' => $this->placeholder,
38 | 'default' => $this->default,
39 | ];
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Form/CustomFormElement/Label.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | #[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
14 | final class Label implements CustomFormElement
15 | {
16 | public function __construct(
17 | private string $text,
18 | ) {
19 | }
20 |
21 | public function extract(mixed $data): mixed
22 | {
23 | if (null !== $data) {
24 | throw new FormValidationException('Invalid response to Label element!');
25 | }
26 |
27 | return null;
28 | }
29 |
30 | public function jsonSerialize(): mixed
31 | {
32 | return [
33 | 'type' => 'label',
34 | 'text' => $this->text,
35 | ];
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Form/CustomFormElement/Slider.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | #[Attribute(Attribute::TARGET_PROPERTY)]
14 | final class Slider implements CustomFormElement
15 | {
16 | public function __construct(
17 | private string $text,
18 | private float $min,
19 | private float $max,
20 | private float $step = 1.0,
21 | private ?float $default = null,
22 | ) {
23 | }
24 |
25 | public function extract(mixed $data): float
26 | {
27 | if ((!is_float($data) && !is_int($data)) || $data < $this->min || $data > $this->max) {
28 | throw new FormValidationException('Invalid response to Slider element!');
29 | }
30 |
31 | return (float) $data;
32 | }
33 |
34 | public function jsonSerialize(): mixed
35 | {
36 | return [
37 | 'type' => 'slider',
38 | 'text' => $this->text,
39 | 'min' => $this->min,
40 | 'max' => $this->max,
41 | 'step' => $this->step,
42 | 'default' => $this->default ?? $this->min,
43 | ];
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Form/CustomFormElement/StepSlider.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | #[Attribute(Attribute::TARGET_PROPERTY)]
14 | final class StepSlider implements CustomFormElement
15 | {
16 | /**
17 | * @param array $steps
18 | */
19 | public function __construct(
20 | private string $text,
21 | private array $steps,
22 | private int $defaultOption = 0,
23 | ) {
24 | }
25 |
26 | public function extract(mixed $data): int
27 | {
28 | if (!is_int($data) || !isset($this->steps[$data])) {
29 | throw new FormValidationException('Invalid response to StepSlider element!');
30 | }
31 |
32 | return $data;
33 | }
34 |
35 | public function jsonSerialize(): mixed
36 | {
37 | return [
38 | 'type' => 'step_slider',
39 | 'text' => $this->text,
40 | 'steps' => $this->steps,
41 | 'default' => $this->defaultOption,
42 | ];
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Form/CustomFormElement/Toggle.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | #[Attribute(Attribute::TARGET_PROPERTY)]
14 | final class Toggle implements CustomFormElement
15 | {
16 | public function __construct(
17 | private string $text,
18 | private bool $default = false,
19 | ) {
20 | }
21 |
22 | public function extract(mixed $data): bool
23 | {
24 | if (!is_bool($data)) {
25 | throw new FormValidationException('Invalid response to Toggle element!');
26 | }
27 |
28 | return $data;
29 | }
30 |
31 | public function jsonSerialize(): mixed
32 | {
33 | return [
34 | 'type' => 'toggle',
35 | 'text' => $this->text,
36 | 'default' => $this->default,
37 | ];
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Form/CustomFormResultTrait.php:
--------------------------------------------------------------------------------
1 |
40 | */
41 | public static function custom2then(Player $player, string $title): Thenable
42 | {
43 | $elements = [];
44 | $index2property = [];
45 | $reflection = new ReflectionClass(static::class);
46 | $properties = $reflection->getProperties();
47 | foreach ($properties as $property) {
48 | $attrs = $property->getAttributes(CustomFormElement::class, ReflectionAttribute::IS_INSTANCEOF);
49 | $nonLabelFound = false;
50 | foreach ($attrs as $attr) {
51 | $element = $attr->newInstance();
52 | if (!$element instanceof Label) {
53 | if ($nonLabelFound) {
54 | $name = $property->getName();
55 | $class = static::class;
56 | throw new LogicException("Multiple non-Label element attributes on property \"$name\" of class \"$class\"");
57 | }
58 | $index2property[count($elements)] = $property;
59 | $nonLabelFound = true;
60 | }
61 | $elements[] = $element;
62 | }
63 | }
64 |
65 | // @phpstan-ignore-next-line
66 | return Thenable::promise(function ($resolve, $reject) use ($player, $title, $elements, $index2property, $reflection) {
67 | $player->sendForm(new InternalCustomForm(
68 | function (?array $response) use ($resolve, $index2property, $reflection) {
69 | if (null === $response) {
70 | return null;
71 | }
72 | $self = $reflection->newInstanceWithoutConstructor();
73 | foreach ($index2property as $index => $property) {
74 | $property->setAccessible(true);
75 | $property->setValue($self, $response[$index]);
76 | }
77 | $resolve($self);
78 | },
79 | $reject, $title, $elements
80 | ));
81 | });
82 | }
83 |
84 | /**
85 | * @return Generator
86 | */
87 | public static function custom2gen(Player $player, string $title): Generator
88 | {
89 | $elements = [];
90 | $index2property = [];
91 | $reflection = new ReflectionClass(static::class);
92 | $properties = $reflection->getProperties();
93 | foreach ($properties as $property) {
94 | $attrs = $property->getAttributes(CustomFormElement::class, ReflectionAttribute::IS_INSTANCEOF);
95 | $nonLabelFound = false;
96 | foreach ($attrs as $attr) {
97 | $element = $attr->newInstance();
98 | if (!$element instanceof Label) {
99 | if ($nonLabelFound) {
100 | $name = $property->getName();
101 | $class = static::class;
102 | throw new LogicException("Multiple non-Label element attributes on property \"$name\" of class \"$class\"");
103 | }
104 | $index2property[count($elements)] = $property;
105 | $nonLabelFound = true;
106 | }
107 | $elements[] = $element;
108 | }
109 | }
110 |
111 | /** @var ?array $response */
112 | $response = yield from Await::promise(function ($resolve, $reject) use ($player, $title, $elements) {
113 | $player->sendForm(new InternalCustomForm($resolve, $reject, $title, $elements
114 | ));
115 | });
116 |
117 | if (null === $response) {
118 | return null;
119 | }
120 |
121 | $self = $reflection->newInstanceWithoutConstructor();
122 | foreach ($index2property as $index => $property) {
123 | $property->setAccessible(true);
124 | $property->setValue($self, $response[$index]);
125 | }
126 |
127 | return $self;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Form/Forms.php:
--------------------------------------------------------------------------------
1 |
57 | */
58 | public static function modal2then(
59 | Player $player,
60 | string $title,
61 | string $content,
62 | string $yesText = 'gui.yes',
63 | string $noText = 'gui.no',
64 | ): Thenable {
65 | /* @phpstan-ignore-next-line */
66 | return Thenable::promise(function ($resolve, $reject) use ($player, $title, $content, $yesText, $noText) {
67 | $player->sendForm(new InternalModalForm($resolve, $reject, $title, $content, $yesText, $noText));
68 | });
69 | }
70 |
71 | /**
72 | * Sends a modal form that consists of a title,
73 | * content, and two buttons. The player must choose either
74 | * yes or no.
75 | *
76 | * This function returns a generator suitable with
77 | * `sof3/await-generator`, and the generator returns true
78 | * or false according to the player's choice.
79 | *
80 | * @return Generator
81 | */
82 | public static function modal2gen(
83 | Player $player,
84 | string $title,
85 | string $content,
86 | string $yesText = 'gui.yes',
87 | string $noText = 'gui.no',
88 | ): Generator {
89 | /** @var bool $response */
90 | $response = yield from Await::promise(function ($resolve, $reject) use ($player, $title, $content, $yesText, $noText) {
91 | $player->sendForm(new InternalModalForm($resolve, $reject, $title, $content, $yesText, $noText));
92 | });
93 |
94 | return $response;
95 | }
96 |
97 | /**
98 | * Sends a menu form that consists of a title,
99 | * content, and a number of buttons. The player
100 | * must choose a single button. The array of buttons
101 | * passed to this function must be a list, meaning its
102 | * first entry's key is zero, the next being one, etc.
103 | *
104 | * This function returns a Thenable that may be used when
105 | * `sof3/await-generator` is not present. Otherwise it's
106 | * recommended to use `Forms::menu2gen()` because of
107 | * AwaitGenerator's simpler syntax.
108 | *
109 | * @param array $buttons
110 | * @phpstan-return Thenable
111 | */
112 | public static function menu2then(
113 | Player $player,
114 | string $title,
115 | string $content,
116 | array $buttons,
117 | ): Thenable {
118 | // @phpstan-ignore-next-line
119 | if ([] !== $buttons || $buttons !== array_values($buttons)) {
120 | $expected = 0;
121 | foreach (array_keys($buttons) as $index) {
122 | if ($index !== $expected++) {
123 | throw new InvalidArgumentException('The passed array of buttons is not a list!');
124 | }
125 | }
126 | }
127 | /* @phpstan-ignore-next-line */
128 | return Thenable::promise(function ($resolve, $reject) use ($player, $title, $content, $buttons) {
129 | $player->sendForm(new InternalMenuForm(
130 | $resolve,
131 | $reject,
132 | $title,
133 | $content,
134 | $buttons
135 | ));
136 | });
137 | }
138 |
139 | /**
140 | * Sends a menu form that consists of a title,
141 | * content, and a number of buttons. The player
142 | * must choose a single button. The array of buttons
143 | * passed to this function must be a list, meaning its
144 | * first entry's key is zero, the next being one, etc.
145 | *
146 | * This function returns a generator suitable with
147 | * `sof3/await-generator`, and the generator returns the
148 | * index of the button chosen by the player.
149 | *
150 | * @param array $buttons
151 | *
152 | * @return Generator
153 | */
154 | public static function menu2gen(
155 | Player $player,
156 | string $title,
157 | string $content,
158 | array $buttons,
159 | ): Generator {
160 | // @phpstan-ignore-next-line
161 | if ([] !== $buttons || $buttons !== array_values($buttons)) {
162 | $expected = 0;
163 | foreach (array_keys($buttons) as $index) {
164 | if ($index !== $expected++) {
165 | throw new InvalidArgumentException('The passed array of buttons is not a list!');
166 | }
167 | }
168 | }
169 | /** @var ?int $response */
170 | $response = yield from Await::promise(function ($resolve, $reject) use ($player, $title, $content, $buttons) {
171 | $player->sendForm(new InternalMenuForm(
172 | $resolve,
173 | $reject,
174 | $title,
175 | $content,
176 | $buttons
177 | ));
178 | });
179 |
180 | return $response;
181 | }
182 |
183 | /**
184 | * Sends a custom form that consists of a title and
185 | * an array of elements. The player may fill in multiple
186 | * elements before pressing the submit button.
187 | *
188 | * This function returns a Thenable that may be used when
189 | * `sof3/await-generator` is not present. Otherwise it's
190 | * recommended to use `Forms::menu2gen()` because of
191 | * AwaitGenerator's simpler syntax.
192 | *
193 | * @param array $elements
194 | *
195 | * @phpstan-return Thenable, FormValidationException>
196 | */
197 | public static function custom2then(
198 | Player $player,
199 | string $title,
200 | array $elements,
201 | ): Thenable {
202 | // @phpstan-ignore-next-line
203 | if ([] !== $elements || $elements !== array_values($elements)) {
204 | $expected = 0;
205 | foreach (array_keys($elements) as $index) {
206 | if ($index !== $expected++) {
207 | throw new InvalidArgumentException('The passed array of elements is not a list!');
208 | }
209 | }
210 | }
211 | /* @phpstan-ignore-next-line */
212 | return Thenable::promise(function ($resolve, $reject) use ($player, $title, $elements) {
213 | $player->sendForm(new InternalCustomForm(
214 | $resolve,
215 | $reject,
216 | $title,
217 | $elements
218 | ));
219 | });
220 | }
221 |
222 | /**
223 | * Sends a custom form that consists of a title and
224 | * an array of elements. The player may fill in multiple
225 | * elements before pressing the submit button.
226 | *
227 | * This function returns a generator suitable with
228 | * `sof3/await-generator`, and the generator returns the
229 | * index of the button chosen by the player.
230 | *
231 | * @param array $elements
232 | *
233 | * @return Generator>
234 | */
235 | public static function custom2gen(
236 | Player $player,
237 | string $title,
238 | array $elements,
239 | ): Generator {
240 | // @phpstan-ignore-next-line
241 | if ([] !== $elements || $elements !== array_values($elements)) {
242 | $expected = 0;
243 | foreach (array_keys($elements) as $index) {
244 | if ($index !== $expected++) {
245 | throw new InvalidArgumentException('The passed array of elements is not a list!');
246 | }
247 | }
248 | }
249 | /** @var ?array $response */
250 | $response = yield from Await::promise(function ($resolve, $reject) use ($player, $title, $elements) {
251 | $player->sendForm(new InternalCustomForm(
252 | $resolve,
253 | $reject,
254 | $title,
255 | $elements
256 | ));
257 | });
258 |
259 | return $response;
260 | }
261 | }
262 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Form/InternalCustomForm.php:
--------------------------------------------------------------------------------
1 | $elements
28 | */
29 | public function __construct(
30 | private Closure $resolve,
31 | private Closure $reject,
32 | private string $title,
33 | private array $elements,
34 | ) {
35 | }
36 |
37 | public function handleResponse(Player $player, $data): void
38 | {
39 | if (null === $data) {
40 | ($this->resolve)(null);
41 |
42 | return;
43 | }
44 |
45 | if (!is_array($data)) {
46 | $exception = new FormValidationException('Expected a response of type null|array, got type '.gettype($data).' instead!');
47 | ($this->reject)($exception);
48 | throw $exception;
49 | }
50 |
51 | $extracted = [];
52 | try {
53 | foreach ($this->elements as $index => $element) {
54 | if (!array_key_exists($index, $data)) {
55 | throw new FormValidationException("Expected response to have a value at index {$index}, but nothing was given!");
56 | }
57 | $extracted[$index] = $element->extract($data[$index]);
58 | }
59 | } catch (FormValidationException $e) {
60 | ($this->reject)($e);
61 | throw $e;
62 | }
63 |
64 | ($this->resolve)($extracted);
65 | }
66 |
67 | public function jsonSerialize(): mixed
68 | {
69 | return [
70 | 'type' => 'custom_form',
71 | 'title' => $this->title,
72 | 'content' => $this->elements,
73 | ];
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Form/InternalMenuForm.php:
--------------------------------------------------------------------------------
1 | $buttons
22 | */
23 | public function __construct(
24 | private Closure $resolve,
25 | private Closure $reject,
26 | private string $title,
27 | private string $content,
28 | private array $buttons,
29 | ) {
30 | }
31 |
32 | public function handleResponse(Player $player, $data): void
33 | {
34 | if (
35 | null !== $data &&
36 | (!is_int($data) || $data >= count($this->buttons))
37 | ) {
38 | $exception = new FormValidationException('Expected a response of type null|int, got type '.gettype($data).' instead!');
39 | ($this->reject)($exception);
40 | throw $exception;
41 | }
42 |
43 | ($this->resolve)($data);
44 | }
45 |
46 | public function jsonSerialize(): mixed
47 | {
48 | return [
49 | 'type' => 'form',
50 | 'title' => $this->title,
51 | 'content' => $this->content,
52 | 'buttons' => $this->buttons,
53 | ];
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Form/InternalModalForm.php:
--------------------------------------------------------------------------------
1 | reject)($exception);
34 | throw $exception;
35 | }
36 |
37 | ($this->resolve)($data);
38 | }
39 |
40 | public function jsonSerialize(): mixed
41 | {
42 | return [
43 | 'type' => 'modal',
44 | 'title' => $this->title,
45 | 'content' => $this->content,
46 | 'button1' => $this->yesText,
47 | 'button2' => $this->noText,
48 | ];
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Form/MenuFormElement/MenuFormButton.php:
--------------------------------------------------------------------------------
1 | $this->text];
20 | if (null !== $this->image) {
21 | $data['image'] = $this->image;
22 | }
23 |
24 | return $data;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Form/MenuFormElement/MenuFormImage.php:
--------------------------------------------------------------------------------
1 | $this->type,
52 | 'data' => $this->location,
53 | ];
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Remark.php:
--------------------------------------------------------------------------------
1 | getCommandMap();
40 | }
41 |
42 | $boundCommands = [];
43 | $reflection = new ReflectionClass($handler);
44 |
45 | $configs = $reflection->getAttributes(CmdConfig::class);
46 | foreach ($configs as $config) {
47 | $config = $config->newInstance();
48 | $boundCommands[$config->name()] = new BoundCommand(
49 | $plugin,
50 | $config->name(), $config->description(), $config->aliases(),
51 | $config->permission(),
52 | );
53 | }
54 |
55 | $methods = $reflection->getMethods();
56 | foreach ($methods as $m) {
57 | $cmdAttrs = $m->getAttributes(Cmd::class);
58 | if (0 === count($cmdAttrs)) {
59 | // This method is not a HandlerMethod.
60 | continue;
61 | }
62 |
63 | $guards = $m->getAttributes(Guard::class, ReflectionAttribute::IS_INSTANCEOF);
64 | $guards = array_map(fn ($x) => $x->newInstance(), $guards);
65 | $args = $m->getAttributes(Arg::class, ReflectionAttribute::IS_INSTANCEOF);
66 | $args = array_map(fn ($x) => $x->newInstance(), $args);
67 | $parameters = $m->getParameters();
68 |
69 | if (count($parameters) !== count($args)) {
70 | $name = $m->getName();
71 | throw new InvalidArgumentException("There must be the same number of parameters as Arg's for method $name!");
72 | }
73 |
74 | foreach ($args as $index => $arg) {
75 | /** @var Arg $arg @phpstan-ignore-next-line */
76 | $arg->setParameter($parameters[$index]);
77 | }
78 |
79 | $handlerMethod = new HandlerMethod($handler, $m, $guards, $args);
80 |
81 | foreach ($cmdAttrs as $cmd) {
82 | $cmd = $cmd->newInstance();
83 | if (!isset($boundCommands[$cmd->name()])) {
84 | $boundCommands[$cmd->name()] = new BoundCommand(
85 | $plugin,
86 | $cmd->name(), '', [], null,
87 | );
88 | }
89 | $bound = $boundCommands[$cmd->name()];
90 | $bound->attach($cmd->subNames(), $handlerMethod);
91 | }
92 | }
93 |
94 | $cm->registerAll(mb_strtolower($plugin->getName()), $boundCommands);
95 | }
96 |
97 | /**
98 | * Registers a packet listener to give command tab-completion to players.
99 | */
100 | public static function activate(Plugin $plugin): void
101 | {
102 | $pm = Server::getInstance()->getPluginManager();
103 | $pm->registerEvents(new CommandHintListener(), $plugin);
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/DiamondStrider1/Remark/Types/RelativeVector3.php:
--------------------------------------------------------------------------------
1 | isOffsetX ? $vector->getX() + $this->valueX : $this->valueX;
24 | $y = $this->isOffsetY ? $vector->getY() + $this->valueY : $this->valueY;
25 | $z = $this->isOffsetZ ? $vector->getZ() + $this->valueZ : $this->valueZ;
26 |
27 | return new Vector3($x, $y, $z);
28 | }
29 |
30 | public function getValueX(): int|float
31 | {
32 | return $this->valueX;
33 | }
34 |
35 | public function getValueY(): int|float
36 | {
37 | return $this->valueY;
38 | }
39 |
40 | public function getValueZ(): int|float
41 | {
42 | return $this->valueZ;
43 | }
44 |
45 | public function isOffsetX(): bool
46 | {
47 | return $this->isOffsetX;
48 | }
49 |
50 | public function isOffsetY(): bool
51 | {
52 | return $this->isOffsetY;
53 | }
54 |
55 | public function isOffsetZ(): bool
56 | {
57 | return $this->isOffsetZ;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/tools/php-cs-fixer/.php-cs-fixer.php:
--------------------------------------------------------------------------------
1 | in($root . $sep . 'src');
8 |
9 | $config = new PhpCsFixer\Config();
10 |
11 | return $config
12 | ->setRules([
13 | '@Symfony' => true,
14 | 'phpdoc_to_comment' => [
15 | 'ignored_tags' => ['var']
16 | ]
17 | ])
18 | ->setFinder($finder);
19 |
--------------------------------------------------------------------------------
/tools/php-cs-fixer/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "friendsofphp/php-cs-fixer": "^3.8"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/tools/phpstan/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "phpstan/phpstan": "^1.7",
4 | "phpstan/extension-installer": "^1.1",
5 | "phpstan/phpstan-strict-rules": "^1.2"
6 | },
7 | "config": {
8 | "allow-plugins": {
9 | "phpstan/extension-installer": true
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/tools/phpstan/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": "5050285372ea6f613ae86d63e2044032",
8 | "packages": [
9 | {
10 | "name": "phpstan/extension-installer",
11 | "version": "1.1.0",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/phpstan/extension-installer.git",
15 | "reference": "66c7adc9dfa38b6b5838a9fb728b68a7d8348051"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/66c7adc9dfa38b6b5838a9fb728b68a7d8348051",
20 | "reference": "66c7adc9dfa38b6b5838a9fb728b68a7d8348051",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "composer-plugin-api": "^1.1 || ^2.0",
25 | "php": "^7.1 || ^8.0",
26 | "phpstan/phpstan": ">=0.11.6"
27 | },
28 | "require-dev": {
29 | "composer/composer": "^1.8",
30 | "phing/phing": "^2.16.3",
31 | "php-parallel-lint/php-parallel-lint": "^1.2.0",
32 | "phpstan/phpstan-strict-rules": "^0.11 || ^0.12"
33 | },
34 | "type": "composer-plugin",
35 | "extra": {
36 | "class": "PHPStan\\ExtensionInstaller\\Plugin"
37 | },
38 | "autoload": {
39 | "psr-4": {
40 | "PHPStan\\ExtensionInstaller\\": "src/"
41 | }
42 | },
43 | "notification-url": "https://packagist.org/downloads/",
44 | "license": [
45 | "MIT"
46 | ],
47 | "description": "Composer plugin for automatic installation of PHPStan extensions",
48 | "support": {
49 | "issues": "https://github.com/phpstan/extension-installer/issues",
50 | "source": "https://github.com/phpstan/extension-installer/tree/1.1.0"
51 | },
52 | "time": "2020-12-13T13:06:13+00:00"
53 | },
54 | {
55 | "name": "phpstan/phpstan",
56 | "version": "1.7.6",
57 | "source": {
58 | "type": "git",
59 | "url": "https://github.com/phpstan/phpstan.git",
60 | "reference": "1af9271ea529f995e57a1f493bebba6b830a97d0"
61 | },
62 | "dist": {
63 | "type": "zip",
64 | "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1af9271ea529f995e57a1f493bebba6b830a97d0",
65 | "reference": "1af9271ea529f995e57a1f493bebba6b830a97d0",
66 | "shasum": ""
67 | },
68 | "require": {
69 | "php": "^7.2|^8.0"
70 | },
71 | "conflict": {
72 | "phpstan/phpstan-shim": "*"
73 | },
74 | "bin": [
75 | "phpstan",
76 | "phpstan.phar"
77 | ],
78 | "type": "library",
79 | "autoload": {
80 | "files": [
81 | "bootstrap.php"
82 | ]
83 | },
84 | "notification-url": "https://packagist.org/downloads/",
85 | "license": [
86 | "MIT"
87 | ],
88 | "description": "PHPStan - PHP Static Analysis Tool",
89 | "support": {
90 | "issues": "https://github.com/phpstan/phpstan/issues",
91 | "source": "https://github.com/phpstan/phpstan/tree/1.7.6"
92 | },
93 | "funding": [
94 | {
95 | "url": "https://github.com/ondrejmirtes",
96 | "type": "github"
97 | },
98 | {
99 | "url": "https://github.com/phpstan",
100 | "type": "github"
101 | },
102 | {
103 | "url": "https://www.patreon.com/phpstan",
104 | "type": "patreon"
105 | },
106 | {
107 | "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
108 | "type": "tidelift"
109 | }
110 | ],
111 | "time": "2022-05-30T21:29:45+00:00"
112 | },
113 | {
114 | "name": "phpstan/phpstan-strict-rules",
115 | "version": "1.2.3",
116 | "source": {
117 | "type": "git",
118 | "url": "https://github.com/phpstan/phpstan-strict-rules.git",
119 | "reference": "0c82c96f2a55d8b91bbc7ee6512c94f68a206b43"
120 | },
121 | "dist": {
122 | "type": "zip",
123 | "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/0c82c96f2a55d8b91bbc7ee6512c94f68a206b43",
124 | "reference": "0c82c96f2a55d8b91bbc7ee6512c94f68a206b43",
125 | "shasum": ""
126 | },
127 | "require": {
128 | "php": "^7.2 || ^8.0",
129 | "phpstan/phpstan": "^1.6.3"
130 | },
131 | "require-dev": {
132 | "nikic/php-parser": "^4.13.0",
133 | "php-parallel-lint/php-parallel-lint": "^1.2",
134 | "phpstan/phpstan-phpunit": "^1.0",
135 | "phpunit/phpunit": "^9.5"
136 | },
137 | "type": "phpstan-extension",
138 | "extra": {
139 | "phpstan": {
140 | "includes": [
141 | "rules.neon"
142 | ]
143 | }
144 | },
145 | "autoload": {
146 | "psr-4": {
147 | "PHPStan\\": "src/"
148 | }
149 | },
150 | "notification-url": "https://packagist.org/downloads/",
151 | "license": [
152 | "MIT"
153 | ],
154 | "description": "Extra strict and opinionated rules for PHPStan",
155 | "support": {
156 | "issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
157 | "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.2.3"
158 | },
159 | "time": "2022-05-04T15:20:40+00:00"
160 | }
161 | ],
162 | "packages-dev": [],
163 | "aliases": [],
164 | "minimum-stability": "stable",
165 | "stability-flags": [],
166 | "prefer-stable": false,
167 | "prefer-lowest": false,
168 | "platform": [],
169 | "platform-dev": [],
170 | "plugin-api-version": "2.3.0"
171 | }
172 |
--------------------------------------------------------------------------------
/tools/phpstan/phpstan.neon.dist:
--------------------------------------------------------------------------------
1 | parameters:
2 | level: max
3 | paths:
4 | - ../../src
5 |
--------------------------------------------------------------------------------
/tutorial/README.md:
--------------------------------------------------------------------------------
1 | # The Remark Tutorial
2 |
3 | The following are the source files for *The Remark Tutorial*. The book can be read [here](https://swift-strider.github.io/Remark/)
4 |
--------------------------------------------------------------------------------
/tutorial/book.toml:
--------------------------------------------------------------------------------
1 | [book]
2 | authors = ["DiamondStrider1"]
3 | language = "en"
4 | multilingual = false
5 | src = "src"
6 | title = "The Remark Tutorial"
7 |
--------------------------------------------------------------------------------
/tutorial/src/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | [Introduction](./introduction.md)
4 |
5 | - [Quick Guide](./quick-guide/index.md)
6 | - [Command Handling](./quick-guide/command_handling.md)
7 | - [Forms](./quick-guide/forms.md)
8 | - [In Depth](./in-depth/index.md)
9 | - [Installing](./in-depth/installing.md)
10 | - [Commands](./in-depth/commands.md)
11 | - [Forms](./in-depth/forms.md)
12 | - [Command Arg's](./in-depth/command_args.md)
13 | - [Command Guard's](./in-depth/command_guards.md)
14 | - [Custom Form Elements](./in-depth/custom_form_elements.md)
15 |
--------------------------------------------------------------------------------
/tutorial/src/in-depth/command_args.md:
--------------------------------------------------------------------------------
1 | # Command Args
2 | An `Arg` provides a value to its corresponding parameter of a HandlerMethod. The number of `Args` in a HandlerMethod must be equal to its number of parameters. An `Arg` may extract its value from the arguments given when the command is run, or it may get its value from another source.
3 |
4 | ## sender
5 | Extracts the `CommandSender`. If its corresponding parameter has the type `Player` it will do an instance-of check automatically. Otherwise the type of the parameter must be CommandSender.
6 |
7 | *`sender()` does not take any arguments.*
8 |
9 | ## player_arg
10 | Extracts a `Player` using the name provided by a command argument.
11 | ```php
12 | bool $exact = false,
13 | ```
14 | * exact - whether to not match by prefix
15 |
16 | ## text
17 | Extracts one or more strings from the command arguments. Depending on the parameters given to this Arg, the corresponding parameter must have a type of `string`, `?string`, or `array`.
18 | ```php
19 | int $count = 1,
20 | bool $require = true,
21 | ```
22 | * count - number of arguments to take
23 | * require - wether to fail if the number
24 | of arguments remaining is less than count
25 |
26 | ## remaining
27 | Extracts the remaining strings from the command arguments.
28 |
29 | *`remaining()` does not take any arguments.*
30 |
31 | ## enum
32 | Extracts a string that must be in an immutable set of predefined strings.
33 | ```php
34 | string $name,
35 | string $choice,
36 | string ...$otherChoices,
37 | ```
38 | * name - the enum's name, in-game it will show
39 | as a command hint (i. e. ``)
40 | * choice - A possible choice
41 | * otherChoices - Other possible choices
42 |
43 | ## bool_arg
44 | Extracts a true / false boolean.
45 | Valid command arguments for both choices are:
46 | - true: "true", "on", and "yes"
47 | - false: "false", "off", and "no"
48 |
49 | *`bool_arg()` does not take any arguments.*
50 |
51 | ## int_arg
52 | Extracts an integer.
53 |
54 | *`int_arg()` does not take any arguments.*
55 |
56 | ## float_arg
57 | Extracts a float.
58 |
59 | *`float_arg()` does not take any arguments.*
60 |
61 | ## vector_arg
62 | Extracts either a Vector3 or RelativeVector3 depending on the type of it's corresponding parameter.
63 |
64 | If your parameter has the type RelativeVector3, you can then call `$relativeVector->relativeTo($vector)` to get a real Vector3.
65 |
66 | *`vector_arg()` does not take any arguments.*
67 |
68 | ## json_arg
69 | Extracts a string **WITHOUT** validating that it's proper json. This is more of a marker, telling the player's client that JSON is needed. You **MUST** verify that the JSON is valid, yourself.
70 |
71 | *`json_arg()` does not take any arguments.*
72 |
73 | ## command_arg
74 | Extracts a string **WITHOUT** validating that it's the name of a command. This is more of a marker, telling the player's client that a command name is needed. You **MUST** verify that the command name is valid, yourself.
75 |
76 | *`command_arg()` does not take any arguments.*
77 |
--------------------------------------------------------------------------------
/tutorial/src/in-depth/command_guards.md:
--------------------------------------------------------------------------------
1 | # Command Guards
2 | A `Guard` prevents a HandlerMethod from being ran when a requirement isn't satisfied. I. e. the command sender not having permission.
3 |
4 | ## permission
5 | Requires that the `CommandSender` has all of the permissions passed in.
6 | ```php
7 | string $permission,
8 | string ...$otherPermissions,
9 | ```
10 | * permission - One required permission
11 | * otherPermissions - Other required permissions
12 |
--------------------------------------------------------------------------------
/tutorial/src/in-depth/commands.md:
--------------------------------------------------------------------------------
1 | # In Depth | Commands
2 |
3 | `Remark::command()` is used to bind the HandlerMethods of an object to a CommandMap. By default, `Remark::command()` uses the CommandMap attached to the running PocketMine Server. Under-the-hood, a new `BoundCommand` is made for every `CmdConfig`, and they implement `PluginOwned` returning the plugin passed to `Remark::command()`.
4 |
5 | `Remark::activate()` registers a listener which will add `TAB`-completion for players.
6 |
7 | ## CmdConfig
8 | Configures the commands of a handler object.
9 | ```php
10 | string $name,
11 | string $description,
12 | array $aliases = [],
13 | ?string $permission = null,
14 | ```
15 | * name - The name of the underlying command
16 | * description - The description of the command
17 | * aliases - The aliases of the command
18 | * permission - If set, one or more permissions separated by `;`
19 |
20 | ## Cmd
21 | Marks a method as a handler for a command. You may bind a HandlerMethod to multiple commands by repeating this attribute.
22 | ```php
23 | string $name,
24 | string ...$subNames,
25 | ```
26 | * name - The name of the command to attach this HandlerMethod to
27 | * subNames - Zero or more subcommand names
28 |
--------------------------------------------------------------------------------
/tutorial/src/in-depth/custom_form_elements.md:
--------------------------------------------------------------------------------
1 | # Custom Form Elements
2 |
3 | `Dropdown`, `Input`, `Label`, `Slider`, `StepSlider`, and `Toggle` are found in the `DiamondStrider1\Remark\Forms\CustomFormElement` namespace and all implement `CustomFormElement`. They may be used as normal classes (`new Label('Some Text')`) or as attributes (`#[Label('Some Text')]`).
4 |
5 | Information on creating custom forms can be found [here](forms.md#custom-form).
6 |
7 | ## Dropdown
8 | Returns an integer which is the index of the choice the player selected.
9 | ```php
10 | string $text,
11 | array $options,
12 | int $defaultOption = 0,
13 | bool $allowDefault = true,
14 | ```
15 | * allowDefault - whether the player may skip filling out a dropdown when the Dropdown's default value is -1
16 |
17 | ## Input
18 | Returns a string that the player entered.
19 | ```php
20 | string $text,
21 | string $placeholder = '',
22 | string $default = '',
23 | ```
24 |
25 | ## Label
26 | Does not return anything, but it does place text at its location.
27 | ```php
28 | string $text
29 | ```
30 |
31 | ## Slider
32 | Returns a float in within the range [min, max]. It **DOES NOT** validate the step, however, so that responsibility is left to the developer.
33 | ```php
34 | string $text,
35 | float $min,
36 | float $max,
37 | float $step = 1.0,
38 | ?float $default = null,
39 | ```
40 |
41 | ## StepSlider
42 | Returns an integer, the index of the step the player chose. Visually, looks like a Slider but the player chooses one of the steps.
43 | ```php
44 | string $text,
45 | array $steps,
46 | int $defaultOption = 0,
47 | ```
48 | * steps - list of strings to choose from
49 |
50 | ## Toggle
51 | Returns a boolean. Creates a switch that the player can toggle.
52 | ```php
53 | string $text,
54 | bool $default = false,
55 | ```
56 |
--------------------------------------------------------------------------------
/tutorial/src/in-depth/forms.md:
--------------------------------------------------------------------------------
1 | # In Depth | Forms
2 | The `DiamondStrider1\Remark\Form\Forms` class holds static methods for creating forms. They are `modal2then()`, `modal2gen()`, `menu2then()`, `menu2gen()`, `custom2then()`, and `custom2gen()`. The `*2gen()` methods return generators to be used with await-generator, and the `*2then()` methods return `Thenable` a type defined by Remark.
3 |
4 | # Thenable
5 | A `Thenable` is much like a promise. One may call `$thenable->then($onResolve, $onReject)`. If you omit `$onReject` Remark will throw an `UnhandledAsyncException` if the `Thenable` is rejected.
6 |
7 | # Modal Form
8 | Use either `Forms::modal2then()` or `Forms::modal2gen()`. Resolves with a boolean, `true` if the yes button was hit, `false` if the no button was.
9 | ```php
10 | Player $player,
11 | string $title,
12 | string $content,
13 | string $yesText = 'gui.yes',
14 | string $noText = 'gui.no',
15 | ```
16 |
17 | # Menu Form
18 | Use either `Forms::menu2then()` or `Forms::menu2gen()`. Resolves with an integer, the index of the button the player chose.
19 | ```php
20 | Player $player,
21 | string $title,
22 | string $content,
23 | array $buttons,
24 | ```
25 | * buttons - A list of `MenuFormButton`s
26 |
27 | ## MenuFormButton
28 | A button for a menu form.
29 | ```php
30 | string $text,
31 | ?MenuFormImage $image = null,
32 | ```
33 | * text - The text of the button
34 | * image - An optional image to display
35 |
36 | ## MenuFormImage
37 | An image that can be to present on a menu form.
38 | ```php
39 | string $type,
40 | string $location,
41 | ```
42 | * type - Either 'url' or 'path'.
43 | * location - The location of the image
44 |
45 | If the image's type is `url`, Minecraft will
46 | fetch the image from online, and it may take
47 | some time to load. If the type is `path`,
48 | Minecraft will instantly load the image from
49 | the resource pack.
50 |
51 | On Minecraft Windows 10, `url` images may not
52 | show until ALT-TAB'ing out of then back into Minecraft.
53 |
54 | An example location of a `path` type image is
55 | "textures/block/dirt.png" without the leading
56 | slash.
57 |
58 | # Custom Form
59 | You may use `Forms::custom2then()`/`Forms::custom2gen()` or `CustomFormResultTrait`.
60 |
61 | ## Custom Form, static functions
62 | Returns an array with the indexes of elements mapped to the values of the player's response.
63 | ```php
64 | Player $player,
65 | string $title,
66 | array $elements,
67 | ```
68 | * elements - A list of `CustomFormElement`s
69 |
70 | ## Custom Form, CustomFormResultTrait
71 |
72 | A class that uses this trait will have the static methods `custom2gen()` and `custom2then()` added which return a new instance of the class instead of an array.
73 |
74 | A class using CustomFormResultTrait must meet the following
75 | requirements:
76 | - Must not be abstract
77 | - Every property to be filled in...
78 | - May be marked with any number of Label attributes
79 | - Must be marked with at most one CustomFormElement that isn't Label
80 |
81 | Properties are filled in according to the non-Label attribute
82 | attached to them, or ignored if only Labels are attached to them.
83 |
84 | All properties without CustomFormElement attributes are ignored.
85 |
--------------------------------------------------------------------------------
/tutorial/src/in-depth/index.md:
--------------------------------------------------------------------------------
1 | # In Depth
2 |
3 | For installing for development and production check [Installing].
4 |
5 | Documentation can be found for [Command Args], [Command Guards], and [Custom Form Elements].
6 |
7 | [Installing]: installing.md
8 | [Command Args]: command_args.md
9 | [Command Guards]: command_guards.md
10 | [Custom Form Elements]: custom_form_elements.md
11 |
--------------------------------------------------------------------------------
/tutorial/src/in-depth/installing.md:
--------------------------------------------------------------------------------
1 | # Installing
2 |
3 | Setting up your plugin to use Remark is quite easy!
4 |
5 | It's advised that during development [DEVirion](#using-devirion) is used and when the plugin is ready production that this library is either installed [by Poggit](#installing-with-poggit) or installed [by hand](#manually-installing-into-a-plugin-phar).
6 |
7 | ## Using DEVirion
8 |
9 | DEVirion allows your plugin to use Remark.
10 |
11 | 1. Install `Remark.phar` from this project's [Github Releases](https://github.com/Swift-Strider/Remark/releases/).
12 | 1. Place `Remark.phar` into your server's `virions` folder, which is next to the `plugins` folder.
13 | 1. If not already downloaded, get DEVirion from [Poggit](https://poggit.pmmp.io/p/DEVirion/).
14 |
15 | ## Installing with Poggit
16 |
17 | Plugins that are built on poggit and use virions should declare there dependencies in `.poggit.yml`. To use Remark add an entry like this.
18 | ```yml
19 | projects:
20 | my-pugin-project:
21 | path: ""
22 | libs:
23 | - src: Swift-Strider/Remark/Remark
24 | version: ^3.3.0
25 | epitope: .random
26 | ```
27 |
28 | ## Manually Installing into a Plugin Phar
29 |
30 | Install `Remark.phar` from this project's [Github Releases](https://github.com/Swift-Strider/Remark/releases/).
31 |
32 | If you haven't already, build your plugin into a phar file. This example script assumes you're in your plugin's directory and that the files/directories `plugin.yml`, `src`, and `resources` exist. The following works on both `Windows 10 (Powershell)` and `Ubuntu`.
33 | ```sh
34 | wget https://raw.githubusercontent.com/pmmp/DevTools/master/src/ConsoleScript.php -O ConsoleScript.php
35 | php -dphar.readonly=0 ConsoleScript.php --make src,resources,plugin.yml --relative . --out plugin.phar
36 | ```
37 |
38 | Next you will "infect" your plugin's `.phar` file, embedding the Remark library inside of your plugin.
39 | ```sh
40 | php -dphar.readonly=0 Remark.phar plugin.phar
41 | ```
42 |
--------------------------------------------------------------------------------
/tutorial/src/introduction.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | For onlooking developers, Remark simplifies Command Handling and Forms through a declarative and asynchronous syntax. It is a virion that makes heavy uses of PHP 8's new attributes to provide simple, elegant solutions for usually boilerplate-heavy command handling. It tackles the async nature of PocketMine through await-generator, making complex forms easy.
4 |
5 | For getting started check out the [Quick Guide]!
6 |
7 | [Quick Guide]: quick-guide/index.md
8 | [Command Handling]: quick-guide/command_handling.md
9 | [Forms]: quick-guide/forms.md
10 |
--------------------------------------------------------------------------------
/tutorial/src/quick-guide/command_handling.md:
--------------------------------------------------------------------------------
1 | # Command Handling
2 |
3 | Prerequisites:
4 | - Basic knowledge of PHP
5 |
6 | ## About Attributes
7 |
8 | Skip to [Your First Command](#your-first-command) if you already know PHP 8's attributes.
9 |
10 | PHP 8 added attributes, markers that can be placed on classes or methods and later found by Remark through reflection.
11 | ```php
12 | #[Test(debug: false)]
13 | public function myMethod(): void {}
14 | ```
15 | PHP 8.1 attributes look a lot like comments. They start with `#[` and end with `]` and inside the brackets the name of attributes are placed. Attributes are really just classes, and inside you put parameters for their constructor. You have to make sure to import the attribute just like you would for a class.
16 |
17 | You can have multiple attributes inside the same `#[]` structure.
18 | ```php
19 | #[Test(debug: false), Logging(level: 2)]
20 | public function myMethod(): void {}
21 | ```
22 | You can also omit the parameter names, to make the attributes more concise.
23 | ```php
24 | #[Test(false), Logging(2)]
25 | public function myMethod(): void {}
26 | ```
27 |
28 | ## Your First Command
29 |
30 | In Remark, you mark methods using attributes to describe how you want to take arguments from the command line.
31 | ```php
32 | use DiamondStrider1\Remark\Command\Cmd;
33 | use DiamondStrider1\Remark\Command\CmdConfig;
34 | use DiamondStrider1\Remark\Command\Arg\sender;
35 | use DiamondStrider1\Remark\Command\Arg\text;
36 | use DiamondStrider1\Remark\Command\Guard\permission;
37 | use pocketmine\command\CommandSender;
38 |
39 | #[CmdConfig(
40 | name: 'helloworld',
41 | description: 'Run my hello world command!',
42 | aliases: ['hw'],
43 | permission: 'plugin.helloworld.use'
44 | )]
45 | class MyCommand {
46 | #[Cmd('helloworld'), permission('plugin.helloworld.use')]
47 | #[sender(), text()]
48 | public function myFirstCommand(CommandSender $sender, string $text): void
49 | {
50 | $sender->sendMessage("§aHello Command Handling! - $text");
51 | }
52 | }
53 | ```
54 | This is a lot of code to tackle, but before going over it let's register our command so it can be used.
55 |
56 | ## Registering Your First Command
57 | ```php
58 | public function onEnable(): void
59 | {
60 | Remark::command($this, new MyCommand());
61 | Remark::activate($this);
62 | }
63 | ```
64 |
65 | `Remark::command()` adds one or more `BoundCommand`s that implement `PluginOwned`. `Remark::activate()` registers a listener that will add `TAB`-completion for players.
66 |
67 | ## The Breakdown
68 | Let's go over everything in `MyCommand.php`.
69 |
70 | #### CmdConfig Attribute
71 | ```php
72 | #[CmdConfig( /* ... */ )]
73 | ```
74 | CmdConfig customizes the underlying command that will ultimately be registered to PocketMine's CommandMap. Everything is pretty self-explanatory. `name` is the name of the command (i. e. `/command_name`).`permission` is set as the permission of the command, hiding the command from those who have insufficient permissions. **IT DOES NOT**, however, stop people from running the command by itself. We will get into that soon.
75 |
76 | #### Cmd and permission
77 | ```php
78 | #[Cmd('helloworld'), permission('plugin.helloworld.use')]
79 | ```
80 | This line uses two attributes at once. The method these attributes are placed on are called a HandlerMethod. A method simply has to be marked by `Cmd` to become a HandlerMethod.
81 |
82 | #### Cmd Attribute
83 | The `Cmd` is passed the name of the command. You can change the command to a subcommand by adding a comma followed by another string. For example, `#[Cmd('cmd', 'subcmd')]` would bind the method to the subcommand at `/cmd subcmd`. The command or subcommand chosen must be unique, meaning there is one HandlerMethod per command/subcommand.
84 |
85 | #### The permission Guard
86 | The `permission` is something Remark calls a `Guard`. `Guard`s prevent unauthorized access to commands by ensuring that certain requirements are met. If the `permission` guard fails, it will send a generic not-enough-permissions error to the command sender. The HandlerMethod only runs if all `Guard`s attached to it succeed. Unlike the `permission` property of `CmdConfig`, this guard actually enforces that the command sender has permission to using the command. More permissions can be added by adding a comma and another string, ex: `permission('perm1', 'perm2')`.
87 |
88 | #### Args
89 | ```php
90 | #[sender(), text()]
91 | ```
92 |
93 | Ranked's `Arg`s have many responsibilities.
94 | * Extracting data from command line arguments
95 | * Validation
96 | * Converting from string to a useful type (i. e. int)
97 |
98 | It's required that for every `Arg` of a HandlerMethod that there is a corresponding parameter of the correct type. That parameter will be given the value extracted by the Arg whenever the command is run.
99 |
100 | #### The sender() Arg
101 | The `sender()` Arg requires that it's parameter is of type `CommandSender` or `Player`. It doesn't actually take any arguments from the command line, but simply supplies the command sender performing an `instanceof` check if needed.
102 |
103 | #### The text() Arg
104 | The `text()` Arg requires that it's parameter is of type `string`, `?string` or `array` depending on the arguments passed to it. In this case the type of the parameter must be `string`. By default `text()` takes a single string from the command line arguments, and errors if none was given.
105 |
106 | # Asynchronous Commands
107 | A key aspect of Remark is it's asynchronous approach to UI. This is important because of how complex asynchronous programming will become if not given care. AwaitGenerator is a virion that allows async/await style programming through Generators and their pause/resume functions. It's not required you use AwaitGenerator, but it is recommended and supported by Remark.
108 |
109 | Here is the example extended to use Remark's first-class support of AwaitGenerator.
110 | ```php
111 | #[Cmd('helloworld'), permission('plugin.helloworld.use')]
112 | #[sender(), text()]
113 | public function myFirstCommand(CommandSender $sender, string $text): Generator
114 | {
115 | $sender->sendMessage("§aHello Command Handling! - $text");
116 | $response = yield from AsyncApi::fetch($text);
117 | if ($sender instanceof Player && !$sender->isConnected()) {
118 | return;
119 | }
120 | $sender->sendMessage("Got Response: $response");
121 | }
122 | ```
123 |
124 | The return type of the function is now `Generator`, and `yield from` is used to call other AwaitGenerator functions. Remember to check that the player is still connected after doing any asynchronous logic.
125 |
126 | If you got all of that, let's move on to [forms](forms.md)!
127 |
--------------------------------------------------------------------------------
/tutorial/src/quick-guide/forms.md:
--------------------------------------------------------------------------------
1 | # Forms
2 |
3 | Prerequisites:
4 | - Basic knowledge of PHP
5 | - Basic knowledge of PHP 8's attributes
6 |
7 | The main way to send forms to players is through Remark's `DiamondStrider1\Remark\Form\Forms` class. It contains the methods `modal2then()`, `modal2gen()`, `menu2then()`, `menu2gen()`, `custom2then()`, and `custom2gen()`. Methods ending in `gen` return an AwaitGenerator compatible generator that sends the form of its type and returns it's result. Methods ending in `then` exist in case AwaitGenerator isn't available, and return Thenable's that are resolved with the form's result.
8 |
9 | Let's continue the example from [Command Handling](command_handling.md).
10 | ```php
11 | #[Cmd('helloworld'), permission('plugin.helloworld.use')]
12 | #[sender(), text()]
13 | public function myFirstCommand(CommandSender $sender, string $text): Generator
14 | {
15 | $sender->sendMessage("§aHello Command Handling! - $text");
16 | $response = yield from AsyncApi::fetch($text);
17 | if ($sender instanceof Player && !$sender->isConnected()) {
18 | return;
19 | }
20 | $sender->sendMessage("Got Response: $response");
21 | }
22 | ```
23 | For those not familiar with Remark's [command handling](command_handling.md), the method `myFirstCommand()` will be ran whenever a player (or the console) runs `/helloworld` and has the permission `plugin.helloworld.use`.
24 |
25 | ## ModalForm
26 | Starting off simple, we will send the player a yes/no modal form. Modal forms cannot be closed out of, if you try to hit `ESC` the game doesn't acts as if you had pressed the no button.
27 | ```php
28 | /** @var bool $choice */
29 | $choice = yield from Forms::modal2gen(
30 | $sender,
31 | 'What is your favorite ice cream?',
32 | 'Pick from these ice cream flavors.',
33 | );
34 | $choice = match ($choice) {
35 | true => '§aYes',
36 | false => '§cNo',
37 | };
38 | $sender->sendMessage("You said {$choice}§r.");
39 | ```
40 | Notice we don't have to check if the player's online because when a PocketMine guarantees the player is still connected when a form is submitted.
41 |
42 | ## MenuForm
43 | Let's now give the player a menu form with options to choose from.
44 | ```php
45 | /** @var ?int $choice */
46 | $choice = yield from Forms::menu2gen(
47 | $sender,
48 | 'What is your favorite ice cream?',
49 | 'Pick from these ice cream flavors.',
50 | [
51 | new MenuFormButton('Vanilla'),
52 | new MenuFormButton('Blueberry'),
53 | new MenuFormButton('Lime'),
54 | ]
55 | );
56 | if (null !== $choice) {
57 | $choice = ['vanilla', 'blueberry', 'lime'][$choice];
58 | $sender->sendMessage("You chose §g{$choice}§r.");
59 | }
60 | ```
61 | A MenuFormButton can also have an attached MenuFormImage like so.
62 | ```php
63 | new MenuFormButton('My Button', new MenuFormImage(type: 'url', location: 'https://my.image.com/image'))
64 | new MenuFormButton('My Button', new MenuFormImage(type: 'path', location: 'textures/blocks/dirt.png'))
65 | ```
66 |
67 | ## CustomForm
68 | Now for the most complex type of form. A custom form sends a list of elements for the player to fill out and a submit button to press when the player is finished. Remark gives you two ways to create a custom form. First we will start with the `custom2gen()`/`custom2then()` method.
69 | ```php
70 | /** @var array $response */
71 | $response = yield from Forms::custom2gen(
72 | $sender,
73 | 'What is your favorite ice cream?',
74 | [
75 | new Label('Type a valid ice cream flavor!'),
76 | new Input('Ice Cream Flavor', placeholder: 'Vanilla'),
77 | ]
78 | );
79 | $sender->sendMessage("You said {$response[1]}§r.");
80 | ```
81 | Using this method of sending custom forms, you have to index the response data with the index of the element. Remark already handles the validation for you.
82 |
83 | Another way to make custom forms is through attributes.
84 | ```php
85 | use DiamondStrider1\Remark\Form\CustomFormElement\Label;
86 | use DiamondStrider1\Remark\Form\CustomFormElement\Input;
87 | use DiamondStrider1\Remark\Form\CustomFormResultTrait;
88 |
89 | final class MySurveyForm
90 | {
91 | use CustomFormResultTrait;
92 |
93 | #[Label('Type a valid ice cream flavor!')]
94 | #[Input('Ice Cream Flavor', placeholder: 'Vanilla')]
95 | public string $name;
96 | }
97 | ```
98 | ```php
99 | /** @var MySurveyForm $formResult */
100 | $formResult = yield from MySurveyForm::custom2gen(
101 | $sender, 'What is your favorite ice cream?'
102 | );
103 | $sender->sendMessage("You said {$formResult->name}§r.");
104 | ```
105 | The CustomFormResultTrait adds the static functions `custom2gen()` and `custom2then()` that take a player and a title for the form. It's recommended that you mark form fields as public so they are easily accessible.
106 |
--------------------------------------------------------------------------------
/tutorial/src/quick-guide/index.md:
--------------------------------------------------------------------------------
1 | # Quick Guide
2 |
3 | Get familiar with Remark, fast!
4 |
5 | Start with learning [Command Handling] by building a plugin step-by-step.
6 |
7 | Or skip straight to [Forms] if that's what you're looking for.
8 |
9 | [Command Handling]: command_handling.md
10 | [Forms]: forms.md
11 |
--------------------------------------------------------------------------------
/virion.yml:
--------------------------------------------------------------------------------
1 | name: Remark
2 | antigen: DiamondStrider1\Remark
3 | version: 1.2.2
4 | api: 4.0.0
5 | author: DiamondStrider1
6 |
--------------------------------------------------------------------------------