Compare changes

Choose any two refs to compare.

Changed files
+8256 -2748
.github
docs
gradle
src
main
java
me
melontini
commander
api
impl
builtin
command
event
expression
mixin
util
resources
testmod
java
resources
data
commander
commander
events
expression_library
+24
.github/dependabot.yml
··· 1 + version: 2 2 + registries: 3 + fabric: 4 + url: "https://maven.fabricmc.net/" 5 + type: maven-repository 6 + gradle-plugins: 7 + url: "https://plugins.gradle.org/m2/" 8 + type: maven-repository 9 + maven-central: 10 + url: "https://repo1.maven.org/maven2/" 11 + type: maven-repository 12 + 13 + updates: 14 + - package-ecosystem: gradle 15 + registries: 16 + - fabric 17 + - gradle-plugins 18 + - maven-central 19 + ignore: 20 + - dependency-name: me.melontini:dark-matter* 21 + - dependency-name: net.fabricmc.fabric-api:fabric-api 22 + directory: / 23 + schedule: 24 + interval: weekly
+18
.github/workflows/build.yml
··· 1 + 2 + name: build 3 + on: [ push, pull_request ] 4 + 5 + jobs: 6 + build: 7 + uses: constellation-mc/actions/.github/workflows/gradle-build.yml@main 8 + with: 9 + java: 21 10 + gradle_tasks: spotbugs 11 + reviewdog: | 12 + cat ./build/reports/spotbugs/main/spotbugs.sarif | reviewdog -name="spotbugs" -f=sarif -reporter=github-check -level=warning 13 + 14 + run_tests: 15 + uses: constellation-mc/actions/.github/workflows/mc-tests.yml@main 16 + with: 17 + java: 21 18 + server_task: runTestServer
-63
.github/workflows/deploy.yml
··· 1 - # Sample workflow for building and deploying a VitePress site to GitHub Pages 2 - # 3 - name: Deploy VitePress site to Pages 4 - 5 - on: 6 - # Runs on pushes targeting the `main` branch. Change this to `master` if you're 7 - # using the `master` branch as the default branch. 8 - push: 9 - branches: [documentation] 10 - 11 - # Allows you to run this workflow manually from the Actions tab 12 - workflow_dispatch: 13 - 14 - # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 15 - permissions: 16 - contents: read 17 - pages: write 18 - id-token: write 19 - 20 - # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 21 - # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 22 - concurrency: 23 - group: pages 24 - cancel-in-progress: false 25 - 26 - jobs: 27 - # Build job 28 - build: 29 - runs-on: ubuntu-latest 30 - steps: 31 - - name: Checkout 32 - uses: actions/checkout@v4 33 - with: 34 - fetch-depth: 0 # Not needed if lastUpdated is not enabled 35 - # - uses: pnpm/action-setup@v3 # Uncomment this if you're using pnpm 36 - - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun 37 - - name: Setup Node 38 - uses: actions/setup-node@v4 39 - with: 40 - node-version: 20 41 - - name: Setup Pages 42 - uses: actions/configure-pages@v4 43 - - name: Install dependencies 44 - run: bun install # or pnpm install / yarn install / bun install 45 - - name: Build with VitePress 46 - run: bun run docs:build # or pnpm docs:build / yarn docs:build / bun run docs:build 47 - - name: Upload artifact 48 - uses: actions/upload-pages-artifact@v3 49 - with: 50 - path: docs/.vitepress/dist 51 - 52 - # Deployment job 53 - deploy: 54 - environment: 55 - name: github-pages 56 - url: ${{ steps.deployment.outputs.page_url }} 57 - needs: build 58 - runs-on: ubuntu-latest 59 - name: Deploy 60 - steps: 61 - - name: Deploy to GitHub Pages 62 - id: deployment 63 - uses: actions/deploy-pages@v4
+23
.github/workflows/publish.yml
··· 1 + 2 + name: publish 3 + on: 4 + workflow_dispatch: 5 + inputs: 6 + version_type: 7 + description: "The type of this version. e.g alpha" 8 + type: choice 9 + default: BETA 10 + options: 11 + - STABLE 12 + - BETA 13 + - ALPHA 14 + - NONE 15 + required: false 16 + 17 + jobs: 18 + publish: 19 + uses: constellation-mc/actions/.github/workflows/mc-publish.yml@main 20 + with: 21 + java: 21 22 + version_type: ${{ inputs.version_type }} 23 + secrets: inherit
+121 -3
.gitignore
··· 1 - node_modules 2 - docs/.vitepress/cache/* 3 - docs/.vitepress/dist/* 1 + # User-specific stuff 2 + .idea/ 3 + 4 + *.iml 5 + *.ipr 6 + *.iws 7 + 8 + # IntelliJ 9 + out/ 10 + # mpeltonen/sbt-idea plugin 11 + .idea_modules/ 12 + 13 + # JIRA plugin 14 + atlassian-ide-plugin.xml 15 + 16 + # Compiled class file 17 + *.class 18 + 19 + # Log file 20 + *.log 21 + 22 + # BlueJ files 23 + *.ctxt 24 + 25 + # Package Files # 26 + *.jar 27 + *.war 28 + *.nar 29 + *.ear 30 + *.zip 31 + *.tar.gz 32 + *.rar 33 + 34 + # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 35 + hs_err_pid* 36 + 37 + *~ 38 + 39 + # temporary files which can be created if a process still has a handle open of a deleted file 40 + .fuse_hidden* 41 + 42 + # KDE directory preferences 43 + .directory 44 + 45 + # Linux trash folder which might appear on any partition or disk 46 + .Trash-* 47 + 48 + # .nfs files are created when an open file is removed but is still being accessed 49 + .nfs* 50 + 51 + # General 52 + .DS_Store 53 + .AppleDouble 54 + .LSOverride 55 + 56 + # Icon must end with two \r 57 + Icon 58 + 59 + # Thumbnails 60 + ._* 61 + 62 + # Files that might appear in the root of a volume 63 + .DocumentRevisions-V100 64 + .fseventsd 65 + .Spotlight-V100 66 + .TemporaryItems 67 + .Trashes 68 + .VolumeIcon.icns 69 + .com.apple.timemachine.donotpresent 70 + 71 + # Directories potentially created on remote AFP share 72 + .AppleDB 73 + .AppleDesktop 74 + Network Trash Folder 75 + Temporary Items 76 + .apdisk 77 + 78 + # Windows thumbnail cache files 79 + Thumbs.db 80 + Thumbs.db:encryptable 81 + ehthumbs.db 82 + ehthumbs_vista.db 83 + 84 + # Dump file 85 + *.stackdump 86 + 87 + # Folder config file 88 + [Dd]esktop.ini 89 + 90 + # Recycle Bin used on file shares 91 + $RECYCLE.BIN/ 92 + 93 + # Windows Installer files 94 + *.cab 95 + *.msi 96 + *.msix 97 + *.msm 98 + *.msp 99 + 100 + # Windows shortcuts 101 + *.lnk 102 + 103 + .gradle 104 + build/ 105 + 106 + # Ignore Gradle GUI config 107 + gradle-app.setting 108 + 109 + # Cache of project 110 + .gradletasknamecache 111 + 112 + **/build/ 113 + 114 + # Common working directory 115 + run/ 116 + run_test_server/ 117 + 118 + # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 119 + !gradle-wrapper.jar 120 + 121 + src/main/resources/data/commander/commander/events/test/
+43
CHANGELOG.md
··· 1 + ### What's New: 2 + 3 + User Changes: 4 + 5 + * Added the (data pack) expression library! 6 + 7 + This feature allows adding common expressions to a library where each is identified using an Identifier. 8 + 9 + ```json5 10 + { 11 + "replace": false, // Can be omitted. 12 + "expressions": { 13 + "test:cool_expression": "score * 2.5", 14 + "test:boolean_expression": "level.isDay && !level.isRaining" 15 + } 16 + } 17 + ``` 18 + 19 + Later on, these expressions can be evaluated inside other expressions using the library container, like so: 20 + 21 + ``` 22 + sqrt(library.my_id:some_value) * library.my_id:other_value 23 + ``` 24 + 25 + Or in `execute`/`scoreboard` brigadier commands using `cmd:library`. 26 + Prefer using the library in Brigadier commands! 27 + It does not require parsing the expression in-place! 28 + 29 + * Brigadier macros in JSON commands should now correctly fail on dangling braces. 30 + 31 + Dev Changes: 32 + 33 + * Added more javadoc to the `api` package. 34 + * Removed `evalex` and `mapping-io` from pom.xml 35 + * Added `LongExpression`. Similar to `Arithmetica` and `BooleanExpression`, but for longs! 36 + * Tried to fix expression equality. 37 + * Added missing 'parameter' methods to `Arithmetica`, `BooleanExpression` and `BrigadierMacro`. 38 + * Added `Expression.Result#NULL`. 39 + 40 + Other Changes: 41 + 42 + * The mod should now fail with slightly better error messages. 43 + * Inlined constant `BooleanExpression` instances.
+21
LICENSE
··· 1 + The MIT License (MIT) 2 + 3 + Copyright (c) 2024 melontini 4 + 5 + Permission is hereby granted, free of charge, to any person obtaining a copy 6 + of this software and associated documentation files (the "Software"), to deal 7 + in the Software without restriction, including without limitation the rights 8 + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 + copies of the Software, and to permit persons to whom the Software is 10 + furnished to do so, subject to the following conditions: 11 + 12 + The above copyright notice and this permission notice shall be included in 13 + all copies or substantial portions of the Software. 14 + 15 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 + THE SOFTWARE.
-438
LICENSE.txt
··· 1 - Attribution-NonCommercial-ShareAlike 4.0 International 2 - 3 - ======================================================================= 4 - 5 - Creative Commons Corporation ("Creative Commons") is not a law firm and 6 - does not provide legal services or legal advice. Distribution of 7 - Creative Commons public licenses does not create a lawyer-client or 8 - other relationship. Creative Commons makes its licenses and related 9 - information available on an "as-is" basis. Creative Commons gives no 10 - warranties regarding its licenses, any material licensed under their 11 - terms and conditions, or any related information. Creative Commons 12 - disclaims all liability for damages resulting from their use to the 13 - fullest extent possible. 14 - 15 - Using Creative Commons Public Licenses 16 - 17 - Creative Commons public licenses provide a standard set of terms and 18 - conditions that creators and other rights holders may use to share 19 - original works of authorship and other material subject to copyright 20 - and certain other rights specified in the public license below. The 21 - following considerations are for informational purposes only, are not 22 - exhaustive, and do not form part of our licenses. 23 - 24 - Considerations for licensors: Our public licenses are 25 - intended for use by those authorized to give the public 26 - permission to use material in ways otherwise restricted by 27 - copyright and certain other rights. Our licenses are 28 - irrevocable. Licensors should read and understand the terms 29 - and conditions of the license they choose before applying it. 30 - Licensors should also secure all rights necessary before 31 - applying our licenses so that the public can reuse the 32 - material as expected. Licensors should clearly mark any 33 - material not subject to the license. This includes other CC- 34 - licensed material, or material used under an exception or 35 - limitation to copyright. More considerations for licensors: 36 - wiki.creativecommons.org/Considerations_for_licensors 37 - 38 - Considerations for the public: By using one of our public 39 - licenses, a licensor grants the public permission to use the 40 - licensed material under specified terms and conditions. If 41 - the licensor's permission is not necessary for any reason--for 42 - example, because of any applicable exception or limitation to 43 - copyright--then that use is not regulated by the license. Our 44 - licenses grant only permissions under copyright and certain 45 - other rights that a licensor has authority to grant. Use of 46 - the licensed material may still be restricted for other 47 - reasons, including because others have copyright or other 48 - rights in the material. A licensor may make special requests, 49 - such as asking that all changes be marked or described. 50 - Although not required by our licenses, you are encouraged to 51 - respect those requests where reasonable. More considerations 52 - for the public: 53 - wiki.creativecommons.org/Considerations_for_licensees 54 - 55 - ======================================================================= 56 - 57 - Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International 58 - Public License 59 - 60 - By exercising the Licensed Rights (defined below), You accept and agree 61 - to be bound by the terms and conditions of this Creative Commons 62 - Attribution-NonCommercial-ShareAlike 4.0 International Public License 63 - ("Public License"). To the extent this Public License may be 64 - interpreted as a contract, You are granted the Licensed Rights in 65 - consideration of Your acceptance of these terms and conditions, and the 66 - Licensor grants You such rights in consideration of benefits the 67 - Licensor receives from making the Licensed Material available under 68 - these terms and conditions. 69 - 70 - 71 - Section 1 -- Definitions. 72 - 73 - a. Adapted Material means material subject to Copyright and Similar 74 - Rights that is derived from or based upon the Licensed Material 75 - and in which the Licensed Material is translated, altered, 76 - arranged, transformed, or otherwise modified in a manner requiring 77 - permission under the Copyright and Similar Rights held by the 78 - Licensor. For purposes of this Public License, where the Licensed 79 - Material is a musical work, performance, or sound recording, 80 - Adapted Material is always produced where the Licensed Material is 81 - synched in timed relation with a moving image. 82 - 83 - b. Adapter's License means the license You apply to Your Copyright 84 - and Similar Rights in Your contributions to Adapted Material in 85 - accordance with the terms and conditions of this Public License. 86 - 87 - c. BY-NC-SA Compatible License means a license listed at 88 - creativecommons.org/compatiblelicenses, approved by Creative 89 - Commons as essentially the equivalent of this Public License. 90 - 91 - d. Copyright and Similar Rights means copyright and/or similar rights 92 - closely related to copyright including, without limitation, 93 - performance, broadcast, sound recording, and Sui Generis Database 94 - Rights, without regard to how the rights are labeled or 95 - categorized. For purposes of this Public License, the rights 96 - specified in Section 2(b)(1)-(2) are not Copyright and Similar 97 - Rights. 98 - 99 - e. Effective Technological Measures means those measures that, in the 100 - absence of proper authority, may not be circumvented under laws 101 - fulfilling obligations under Article 11 of the WIPO Copyright 102 - Treaty adopted on December 20, 1996, and/or similar international 103 - agreements. 104 - 105 - f. Exceptions and Limitations means fair use, fair dealing, and/or 106 - any other exception or limitation to Copyright and Similar Rights 107 - that applies to Your use of the Licensed Material. 108 - 109 - g. License Elements means the license attributes listed in the name 110 - of a Creative Commons Public License. The License Elements of this 111 - Public License are Attribution, NonCommercial, and ShareAlike. 112 - 113 - h. Licensed Material means the artistic or literary work, database, 114 - or other material to which the Licensor applied this Public 115 - License. 116 - 117 - i. Licensed Rights means the rights granted to You subject to the 118 - terms and conditions of this Public License, which are limited to 119 - all Copyright and Similar Rights that apply to Your use of the 120 - Licensed Material and that the Licensor has authority to license. 121 - 122 - j. Licensor means the individual(s) or entity(ies) granting rights 123 - under this Public License. 124 - 125 - k. NonCommercial means not primarily intended for or directed towards 126 - commercial advantage or monetary compensation. For purposes of 127 - this Public License, the exchange of the Licensed Material for 128 - other material subject to Copyright and Similar Rights by digital 129 - file-sharing or similar means is NonCommercial provided there is 130 - no payment of monetary compensation in connection with the 131 - exchange. 132 - 133 - l. Share means to provide material to the public by any means or 134 - process that requires permission under the Licensed Rights, such 135 - as reproduction, public display, public performance, distribution, 136 - dissemination, communication, or importation, and to make material 137 - available to the public including in ways that members of the 138 - public may access the material from a place and at a time 139 - individually chosen by them. 140 - 141 - m. Sui Generis Database Rights means rights other than copyright 142 - resulting from Directive 96/9/EC of the European Parliament and of 143 - the Council of 11 March 1996 on the legal protection of databases, 144 - as amended and/or succeeded, as well as other essentially 145 - equivalent rights anywhere in the world. 146 - 147 - n. You means the individual or entity exercising the Licensed Rights 148 - under this Public License. Your has a corresponding meaning. 149 - 150 - 151 - Section 2 -- Scope. 152 - 153 - a. License grant. 154 - 155 - 1. Subject to the terms and conditions of this Public License, 156 - the Licensor hereby grants You a worldwide, royalty-free, 157 - non-sublicensable, non-exclusive, irrevocable license to 158 - exercise the Licensed Rights in the Licensed Material to: 159 - 160 - a. reproduce and Share the Licensed Material, in whole or 161 - in part, for NonCommercial purposes only; and 162 - 163 - b. produce, reproduce, and Share Adapted Material for 164 - NonCommercial purposes only. 165 - 166 - 2. Exceptions and Limitations. For the avoidance of doubt, where 167 - Exceptions and Limitations apply to Your use, this Public 168 - License does not apply, and You do not need to comply with 169 - its terms and conditions. 170 - 171 - 3. Term. The term of this Public License is specified in Section 172 - 6(a). 173 - 174 - 4. Media and formats; technical modifications allowed. The 175 - Licensor authorizes You to exercise the Licensed Rights in 176 - all media and formats whether now known or hereafter created, 177 - and to make technical modifications necessary to do so. The 178 - Licensor waives and/or agrees not to assert any right or 179 - authority to forbid You from making technical modifications 180 - necessary to exercise the Licensed Rights, including 181 - technical modifications necessary to circumvent Effective 182 - Technological Measures. For purposes of this Public License, 183 - simply making modifications authorized by this Section 2(a) 184 - (4) never produces Adapted Material. 185 - 186 - 5. Downstream recipients. 187 - 188 - a. Offer from the Licensor -- Licensed Material. Every 189 - recipient of the Licensed Material automatically 190 - receives an offer from the Licensor to exercise the 191 - Licensed Rights under the terms and conditions of this 192 - Public License. 193 - 194 - b. Additional offer from the Licensor -- Adapted Material. 195 - Every recipient of Adapted Material from You 196 - automatically receives an offer from the Licensor to 197 - exercise the Licensed Rights in the Adapted Material 198 - under the conditions of the Adapter's License You apply. 199 - 200 - c. No downstream restrictions. You may not offer or impose 201 - any additional or different terms or conditions on, or 202 - apply any Effective Technological Measures to, the 203 - Licensed Material if doing so restricts exercise of the 204 - Licensed Rights by any recipient of the Licensed 205 - Material. 206 - 207 - 6. No endorsement. Nothing in this Public License constitutes or 208 - may be construed as permission to assert or imply that You 209 - are, or that Your use of the Licensed Material is, connected 210 - with, or sponsored, endorsed, or granted official status by, 211 - the Licensor or others designated to receive attribution as 212 - provided in Section 3(a)(1)(A)(i). 213 - 214 - b. Other rights. 215 - 216 - 1. Moral rights, such as the right of integrity, are not 217 - licensed under this Public License, nor are publicity, 218 - privacy, and/or other similar personality rights; however, to 219 - the extent possible, the Licensor waives and/or agrees not to 220 - assert any such rights held by the Licensor to the limited 221 - extent necessary to allow You to exercise the Licensed 222 - Rights, but not otherwise. 223 - 224 - 2. Patent and trademark rights are not licensed under this 225 - Public License. 226 - 227 - 3. To the extent possible, the Licensor waives any right to 228 - collect royalties from You for the exercise of the Licensed 229 - Rights, whether directly or through a collecting society 230 - under any voluntary or waivable statutory or compulsory 231 - licensing scheme. In all other cases the Licensor expressly 232 - reserves any right to collect such royalties, including when 233 - the Licensed Material is used other than for NonCommercial 234 - purposes. 235 - 236 - 237 - Section 3 -- License Conditions. 238 - 239 - Your exercise of the Licensed Rights is expressly made subject to the 240 - following conditions. 241 - 242 - a. Attribution. 243 - 244 - 1. If You Share the Licensed Material (including in modified 245 - form), You must: 246 - 247 - a. retain the following if it is supplied by the Licensor 248 - with the Licensed Material: 249 - 250 - i. identification of the creator(s) of the Licensed 251 - Material and any others designated to receive 252 - attribution, in any reasonable manner requested by 253 - the Licensor (including by pseudonym if 254 - designated); 255 - 256 - ii. a copyright notice; 257 - 258 - iii. a notice that refers to this Public License; 259 - 260 - iv. a notice that refers to the disclaimer of 261 - warranties; 262 - 263 - v. a URI or hyperlink to the Licensed Material to the 264 - extent reasonably practicable; 265 - 266 - b. indicate if You modified the Licensed Material and 267 - retain an indication of any previous modifications; and 268 - 269 - c. indicate the Licensed Material is licensed under this 270 - Public License, and include the text of, or the URI or 271 - hyperlink to, this Public License. 272 - 273 - 2. You may satisfy the conditions in Section 3(a)(1) in any 274 - reasonable manner based on the medium, means, and context in 275 - which You Share the Licensed Material. For example, it may be 276 - reasonable to satisfy the conditions by providing a URI or 277 - hyperlink to a resource that includes the required 278 - information. 279 - 3. If requested by the Licensor, You must remove any of the 280 - information required by Section 3(a)(1)(A) to the extent 281 - reasonably practicable. 282 - 283 - b. ShareAlike. 284 - 285 - In addition to the conditions in Section 3(a), if You Share 286 - Adapted Material You produce, the following conditions also apply. 287 - 288 - 1. The Adapter's License You apply must be a Creative Commons 289 - license with the same License Elements, this version or 290 - later, or a BY-NC-SA Compatible License. 291 - 292 - 2. You must include the text of, or the URI or hyperlink to, the 293 - Adapter's License You apply. You may satisfy this condition 294 - in any reasonable manner based on the medium, means, and 295 - context in which You Share Adapted Material. 296 - 297 - 3. You may not offer or impose any additional or different terms 298 - or conditions on, or apply any Effective Technological 299 - Measures to, Adapted Material that restrict exercise of the 300 - rights granted under the Adapter's License You apply. 301 - 302 - 303 - Section 4 -- Sui Generis Database Rights. 304 - 305 - Where the Licensed Rights include Sui Generis Database Rights that 306 - apply to Your use of the Licensed Material: 307 - 308 - a. for the avoidance of doubt, Section 2(a)(1) grants You the right 309 - to extract, reuse, reproduce, and Share all or a substantial 310 - portion of the contents of the database for NonCommercial purposes 311 - only; 312 - 313 - b. if You include all or a substantial portion of the database 314 - contents in a database in which You have Sui Generis Database 315 - Rights, then the database in which You have Sui Generis Database 316 - Rights (but not its individual contents) is Adapted Material, 317 - including for purposes of Section 3(b); and 318 - 319 - c. You must comply with the conditions in Section 3(a) if You Share 320 - all or a substantial portion of the contents of the database. 321 - 322 - For the avoidance of doubt, this Section 4 supplements and does not 323 - replace Your obligations under this Public License where the Licensed 324 - Rights include other Copyright and Similar Rights. 325 - 326 - 327 - Section 5 -- Disclaimer of Warranties and Limitation of Liability. 328 - 329 - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 330 - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 331 - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 332 - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 333 - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 334 - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 335 - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 336 - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 337 - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 338 - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 339 - 340 - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 341 - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 342 - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 343 - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 344 - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 345 - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 346 - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 347 - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 348 - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 349 - 350 - c. The disclaimer of warranties and limitation of liability provided 351 - above shall be interpreted in a manner that, to the extent 352 - possible, most closely approximates an absolute disclaimer and 353 - waiver of all liability. 354 - 355 - 356 - Section 6 -- Term and Termination. 357 - 358 - a. This Public License applies for the term of the Copyright and 359 - Similar Rights licensed here. However, if You fail to comply with 360 - this Public License, then Your rights under this Public License 361 - terminate automatically. 362 - 363 - b. Where Your right to use the Licensed Material has terminated under 364 - Section 6(a), it reinstates: 365 - 366 - 1. automatically as of the date the violation is cured, provided 367 - it is cured within 30 days of Your discovery of the 368 - violation; or 369 - 370 - 2. upon express reinstatement by the Licensor. 371 - 372 - For the avoidance of doubt, this Section 6(b) does not affect any 373 - right the Licensor may have to seek remedies for Your violations 374 - of this Public License. 375 - 376 - c. For the avoidance of doubt, the Licensor may also offer the 377 - Licensed Material under separate terms or conditions or stop 378 - distributing the Licensed Material at any time; however, doing so 379 - will not terminate this Public License. 380 - 381 - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 382 - License. 383 - 384 - 385 - Section 7 -- Other Terms and Conditions. 386 - 387 - a. The Licensor shall not be bound by any additional or different 388 - terms or conditions communicated by You unless expressly agreed. 389 - 390 - b. Any arrangements, understandings, or agreements regarding the 391 - Licensed Material not stated herein are separate from and 392 - independent of the terms and conditions of this Public License. 393 - 394 - 395 - Section 8 -- Interpretation. 396 - 397 - a. For the avoidance of doubt, this Public License does not, and 398 - shall not be interpreted to, reduce, limit, restrict, or impose 399 - conditions on any use of the Licensed Material that could lawfully 400 - be made without permission under this Public License. 401 - 402 - b. To the extent possible, if any provision of this Public License is 403 - deemed unenforceable, it shall be automatically reformed to the 404 - minimum extent necessary to make it enforceable. If the provision 405 - cannot be reformed, it shall be severed from this Public License 406 - without affecting the enforceability of the remaining terms and 407 - conditions. 408 - 409 - c. No term or condition of this Public License will be waived and no 410 - failure to comply consented to unless expressly agreed to by the 411 - Licensor. 412 - 413 - d. Nothing in this Public License constitutes or may be interpreted 414 - as a limitation upon, or waiver of, any privileges and immunities 415 - that apply to the Licensor or You, including from the legal 416 - processes of any jurisdiction or authority. 417 - 418 - ======================================================================= 419 - 420 - Creative Commons is not a party to its public 421 - licenses. Notwithstanding, Creative Commons may elect to apply one of 422 - its public licenses to material it publishes and in those instances 423 - will be considered the โ€œLicensor.โ€ The text of the Creative Commons 424 - public licenses is dedicated to the public domain under the CC0 Public 425 - Domain Dedication. Except for the limited purpose of indicating that 426 - material is shared under a Creative Commons public license or as 427 - otherwise permitted by the Creative Commons policies published at 428 - creativecommons.org/policies, Creative Commons does not authorize the 429 - use of the trademark "Creative Commons" or any other trademark or logo 430 - of Creative Commons without its prior written consent including, 431 - without limitation, in connection with any unauthorized modifications 432 - to any of its public licenses or any other arrangements, 433 - understandings, or agreements concerning use of licensed material. For 434 - the avoidance of doubt, this paragraph does not form part of the 435 - public licenses. 436 - 437 - Creative Commons may be contacted at creativecommons.org. 438 -
+201
LICENSE_EvalEx
··· 1 + Apache License 2 + Version 2.0, January 2004 3 + http://www.apache.org/licenses/ 4 + 5 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 + 7 + 1. Definitions. 8 + 9 + "License" shall mean the terms and conditions for use, reproduction, 10 + and distribution as defined by Sections 1 through 9 of this document. 11 + 12 + "Licensor" shall mean the copyright owner or entity authorized by 13 + the copyright owner that is granting the License. 14 + 15 + "Legal Entity" shall mean the union of the acting entity and all 16 + other entities that control, are controlled by, or are under common 17 + control with that entity. For the purposes of this definition, 18 + "control" means (i) the power, direct or indirect, to cause the 19 + direction or management of such entity, whether by contract or 20 + otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 + outstanding shares, or (iii) beneficial ownership of such entity. 22 + 23 + "You" (or "Your") shall mean an individual or Legal Entity 24 + exercising permissions granted by this License. 25 + 26 + "Source" form shall mean the preferred form for making modifications, 27 + including but not limited to software source code, documentation 28 + source, and configuration files. 29 + 30 + "Object" form shall mean any form resulting from mechanical 31 + transformation or translation of a Source form, including but 32 + not limited to compiled object code, generated documentation, 33 + and conversions to other media types. 34 + 35 + "Work" shall mean the work of authorship, whether in Source or 36 + Object form, made available under the License, as indicated by a 37 + copyright notice that is included in or attached to the work 38 + (an example is provided in the Appendix below). 39 + 40 + "Derivative Works" shall mean any work, whether in Source or Object 41 + form, that is based on (or derived from) the Work and for which the 42 + editorial revisions, annotations, elaborations, or other modifications 43 + represent, as a whole, an original work of authorship. For the purposes 44 + of this License, Derivative Works shall not include works that remain 45 + separable from, or merely link (or bind by name) to the interfaces of, 46 + the Work and Derivative Works thereof. 47 + 48 + "Contribution" shall mean any work of authorship, including 49 + the original version of the Work and any modifications or additions 50 + to that Work or Derivative Works thereof, that is intentionally 51 + submitted to Licensor for inclusion in the Work by the copyright owner 52 + or by an individual or Legal Entity authorized to submit on behalf of 53 + the copyright owner. For the purposes of this definition, "submitted" 54 + means any form of electronic, verbal, or written communication sent 55 + to the Licensor or its representatives, including but not limited to 56 + communication on electronic mailing lists, source code control systems, 57 + and issue tracking systems that are managed by, or on behalf of, the 58 + Licensor for the purpose of discussing and improving the Work, but 59 + excluding communication that is conspicuously marked or otherwise 60 + designated in writing by the copyright owner as "Not a Contribution." 61 + 62 + "Contributor" shall mean Licensor and any individual or Legal Entity 63 + on behalf of whom a Contribution has been received by Licensor and 64 + subsequently incorporated within the Work. 65 + 66 + 2. Grant of Copyright License. Subject to the terms and conditions of 67 + this License, each Contributor hereby grants to You a perpetual, 68 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 + copyright license to reproduce, prepare Derivative Works of, 70 + publicly display, publicly perform, sublicense, and distribute the 71 + Work and such Derivative Works in Source or Object form. 72 + 73 + 3. Grant of Patent License. Subject to the terms and conditions of 74 + this License, each Contributor hereby grants to You a perpetual, 75 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 + (except as stated in this section) patent license to make, have made, 77 + use, offer to sell, sell, import, and otherwise transfer the Work, 78 + where such license applies only to those patent claims licensable 79 + by such Contributor that are necessarily infringed by their 80 + Contribution(s) alone or by combination of their Contribution(s) 81 + with the Work to which such Contribution(s) was submitted. If You 82 + institute patent litigation against any entity (including a 83 + cross-claim or counterclaim in a lawsuit) alleging that the Work 84 + or a Contribution incorporated within the Work constitutes direct 85 + or contributory patent infringement, then any patent licenses 86 + granted to You under this License for that Work shall terminate 87 + as of the date such litigation is filed. 88 + 89 + 4. Redistribution. You may reproduce and distribute copies of the 90 + Work or Derivative Works thereof in any medium, with or without 91 + modifications, and in Source or Object form, provided that You 92 + meet the following conditions: 93 + 94 + (a) You must give any other recipients of the Work or 95 + Derivative Works a copy of this License; and 96 + 97 + (b) You must cause any modified files to carry prominent notices 98 + stating that You changed the files; and 99 + 100 + (c) You must retain, in the Source form of any Derivative Works 101 + that You distribute, all copyright, patent, trademark, and 102 + attribution notices from the Source form of the Work, 103 + excluding those notices that do not pertain to any part of 104 + the Derivative Works; and 105 + 106 + (d) If the Work includes a "NOTICE" text file as part of its 107 + distribution, then any Derivative Works that You distribute must 108 + include a readable copy of the attribution notices contained 109 + within such NOTICE file, excluding those notices that do not 110 + pertain to any part of the Derivative Works, in at least one 111 + of the following places: within a NOTICE text file distributed 112 + as part of the Derivative Works; within the Source form or 113 + documentation, if provided along with the Derivative Works; or, 114 + within a display generated by the Derivative Works, if and 115 + wherever such third-party notices normally appear. The contents 116 + of the NOTICE file are for informational purposes only and 117 + do not modify the License. You may add Your own attribution 118 + notices within Derivative Works that You distribute, alongside 119 + or as an addendum to the NOTICE text from the Work, provided 120 + that such additional attribution notices cannot be construed 121 + as modifying the License. 122 + 123 + You may add Your own copyright statement to Your modifications and 124 + may provide additional or different license terms and conditions 125 + for use, reproduction, or distribution of Your modifications, or 126 + for any such Derivative Works as a whole, provided Your use, 127 + reproduction, and distribution of the Work otherwise complies with 128 + the conditions stated in this License. 129 + 130 + 5. Submission of Contributions. Unless You explicitly state otherwise, 131 + any Contribution intentionally submitted for inclusion in the Work 132 + by You to the Licensor shall be under the terms and conditions of 133 + this License, without any additional terms or conditions. 134 + Notwithstanding the above, nothing herein shall supersede or modify 135 + the terms of any separate license agreement you may have executed 136 + with Licensor regarding such Contributions. 137 + 138 + 6. Trademarks. This License does not grant permission to use the trade 139 + names, trademarks, service marks, or product names of the Licensor, 140 + except as required for reasonable and customary use in describing the 141 + origin of the Work and reproducing the content of the NOTICE file. 142 + 143 + 7. Disclaimer of Warranty. Unless required by applicable law or 144 + agreed to in writing, Licensor provides the Work (and each 145 + Contributor provides its Contributions) on an "AS IS" BASIS, 146 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 + implied, including, without limitation, any warranties or conditions 148 + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 + PARTICULAR PURPOSE. You are solely responsible for determining the 150 + appropriateness of using or redistributing the Work and assume any 151 + risks associated with Your exercise of permissions under this License. 152 + 153 + 8. Limitation of Liability. In no event and under no legal theory, 154 + whether in tort (including negligence), contract, or otherwise, 155 + unless required by applicable law (such as deliberate and grossly 156 + negligent acts) or agreed to in writing, shall any Contributor be 157 + liable to You for damages, including any direct, indirect, special, 158 + incidental, or consequential damages of any character arising as a 159 + result of this License or out of the use or inability to use the 160 + Work (including but not limited to damages for loss of goodwill, 161 + work stoppage, computer failure or malfunction, or any and all 162 + other commercial damages or losses), even if such Contributor 163 + has been advised of the possibility of such damages. 164 + 165 + 9. Accepting Warranty or Additional Liability. While redistributing 166 + the Work or Derivative Works thereof, You may choose to offer, 167 + and charge a fee for, acceptance of support, warranty, indemnity, 168 + or other liability obligations and/or rights consistent with this 169 + License. However, in accepting such obligations, You may act only 170 + on Your own behalf and on Your sole responsibility, not on behalf 171 + of any other Contributor, and only if You agree to indemnify, 172 + defend, and hold each Contributor harmless for any liability 173 + incurred by, or claims asserted against, such Contributor by reason 174 + of your accepting any such warranty or additional liability. 175 + 176 + END OF TERMS AND CONDITIONS 177 + 178 + APPENDIX: How to apply the Apache License to your work. 179 + 180 + To apply the Apache License to your work, attach the following 181 + boilerplate notice, with the fields enclosed by brackets "[]" 182 + replaced with your own identifying information. (Don't include 183 + the brackets!) The text should be enclosed in the appropriate 184 + comment syntax for the file format. We also recommend that a 185 + file or class name and description of purpose be included on the 186 + same "printed page" as the copyright notice for easier 187 + identification within third-party archives. 188 + 189 + Copyright [yyyy] [name of copyright owner] 190 + 191 + Licensed under the Apache License, Version 2.0 (the "License"); 192 + you may not use this file except in compliance with the License. 193 + You may obtain a copy of the License at 194 + 195 + http://www.apache.org/licenses/LICENSE-2.0 196 + 197 + Unless required by applicable law or agreed to in writing, software 198 + distributed under the License is distributed on an "AS IS" BASIS, 199 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 + See the License for the specific language governing permissions and 201 + limitations under the License.
+201
LICENSE_mapping-io
··· 1 + Apache License 2 + Version 2.0, January 2004 3 + http://www.apache.org/licenses/ 4 + 5 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 + 7 + 1. Definitions. 8 + 9 + "License" shall mean the terms and conditions for use, reproduction, 10 + and distribution as defined by Sections 1 through 9 of this document. 11 + 12 + "Licensor" shall mean the copyright owner or entity authorized by 13 + the copyright owner that is granting the License. 14 + 15 + "Legal Entity" shall mean the union of the acting entity and all 16 + other entities that control, are controlled by, or are under common 17 + control with that entity. For the purposes of this definition, 18 + "control" means (i) the power, direct or indirect, to cause the 19 + direction or management of such entity, whether by contract or 20 + otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 + outstanding shares, or (iii) beneficial ownership of such entity. 22 + 23 + "You" (or "Your") shall mean an individual or Legal Entity 24 + exercising permissions granted by this License. 25 + 26 + "Source" form shall mean the preferred form for making modifications, 27 + including but not limited to software source code, documentation 28 + source, and configuration files. 29 + 30 + "Object" form shall mean any form resulting from mechanical 31 + transformation or translation of a Source form, including but 32 + not limited to compiled object code, generated documentation, 33 + and conversions to other media types. 34 + 35 + "Work" shall mean the work of authorship, whether in Source or 36 + Object form, made available under the License, as indicated by a 37 + copyright notice that is included in or attached to the work 38 + (an example is provided in the Appendix below). 39 + 40 + "Derivative Works" shall mean any work, whether in Source or Object 41 + form, that is based on (or derived from) the Work and for which the 42 + editorial revisions, annotations, elaborations, or other modifications 43 + represent, as a whole, an original work of authorship. For the purposes 44 + of this License, Derivative Works shall not include works that remain 45 + separable from, or merely link (or bind by name) to the interfaces of, 46 + the Work and Derivative Works thereof. 47 + 48 + "Contribution" shall mean any work of authorship, including 49 + the original version of the Work and any modifications or additions 50 + to that Work or Derivative Works thereof, that is intentionally 51 + submitted to Licensor for inclusion in the Work by the copyright owner 52 + or by an individual or Legal Entity authorized to submit on behalf of 53 + the copyright owner. For the purposes of this definition, "submitted" 54 + means any form of electronic, verbal, or written communication sent 55 + to the Licensor or its representatives, including but not limited to 56 + communication on electronic mailing lists, source code control systems, 57 + and issue tracking systems that are managed by, or on behalf of, the 58 + Licensor for the purpose of discussing and improving the Work, but 59 + excluding communication that is conspicuously marked or otherwise 60 + designated in writing by the copyright owner as "Not a Contribution." 61 + 62 + "Contributor" shall mean Licensor and any individual or Legal Entity 63 + on behalf of whom a Contribution has been received by Licensor and 64 + subsequently incorporated within the Work. 65 + 66 + 2. Grant of Copyright License. Subject to the terms and conditions of 67 + this License, each Contributor hereby grants to You a perpetual, 68 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 + copyright license to reproduce, prepare Derivative Works of, 70 + publicly display, publicly perform, sublicense, and distribute the 71 + Work and such Derivative Works in Source or Object form. 72 + 73 + 3. Grant of Patent License. Subject to the terms and conditions of 74 + this License, each Contributor hereby grants to You a perpetual, 75 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 + (except as stated in this section) patent license to make, have made, 77 + use, offer to sell, sell, import, and otherwise transfer the Work, 78 + where such license applies only to those patent claims licensable 79 + by such Contributor that are necessarily infringed by their 80 + Contribution(s) alone or by combination of their Contribution(s) 81 + with the Work to which such Contribution(s) was submitted. If You 82 + institute patent litigation against any entity (including a 83 + cross-claim or counterclaim in a lawsuit) alleging that the Work 84 + or a Contribution incorporated within the Work constitutes direct 85 + or contributory patent infringement, then any patent licenses 86 + granted to You under this License for that Work shall terminate 87 + as of the date such litigation is filed. 88 + 89 + 4. Redistribution. You may reproduce and distribute copies of the 90 + Work or Derivative Works thereof in any medium, with or without 91 + modifications, and in Source or Object form, provided that You 92 + meet the following conditions: 93 + 94 + (a) You must give any other recipients of the Work or 95 + Derivative Works a copy of this License; and 96 + 97 + (b) You must cause any modified files to carry prominent notices 98 + stating that You changed the files; and 99 + 100 + (c) You must retain, in the Source form of any Derivative Works 101 + that You distribute, all copyright, patent, trademark, and 102 + attribution notices from the Source form of the Work, 103 + excluding those notices that do not pertain to any part of 104 + the Derivative Works; and 105 + 106 + (d) If the Work includes a "NOTICE" text file as part of its 107 + distribution, then any Derivative Works that You distribute must 108 + include a readable copy of the attribution notices contained 109 + within such NOTICE file, excluding those notices that do not 110 + pertain to any part of the Derivative Works, in at least one 111 + of the following places: within a NOTICE text file distributed 112 + as part of the Derivative Works; within the Source form or 113 + documentation, if provided along with the Derivative Works; or, 114 + within a display generated by the Derivative Works, if and 115 + wherever such third-party notices normally appear. The contents 116 + of the NOTICE file are for informational purposes only and 117 + do not modify the License. You may add Your own attribution 118 + notices within Derivative Works that You distribute, alongside 119 + or as an addendum to the NOTICE text from the Work, provided 120 + that such additional attribution notices cannot be construed 121 + as modifying the License. 122 + 123 + You may add Your own copyright statement to Your modifications and 124 + may provide additional or different license terms and conditions 125 + for use, reproduction, or distribution of Your modifications, or 126 + for any such Derivative Works as a whole, provided Your use, 127 + reproduction, and distribution of the Work otherwise complies with 128 + the conditions stated in this License. 129 + 130 + 5. Submission of Contributions. Unless You explicitly state otherwise, 131 + any Contribution intentionally submitted for inclusion in the Work 132 + by You to the Licensor shall be under the terms and conditions of 133 + this License, without any additional terms or conditions. 134 + Notwithstanding the above, nothing herein shall supersede or modify 135 + the terms of any separate license agreement you may have executed 136 + with Licensor regarding such Contributions. 137 + 138 + 6. Trademarks. This License does not grant permission to use the trade 139 + names, trademarks, service marks, or product names of the Licensor, 140 + except as required for reasonable and customary use in describing the 141 + origin of the Work and reproducing the content of the NOTICE file. 142 + 143 + 7. Disclaimer of Warranty. Unless required by applicable law or 144 + agreed to in writing, Licensor provides the Work (and each 145 + Contributor provides its Contributions) on an "AS IS" BASIS, 146 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 + implied, including, without limitation, any warranties or conditions 148 + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 + PARTICULAR PURPOSE. You are solely responsible for determining the 150 + appropriateness of using or redistributing the Work and assume any 151 + risks associated with Your exercise of permissions under this License. 152 + 153 + 8. Limitation of Liability. In no event and under no legal theory, 154 + whether in tort (including negligence), contract, or otherwise, 155 + unless required by applicable law (such as deliberate and grossly 156 + negligent acts) or agreed to in writing, shall any Contributor be 157 + liable to You for damages, including any direct, indirect, special, 158 + incidental, or consequential damages of any character arising as a 159 + result of this License or out of the use or inability to use the 160 + Work (including but not limited to damages for loss of goodwill, 161 + work stoppage, computer failure or malfunction, or any and all 162 + other commercial damages or losses), even if such Contributor 163 + has been advised of the possibility of such damages. 164 + 165 + 9. Accepting Warranty or Additional Liability. While redistributing 166 + the Work or Derivative Works thereof, You may choose to offer, 167 + and charge a fee for, acceptance of support, warranty, indemnity, 168 + or other liability obligations and/or rights consistent with this 169 + License. However, in accepting such obligations, You may act only 170 + on Your own behalf and on Your sole responsibility, not on behalf 171 + of any other Contributor, and only if You agree to indemnify, 172 + defend, and hold each Contributor harmless for any liability 173 + incurred by, or claims asserted against, such Contributor by reason 174 + of your accepting any such warranty or additional liability. 175 + 176 + END OF TERMS AND CONDITIONS 177 + 178 + APPENDIX: How to apply the Apache License to your work. 179 + 180 + To apply the Apache License to your work, attach the following 181 + boilerplate notice, with the fields enclosed by brackets "{}" 182 + replaced with your own identifying information. (Don't include 183 + the brackets!) The text should be enclosed in the appropriate 184 + comment syntax for the file format. We also recommend that a 185 + file or class name and description of purpose be included on the 186 + same "printed page" as the copyright notice for easier 187 + identification within third-party archives. 188 + 189 + Copyright {yyyy} {name of copyright owner} 190 + 191 + Licensed under the Apache License, Version 2.0 (the "License"); 192 + you may not use this file except in compliance with the License. 193 + You may obtain a copy of the License at 194 + 195 + http://www.apache.org/licenses/LICENSE-2.0 196 + 197 + Unless required by applicable law or agreed to in writing, software 198 + distributed under the License is distributed on an "AS IS" BASIS, 199 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 + See the License for the specific language governing permissions and 201 + limitations under the License.
+21
README.md
··· 1 + ## Commander 2 + 3 + [![Available on Modrinth](https://raw.githubusercontent.com/melontini/mini-badges/main/minecraft/modrinth.svg)](https://modrinth.com/mod/cmd) 4 + [![Available on CurseForge](https://raw.githubusercontent.com/melontini/mini-badges/main/minecraft/curseforge.svg)](https://www.curseforge.com/minecraft/mc-mods/cmd-project) 5 + ![License MIT](https://raw.githubusercontent.com/melontini/mini-badges/main/licenses/MIT.svg) 6 + 7 + Checkout the wiki to learn more! https://constellation-mc.github.io/commander/ 8 + 9 + If you have a suggestion or a feature request, be sure to share it here: https://github.com/constellation-mc/commander/discussions/categories/ideas 10 + 11 + ### Quick Introduction 12 + 13 + **Commander** is an extension of the vanilla data pack system. 14 + 15 + It adds a new event system, flexible json commands, new `/` commands, support for advanced expressions with data access and more! 16 + 17 + There are no client-side features available, everything is executed on the server. 18 + 19 + *** 20 + 21 + - [EvalEx](https://ezylang.github.io/EvalEx/) - math expressions eval. [Apache License 2.0](https://github.com/ezylang/EvalEx/blob/main/LICENSE)
+285
build.gradle
··· 1 + plugins { 2 + alias libs.plugins.fabric.loom 3 + id 'maven-publish' 4 + alias libs.plugins.shadow apply false 5 + alias libs.plugins.lombok 6 + alias libs.plugins.spotbugs.base 7 + alias libs.plugins.spotless 8 + alias libs.plugins.modmuss50.publish 9 + } 10 + 11 + import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar 12 + import com.github.spotbugs.snom.SpotBugsTask 13 + import me.modmuss50.mpp.ReleaseType 14 + import net.fabricmc.loom.api.RemapConfigurationSettings 15 + 16 + def local = !System.getenv().containsKey("GITHUB_RUN_NUMBER"); 17 + 18 + version = "${project.mod_version}-${project.minecraft_version}-${local ? 'local' : "build.${System.getenv("GITHUB_RUN_NUMBER")}"}" 19 + group = project.maven_group 20 + 21 + base { 22 + archivesName = project.archives_base_name 23 + } 24 + 25 + apply from: "https://raw.githubusercontent.com/constellation-mc/artifacts/main/artifacts.groovy" 26 + constellationRepo(project, "dark-matter") 27 + anyGitHubRepo(project, "melontini", "EvalEx") 28 + 29 + repositories { 30 + maven { url 'https://jitpack.io' } 31 + } 32 + 33 + configurations.register("shade") { 34 + transitive = false 35 + } 36 + 37 + tasks.register('dependenciesJar', ShadowJar) { 38 + configurations = [project.configurations.shade] 39 + archiveClassifier = "dependencies" 40 + 41 + exclude("META-INF/**") 42 + } 43 + 44 + tasks.register('fullDependenciesJar', ShadowJar) { 45 + dependsOn(dependenciesJar) 46 + mustRunAfter(jar) 47 + archiveClassifier = "full-dependencies" 48 + 49 + from(tasks.jar) 50 + from(dependenciesJar) 51 + 52 + var pck = "me.melontini.commander.impl.lib." 53 + 54 + mergeServiceFiles() 55 + relocate("com.ezylang.evalex", pck + "com.ezylang.evalex") 56 + relocate("net.fabricmc.mappingio", pck + "net.fabricmc.mappingio") 57 + } 58 + 59 + remapJar { 60 + dependsOn(fullDependenciesJar) 61 + getInputFile().set(fullDependenciesJar.archiveFile.get()) 62 + } 63 + tasks.jar.archiveClassifier = "slim" 64 + 65 + sourceSets { 66 + testmod { 67 + compileClasspath += main.compileClasspath 68 + runtimeClasspath += main.runtimeClasspath 69 + } 70 + } 71 + 72 + loom { 73 + addRemapConfiguration("testmodRemapImplementation", (RemapConfigurationSettings configuration) -> { 74 + configuration.getTargetConfigurationName().convention("testmodImplementation") 75 + configuration.getSourceSet().convention(sourceSets.testmod) 76 + configuration.getOnCompileClasspath().convention(true) 77 + configuration.getOnRuntimeClasspath().convention(true) 78 + configuration.getPublishingMode().convention(RemapConfigurationSettings.PublishingMode.NONE) 79 + }) 80 + } 81 + 82 + dependencies { 83 + // To change the versions see the gradle.properties file 84 + minecraft "com.mojang:minecraft:${project.minecraft_version}" 85 + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" 86 + modImplementation libs.fabric.loader 87 + 88 + // Fabric API. This is technically optional, but you probably want it anyway. 89 + modImplementation libs.fabric.api 90 + 91 + def dm = libs.dark.matter 92 + [dm.base, dm.mixin, dm.minecraft, dm.data].each { 93 + modApi it 94 + include it 95 + } 96 + 97 + [libs.evalx, libs.mapping.io].each { 98 + compileOnly it 99 + localRuntime it 100 + shade it 101 + } 102 + 103 + testmodImplementation sourceSets.main.output 104 + testmodRemapImplementation(libs.handy.tests) { 105 + exclude(group: "net.fabricmc", module: "fabric-loader") 106 + } 107 + testmodImplementation libs.assertj.core 108 + } 109 + 110 + tasks.register("spotbugs", SpotBugsTask) { task -> 111 + sourceSets.each { 112 + task.sourceDirs.from(task.sourceDirs.files, it.allSource.sourceDirectories) 113 + task.classDirs.from(task.classDirs.files, it.output) 114 + task.auxClassPaths.from(task.auxClassPaths.files, it.compileClasspath) 115 + } 116 + excludeFilter = file("spotbugs.xml") 117 + ignoreFailures = true 118 + reports { 119 + if (local) { 120 + html { 121 + required = true 122 + outputLocation = file("${layout.buildDirectory.get().asFile}/reports/spotbugs/main/spotbugs.html") 123 + stylesheet = 'fancy-hist.xsl' 124 + } 125 + } else { 126 + sarif { 127 + required = true 128 + outputLocation = file("${layout.buildDirectory.get().asFile}/reports/spotbugs/main/spotbugs.sarif") 129 + } 130 + } 131 + } 132 + } 133 + 134 + spotless { 135 + java { 136 + removeUnusedImports() 137 + palantirJavaFormat('2.47.0').style("GOOGLE") 138 + trimTrailingWhitespace() 139 + formatAnnotations() 140 + } 141 + } 142 + 143 + loom { 144 + accessWidenerPath = file("src/main/resources/commander.accesswidener") 145 + 146 + runs { 147 + testServer { 148 + server() 149 + source sourceSets.testmod 150 + runDir("run_test_server") 151 + 152 + properties([ 153 + "handy-tests.auto-test": "true" 154 + ]) 155 + } 156 + } 157 + 158 + //https://gist.github.com/maityyy/3dbcd558d58a6412c3a2a38c72706e8e 159 + afterEvaluate { 160 + loom.runs.configureEach { 161 + vmArgs("-XX:+IgnoreUnrecognizedVMOptions", "-XX:+AllowEnhancedClassRedefinition") 162 + 163 + vmArg "-javaagent:${ configurations.compileClasspath.find { it.name.contains("sponge-mixin") } }" 164 + property("mixin.debug.export", "true") 165 + } 166 + } 167 + 168 + mods.register(project.name) { 169 + sourceSet project.sourceSets.main 170 + } 171 + 172 + mods.register(project.name + "-testmod") { 173 + sourceSet project.sourceSets.testmod 174 + } 175 + } 176 + 177 + processResources { 178 + inputs.property "version", project.version 179 + inputs.property "minecraft_version", project.minecraft_version 180 + inputs.property "loader_version", libs.fabric.loader.get().version 181 + filteringCharset "UTF-8" 182 + 183 + filesMatching("fabric.mod.json") { 184 + expand "version": project.version, 185 + "minecraft_version": project.minecraft_version, 186 + "loader_version": libs.fabric.loader.get().version 187 + } 188 + } 189 + 190 + def targetJavaVersion = 17 191 + tasks.withType(JavaCompile).configureEach { 192 + // ensure that the encoding is set to UTF-8, no matter what the system default is 193 + // this fixes some edge cases with special characters not displaying correctly 194 + // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html 195 + // If Javadoc is generated, this must be specified in that task too. 196 + it.options.encoding = "UTF-8" 197 + if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { 198 + it.options.release.set(targetJavaVersion) 199 + } 200 + } 201 + 202 + java { 203 + def javaVersion = JavaVersion.toVersion(targetJavaVersion) 204 + if (JavaVersion.current() < javaVersion) { 205 + toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) 206 + } 207 + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task 208 + // if it is present. 209 + // If you remove this line, sources will not be generated. 210 + withSourcesJar() 211 + } 212 + 213 + jar { 214 + from("LICENSE") { 215 + rename { "${it}_${project.archivesBaseName}"} 216 + } 217 + from("LICENSE_mapping-io") 218 + from("LICENSE_EvalEx") 219 + } 220 + 221 + sourcesJar { 222 + exclude { 223 + sourceSets.main.allSource.contains it.file 224 + } 225 + from delombok 226 + } 227 + 228 + // configure the maven publication 229 + publishing { 230 + publications { 231 + mavenJava(MavenPublication) { 232 + from components.java 233 + } 234 + } 235 + 236 + if (!local) { 237 + repositories { 238 + maven { 239 + name = "GitHubPackages" 240 + url = "https://maven.pkg.github.com/constellation-mc/commander" 241 + credentials { 242 + username = System.getenv("GITHUB_ACTOR") 243 + password = System.getenv("GITHUB_TOKEN") 244 + } 245 + } 246 + } 247 + } 248 + } 249 + 250 + publishMods { 251 + file = remapJar.archiveFile 252 + additionalFiles.from(remapSourcesJar.archiveFile) 253 + 254 + changelog = file("CHANGELOG.md").text 255 + type = ReleaseType.valueOf(providers.environmentVariable("VERSION_TYPE").getOrElse("BETA")) 256 + modLoaders.add("fabric") 257 + 258 + displayName = "${project.mod_version} (${project.minecraft_version})" 259 + 260 + modrinth { 261 + projectId = "86bUtxWv" 262 + accessToken = providers.environmentVariable("MODRINTH_TOKEN") 263 + minecraftVersions.add("${project.minecraft_version}") 264 + 265 + requires("fabric-api") 266 + embeds("dark-matter") 267 + } 268 + curseforge { 269 + projectId = "1009687" 270 + accessToken = providers.environmentVariable("CURSEFORGE_TOKEN") 271 + minecraftVersions.add("${project.minecraft_version}") 272 + 273 + requires("fabric-api") 274 + embeds("dark-matter") 275 + } 276 + github { 277 + repository = "constellation-mc/commander" 278 + accessToken = providers.environmentVariable("GITHUB_TOKEN") 279 + commitish = providers.environmentVariable("GITHUB_SHA").getOrElse("${project.minecraft_version}-fabric") 280 + 281 + type = STABLE 282 + } 283 + 284 + dryRun = local 285 + }
bun.lockb

This is a binary file and will not be displayed.

-57
docs/.vitepress/config.mts
··· 1 - import { defineConfig } from 'vitepress' 2 - import { en } from "./en" 3 - import { zh_cn } from "./zh_cn" 4 - 5 - // https://vitepress.dev/reference/site-config 6 - export default defineConfig({ 7 - title: "Commander", 8 - base: "/commander/", 9 - 10 - cleanUrls: true, 11 - lastUpdated: true, 12 - 13 - head: [ 14 - ['link', { rel: 'icon', type: 'image/png', href: '/commander/favicon.png' }], 15 - ['link', { rel: 'shortcut icon', type: 'image/png', href: '/commander/favicon.png' }], 16 - ['meta', { name: "robots", content: "noai, noimageai" }], 17 - ['link', { rel: 'preconnect', href: 'https://fonts.googleapis.com' }], 18 - ['link', { rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: '' }], 19 - ['link', { href: 'https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300..900;1,300..900&family=Ubuntu+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap', rel: 'stylesheet' }] 20 - ], 21 - 22 - markdown: { 23 - image: { 24 - lazyLoading: true 25 - } 26 - }, 27 - 28 - themeConfig: { 29 - // https://vitepress.dev/reference/default-theme-config 30 - logo: { src: '/logo.png', width: 24, height: 24 }, 31 - externalLinkIcon: false, 32 - 33 - editLink: { 34 - pattern: 'https://github.com/constellation-mc/commander/edit/documentation/docs/:path' 35 - }, 36 - 37 - socialLinks: [ 38 - { icon: 'github', link: 'https://github.com/constellation-mc/commander' }, 39 - { icon: { 40 - svg: '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M12.252.004a11.78 11.768 0 0 0-8.92 3.73a11 10.999 0 0 0-2.17 3.11a11.37 11.359 0 0 0-1.16 5.169c0 1.42.17 2.5.6 3.77c.24.759.77 1.899 1.17 2.529a12.3 12.298 0 0 0 8.85 5.639c.44.05 2.54.07 2.76.02c.2-.04.22.1-.26-1.7l-.36-1.37l-1.01-.06a8.5 8.489 0 0 1-5.18-1.8a5.34 5.34 0 0 1-1.3-1.26c0-.05.34-.28.74-.5a37.572 37.545 0 0 1 2.88-1.629c.03 0 .5.45 1.06.98l1 .97l2.07-.43l2.06-.43l1.47-1.47c.8-.8 1.48-1.5 1.48-1.52c0-.09-.42-1.63-.46-1.7c-.04-.06-.2-.03-1.02.18c-.53.13-1.2.3-1.45.4l-.48.15l-.53.53l-.53.53l-.93.1l-.93.07l-.52-.5a2.7 2.7 0 0 1-.96-1.7l-.13-.6l.43-.57c.68-.9.68-.9 1.46-1.1c.4-.1.65-.2.83-.33c.13-.099.65-.579 1.14-1.069l.9-.9l-.7-.7l-.7-.7l-1.95.54c-1.07.3-1.96.53-1.97.53c-.03 0-2.23 2.48-2.63 2.97l-.29.35l.28 1.03c.16.56.3 1.16.31 1.34l.03.3l-.34.23c-.37.23-2.22 1.3-2.84 1.63c-.36.2-.37.2-.44.1c-.08-.1-.23-.6-.32-1.03c-.18-.86-.17-2.75.02-3.73a8.84 8.839 0 0 1 7.9-6.93c.43-.03.77-.08.78-.1c.06-.17.5-2.999.47-3.039c-.01-.02-.1-.02-.2-.03Zm3.68.67c-.2 0-.3.1-.37.38c-.06.23-.46 2.42-.46 2.52c0 .04.1.11.22.16a8.51 8.499 0 0 1 2.99 2a8.38 8.379 0 0 1 2.16 3.449a6.9 6.9 0 0 1 .4 2.8c0 1.07 0 1.27-.1 1.73a9.37 9.369 0 0 1-1.76 3.769c-.32.4-.98 1.06-1.37 1.38c-.38.32-1.54 1.1-1.7 1.14c-.1.03-.1.06-.07.26c.03.18.64 2.56.7 2.78l.06.06a12.07 12.058 0 0 0 7.27-9.4c.13-.77.13-2.58 0-3.4a11.96 11.948 0 0 0-5.73-8.578c-.7-.42-2.05-1.06-2.25-1.06Z"/></svg>' 41 - }, link: 'https://modrinth.com/mod/cmd'} 42 - ], 43 - }, 44 - 45 - locales: { 46 - "root": { 47 - label: "English", 48 - link: "/", 49 - ...en 50 - }, 51 - "zh-cn": { 52 - label: "็ฎ€ไฝ“ไธญๆ–‡", 53 - link: "/zh-cn/", 54 - ...zh_cn 55 - } 56 - } 57 - })
-44
docs/.vitepress/en.ts
··· 1 - import { defineConfig } from 'vitepress' 2 - 3 - export const en = defineConfig({ 4 - lang: "en-US", 5 - title: "Commander", 6 - description: "An extension of the data pack system.", 7 - 8 - themeConfig: { 9 - sidebar: [ 10 - { 11 - items: [ 12 - { text: 'Welcome!', link: '/' } 13 - ] 14 - }, 15 - { 16 - text: 'Use', 17 - items: [ 18 - { text: 'Events', link: '/Events' }, 19 - { text: 'Commands', link: '/Commands' }, 20 - { text: 'Expressions', link: '/Expressions' }, 21 - { text: 'Brigadier Commands', link: '/BrigadierCommands' }, 22 - ] 23 - }, 24 - { 25 - text: 'Develop', 26 - items: [ 27 - { text: 'Events', link: '/develop/Events'}, 28 - { text: 'Commands', link: '/develop/Commands'}, 29 - { text: 'Expressions', link: '/develop/Expressions' } 30 - ] 31 - }, 32 - { 33 - text: 'Meta', 34 - items: [ 35 - { text: 'Badges', link: 'https://github.com/constellation-mc/commander/discussions/3' } 36 - ] 37 - } 38 - ], 39 - 40 - search: { 41 - provider: 'local', 42 - } 43 - } 44 - })
-59
docs/.vitepress/theme/index.ts
··· 1 - // https://vitepress.dev/guide/custom-theme 2 - import giscusTalk from 'vitepress-plugin-comment-with-giscus'; 3 - import type { Theme, VitePressData } from 'vitepress' 4 - import DefaultTheme from 'vitepress/theme' 5 - import './style.css' 6 - import { useData, useRoute } from 'vitepress'; 7 - import { toRefs } from "vue"; 8 - import { watch, nextTick, onMounted } from 'vue'; 9 - import { GiscusProps } from '@giscus/vue/dist/types' 10 - 11 - export default { 12 - ...DefaultTheme, 13 - enhanceApp(ctx) { 14 - DefaultTheme.enhanceApp(ctx); 15 - }, 16 - setup() { 17 - // Get frontmatter and route 18 - const data = useData(); 19 - const { frontmatter } = toRefs(data); 20 - const route = useRoute(); 21 - 22 - var props: GiscusProps = { 23 - repo: 'constellation-mc/commander', 24 - repoId: 'R_kgDOLkDGFQ', 25 - category: 'Comments', 26 - categoryId: 'DIC_kwDOLkDGFc4Ceihw', 27 - mapping: 'pathname', 28 - inputPosition: 'top', 29 - reactionsEnabled: '1', 30 - loading: 'lazy' 31 - } 32 - 33 - onMounted(() => { 34 - fixLang(props, data); 35 - }) 36 - watch(() => route.path, () => nextTick(() => { 37 - fixLang(props, data); 38 - })) 39 - 40 - // Obtain configuration from: https://giscus.app/ 41 - giscusTalk(props, { frontmatter, route }, true); 42 - } 43 - } satisfies Theme 44 - 45 - const fixLang = (props: GiscusProps, data: VitePressData) => { 46 - var lang = data.lang.value 47 - switch (lang) { 48 - case "en-US": 49 - lang = "en" 50 - break; 51 - case "zh-CN": 52 - lang = "zh-CN" 53 - break; 54 - default: 55 - lang = "en" 56 - break; 57 - } 58 - props.lang = lang 59 - }
-160
docs/.vitepress/theme/style.css
··· 1 - /** 2 - * Customize default theme styling by overriding CSS variables: 3 - * https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css 4 - */ 5 - 6 - /** 7 - * Colors 8 - * 9 - * Each colors have exact same color scale system with 3 levels of solid 10 - * colors with different brightness, and 1 soft color. 11 - * 12 - * - `XXX-1`: The most solid color used mainly for colored text. It must 13 - * satisfy the contrast ratio against when used on top of `XXX-soft`. 14 - * 15 - * - `XXX-2`: The color used mainly for hover state of the button. 16 - * 17 - * - `XXX-3`: The color for solid background, such as bg color of the button. 18 - * It must satisfy the contrast ratio with pure white (#ffffff) text on 19 - * top of it. 20 - * 21 - * - `XXX-soft`: The color used for subtle background such as custom container 22 - * or badges. It must satisfy the contrast ratio when putting `XXX-1` colors 23 - * on top of it. 24 - * 25 - * The soft color must be semi transparent alpha channel. This is crucial 26 - * because it allows adding multiple "soft" colors on top of each other 27 - * to create a accent, such as when having inline code block inside 28 - * custom containers. 29 - * 30 - * - `default`: The color used purely for subtle indication without any 31 - * special meanings attched to it such as bg color for menu hover state. 32 - * 33 - * - `brand`: Used for primary brand colors, such as link text, button with 34 - * brand theme, etc. 35 - * 36 - * - `tip`: Used to indicate useful information. The default theme uses the 37 - * brand color for this by default. 38 - * 39 - * - `warning`: Used to indicate warning to the users. Used in custom 40 - * container, badges, etc. 41 - * 42 - * - `danger`: Used to show error, or dangerous message to the users. Used 43 - * in custom container, badges, etc. 44 - * -------------------------------------------------------------------------- */ 45 - 46 - :root { 47 - --vp-c-default-1: var(--vp-c-gray-1); 48 - --vp-c-default-2: var(--vp-c-gray-2); 49 - --vp-c-default-3: var(--vp-c-gray-3); 50 - --vp-c-default-soft: var(--vp-c-gray-soft); 51 - 52 - --vp-c-brand-1: var(--vp-c-yellow-1); 53 - --vp-c-brand-2: var(--vp-c-yellow-2); 54 - --vp-c-brand-3: var(--vp-c-yellow-3); 55 - --vp-c-brand-soft: var(--vp-c-yellow-soft); 56 - 57 - --vp-c-tip-1: var(--vp-c-brand-1); 58 - --vp-c-tip-2: var(--vp-c-brand-2); 59 - --vp-c-tip-3: var(--vp-c-brand-3); 60 - --vp-c-tip-soft: var(--vp-c-brand-soft); 61 - 62 - --vp-c-warning-1: var(--vp-c-yellow-1); 63 - --vp-c-warning-2: var(--vp-c-yellow-2); 64 - --vp-c-warning-3: var(--vp-c-yellow-3); 65 - --vp-c-warning-soft: var(--vp-c-yellow-soft); 66 - 67 - --vp-c-danger-1: var(--vp-c-red-1); 68 - --vp-c-danger-2: var(--vp-c-red-2); 69 - --vp-c-danger-3: var(--vp-c-red-3); 70 - --vp-c-danger-soft: var(--vp-c-red-soft); 71 - 72 - --vp-font-family-base: "Rubik", sans-serif; 73 - --vp-font-family-mono: "Ubuntu Mono", monospace; 74 - } 75 - 76 - /** 77 - * Component: Button 78 - * -------------------------------------------------------------------------- */ 79 - 80 - :root { 81 - --vp-button-brand-border: transparent; 82 - --vp-button-brand-text: var(--vp-c-white); 83 - --vp-button-brand-bg: var(--vp-c-brand-3); 84 - --vp-button-brand-hover-border: transparent; 85 - --vp-button-brand-hover-text: var(--vp-c-white); 86 - --vp-button-brand-hover-bg: var(--vp-c-brand-2); 87 - --vp-button-brand-active-border: transparent; 88 - --vp-button-brand-active-text: var(--vp-c-white); 89 - --vp-button-brand-active-bg: var(--vp-c-brand-1); 90 - } 91 - 92 - /** 93 - * Component: Home 94 - * -------------------------------------------------------------------------- */ 95 - 96 - :root { 97 - --vp-home-hero-name-color: transparent; 98 - --vp-home-hero-name-background: -webkit-linear-gradient( 99 - 120deg, 100 - #bd34fe 30%, 101 - #41d1ff 102 - ); 103 - 104 - --vp-home-hero-image-background-image: linear-gradient( 105 - -45deg, 106 - #bd34fe 50%, 107 - #47caff 50% 108 - ); 109 - --vp-home-hero-image-filter: blur(44px); 110 - } 111 - 112 - @media (min-width: 640px) { 113 - :root { 114 - --vp-home-hero-image-filter: blur(56px); 115 - } 116 - } 117 - 118 - @media (min-width: 960px) { 119 - :root { 120 - --vp-home-hero-image-filter: blur(68px); 121 - } 122 - } 123 - 124 - /** 125 - * Component: Custom Block 126 - * -------------------------------------------------------------------------- */ 127 - 128 - :root { 129 - --vp-custom-block-tip-border: transparent; 130 - --vp-custom-block-tip-text: var(--vp-c-text-1); 131 - --vp-custom-block-tip-bg: var(--vp-c-brand-soft); 132 - --vp-custom-block-tip-code-bg: var(--vp-c-brand-soft); 133 - } 134 - 135 - /** 136 - * Component: Algolia 137 - * -------------------------------------------------------------------------- */ 138 - 139 - .DocSearch { 140 - --docsearch-primary-color: var(--vp-c-brand-1) !important; 141 - } 142 - 143 - .badge-holder p { 144 - display: flex; 145 - flex-wrap: wrap; 146 - } 147 - 148 - .badge-holder p { 149 - margin: 0; 150 - } 151 - 152 - .badge-holder a { 153 - height: 27px; 154 - } 155 - 156 - .badge-holder img { 157 - display: inline-block; 158 - height: 21px; 159 - margin: 3px; 160 - }
-64
docs/.vitepress/zh_cn.ts
··· 1 - import { defineConfig } from 'vitepress' 2 - 3 - export const zh_cn = defineConfig({ 4 - lang: "zh-CN", 5 - title: "ๅ‘ฝไปคๅฎ˜", 6 - description: "ๅฏนๅŽŸ็‰ˆๆ•ฐๆฎๅŒ…็ณป็ปŸ็š„่กฅๅ……ๆจก็ป„ใ€‚", 7 - 8 - themeConfig: { 9 - sidebar: [ 10 - { 11 - items: [ 12 - { text: 'ๆฌข่ฟŽ๏ผ', link: '/zh-cn/' } 13 - ] 14 - }, 15 - { 16 - text: 'ไฝฟ็”จ', 17 - items: [ 18 - { text: 'ไบ‹ไปถ', link: '/zh-cn/Events' }, 19 - { text: 'ๅ‘ฝไปค', link: '/zh-cn/Commands' }, 20 - { text: '่กจ่พพๅผ', link: '/zh-cn/Expressions' }, 21 - { text: 'ๆ–œๆ ๅ‘ฝไปค', link: '/zh-cn/BrigadierCommands' }, 22 - ] 23 - }, 24 - { 25 - text: 'ๅผ€ๅ‘', 26 - items: [ 27 - { text: 'ไบ‹ไปถ', link: '/zh-cn/develop/Events'}, 28 - { text: 'ๅ‘ฝไปค', link: '/zh-cn/develop/Commands'}, 29 - { text: '่กจ่พพๅผ', link: '/zh-cn/develop/Expressions' } 30 - ] 31 - }, 32 - { 33 - text: 'Meta', 34 - items: [ 35 - { text: '่ฎพ่ฎก', link: 'https://github.com/constellation-mc/commander/discussions/3' } 36 - ] 37 - } 38 - ], 39 - 40 - search: { 41 - provider: 'local', 42 - options: { 43 - locales: { 44 - "zh-cn": { 45 - translations: { 46 - button: { 47 - buttonText: 'ๆœ็ดขๆ–‡ๆกฃ', 48 - buttonAriaLabel: 'ๆœ็ดขๆ–‡ๆกฃ' 49 - }, 50 - modal: { 51 - noResultsText: 'ๆ— ๆณ•ๆ‰พๅˆฐ็›ธๅ…ณ็ป“ๆžœ', 52 - resetButtonTitle: 'ๆธ…้™คๆŸฅ่ฏขๆกไปถ', 53 - footer: { 54 - selectText: '้€‰ๆ‹ฉ', 55 - navigateText: 'ๅˆ‡ๆข' 56 - } 57 - } 58 - } 59 - } 60 - } 61 - } 62 - } 63 - } 64 - })
-86
docs/BrigadierCommands.md
··· 1 - # Brigadier Commands 2 - 3 - Commander introduces a handful of `/` commands. All commands require perm level 2. 4 - 5 - ## `cmd:arithmetica` 6 - 7 - This command allows you evaluate expressions right in the chat. Context: `minecraft:level`, `minecraft:origin` and optional `minecraft:this_entity`. This command requires a string expression and an optional cast. 8 - 9 - ``` 10 - cmd:arithmetica -> 11 - \- expression 12 - \- cast (default: none) 13 - ``` 14 - 15 - Example: `cmd:arithmetica "sin(level.getDayTime)" bool` 16 - 17 - ## `cmd:explode` 18 - 19 - This simple command allows you to spawn explosions. The command tree is as follows: 20 - 21 - ``` 22 - cmd:explode -> 23 - \- entity (who caused the explosion) 24 - \- position 25 - \- [power] (default: 4) 26 - \- [fire] (default: false) 27 - \- position 28 - \- [power] (default: 4) 29 - \- [fire] (default: false) 30 - ``` 31 - 32 - Example: `cmd:explode @s ~ ~ ~ 6.4 true` 33 - 34 - ## `cmd:data` 35 - 36 - This command allows you to read and write special, persistent data which can later be referenced in expressions. Provided data must be either a number or a string. The command tree is as follows: 37 - 38 - ::: details Tree 39 - ``` 40 - cmd:data 41 - \- read 42 - \- level 43 - \- key 44 - \- chunk 45 - \- position 46 - \- key 47 - \- entity 48 - \- entity 49 - \- key 50 - \- block_entity 51 - \- position 52 - \- key 53 - \- write 54 - \- level 55 - \- key 56 - \- data 57 - \- chunk 58 - \- position 59 - \- key 60 - \- data 61 - \- entity 62 - \- entity 63 - \- key 64 - \- data 65 - \- block_entity 66 - \- position 67 - \- key 68 - \- data 69 - \- remove 70 - \- level 71 - \- key 72 - \- chunk 73 - \- position 74 - \- key 75 - \- entity 76 - \- entity 77 - \- key 78 - \- block_entity 79 - \- position 80 - \- key 81 - ``` 82 - ::: 83 - 84 - Example: `cmd:data write entity @s "my_test_data" "Hello "`, `cmd:data read entity @s "my_test_data"`, `cmd:data remove entity @s "my_test_data"` 85 - 86 - Example reference: `this_entity.storage.my_test_data + 'World!'`
-168
docs/Commands.md
··· 1 - # Commands 2 - 3 - Commands in Commander are the main actors in your subscription. 4 - 5 - In JSON, a command is an object. The required `type` field selects the command that will be executed when the event is invoked. 6 - 7 - All command types accept a special `condition` field, that can operate on the current event context. The field uses the vanilla predicate system, so [misode.github.io](https://misode.github.io/predicate/) can be used to quickly create predicates. 8 - 9 - Various command types can take additional parameters. 10 - 11 - You can also use Commander commands in advancement rewards: 12 - 13 - ```json 14 - { 15 - "rewards": { 16 - "commander:commands": [ 17 - { 18 - "type": "commander:print", 19 - "text": "42" 20 - } 21 - ] 22 - } 23 - } 24 - ``` 25 - 26 - And other projects might integrate commands to other contexts! 27 - 28 - [[toc]] 29 - 30 - ## Selectors 31 - 32 - Some commands require you to specify a "selector". A selector is a simple identifier that allows you to select a position and (optionally) an entity that will be used to perform an action. Selectors, with some exceptions, mimic vanilla loot contexts, built-in selectors can be found here: [BuiltInSelectors](https://github.com/constellation-mc/commander/blob/main/src/main/java/me/melontini/commander/impl/builtin/BuiltInSelectors.java) 33 - 34 - ## Built-in commands 35 - The mod comes with a minimal set of commands, this is by design, as game interactions should be handled by brigadier `/` commands, or functions. 36 - 37 - ### `commander:commands` 38 - This is the main type of command you will use when writing subscriptions. 39 - 40 - This command type expects a selector and either a list of commands or a function identifier. It's recommended that you provide a function identifier, as these are verified by the function loader on reload. 41 - 42 - ::: details Example 43 - ```json 44 - { 45 - "type": "commander:commands", 46 - "selector": "commander:random_player", 47 - "commands": [ 48 - "/cmd:explode ~ ~1 ~ 2" 49 - ] 50 - } 51 - ``` 52 - ::: 53 - 54 - #### Command Macros 55 - 56 - If you opt into specifying commands using the array, you are a given an option of using command macros. 57 - 58 - A macro is a simple `${{}}` block, which allows you to dynamically insert info using expressions. For example: 59 - ``` 60 - "/say hi, my name is ${{level.dimension.location}}!" 61 - ``` 62 - will say `hi, my name is minecraft:overworld` in the overworld. 63 - 64 - You can learn more about expressions on this page: [Expressions](Expressions). 65 - 66 - By default macros return a string, but you can cast (convert) a macro into some other types like so: 67 - 68 - ``` 69 - $(long){{random(0, 34)}} 34.52946 -> 34 70 - $(double){{true}} true -> 1.0 71 - $(bool){{0}} 0 -> false 72 - ``` 73 - 74 - ### `commander:cancel` 75 - This is a special command type that can only be used with supported event types. 76 - 77 - The only field required by this command is `value` with an appropriate return value. 78 - 79 - ::: details Example 80 - ```json 81 - { 82 - "type": "commander:cancel", 83 - "value": false 84 - } 85 - ``` 86 - 87 - ```json 88 - { 89 - "type": "commander:cancel", 90 - "value": "fail" 91 - } 92 - ``` 93 - ::: 94 - 95 - ### `commander:all_of`, `commander:any_of`, `commander:defaulted` 96 - All three of these commands have a similar function. They accept a list of commands and if the condition is true, they execute the command specified in the optional `then` block. 97 - 98 - `all_of` requires all commands to execute successfully, `any_of` requires one command to execute successfully, and `defaulted` requires one command to fail. 99 - 100 - Note: even if the condition is true early, the command will still execute all commands! To chanage this, you can set `short_circuit` to true, in which case commands will be aborted immediately if the condition fails. 101 - 102 - ::: details Example 103 - ```json 104 - { 105 - "event": "commander:player_use/item", 106 - "commands": [ 107 - { 108 - "type": "commander:all_of", 109 - "condition": { 110 - "condition": "minecraft:match_tool", 111 - "predicate": { 112 - "items": [ 113 - "minecraft:diamond" 114 - ] 115 - } 116 - }, 117 - "commands": [ 118 - { 119 - "type": "commander:commands", 120 - "selector": "this_entity", 121 - "commands": [ 122 - "/say mmm... diamond..." 123 - ] 124 - }, 125 - { 126 - "type": "commander:cancel", 127 - "value": "success" 128 - } 129 - ] 130 - } 131 - ] 132 - } 133 - ``` 134 - ::: 135 - 136 - ### `commander:random` 137 - This command type takes a weighted list of other commands and randomly executes some of them. By default, it rolls only once, but the number of rolls can be adjusted using the optional `rolls` field (Supports [Expressions](Expressions)). 138 - 139 - ::: details Example 140 - ```json 141 - { 142 - "type": "commander:random", 143 - "rolls": 2, 144 - "commands": [ 145 - { 146 - "weight": 2, 147 - "data": { 148 - "type": "commander:commands", 149 - "selector": "this_entity", 150 - "commands": [ 151 - "/say weight 2" 152 - ] 153 - } 154 - }, 155 - { 156 - "weight": 6, 157 - "data": { 158 - "type": "commander:commands", 159 - "selector": "this_entity", 160 - "commands": [ 161 - "/say weight 6" 162 - ] 163 - } 164 - } 165 - ] 166 - } 167 - ``` 168 - :::
-68
docs/Events.md
··· 1 - # Events 2 - 3 - Events in Commander are points during gameplay when your [commands](Commands) will be invoked. 4 - 5 - ## Introduction to subscriptions 6 - 7 - An event declaration in your subscription file is an identifier of the event you want to subscribe to. An event can accept additional parameters in the `parameters` block, but built-in events do not require them (yet). Subscription files are read from `commander/events`. 8 - 9 - ``` 10 - |- recipes 11 - |- commander 12 - |- events 13 - |- test_event.json 14 - |- folder 15 - |- nested.json 16 - |- tags 17 - ``` 18 - 19 - A typical event declaration might look something like this: 20 - 21 - ::: details Example 22 - ```json 23 - { 24 - "event": "commander:after_killed_by_other", 25 - "commands": [ 26 - 27 - ] 28 - } 29 - ``` 30 - <br/> 31 - 32 - ```json 33 - { 34 - "event": "modid:custom_event", 35 - "parameters": { 36 - }, 37 - "commands": [ 38 - 39 - ] 40 - } 41 - ``` 42 - ::: 43 - 44 - The subscription file can contain multiple subscriptions to different (or identical) events. 45 - 46 - ::: details Example 47 - ```json 48 - { 49 - "events": [ 50 - { 51 - "event": "commander:after_killed_by_other" 52 - }, 53 - { 54 - "event": "commander:allow_damage" 55 - }, 56 - { 57 - "event": "commander:player_attack/block" 58 - } 59 - ] 60 - } 61 - ``` 62 - ::: 63 - 64 - ## Built-in events 65 - 66 - Commander wraps most compatible fabric events under the `commander` namespace. "Return" here means the [cancel command](Commands#commandercancel) 67 - 68 - Currently available events can be seen here: [EntityEvents](https://github.com/constellation-mc/commander/blob/main/src/main/java/me/melontini/commander/impl/builtin/events/EntityEvents.java), [PlayerEvents](https://github.com/constellation-mc/commander/blob/main/src/main/java/me/melontini/commander/impl/builtin/events/PlayerEvents.java), [ServerLifecycle](https://github.com/constellation-mc/commander/blob/main/src/main/java/me/melontini/commander/impl/builtin/events/ServerLifecycle.java), [ServerTick](https://github.com/constellation-mc/commander/blob/main/src/main/java/me/melontini/commander/impl/builtin/events/ServerTick.java)
-218
docs/Expressions.md
··· 1 - # Expressions 2 - 3 - Commander introduces support for evaluating math expressions with dynamic context data. 4 - 5 - Expressions use [EvalEx](https://ezylang.github.io/EvalEx/) for evaluation, it may be useful to familiarize yourself with it. One major difference is that all function, variable, contant names are case-sensitive and all funtion names are camelCase and not UPPER_SNAKE_CASE. 6 - 7 - ## Additional functions and operators 8 - 9 - Commander adds some additional functions to EvalEx. 10 - 11 - A "Lambda" here means a regular expression, but with the `it` parameter. Example: `arrayFind(arrayOf(0, 1, 2), it == 1)`. Lambda parameters are marked with `ฮป`. 12 - 13 - Variable Arguments (VarArgs) are marked with `...`. (This means you can specify as many arguments as you need) 14 - 15 - ::: details Math 16 - 17 - | Function | Description | Arguments | Example | 18 - |---|---|---|---| 19 - | `random` | Generates a random number in range. | `min`, `max` | `random(0, 23)` | 20 - | `clamp` | Clamps the value between two other arguments. | `value`, `min`, `max` | `clamp(12, 14, 16)` | 21 - | `lerp` | Smoothly progresses the value to the target. | `delta`, `start`, `end` | `lerp(0.5, 10, 16)` | 22 - 23 - ::: 24 - 25 - ::: details Arrays 26 - 27 - All functions construct new arrays and do not mutate the original array. 28 - 29 - | Function | Description | Arguments | Example | 30 - |---|---|---|---| 31 - | `arrayOf` | Construct an array from the specified objects. | `args...` | `arrayOf(0, 23)` | 32 - | `arrayMap` | Mutates all objects in this array. | `array`, `function(ฮป)` | `arrayMap(arrayOf(0,1,2), sqrt(it))` | 33 - | `arrayFind` | Filters all objects not matching the predicate. | `array`, `predicate(ฮป)` | `arrayFind(arrayOf(0,1,2), it == 1)` | 34 - | `arrayAnyMatch` | Checks if any of the objects in this array match the predicate. | `array`, `predicate(ฮป)` | `arrayAnyMatch(arrayOf(0,1,2), it == 1)` | 35 - | `arrayNoneMatch` | Checks if none of the objects in this array match the predicate. | `array`, `predicate(ฮป)` | `arrayNoneMatch(arrayOf(0,1,2), it == 1)` | 36 - | `arrayAllMatch` | Checks if all objects in this array match the predicate. | `array`, `predicate(ฮป)` | `arrayAllMatch(arrayOf(0,1,2), it == 1)` | 37 - | `arrayFindFirst` | Returns the first object in the array or `null` if empty. | `array` | `arrayFindFirst(arrayOf(0, 23))` | 38 - 39 - ::: 40 - 41 - ::: details Registries 42 - 43 - | Function | Description | Arguments | Example | 44 - |---|---|---|---| 45 - | `Registry` | Returns a static content registry | `identifier` | `Registry('entity_type')` | 46 - | `Item` | Returns an item from the registry | `identifier` | `Item('diamond')` | 47 - | `Block` | Returns a block from the registry | `identifier` | `Block('chest')` | 48 - | `DynamicRegistry` | Returns a dynamic content registry | `identifier` | `DynamicRegistry('worldgen/biome')` | 49 - | `Biome` | Returns a biome from the registry | `identifier` | `Biome('the_void')` | 50 - | `DimensionType` | Returns a dimension type from the registry | `identifier` | `DimensionType('overworld')` | 51 - 52 - ::: 53 - 54 - ::: details Misc 55 - 56 - | Function | Description | Arguments | Example | 57 - |---|---|---|---| 58 - | `structContainsKey` | Checks if a struct contains some key. | `struct`, `key...` | `structContainsKey(block_state.properties, 'candles')` | 59 - | `hasContext` | Checks if an expression parameter is available | `key...` | `hasContext('tool')` | 60 - | `length` | Returns the length of the specified object or 0. | `value` | `length('Hello World!')` | 61 - | `strFormat` | Formats a string according to the pattern. | `pattern`, `args...` | `strFormat('Hello %s World!', 23)` | 62 - | `ifMatches` | Simillar to built-in `if`, but with Lambdas. | `value`, `predicate(ฮป)`, `ifTrue(ฮป)`, `ifFalse(ฮป)` | `ifMatches(arrayFind(arrayOf(0,1,2), it == 1), length(it) > 0, it[0], 0)` | 63 - | `chain` | Chains multiple functions using lambdas. `it` is the last result. | `value`, `actions(ฮป)...` | `chain(23, it + 3, sqrt(it))` | 64 - 65 - ::: 66 - 67 - ::: details Operators 68 - 69 - | Function | Description | Arguments | Example | 70 - |---|---|---|---| 71 - | `?.` | Attempts to get a field from a structure or returns `null` | `struct`, `key` | `struct?.field` | 72 - | `?` | Returns `b` if `a` is `null`. `a` otherwise. | `a`, `b` | `struct?.y ? 5` | 73 - 74 - ::: 75 - 76 - ## Context data access 77 - 78 - Because Expressions require minecraft's loot context, you can read source data using a special syntax. 79 - ``` 80 - identifier.field 81 - identifier.method 82 - identifier.method.field.method 83 - ``` 84 - Keep in mind that requested fields and context **must** be present. Expression will fail without them. 85 - 86 - Some examples: 87 - ``` 88 - minecraft:this_entity.getX 89 - this_entity.getHealth 90 - minecraft:this_entity.isInWaterOrRain 91 - this_entity.blockPosition.getX 92 - origin.x 93 - origin.reverse.y 94 - minecraft:level.getDayTime 95 - ``` 96 - 97 - ### Commander Extensions: 98 - 99 - Commander adds special fields to objects to extend expression capabilities. 100 - 101 - ::: details `nbt` (Stacks, Entities, Block Entities) 102 - 103 - Added to item stacks, entities, and block entities. Allows you to read the NBT of an object. While this can be convenient, NBT access is not fast, and when used in frequently invoked places (like tick events), can bring the game to a halt. 104 - 105 - Example: `this_entity.nbt.Air` 106 - 107 - ::: 108 - 109 - ::: details `properties` (Block States) 110 - 111 - Added to states, like block states. 112 - 113 - Example: `block_state.properties.candles` 114 - 115 - ::: 116 - 117 - ::: details `attributes` (Living Entities) 118 - 119 - Allows you to access entity attributes. The key is an identifier, so both `generic.luck` and `minecraft:generic.luck` are acceptible. 120 - 121 - Example: `this_entity.attributes.'generic.luck'` 122 - 123 - ::: 124 - 125 - ::: details `storage` (Levels, Chunks, Entities, Block Entities) 126 - 127 - Allows you to access persistent data storage. The data is modified and written by [`/cmd:data`](/BrigadierCommands#cmd-data) 128 - 129 - ::: 130 - 131 - ### P.S. 132 - 133 - If you have to do a lot of expensive operations in expressions in tick events, you should delay the execution, if possible, by checking `level.getDayTime % 20 == 0`, this will make sure that the expression is executed every second and not 20 times per second. `% 40` is every 2 seconds and `% 10` is every 1/2 second. 134 - 135 - Expressions use Mojang Mappings (downloaded on first load), this means that almost all public fields and getter methods are available in expressions. If Commander fails to setup mappings, you'll have to rely on platform names. (e.g. intermediary on Fabric) 136 - 137 - ::: details Random examples 138 - 139 - Default minecart speed is computed using this formula: 140 - 141 - `if(this_entity.isInWater, 4, 8) / 20`, and furnace: `if(this_entity.isInWater, 3, 4) / 20` 142 - 143 - *** 144 - 145 - This ridicilous expression can print the current level time in human time. https://bukkit.org/threads/how-can-i-convert-minecraft-long-time-to-real-hours-and-minutes.122912/ 146 - 147 - `strFormat('%02.0f:%02.0f', floor((level.getDayTime / 1000 + 8) % 24), floor(60 * (level.getDayTime % 1000) / 1000))` 148 - 149 - So the output can be: `00:00`, or `13:45`, etc. 150 - 151 - ::: 152 - 153 - ## Using Expressions 154 - 155 - The fastest way to get started is to use the `cmd:arithmetica` command. You should wrap your expression with `"` to satisfy Brigadier. 156 - 157 - Other places you can use expressions in: 158 - 159 - ::: details JSON Command Macros 160 - 161 - Command Macros are explained in detail on the Commands page. [Link](Commands#command-macros) 162 - 163 - ::: 164 - 165 - ::: details Loot number provider 166 - 167 - Using the `commander:arithmetica` provider in conditions: 168 - 169 - ```json 170 - { 171 - "condition": "minecraft:value_check", 172 - "value": { 173 - "type": "commander:arithmetica", 174 - "value": "round(random(5, 15))" 175 - }, 176 - "range": { 177 - "min": 12.3, 178 - "max": 32 179 - } 180 - } 181 - ``` 182 - 183 - ::: 184 - 185 - ::: details Loot predicate 186 - 187 - Using the `commander:expression` predicate: 188 - 189 - ```json 190 - { 191 - "condition": "commander:expression", 192 - "value": "level.isDay" 193 - } 194 - ``` 195 - 196 - ::: 197 - 198 - ::: details `execute` command 199 - 200 - You can use expressions as predicates in the `execute if` command. 201 - 202 - ``` 203 - /execute if cmd:expression "level.isDay" run say It is day! 204 - ``` 205 - 206 - ::: 207 - 208 - ::: details `scoreboard players` command 209 - 210 - You can use expressions as modifiers for player predicates. `score` variable allows you to get the current score. 211 - 212 - The current version does not provide entity context. 213 - 214 - ``` 215 - /scoreboard players cmd:operate @s test_objective "(score * 1.5) + level.storage.my_score_modifier" 216 - ``` 217 - 218 - :::
-58
docs/develop/Commands.md
··· 1 - # Commands 2 - 3 - In general, you should prefer to implement Brigadier `/` commands, but there are some cases where some additional flexibility is required. 4 - 5 - ## Creating commands 6 - 7 - Creating new commands is easy, you'll have to implement the `Command` interface, create a [MapCodec](https://forge.gemwire.uk/wiki/Codecs) to serialize/deserialize the command and register the codec with `CommandTypes.register()`. 8 - 9 - Let's create a simple command which will print a string to standard output. 10 - 11 - ```java 12 - public record DummyCommand(String text) implements Command { 13 - 14 - @Override 15 - public boolean execute(EventContext context) { 16 - System.out.println(text()); 17 - return true; //Return if execution was successful. 18 - } 19 - 20 - @Override 21 - public CommandType type() { 22 - return null; 23 - } 24 - } 25 - ``` 26 - 27 - Now create a [Codec](https://forge.gemwire.uk/wiki/Codecs) for your command. 28 - 29 - ```java 30 - public static final MapCodec<DummyCommand> CODEC = Codec.STRING.fieldOf("text").xmap(DummyCommand::new, DummyCommand::text); 31 - ``` 32 - 33 - With that done, we'll have to register the command to get our `CommandType`. 34 - 35 - ```java 36 - public static final CommandType DUMMY = CommandType.register(new Identifier("modid", "print"), DummyCommand.CODEC); 37 - ``` 38 - 39 - Now return this type in `type()`. 40 - 41 - ```java 42 - @Override 43 - public CommandType type() { 44 - return MyModInit.DUMMY; 45 - } 46 - ``` 47 - 48 - ## EventContext 49 - 50 - EventContext allows you to retrieve the `LootContext` which is passed with the event type. 51 - 52 - ```java 53 - context.lootContext().getWorld(); //Returns a ServerWorld. 54 - 55 - context.lootContext().get(LootContextParameters.TOOL); //Returns the prameter or null if not present. 56 - 57 - context.lootContext().requireParameter(LootContextParameters.TOOL); //Returns the parameter or throws an exception if not present. 58 - ```
-69
docs/develop/Events.md
··· 1 - # Events 2 - 3 - Commander introduces a new data pack event system, which allows data packs to listen to events just like mods. 4 - 5 - The best practice is to have a generic Fabric event and register Commander as one of its listeners. 6 - 7 - ## Creating event types 8 - 9 - To implement event support for your mod you must first register an event type for Commander to dispatch. You can build and register events with the `EventType.Builder` class. 10 - 11 - ```java 12 - public static final EventType CUSTOM_EVENT = EventType.builder().build(new Identifier("modid", "custom_event")); 13 - ``` 14 - 15 - If your event requires a return type, you can specify a cancel term codec with `cancelTerm()`. 16 - 17 - ```java 18 - EventType.builder() 19 - .cancelTerm(Codec.INT) 20 - .build(new Identifier("modid", "custom_event")); 21 - ``` 22 - 23 - Events can accept additional parameters using `extension()`. After specifying an extension you can return a custom type. 24 - 25 - ```java 26 - EventType.builder() 27 - .extension(Codec.STRING, subscriptions -> { 28 - //handle data. 29 - return /*Return listeners*/; 30 - }) 31 - .build(new Identifier("modid", "custom_event")); 32 - ``` 33 - 34 - The default return type is `List<Command.Conditioned>`. 35 - 36 - ## Invoking the event 37 - 38 - If you didn't specify an extension, or opted to return a `List<Command.Conditioned>`, you can use the included `EventExecutors` util. 39 - 40 - To use the util, you have to pass the event type, the execution world, and a loot context supplier. 41 - 42 - ```java 43 - CustomEvent.EVENT.register((world, entity) -> runVoid(CUSTOM_EVENT, world, () -> makeContext(world, entity, entity.getPos()))); 44 - ``` 45 - 46 - ```java 47 - private static LootContext makeContext(ServerWorld world, Entity entity, Vec3d origin) { 48 - LootContextParameterSet.Builder builder = new LootContextParameterSet.Builder(world); 49 - builder.add(THIS_ENTITY, entity).add(ORIGIN, origin); 50 - return new LootContext.Builder(builder.build(LootContextTypes.COMMAND)).build(null /*Optional.empty() in 1.20.4*/); 51 - } 52 - ``` 53 - 54 - If you did specify an extension, or are using an unsupported return type, you'll have to write custom resolution logic. 55 - 56 - To do that you'll simply have to create an `EventContext`. `EventContext` is used to pass execution parameters to commands. 57 - 58 - ```java 59 - EventContext context = EventContext.builder(type) 60 - .addParameter(EventKey.LOOT_CONTEXT, /*instance of loot context*/) 61 - .build(); 62 - for (Command.Conditioned subscriber : subscribers) subscriber.execute(context); 63 - ``` 64 - To get the return value, you can call `getReturnValue(def)`, the default value can be null. The return type is generic. 65 - 66 - ```java 67 - boolean val = context.getReturnValue(def); 68 - if (val != def) return val; 69 - ```
-175
docs/develop/Expressions.md
··· 1 - # Expressions 2 - 3 - As explained in [Expressions](/Expressions), Commander introduces an extensive expression system. This system is exposed as part of the API. On this page we will go over usage examples and a possible way to make expressions an optional integration. 4 - 5 - ## Using expressions directly 6 - 7 - The built-in `Expression` class provides a string -> expression codec and a `parse` method. An expression is a `LootContext -> Expression.Result` function. `Expression.Result` represents a result value that can be converted to `BigDecimal`, `boolean`, `String`, `Instant`and `Duration`. 8 - 9 - To apply any of the expression functions you must have an instance of `LootContext`. 10 - 11 - ```java 12 - public static final Expression EXP = Expression.parse("strFormat('%02.0f:%02.0f', floor((level.getDayTime / 1000 + 8) % 24), floor(60 * (level.getDayTime % 1000) / 1000))").result().orElseThrow(); 13 - 14 - public String worldTimeInHumanTime(LootContext context) { 15 - return EXP.apply(context).getAsString(); 16 - } 17 - ``` 18 - 19 - ## Special functions 20 - 21 - Commander comes with `Arithmetica` and `BooleanExpression` which are `double` and `boolean` functions that can be encoded either as a constant or as an expression. It's worth noting that `"true"` will be decoded as an expression, but `true` will not. 22 - 23 - ```json 24 - 2.2, "random(0, 3)" //Arithmetica 25 - 26 - true, "level.isDay" //BooleanExpression 27 - ``` 28 - 29 - ## Expressions as optional integration. 30 - 31 - When implementing support for Commander, you may want to make this integration optional. The easiest way to do this is to create a common interface that is then delegated to one of the implementations. This is how expressions are set-up in Andromeda's new config system. 32 - 33 - Let's start by defining a simple boolean intermediary interface. I recommend using a supplier as the parameter, so you don't construct a `LootContext` for constant values. 34 - 35 - ```java 36 - public interface BooleanIntermediary { 37 - boolean asBoolean(Supplier<LootContext> supplier); 38 - } 39 - ``` 40 - 41 - Now let's implement the constant delegate. 42 - 43 - ```java 44 - public record ConstantBooleanIntermediary(boolean value) implements BooleanIntermediary { 45 - 46 - @Override 47 - public boolean asBoolean(Supplier<LootContext> supplier) { 48 - return this.value; 49 - } 50 - } 51 - ``` 52 - 53 - And our commander delegate. 54 - 55 - ```java 56 - public final class CommanderBooleanIntermediary implements BooleanIntermediary { 57 - 58 - private final BooleanExpression expression; 59 - private final boolean constant; 60 - 61 - public CommanderBooleanIntermediary(BooleanExpression expression) { 62 - this.expression = expression; 63 - this.constant = expression.toSource().left().isPresent(); //micro optimization 64 - } 65 - 66 - @Override 67 - public boolean asBoolean(Supplier<LootContext> supplier) { 68 - return this.expression.applyAsBoolean(constant ? null : supplier.get()); 69 - } 70 - 71 - public BooleanExpression getExpression() { 72 - return this.expression; 73 - } 74 - } 75 - ``` 76 - 77 - With our delegates set-up we have to dynamically pick one or the other. Let's return to our intermediary interface and create a factory for constant values. Here I'm using `Support` from Dark Matter, but you can just copy this method: 78 - 79 - ```java 80 - public static <T, F extends T, S extends T> T support(String mod, Supplier<F> expected, Supplier<S> fallback) { 81 - return FabricLoader.getInstance().isModLoaded(mod) ? expected.get() : fallback.get(); 82 - } 83 - ``` 84 - 85 - The method will check if Commander is installed and will pick the correct factory. 86 - 87 - ```java 88 - public interface BooleanIntermediary { 89 - Function<Boolean, BooleanIntermediary> FACTORY = Support.support("commander", 90 - () -> b -> new CommanderBooleanIntermediary(BooleanExpression.constant(b)), 91 - () -> ConstantBooleanIntermediary::new); 92 - 93 - static BooleanIntermediary of(boolean value) { 94 - return FACTORY.apply(value); 95 - } 96 - //... 97 - } 98 - ``` 99 - 100 - Now we can define expressions in our config! 101 - 102 - ```java 103 - public class Config { 104 - public BooleanIntermediary value = BooleanIntermediary.of(true); 105 - } 106 - ``` 107 - 108 - But wait, what about encoding/decoding? Let's actually get to that. Here we can create a codec for our delegates and use `support` to pick the correct one. 109 - 110 - ```java 111 - public record ConstantBooleanIntermediary(boolean value) implements BooleanIntermediary { 112 - public static final Codec<ConstantBooleanIntermediary> CODEC = Codec.BOOL.xmap(ConstantBooleanIntermediary::new, ConstantBooleanIntermediary::value); 113 - //... 114 - } 115 - 116 - public final class CommanderBooleanIntermediary implements BooleanIntermediary { 117 - public static final Codec<CommanderBooleanIntermediary> CODEC = BooleanExpression.CODEC.xmap(CommanderBooleanIntermediary::new, CommanderBooleanIntermediary::getExpression); 118 - //... 119 - } 120 - ``` 121 - 122 - Now we can use our codecs. 123 - 124 - ```java 125 - Codec<BooleanIntermediary> codec = (Codec<BooleanIntermediary>) Support.fallback("commander", () -> CommanderBooleanIntermediary.CODEC, () -> ConstantBooleanIntermediary.CODEC); 126 - ``` 127 - 128 - ### Using intermediaries in configs. 129 - 130 - ::: info Note 131 - 132 - This section only applies if you use Gson to read/write your configs. 133 - 134 - If you use a third-party library and the library doesn't allow you to pass a custom Gson instance, you could modify it using mixins. 135 - 136 - ::: 137 - 138 - In Gson you can provide custom JsonSerializers/JsonDeserializers, which allows us to encode the intermediary using Codecs. 139 - 140 - Let's create our `CodecSerializer` class. 141 - 142 - ::: details Code 143 - 144 - ```java 145 - public record CodecSerializer<C>(Codec<C> codec) implements JsonSerializer<C>, JsonDeserializer<C> { 146 - 147 - public static <C> CodecSerializer<C> of(Codec<C> codec) { 148 - return new CodecSerializer<>(codec); 149 - } 150 - 151 - @Override 152 - public C deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 153 - var r = this.codec.parse(JsonOps.INSTANCE, json); 154 - if (r.error().isPresent()) throw new JsonParseException(r.error().orElseThrow().message()); 155 - return r.result().orElseThrow(); 156 - } 157 - 158 - @Override 159 - public JsonElement serialize(C src, Type typeOfSrc, JsonSerializationContext context) { 160 - var r = codec.encodeStart(JsonOps.INSTANCE, src); 161 - if (r.error().isPresent()) throw new IllegalStateException(r.error().orElseThrow().message()); 162 - return r.result().orElseThrow(); 163 - } 164 - } 165 - ``` 166 - 167 - ::: 168 - 169 - And now we can register a type hierarchy adapter with our GsonBuilder. 170 - 171 - ```java 172 - builder.registerTypeHierarchyAdapter(BooleanIntermediary.class, CodecSerializer.of(codec)); 173 - ``` 174 - 175 - And we're done!
-29
docs/index.md
··· 1 - --- 2 - comment: false 3 - --- 4 - # Commander <Badge type="warning" text="beta" /> 5 - 6 - <div class="badge-holder"> 7 - 8 - [![Available on Modrinth](https://raw.githubusercontent.com/melontini/mini-badges/main/minecraft/modrinth.svg)](https://modrinth.com/mod/cmd) 9 - [![Available on CurseForge](https://raw.githubusercontent.com/melontini/mini-badges/main/minecraft/curseforge.svg)](https://www.curseforge.com/minecraft/mc-mods/cmd-project) 10 - 11 - </div> 12 - 13 - Commander is an extension of the vanilla data pack system. 14 - 15 - It adds a new event system, flexible json commands, new `/` commands, support for advanced expressions with data access and more! 16 - 17 - There are no client-side features available, everything is executed on the server. 18 - 19 - ::: danger 20 - This mod is only available on [Modrinth](https://modrinth.com/mod/cmd) and [GitHub](https://github.com/constellation-mc/commander). If you got this mod from *anywhere* else, it may contain malware similar to [fractureiser](https://github.com/fractureiser-investigation/fractureiser). 21 - 22 - See [stop mod reposts](https://stopmodreposts.org/) for more info. 23 - 24 - You have been warned! 25 - ::: 26 - 27 - *** 28 - 29 - <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://raw.githubusercontent.com/melontini/mini-badges/main/licenses/cc/CC-BY-NC-SA-4.0.svg" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">Commander Wiki</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="https://github.com/constellation-mc/commander/tree/documentation" property="cc:attributionName" rel="cc:attributionURL">constellation and contributors</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License</a>.
-88
docs/public/ai.txt
··· 1 - # Spawning AI 2 - # Prevent datasets from using the following file types 3 - 4 - User-Agent: * 5 - Disallow: *.txt 6 - Disallow: *.pdf 7 - Disallow: *.doc 8 - Disallow: *.docx 9 - Disallow: *.odt 10 - Disallow: *.rtf 11 - Disallow: *.tex 12 - Disallow: *.wks 13 - Disallow: *.wpd 14 - Disallow: *.wps 15 - Disallow: *.html 16 - Disallow: *.bmp 17 - Disallow: *.gif 18 - Disallow: *.ico 19 - Disallow: *.jpeg 20 - Disallow: *.jpg 21 - Disallow: *.png 22 - Disallow: *.svg 23 - Disallow: *.tif 24 - Disallow: *.tiff 25 - Disallow: *.webp 26 - Disallow: *.aac 27 - Disallow: *.aiff 28 - Disallow: *.amr 29 - Disallow: *.flac 30 - Disallow: *.m4a 31 - Disallow: *.mp3 32 - Disallow: *.oga 33 - Disallow: *.opus 34 - Disallow: *.wav 35 - Disallow: *.wma 36 - Disallow: *.mp4 37 - Disallow: *.webm 38 - Disallow: *.ogg 39 - Disallow: *.avi 40 - Disallow: *.mov 41 - Disallow: *.wmv 42 - Disallow: *.flv 43 - Disallow: *.mkv 44 - Disallow: *.py 45 - Disallow: *.js 46 - Disallow: *.java 47 - Disallow: *.c 48 - Disallow: *.cpp 49 - Disallow: *.cs 50 - Disallow: *.h 51 - Disallow: *.css 52 - Disallow: *.php 53 - Disallow: *.swift 54 - Disallow: *.go 55 - Disallow: *.rb 56 - Disallow: *.pl 57 - Disallow: *.sh 58 - Disallow: *.sql 59 - Disallow: / 60 - Disallow: * 61 - # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 62 - # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@( @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 63 - # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ &@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 64 - # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 65 - # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@% @@@@@@@@@ 66 - # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@ 67 - # @@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 68 - # @@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 69 - # @@@@@@@@@@@( @@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 70 - # @@@@@@@@@@@@ %@@@@@/@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 71 - # @@@@@@@@@@@@ %@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 72 - # @@@@@@@@@@@, @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 73 - # @@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@( %@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 74 - # @@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@ 75 - # @@@@@@@@@@@/@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@ 76 - # @@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 77 - # @@@@@@@@@@@* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@, @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 78 - # @@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@ *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 79 - # @@@@@@@@@@@@ %@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 80 - # @@@@@@@@@@@@ %@@@@& @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 81 - # @@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 82 - # @@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 83 - # @@@@@@@@@@@ .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@ 84 - # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@ 85 - # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 86 - # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 87 - # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 88 - # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ spawning.ai
-1
docs/public/badges/minimal/compact-minimal_vector.svg
··· 1 - <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" fill="none"><rect width="40" height="40" fill="#000" rx="8"/><rect width="37.9" height="37.9" x="1.05" y="1.05" stroke="url(#a)" stroke-opacity=".88" stroke-width="2.1" rx="6.95"/><g fill="#F9F9F9" filter="url(#b)"><path d="M23.34 31.29a5.9 5.9 0 0 1-1.94-1.1l-1.24.39a1.74 1.74 0 0 1-2.07-.78l-.36-.61a1.7 1.7 0 0 1-.23-1.18c.07-.42.26-.77.59-1.05l.97-.84a10.48 10.48 0 0 1 0-2.32l-.97-.88a1.7 1.7 0 0 1-.36-2.2l.36-.6a1.74 1.74 0 0 1 2.08-.78l1.23.39a5.9 5.9 0 0 1 1.95-1.1l.28-1.32a1.72 1.72 0 0 1 1.72-1.4h.78a1.7 1.7 0 0 1 1.72 1.4l.29 1.32a5.9 5.9 0 0 1 1.95 1.1l1.23-.4a1.74 1.74 0 0 1 2.07.78l.36.62c.22.36.3.76.23 1.18a1.7 1.7 0 0 1-.59 1.05l-.97.84a10.5 10.5 0 0 1 0 2.32l.97.87a1.7 1.7 0 0 1 .36 2.2l-.36.61a1.74 1.74 0 0 1-2.08.78l-1.22-.39c-.3.24-.61.45-.93.63a5.9 5.9 0 0 1-1.02.47l-.3 1.32a1.72 1.72 0 0 1-1.7 1.39h-.78a1.7 1.7 0 0 1-1.72-1.39l-.29-1.32Zm2.4-2.46c1.08 0 2-.37 2.76-1.13a3.73 3.73 0 0 0 1.13-2.74c0-1.08-.38-2-1.13-2.75a3.76 3.76 0 0 0-2.76-1.13c-1.08 0-2 .38-2.76 1.13a3.73 3.73 0 0 0-1.13 2.75c0 1.07.38 1.99 1.13 2.74a3.76 3.76 0 0 0 2.76 1.13ZM9.86 26.1a5.1 5.1 0 0 1-1.3-.7l-.84.26a1.18 1.18 0 0 1-1.32-.5l-.24-.4a1.14 1.14 0 0 1-.15-.77c.04-.27.17-.5.38-.67l.65-.55a4.98 4.98 0 0 1 0-1.52l-.65-.56a1.07 1.07 0 0 1-.23-1.4l.26-.44c.14-.22.33-.37.56-.47.24-.1.48-.1.74-.03l.85.26a5.13 5.13 0 0 1 1.3-.7l.17-.85a1.12 1.12 0 0 1 1.11-.9h.47c.28 0 .52.08.72.26.2.17.34.4.4.67l.18.82.66.3c.2.11.41.25.63.4l.85-.26a1.18 1.18 0 0 1 1.32.5l.24.4c.13.24.18.5.14.77a.96.96 0 0 1-.38.67l-.64.56a4.98 4.98 0 0 1 0 1.52l.64.55c.22.18.35.4.38.66.04.26 0 .51-.14.75l-.27.43a1.1 1.1 0 0 1-.55.47c-.24.1-.48.11-.74.03l-.85-.26a5.14 5.14 0 0 1-1.3.7l-.17.85a1.12 1.12 0 0 1-1.12.9h-.47c-.27 0-.51-.08-.72-.26-.2-.17-.33-.4-.4-.67l-.17-.82Zm1.53-1.75c.65 0 1.2-.23 1.66-.7.46-.45.69-1 .69-1.64 0-.65-.23-1.2-.7-1.66a2.27 2.27 0 0 0-1.65-.68c-.65 0-1.2.22-1.66.68-.46.46-.7 1.01-.7 1.66 0 .64.24 1.19.7 1.65.46.46 1.01.69 1.66.69Zm6.9-18.19c-.26.07-.47.21-.62.43-.15.22-.22.47-.2.74l.04.82a5.03 5.03 0 0 0-1.05 1l-.87-.04c-.26-.01-.5.06-.7.21-.2.16-.35.35-.42.6l-.12.44c-.07.25-.05.5.05.76.1.24.29.42.54.54l.75.36a4.9 4.9 0 0 0 .39 1.44l-.47.69a1 1 0 0 0-.2.72c.03.26.14.48.33.67l.36.35c.19.16.4.27.65.3.25.03.48-.02.7-.16l.74-.46a5.05 5.05 0 0 0 1.4.33l.39.76a1.1 1.1 0 0 0 1.29.58l.44-.12c.26-.07.47-.21.62-.43.15-.22.22-.46.2-.74l-.04-.82a4.97 4.97 0 0 0 1.05-1l.87.05a1.15 1.15 0 0 0 1.12-.8l.12-.46c.07-.25.05-.5-.05-.75s-.29-.43-.54-.54l-.75-.37a4.9 4.9 0 0 0-.39-1.44l.47-.69a1 1 0 0 0 .2-.72 1.1 1.1 0 0 0-.33-.67l-.36-.35a1.3 1.3 0 0 0-.65-.3c-.25-.03-.48.02-.7.16l-.74.46a5.04 5.04 0 0 0-1.4-.33l-.39-.76a1.1 1.1 0 0 0-1.29-.58l-.44.12Zm1.11 3.26c.62-.16 1.2-.08 1.75.24.55.31.91.78 1.08 1.39.16.6.08 1.19-.24 1.74s-.78.9-1.4 1.07c-.6.16-1.19.08-1.74-.24a2.21 2.21 0 0 1-1.08-1.39 2.2 2.2 0 0 1 .24-1.74c.32-.55.78-.9 1.4-1.07Z"/></g><defs><linearGradient id="a" x1="0" x2="40" y1="20" y2="20" gradientUnits="userSpaceOnUse"><stop stop-color="#62883F"/><stop offset=".34" stop-color="#CD3935"/><stop offset=".68" stop-color="#A85551"/><stop offset="1" stop-color="#BF873E"/></linearGradient><filter id="b" width="36" height="36" x="2" y="2" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1_68"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1_68" result="shape"/></filter></defs></svg>
-1
docs/public/badges/minimal/cozy-minimal_vector.svg
··· 1 - <svg xmlns="http://www.w3.org/2000/svg" width="56" height="56" fill="none"><rect width="56" height="56" fill="#000" rx="8.4"/><rect width="53.9" height="53.9" x="1.05" y="1.05" stroke="url(#a)" stroke-opacity=".88" stroke-width="2.1" rx="7.35"/><g fill="#F9F9F9" filter="url(#b)"><path d="M32.77 44.13a8.44 8.44 0 0 1-1.46-.67c-.44-.27-.88-.57-1.32-.9l-1.76.55a2.48 2.48 0 0 1-2.96-1.1l-.51-.88a2.48 2.48 0 0 1-.33-1.69c.1-.6.37-1.1.84-1.5l1.39-1.2a15 15 0 0 1 0-3.32l-1.4-1.24a2.43 2.43 0 0 1-.5-3.14l.5-.88a2.5 2.5 0 0 1 2.97-1.1l1.76.55a9.28 9.28 0 0 1 2.78-1.57l.42-1.89a2.46 2.46 0 0 1 2.46-1.98h1.1a2.43 2.43 0 0 1 2.46 1.98l.42 1.9c.52.18 1.01.4 1.46.66.45.26.89.56 1.32.9l1.76-.55a2.48 2.48 0 0 1 2.96 1.1l.51.88a2.47 2.47 0 0 1-.51 3.18l-1.39 1.2a15 15 0 0 1 0 3.32l1.4 1.25a2.45 2.45 0 0 1 .5 3.14l-.5.87a2.48 2.48 0 0 1-2.96 1.1l-1.77-.54a14 14 0 0 1-1.32.9c-.45.26-.94.48-1.46.66l-.42 1.9A2.46 2.46 0 0 1 36.76 48h-1.11a2.43 2.43 0 0 1-2.46-1.98l-.42-1.9Zm3.43-3.51c1.55 0 2.86-.54 3.94-1.62a5.33 5.33 0 0 0 1.62-3.92c0-1.53-.54-2.84-1.62-3.92a5.37 5.37 0 0 0-3.94-1.61 5.4 5.4 0 0 0-3.94 1.61 5.33 5.33 0 0 0-1.62 3.92c0 1.54.54 2.85 1.62 3.92a5.37 5.37 0 0 0 3.94 1.62Zm-22.68-3.9a7.29 7.29 0 0 1-1.85-1l-1.22.37a1.68 1.68 0 0 1-1.89-.71l-.33-.59c-.2-.33-.27-.7-.21-1.08.05-.4.24-.71.54-.96l.93-.8a7.12 7.12 0 0 1 0-2.17l-.93-.8c-.3-.25-.49-.56-.54-.94-.06-.37.01-.73.2-1.06l.39-.63c.2-.3.46-.53.8-.67a1.5 1.5 0 0 1 1.04-.04l1.22.38a7.33 7.33 0 0 1 1.85-1l.25-1.22a1.6 1.6 0 0 1 1.6-1.3h.66c.4 0 .74.13 1.03.38.3.25.48.57.57.96l.25 1.17a7.33 7.33 0 0 1 1.85 1l1.21-.37a1.68 1.68 0 0 1 1.89.71l.34.59c.2.33.26.7.2 1.08-.05.4-.23.71-.54.96l-.92.8a7.11 7.11 0 0 1 0 2.17l.92.8c.31.25.5.56.55.94.05.37-.02.73-.21 1.06l-.38.63c-.2.3-.46.53-.8.67a1.5 1.5 0 0 1-1.05.04l-1.21-.38a7.35 7.35 0 0 1-1.85 1l-.25 1.22a1.6 1.6 0 0 1-1.6 1.3h-.67c-.39 0-.73-.13-1.03-.38a1.7 1.7 0 0 1-.56-.96l-.25-1.17Zm2.18-2.51c.92 0 1.71-.33 2.37-.98.66-.66.99-1.45.99-2.36a3.2 3.2 0 0 0-1-2.37 3.24 3.24 0 0 0-2.36-.98c-.92 0-1.72.33-2.37.98a3.21 3.21 0 0 0-.99 2.37c0 .91.33 1.7.99 2.36.65.65 1.45.98 2.37.98Zm9.85-25.98c-.37.1-.66.3-.88.61-.2.32-.3.67-.29 1.06l.06 1.17a7.17 7.17 0 0 0-1.5 1.42l-1.24-.05a1.6 1.6 0 0 0-1.6 1.15l-.17.64c-.1.36-.08.72.07 1.08.16.35.41.61.77.77l1.07.52a6.98 6.98 0 0 0 .56 2.06l-.68.98c-.22.32-.32.66-.27 1.03.04.37.2.69.47.96l.51.5c.27.23.58.38.93.43.35.04.69-.03 1-.23l1.06-.66a7.23 7.23 0 0 0 2 .48l.55 1.08a1.57 1.57 0 0 0 1.84.83l.64-.17c.37-.1.66-.3.88-.62.21-.31.3-.66.29-1.05l-.06-1.17c.28-.22.54-.44.78-.66.24-.22.48-.47.71-.76l1.25.05a1.64 1.64 0 0 0 1.6-1.15l.17-.64c.1-.37.08-.73-.07-1.08a1.45 1.45 0 0 0-.77-.77l-1.07-.52a6.98 6.98 0 0 0-.56-2.06l.68-.99c.22-.31.32-.65.27-1.02-.04-.37-.2-.7-.47-.96l-.51-.5a1.67 1.67 0 0 0-.93-.43c-.35-.05-.69.03-1 .23l-1.06.66a7.21 7.21 0 0 0-2-.48l-.55-1.09a1.57 1.57 0 0 0-1.84-.82l-.64.17Zm1.6 4.66c.87-.23 1.7-.12 2.5.33a3.2 3.2 0 0 1 1.53 1.99c.23.87.12 1.7-.33 2.48a3.17 3.17 0 0 1-2 1.53c-.87.24-1.7.13-2.5-.33a3.16 3.16 0 0 1-1.53-1.98 3.2 3.2 0 0 1 .33-2.49 3.17 3.17 0 0 1 2-1.53Z"/></g><defs><linearGradient id="a" x1="0" x2="56" y1="28" y2="28" gradientUnits="userSpaceOnUse"><stop stop-color="#62883F"/><stop offset=".34" stop-color="#CD3935"/><stop offset=".68" stop-color="#A85551"/><stop offset="1" stop-color="#BF873E"/></linearGradient><filter id="b" width="51.43" height="51.43" x="2.29" y="2.29" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2.86"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1_62"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1_62" result="shape"/></filter></defs></svg>
-1
docs/public/badges/requires/compact_vector.svg
··· 1 - <svg xmlns="http://www.w3.org/2000/svg" width="229" height="40" fill="none"><rect width="229" height="40" fill="#000" rx="8"/><rect width="226.86" height="37.86" x="1.07" y="1.07" stroke="url(#a)" stroke-opacity=".88" stroke-width="2.14" rx="6.93"/><g fill="#F9F9F9" filter="url(#b)"><path d="M25.34 31.29a5.9 5.9 0 0 1-1.94-1.1l-1.24.39a1.74 1.74 0 0 1-2.07-.78l-.36-.61a1.7 1.7 0 0 1-.23-1.18c.07-.42.26-.77.59-1.05l.97-.84a10.48 10.48 0 0 1 0-2.32l-.97-.88a1.7 1.7 0 0 1-.36-2.2l.36-.6a1.74 1.74 0 0 1 2.08-.78l1.23.39a5.9 5.9 0 0 1 1.95-1.1l.28-1.32a1.72 1.72 0 0 1 1.72-1.4h.78a1.7 1.7 0 0 1 1.72 1.4l.29 1.32a5.9 5.9 0 0 1 1.95 1.1l1.23-.4a1.74 1.74 0 0 1 2.07.78l.36.62c.22.36.3.76.23 1.18a1.7 1.7 0 0 1-.59 1.05l-.97.84a10.5 10.5 0 0 1 0 2.32l.97.87a1.7 1.7 0 0 1 .36 2.2l-.36.61a1.74 1.74 0 0 1-2.08.78l-1.22-.39c-.3.24-.61.45-.93.63a5.9 5.9 0 0 1-1.02.47l-.3 1.32a1.72 1.72 0 0 1-1.7 1.39h-.78a1.7 1.7 0 0 1-1.72-1.39l-.29-1.32Zm2.4-2.46c1.08 0 2-.37 2.76-1.13a3.73 3.73 0 0 0 1.13-2.74c0-1.08-.38-2-1.13-2.75a3.76 3.76 0 0 0-2.76-1.13c-1.08 0-2 .38-2.76 1.13a3.73 3.73 0 0 0-1.13 2.75c0 1.07.38 1.99 1.13 2.74a3.76 3.76 0 0 0 2.76 1.13ZM11.86 26.1a5.1 5.1 0 0 1-1.3-.7l-.84.26a1.18 1.18 0 0 1-1.32-.5l-.24-.4a1.14 1.14 0 0 1-.15-.77c.04-.27.17-.5.38-.67l.65-.55a4.98 4.98 0 0 1 0-1.52l-.65-.56a1.07 1.07 0 0 1-.23-1.4l.26-.44c.14-.22.33-.37.56-.47.24-.1.48-.1.74-.03l.85.26a5.13 5.13 0 0 1 1.3-.7l.17-.85a1.12 1.12 0 0 1 1.11-.9h.47c.28 0 .52.08.72.26.2.17.34.4.4.67l.18.82.66.3c.2.11.41.25.63.4l.85-.26a1.18 1.18 0 0 1 1.32.5l.24.4c.13.24.18.5.14.77a.96.96 0 0 1-.38.67l-.64.56a4.98 4.98 0 0 1 0 1.52l.64.55c.22.18.35.4.38.66.04.26 0 .51-.14.75l-.27.43a1.1 1.1 0 0 1-.55.47c-.24.1-.48.11-.74.03l-.85-.26a5.14 5.14 0 0 1-1.3.7l-.17.85a1.12 1.12 0 0 1-1.12.9h-.47c-.27 0-.51-.08-.72-.26-.2-.17-.33-.4-.4-.67l-.17-.82Zm1.53-1.75c.65 0 1.2-.23 1.66-.7.46-.45.69-1 .69-1.64 0-.65-.23-1.2-.7-1.66a2.27 2.27 0 0 0-1.65-.68c-.65 0-1.2.22-1.66.68-.46.46-.7 1.01-.7 1.66 0 .64.24 1.19.7 1.65.46.46 1.01.69 1.66.69Zm6.9-18.19c-.26.07-.47.21-.62.43-.15.22-.22.47-.2.74l.04.82a5.03 5.03 0 0 0-1.05 1l-.87-.04c-.26-.01-.5.06-.7.21-.2.16-.35.35-.42.6l-.12.44c-.07.25-.05.5.05.76.11.24.29.42.54.54l.75.36a4.9 4.9 0 0 0 .39 1.44l-.47.69a1 1 0 0 0-.2.72c.03.26.14.48.33.67l.36.35c.19.16.4.27.65.3.25.03.48-.02.7-.16l.74-.46a5.05 5.05 0 0 0 1.4.33l.39.76a1.1 1.1 0 0 0 1.29.58l.44-.12c.26-.07.47-.21.62-.43.15-.22.22-.46.2-.74l-.04-.82a4.97 4.97 0 0 0 1.05-1l.87.05a1.15 1.15 0 0 0 1.12-.8l.12-.46c.07-.25.05-.5-.05-.75s-.29-.43-.54-.54l-.75-.37a4.9 4.9 0 0 0-.39-1.44l.47-.69a1 1 0 0 0 .2-.72 1.1 1.1 0 0 0-.33-.67l-.36-.35a1.3 1.3 0 0 0-.65-.3c-.25-.03-.48.02-.7.16l-.74.46a5.04 5.04 0 0 0-1.4-.33l-.39-.76a1.1 1.1 0 0 0-1.29-.58l-.44.12Zm1.11 3.26c.62-.16 1.2-.08 1.75.24.55.31.91.78 1.08 1.39.16.6.08 1.19-.24 1.74s-.78.9-1.4 1.07c-.6.16-1.19.08-1.74-.24a2.21 2.21 0 0 1-1.08-1.39 2.2 2.2 0 0 1 .24-1.74c.32-.55.78-.9 1.4-1.07Z"/></g><g filter="url(#c)"><path fill="#E8E8E8" d="M43.36 26.5V14.14h4.4c.96 0 1.76.16 2.4.5.63.32 1.1.78 1.42 1.36.31.58.47 1.25.47 2.01 0 .76-.16 1.43-.48 2a3.2 3.2 0 0 1-1.42 1.34c-.64.31-1.43.47-2.39.47h-3.34v-1.6h3.17c.6 0 1.1-.09 1.47-.26.39-.18.67-.43.84-.76.18-.33.27-.72.27-1.19 0-.46-.1-.87-.27-1.21-.18-.34-.46-.6-.84-.79a3.41 3.41 0 0 0-1.5-.27h-2.33V26.5h-1.87Zm6.1-5.58 3.06 5.58h-2.13l-3-5.58h2.07Zm8.6 5.77c-.92 0-1.7-.2-2.37-.59a3.97 3.97 0 0 1-1.52-1.66 5.62 5.62 0 0 1-.53-2.52c0-.95.18-1.78.53-2.5.36-.73.86-1.3 1.5-1.7a4.55 4.55 0 0 1 3.78-.35 3.69 3.69 0 0 1 2.24 2.25 6 6 0 0 1 .34 2.15v.64h-7.37v-1.35h5.6c0-.48-.1-.9-.29-1.27a2.2 2.2 0 0 0-2.03-1.2c-.51 0-.95.13-1.33.38-.38.25-.66.57-.87.97-.2.4-.3.82-.3 1.28v1.06c0 .62.1 1.15.32 1.58.23.44.53.77.93 1 .4.22.85.34 1.38.34.34 0 .65-.05.94-.15a1.94 1.94 0 0 0 1.2-1.18l1.7.31c-.13.5-.38.95-.73 1.32-.35.38-.8.67-1.32.88-.53.2-1.13.3-1.8.3Zm12.23 3.29v-4.92h-.1c-.11.2-.27.42-.48.67a2.62 2.62 0 0 1-2.2.95 3.6 3.6 0 0 1-3.39-2.22 6.12 6.12 0 0 1-.5-2.58c0-1 .18-1.86.51-2.57a3.61 3.61 0 0 1 3.39-2.2c.57 0 1.03.1 1.39.29.35.19.62.4.82.66.2.25.35.48.45.67h.16v-1.5h1.76v12.75h-1.8Zm-2.39-4.84c.52 0 .96-.13 1.32-.4.36-.29.63-.67.82-1.16.19-.5.28-1.06.28-1.72 0-.64-.09-1.2-.27-1.69a2.48 2.48 0 0 0-.82-1.12c-.36-.27-.8-.4-1.33-.4-.54 0-1 .13-1.36.41-.36.28-.63.67-.82 1.16a4.7 4.7 0 0 0-.27 1.64c0 .62.1 1.18.28 1.68.18.5.46.88.82 1.17.37.29.82.43 1.35.43Zm12.5-2.48v-5.43h1.81v9.27h-1.77v-1.6h-.1a2.9 2.9 0 0 1-2.77 1.73c-.6 0-1.12-.14-1.58-.4a2.76 2.76 0 0 1-1.08-1.18 4.3 4.3 0 0 1-.38-1.92v-5.9h1.8v5.68c0 .63.18 1.13.53 1.5.35.38.8.57 1.36.57a2.21 2.21 0 0 0 1.85-1.02c.22-.34.33-.78.33-1.3Zm4.24 3.84v-9.27h1.8v9.27h-1.8Zm.9-10.7c-.3 0-.58-.1-.8-.32a1.02 1.02 0 0 1-.33-.76 1 1 0 0 1 .33-.76c.22-.21.5-.32.8-.32.32 0 .59.1.81.32a1 1 0 0 1 .34.76c0 .3-.11.55-.34.76-.22.21-.49.32-.8.32Zm3.33 10.7v-9.27h1.74v1.47h.1c.17-.5.47-.89.9-1.18a2.5 2.5 0 0 1 1.45-.43 7.66 7.66 0 0 1 .77.04v1.73l-.38-.07a3.7 3.7 0 0 0-.56-.04c-.43 0-.8.09-1.14.27a2.02 2.02 0 0 0-1.08 1.82v5.66h-1.8Zm10.19.19c-.92 0-1.7-.2-2.36-.59a3.97 3.97 0 0 1-1.52-1.66 5.62 5.62 0 0 1-.54-2.52c0-.95.18-1.78.54-2.5.35-.73.85-1.3 1.5-1.7a4.54 4.54 0 0 1 3.78-.35 3.7 3.7 0 0 1 2.24 2.25c.22.6.34 1.3.34 2.15v.64h-7.37v-1.35h5.6c0-.48-.1-.9-.3-1.27a2.2 2.2 0 0 0-2.03-1.2 2.46 2.46 0 0 0-2.19 1.35c-.2.4-.3.82-.3 1.28v1.06c0 .62.1 1.15.32 1.58.22.44.53.77.92 1 .4.22.86.34 1.39.34.34 0 .65-.05.93-.15a1.95 1.95 0 0 0 1.2-1.18l1.71.31c-.14.5-.38.95-.73 1.32-.36.38-.8.67-1.33.88-.52.2-1.13.3-1.8.3Zm12.93-7.2-1.64.3a1.93 1.93 0 0 0-.32-.6 1.62 1.62 0 0 0-.6-.47 2.15 2.15 0 0 0-.93-.18c-.51 0-.94.11-1.28.34-.34.23-.52.52-.52.88 0 .3.12.56.35.75.23.19.6.34 1.11.46l1.47.34c.86.2 1.5.5 1.91.91.42.41.63.94.63 1.6 0 .56-.16 1.05-.48 1.49-.32.43-.77.76-1.34 1.01-.57.25-1.22.37-1.97.37a4.46 4.46 0 0 1-2.54-.67 2.8 2.8 0 0 1-1.21-1.9l1.74-.26c.11.45.33.8.67 1.03.34.23.78.34 1.32.34.6 0 1.07-.12 1.42-.37.36-.24.53-.55.53-.9 0-.3-.1-.54-.32-.74-.21-.2-.54-.34-.98-.44l-1.57-.35c-.87-.2-1.51-.5-1.92-.94a2.27 2.27 0 0 1-.62-1.64c0-.54.15-1.02.46-1.43a3 3 0 0 1 1.27-.96 4.62 4.62 0 0 1 1.85-.35c1 0 1.8.21 2.37.65.57.43.95 1 1.14 1.73Z"/><path fill="#fff" d="M129.16 18.62h-3.02a2.6 2.6 0 0 0-.25-.84 2.05 2.05 0 0 0-1.22-1.05 2.8 2.8 0 0 0-.92-.14c-.6 0-1.12.14-1.56.44-.43.3-.76.72-1 1.27a5.29 5.29 0 0 0-.34 2.02c0 .82.12 1.5.35 2.06.24.55.57.97 1 1.25.43.28.94.42 1.53.42a3 3 0 0 0 .9-.13 2 2 0 0 0 .71-.37c.21-.16.38-.36.52-.6.13-.23.23-.5.28-.8l3.02.02a5.17 5.17 0 0 1-3.24 4.11c-.65.26-1.4.39-2.25.39a6.1 6.1 0 0 1-3.01-.74 5.3 5.3 0 0 1-2.09-2.16 7.18 7.18 0 0 1-.77-3.45c0-1.36.26-2.51.78-3.46a5.33 5.33 0 0 1 2.11-2.16 6.6 6.6 0 0 1 5.05-.42 4.63 4.63 0 0 1 2.86 2.36c.3.58.49 1.24.56 1.98Zm6.04 8.06a5 5 0 0 1-2.51-.6 4.1 4.1 0 0 1-1.62-1.68 5.36 5.36 0 0 1-.56-2.5c0-.96.2-1.8.56-2.51a4.1 4.1 0 0 1 1.62-1.68c.7-.4 1.53-.6 2.5-.6.98 0 1.82.2 2.51.6.7.4 1.24.96 1.62 1.68.37.72.56 1.55.56 2.5a5.3 5.3 0 0 1-.56 2.51 4.07 4.07 0 0 1-1.62 1.68c-.7.4-1.53.6-2.5.6Zm.02-2.23c.35 0 .65-.11.9-.33.24-.22.43-.52.56-.9.13-.39.2-.84.2-1.34 0-.52-.07-.97-.2-1.36a2.04 2.04 0 0 0-.56-.9 1.31 1.31 0 0 0-.9-.33c-.37 0-.68.11-.93.33-.25.22-.44.52-.58.9a4.3 4.3 0 0 0-.19 1.36c0 .5.06.95.2 1.34.13.38.32.68.57.9.25.22.56.33.93.33Zm6.18 2.05v-9.27h2.8v1.7h.1a2.64 2.64 0 0 1 2.63-1.82c.64 0 1.19.16 1.65.5.46.32.75.77.87 1.32h.1c.17-.55.51-1 1.01-1.32.5-.34 1.1-.5 1.79-.5.87 0 1.59.28 2.13.84.55.56.82 1.33.82 2.3v6.25h-2.95v-5.57c0-.46-.12-.81-.35-1.05a1.23 1.23 0 0 0-.93-.37c-.4 0-.72.13-.96.4-.23.26-.34.61-.34 1.05v5.54h-2.84v-5.6c0-.43-.12-.77-.35-1.02a1.2 1.2 0 0 0-.92-.37 1.25 1.25 0 0 0-1.15.7 2 2 0 0 0-.16.8v5.5h-2.95Zm15.73 0v-9.27h2.81v1.7h.1a2.64 2.64 0 0 1 2.62-1.82c.65 0 1.2.16 1.66.5.46.32.75.77.87 1.32h.1c.17-.55.51-1 1.01-1.32.5-.34 1.1-.5 1.78-.5.88 0 1.6.28 2.14.84.55.56.82 1.33.82 2.3v6.25h-2.95v-5.57c0-.46-.12-.81-.36-1.05a1.23 1.23 0 0 0-.92-.37c-.4 0-.73.13-.96.4-.23.26-.34.61-.34 1.05v5.54h-2.84v-5.6c0-.43-.12-.77-.35-1.02a1.2 1.2 0 0 0-.92-.37 1.25 1.25 0 0 0-1.15.7c-.11.23-.16.5-.16.8v5.5h-2.96Zm18.36.16c-.6 0-1.12-.1-1.58-.3a2.5 2.5 0 0 1-1.08-.9 2.8 2.8 0 0 1-.4-1.53c0-.52.1-.95.28-1.3.18-.36.43-.65.75-.87.31-.22.68-.39 1.1-.5.41-.12.85-.2 1.32-.23.53-.05.96-.1 1.28-.16.32-.05.55-.13.7-.23a.5.5 0 0 0 .22-.44v-.03c0-.31-.1-.55-.32-.72-.21-.17-.5-.25-.86-.25-.4 0-.7.08-.94.25-.24.17-.4.4-.46.7l-2.72-.1a3.23 3.23 0 0 1 2.01-2.56c.6-.25 1.3-.38 2.13-.38a6 6 0 0 1 1.64.2c.5.15.93.35 1.3.61a2.73 2.73 0 0 1 1.17 2.28v6.3h-2.78v-1.3h-.07a2.63 2.63 0 0 1-1.53 1.3c-.35.1-.73.16-1.16.16Zm.9-1.93a1.82 1.82 0 0 0 1.48-.73c.15-.23.23-.5.23-.8v-.88a5.33 5.33 0 0 1-1.13.3c-.15.04-.3.06-.43.08a2.6 2.6 0 0 0-.71.2c-.2.1-.34.22-.45.36a.88.88 0 0 0-.15.52c0 .31.11.54.33.7.22.17.5.25.84.25Zm9.4-3.52v5.3h-2.96v-9.28h2.81v1.7h.1a2.6 2.6 0 0 1 1.05-1.33c.5-.33 1.09-.5 1.77-.5.65 0 1.22.15 1.7.45.48.29.86.7 1.12 1.22.27.52.4 1.12.4 1.82v5.91h-2.95v-5.33c0-.52-.13-.92-.4-1.2a1.4 1.4 0 0 0-1.09-.44c-.3 0-.58.07-.82.2-.23.14-.41.33-.54.58-.13.25-.2.55-.2.9Zm11.2 5.42a3.42 3.42 0 0 1-3.2-2.12 6.1 6.1 0 0 1-.5-2.64c0-1.09.17-1.99.52-2.7.34-.7.8-1.22 1.35-1.56a3.47 3.47 0 0 1 3.07-.25 2.69 2.69 0 0 1 1.37 1.46h.06v-4.68h2.95V26.5h-2.92V25h-.09c-.12.28-.3.55-.53.8-.23.25-.52.45-.86.6-.34.16-.75.23-1.22.23Zm1.03-2.3c.36 0 .66-.1.92-.3.25-.2.45-.5.58-.86.14-.37.2-.8.2-1.3 0-.51-.06-.95-.2-1.32a1.81 1.81 0 0 0-.58-.84c-.26-.2-.56-.3-.92-.3-.37 0-.68.1-.93.3-.25.2-.45.49-.58.86-.13.36-.2.8-.2 1.3s.07.93.2 1.3c.14.37.33.66.58.86.25.2.56.3.93.3Zm10.8 2.35c-.97 0-1.81-.2-2.52-.58a3.97 3.97 0 0 1-1.61-1.65 5.45 5.45 0 0 1-.56-2.55c0-.96.18-1.8.56-2.51a4.1 4.1 0 0 1 1.6-1.68c.69-.4 1.5-.6 2.44-.6.66 0 1.26.1 1.8.3a3.9 3.9 0 0 1 2.35 2.4c.23.6.34 1.27.34 2.03v.74h-8.06v-1.72h5.31c0-.31-.08-.6-.22-.84a1.52 1.52 0 0 0-.6-.57 1.73 1.73 0 0 0-.87-.21 1.76 1.76 0 0 0-1.5.82c-.16.25-.24.53-.25.85v1.75c0 .37.08.7.23 1 .15.27.36.5.63.65.28.16.6.23.98.23.26 0 .5-.03.71-.1.22-.08.4-.18.55-.32.16-.14.27-.32.35-.52l2.7.08c-.1.6-.35 1.13-.74 1.58-.38.45-.87.8-1.49 1.05-.61.24-1.33.37-2.13.37Zm5.9-.18v-9.27h2.87v1.69h.1c.17-.61.44-1.07.82-1.37.39-.3.83-.45 1.34-.45a3.08 3.08 0 0 1 .83.11v2.57a4.9 4.9 0 0 0-1.16-.16 2 2 0 0 0-.95.23 1.72 1.72 0 0 0-.89 1.62v5.03h-2.96Z"/></g><defs><filter id="b" width="39.2" height="39.2" x="2.4" y=".4" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2.8"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1_56"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1_56" result="shape"/></filter><filter id="c" width="190.43" height="32.43" x="36.29" y="3.79" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2.86"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1_56"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1_56" result="shape"/></filter><linearGradient id="a" x1="0" x2="229" y1="20" y2="20" gradientUnits="userSpaceOnUse"><stop stop-color="#62883F"/><stop offset=".34" stop-color="#CD3935"/><stop offset=".68" stop-color="#A85551"/><stop offset="1" stop-color="#BF873E"/></linearGradient></defs></svg>
-1
docs/public/badges/requires/compacter_vector.svg
··· 1 - <svg xmlns="http://www.w3.org/2000/svg" width="165" height="21" fill="none"><g clip-path="url(#a)"><rect width="165" height="21" fill="#000" rx="5"/><g fill="#F9F9F9" filter="url(#b)"><path d="M14.9 16.95a3.37 3.37 0 0 1-1.1-.63l-.7.22a1 1 0 0 1-1.2-.44l-.2-.35a.99.99 0 0 1-.13-.67.96.96 0 0 1 .34-.6l.55-.48a6.01 6.01 0 0 1 0-1.33l-.55-.5a.97.97 0 0 1-.2-1.25l.2-.36a1 1 0 0 1 .52-.43 1 1 0 0 1 .66 0l.7.21a4.72 4.72 0 0 1 1.12-.62l.17-.76a.98.98 0 0 1 .98-.8h.44a.97.97 0 0 1 .98.8l.17.76a3.37 3.37 0 0 1 1.11.62l.7-.22a1 1 0 0 1 1.2.45l.2.35c.12.2.16.43.13.67a.96.96 0 0 1-.34.6l-.55.48a6.01 6.01 0 0 1 0 1.33l.55.5a.97.97 0 0 1 .2 1.25l-.2.35a.96.96 0 0 1-.52.43 1 1 0 0 1-.66.01l-.7-.22a4.7 4.7 0 0 1-1.12.63l-.17.76a.98.98 0 0 1-.98.79h-.44a.97.97 0 0 1-.98-.8l-.17-.75Zm1.38-1.4c.62 0 1.14-.22 1.58-.65.43-.43.64-.95.64-1.57 0-.61-.21-1.13-.64-1.57a2.15 2.15 0 0 0-1.58-.64c-.62 0-1.14.21-1.57.64-.44.44-.65.96-.65 1.57 0 .62.21 1.14.65 1.57.43.43.95.65 1.57.65ZM7.2 13.99a4.78 4.78 0 0 1-.73-.4l-.49.15a.67.67 0 0 1-.76-.28l-.13-.24a.65.65 0 0 1-.08-.44.6.6 0 0 1 .22-.38l.36-.32a2.85 2.85 0 0 1 0-.87l-.36-.32a.58.58 0 0 1-.22-.37c-.02-.15 0-.3.08-.43l.15-.25a.68.68 0 0 1 .32-.27.6.6 0 0 1 .42-.01l.49.15a2.93 2.93 0 0 1 .74-.4l.1-.49a.64.64 0 0 1 .63-.52h.27c.16 0 .3.05.41.15.12.1.2.23.23.39l.1.47a4.76 4.76 0 0 1 .74.4l.49-.15a.67.67 0 0 1 .76.28l.13.23c.08.14.1.28.08.44a.59.59 0 0 1-.22.38l-.37.32a2.84 2.84 0 0 1 0 .87l.37.32c.13.1.2.22.22.37.02.15 0 .3-.08.43l-.15.25a.68.68 0 0 1-.32.27.6.6 0 0 1-.42.02l-.49-.16a2.93 2.93 0 0 1-.74.4l-.1.5a.64.64 0 0 1-.64.5h-.26a.61.61 0 0 1-.42-.14.68.68 0 0 1-.22-.39L7.2 14Zm.88-1c.37 0 .68-.14.95-.4a1.3 1.3 0 0 0 0-1.89 1.3 1.3 0 0 0-.95-.4c-.37 0-.69.14-.95.4a1.3 1.3 0 0 0 0 1.89c.26.26.58.4.95.4ZM12.02 2.6a.6.6 0 0 0-.35.24.66.66 0 0 0-.12.42l.03.47a4.65 4.65 0 0 0-.6.56l-.5-.02a.66.66 0 0 0-.64.46L9.77 5c-.04.15-.03.29.03.43s.16.25.3.31l.44.2a2.8 2.8 0 0 0 .22.83l-.27.4a.57.57 0 0 0-.11.4c.02.16.08.28.19.39l.2.2c.1.1.23.15.37.17a.6.6 0 0 0 .4-.09l.43-.26a2.88 2.88 0 0 0 .8.19l.22.43a.63.63 0 0 0 .73.33l.26-.07a.6.6 0 0 0 .35-.24.66.66 0 0 0 .12-.42l-.03-.47a4.64 4.64 0 0 0 .6-.57l.5.02c.15 0 .28-.03.4-.12.12-.1.2-.2.24-.34l.07-.25a.64.64 0 0 0-.03-.44.58.58 0 0 0-.3-.3l-.44-.21a2.8 2.8 0 0 0-.22-.83l.27-.39c.1-.13.13-.26.11-.41a.62.62 0 0 0-.19-.38l-.2-.2a.67.67 0 0 0-.37-.18.6.6 0 0 0-.4.1l-.43.26a2.89 2.89 0 0 0-.8-.2l-.22-.43a.63.63 0 0 0-.73-.33l-.26.07Zm.64 1.86c.35-.1.68-.05 1 .13.31.18.52.45.61.8.1.34.05.67-.13.99-.18.31-.45.52-.8.6-.35.1-.68.06-1-.12a1.26 1.26 0 0 1-.61-.8 1.29 1.29 0 0 1 .93-1.6Z"/></g><g filter="url(#c)"><path fill="#E8E8E8" d="M24.04 15.5V6.05h3.37c.73 0 1.34.12 1.82.37.49.26.85.6 1.1 1.05.23.45.35.96.35 1.54a3.1 3.1 0 0 1-.36 1.53c-.24.43-.6.77-1.1 1.02-.48.24-1.08.36-1.82.36h-2.55V10.7h2.42c.47 0 .84-.06 1.13-.2.3-.13.5-.32.64-.57.14-.25.2-.56.2-.91 0-.36-.06-.67-.2-.93a1.34 1.34 0 0 0-.64-.6 2.6 2.6 0 0 0-1.14-.21h-1.8v8.23h-1.42Zm4.67-4.27 2.33 4.27h-1.62l-2.3-4.27h1.59Zm6.56 4.41a3.04 3.04 0 0 1-2.97-1.72 4.3 4.3 0 0 1-.4-1.92c0-.73.13-1.37.4-1.92.28-.55.66-.99 1.15-1.3a3.47 3.47 0 0 1 2.89-.27 2.82 2.82 0 0 1 1.72 1.73c.17.45.26 1 .26 1.64v.49h-5.64v-1.03h4.28c0-.37-.07-.69-.22-.97a1.68 1.68 0 0 0-1.55-.91c-.4 0-.73.1-1.02.28a2.03 2.03 0 0 0-.89 1.73v.8c0 .48.08.88.24 1.21.17.33.4.59.71.76a2 2 0 0 0 1.06.26c.26 0 .5-.03.71-.1a1.48 1.48 0 0 0 .92-.9l1.3.23a2.54 2.54 0 0 1-1.57 1.68c-.4.15-.86.23-1.38.23Zm9.37 2.52V14.4h-.09c-.08.15-.2.32-.36.51a2 2 0 0 1-1.69.73 2.75 2.75 0 0 1-2.58-1.7 4.68 4.68 0 0 1-.38-1.97c0-.77.13-1.42.38-1.97a2.76 2.76 0 0 1 2.59-1.69 2.14 2.14 0 0 1 1.69.73c.15.2.27.37.35.52h.12V8.4h1.35v9.75h-1.38Zm-1.83-3.7c.4 0 .73-.1 1-.31.28-.21.49-.5.63-.88.15-.38.22-.82.22-1.32 0-.49-.07-.92-.21-1.29a1.9 1.9 0 0 0-.63-.86c-.27-.2-.61-.31-1.01-.31-.42 0-.76.1-1.04.32s-.49.51-.63.88c-.14.38-.2.8-.2 1.26 0 .48.06.9.2 1.28.15.38.36.68.63.9.28.22.63.33 1.04.33Zm9.55-1.9V8.4h1.39v7.09h-1.36v-1.23h-.07c-.17.38-.43.7-.79.95-.35.25-.8.37-1.33.37a2.1 2.1 0 0 1-2.03-1.2c-.2-.4-.3-.89-.3-1.47V8.4h1.38v4.34c0 .49.14.87.4 1.16.27.28.62.43 1.05.43a1.7 1.7 0 0 0 1.4-.78c.18-.27.27-.6.26-1Zm3.24 2.94V8.4h1.38v7.1H55.6Zm.7-8.19a.88.88 0 0 1-.62-.24.78.78 0 0 1-.25-.58c0-.22.08-.42.25-.58a.87.87 0 0 1 .62-.24c.24 0 .44.08.61.24.18.16.26.36.26.58 0 .23-.08.42-.26.58a.86.86 0 0 1-.61.24Zm2.54 8.19V8.4h1.33v1.14h.08c.13-.39.35-.69.68-.9.33-.23.7-.34 1.12-.34a5.9 5.9 0 0 1 .58.04v1.32a2.82 2.82 0 0 0-.72-.08c-.32 0-.61.06-.87.2a1.54 1.54 0 0 0-.82 1.39v4.33h-1.38Zm7.8.14c-.7 0-1.3-.15-1.81-.44-.5-.3-.9-.73-1.17-1.27a4.3 4.3 0 0 1-.4-1.93c0-.73.13-1.37.4-1.92.28-.55.66-.99 1.15-1.3a3.47 3.47 0 0 1 2.89-.27 2.82 2.82 0 0 1 1.72 1.73c.17.45.25 1 .25 1.64v.49h-5.63v-1.03h4.28c0-.37-.07-.69-.22-.97a1.68 1.68 0 0 0-1.56-.91c-.38 0-.72.1-1.01.28-.29.2-.51.44-.67.74-.15.3-.23.63-.23.99v.8c0 .48.09.88.25 1.21.17.33.4.59.7.76.31.18.66.26 1.07.26.26 0 .5-.03.71-.1a1.48 1.48 0 0 0 .92-.9l1.3.23a2.62 2.62 0 0 1-1.56 1.68c-.41.15-.87.23-1.39.23Zm9.88-5.5-1.25.22a1.48 1.48 0 0 0-.25-.46 1.23 1.23 0 0 0-.45-.35c-.2-.1-.43-.14-.72-.14-.39 0-.72.09-.98.26-.26.18-.4.4-.4.67 0 .24.1.43.27.58.18.14.46.26.85.35l1.13.26c.65.15 1.14.38 1.46.7.32.31.48.72.48 1.22 0 .43-.13.8-.37 1.14-.25.33-.59.58-1.02.77-.44.19-.94.28-1.51.28a3.4 3.4 0 0 1-1.95-.5 2.2 2.2 0 0 1-.92-1.46l1.34-.2c.08.35.25.6.5.79.27.17.6.26 1.02.26.45 0 .81-.1 1.08-.28.27-.2.41-.42.41-.7 0-.22-.08-.4-.25-.56a1.63 1.63 0 0 0-.75-.34l-1.2-.26a2.88 2.88 0 0 1-1.47-.72 1.74 1.74 0 0 1-.47-1.25c0-.42.12-.79.35-1.1.23-.32.56-.56.97-.74.41-.17.88-.26 1.42-.26.76 0 1.37.16 1.8.5.45.32.74.77.88 1.32Z"/><path fill="#fff" d="M89.3 9.47h-2.31a2 2 0 0 0-.19-.64 1.56 1.56 0 0 0-.94-.8 2.14 2.14 0 0 0-.7-.11c-.46 0-.86.11-1.19.34-.33.22-.58.55-.76.97-.18.43-.26.94-.26 1.54 0 .63.08 1.16.26 1.58.18.42.44.74.76.96.33.21.72.32 1.17.32.26 0 .48-.04.7-.1a1.56 1.56 0 0 0 .94-.74c.1-.18.17-.39.2-.61l2.32.01a3.95 3.95 0 0 1-2.47 3.14c-.5.2-1.08.3-1.73.3-.86 0-1.63-.19-2.3-.56a4.05 4.05 0 0 1-1.6-1.66c-.4-.72-.59-1.6-.59-2.64s.2-1.92.6-2.64a4 4 0 0 1 1.61-1.65 5.05 5.05 0 0 1 3.86-.32 3.54 3.54 0 0 1 2.18 1.8c.24.44.38.94.44 1.51Zm4.62 6.16a3.9 3.9 0 0 1-1.93-.45 3.18 3.18 0 0 1-1.23-1.29 4.1 4.1 0 0 1-.43-1.91c0-.73.15-1.37.43-1.92.3-.55.7-.98 1.23-1.28a3.8 3.8 0 0 1 1.93-.46c.74 0 1.38.15 1.91.46.54.3.95.73 1.23 1.28.3.55.44 1.19.44 1.92 0 .72-.15 1.36-.44 1.91-.28.55-.7.98-1.23 1.29a3.8 3.8 0 0 1-1.91.45Zm0-1.7a1 1 0 0 0 .7-.25c.18-.17.33-.4.43-.7.1-.29.15-.63.15-1.02s-.05-.73-.15-1.03c-.1-.3-.25-.53-.43-.7a1 1 0 0 0-.7-.24c-.27 0-.5.08-.7.25-.2.16-.34.4-.44.69-.1.3-.15.64-.15 1.03 0 .4.05.73.15 1.03.1.3.25.52.44.7.2.16.43.24.7.24Zm4.73 1.57V8.4h2.15v1.31h.08a2.02 2.02 0 0 1 2-1.4c.5 0 .92.13 1.27.39.35.25.57.59.67 1.01h.07c.13-.42.4-.76.78-1.01a2.4 2.4 0 0 1 1.36-.38c.67 0 1.21.21 1.63.64.42.43.63 1.02.63 1.77v4.77h-2.26v-4.26c0-.35-.09-.62-.27-.8a.94.94 0 0 0-.7-.29c-.32 0-.56.1-.74.3-.17.2-.26.48-.26.82v4.23h-2.17v-4.28c0-.33-.1-.59-.27-.78a.92.92 0 0 0-.7-.29.95.95 0 0 0-.88.54 1.4 1.4 0 0 0-.13.61v4.2h-2.26Zm12.04 0V8.4h2.15v1.31h.07a2.02 2.02 0 0 1 2-1.4c.5 0 .92.13 1.27.39.35.25.58.59.67 1.01h.08c.13-.42.39-.76.77-1.01a2.4 2.4 0 0 1 1.36-.38c.67 0 1.22.21 1.64.64.42.43.63 1.02.63 1.77v4.77h-2.26v-4.26c0-.35-.1-.62-.27-.8a.94.94 0 0 0-.71-.29c-.31 0-.56.1-.73.3-.18.2-.27.48-.27.82v4.23h-2.17v-4.28c0-.33-.09-.59-.27-.78a.92.92 0 0 0-.7-.29.95.95 0 0 0-.88.54 1.4 1.4 0 0 0-.12.61v4.2h-2.26Zm14.03.12c-.45 0-.85-.08-1.2-.23a1.88 1.88 0 0 1-.83-.69c-.2-.3-.3-.7-.3-1.17 0-.4.07-.72.21-1 .14-.26.33-.48.57-.65.25-.17.52-.3.84-.39.32-.09.66-.15 1.02-.18.4-.03.73-.07.97-.11.25-.05.43-.1.54-.18a.4.4 0 0 0 .17-.34v-.02a.66.66 0 0 0-.25-.55c-.16-.13-.38-.2-.66-.2-.3 0-.53.07-.72.2a.8.8 0 0 0-.34.53l-2.08-.07a2.46 2.46 0 0 1 1.53-1.96 4.6 4.6 0 0 1 2.88-.13c.39.1.72.26 1 .46a2.1 2.1 0 0 1 .9 1.74v4.82h-2.13v-.99h-.06a2 2 0 0 1-1.17.99c-.26.08-.56.12-.89.12Zm.7-1.48c.24 0 .46-.05.66-.15a1.07 1.07 0 0 0 .65-1.02v-.66l-.24.09a4.1 4.1 0 0 1-.63.14l-.33.05c-.21.04-.4.09-.54.16a.86.86 0 0 0-.34.28.68.68 0 0 0-.12.4c0 .23.08.4.25.53.17.12.38.18.64.18Zm7.18-2.68v4.04h-2.26V8.4h2.15v1.31h.08c.15-.43.42-.77.8-1.02a2.4 2.4 0 0 1 1.35-.37c.5 0 .93.1 1.3.33s.66.54.86.94c.2.4.3.86.3 1.39v4.52h-2.25v-4.08c0-.39-.1-.7-.3-.92-.2-.22-.48-.33-.84-.33-.24 0-.45.05-.63.15-.18.1-.31.25-.41.44-.1.2-.15.43-.15.7Zm8.57 4.14a2.61 2.61 0 0 1-2.45-1.62 4.73 4.73 0 0 1-.38-2.02c0-.83.13-1.52.4-2.06.26-.54.6-.94 1.03-1.2a2.65 2.65 0 0 1 2.35-.19c.26.13.48.29.65.49.17.2.3.4.4.62h.04V6.05h2.26v9.45h-2.24v-1.15h-.06c-.1.22-.24.42-.41.61a1.98 1.98 0 0 1-1.59.64Zm.79-1.76c.27 0 .5-.07.7-.23.2-.16.34-.38.44-.66.11-.28.16-.61.16-1 0-.38-.05-.72-.16-1-.1-.28-.25-.5-.44-.64a1.1 1.1 0 0 0-.7-.23c-.28 0-.52.08-.72.23-.19.15-.34.37-.44.65-.1.28-.15.61-.15 1 0 .38.05.7.16 1 .1.27.24.5.43.65.2.16.44.23.72.23Zm8.25 1.8a4 4 0 0 1-1.92-.44c-.53-.3-.95-.72-1.24-1.26a4.17 4.17 0 0 1-.43-1.96c0-.73.15-1.37.44-1.92.29-.55.7-.98 1.22-1.28a4.14 4.14 0 0 1 3.25-.23 2.97 2.97 0 0 1 1.8 1.84c.16.45.25.96.25 1.55v.56h-6.17v-1.31h4.07c0-.24-.06-.46-.17-.64a1.16 1.16 0 0 0-.46-.44c-.2-.11-.41-.16-.66-.16a1.35 1.35 0 0 0-1.16.62c-.11.2-.18.41-.18.66v1.33c0 .29.06.54.17.76.11.22.27.38.48.5.21.12.46.18.75.18.2 0 .38-.02.55-.08.16-.05.3-.14.42-.24a1 1 0 0 0 .26-.4l2.07.06a2.5 2.5 0 0 1-.56 1.21c-.3.35-.67.61-1.15.8a4.4 4.4 0 0 1-1.63.28Zm4.52-.14V8.4h2.2v1.3h.07c.13-.47.34-.81.63-1.04.3-.23.63-.35 1.02-.35a2.36 2.36 0 0 1 .63.09v1.96a3.76 3.76 0 0 0-.89-.12c-.26 0-.5.06-.72.18a1.3 1.3 0 0 0-.5.5c-.12.2-.18.45-.18.73v3.85h-2.26Z"/></g></g><rect width="163.8" height="19.8" x=".6" y=".6" stroke="url(#d)" stroke-opacity=".88" stroke-width="1.2" rx="4.4"/><defs><filter id="b" width="27.2" height="27.2" x="-.6" y="-3.1" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2.8"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_5_17"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_5_17" result="shape"/></filter><filter id="c" width="148.43" height="27.43" x="17.29" y="-3.21" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2.86"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_5_17"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_5_17" result="shape"/></filter><linearGradient id="d" x1="0" x2="165" y1="10.5" y2="10.5" gradientUnits="userSpaceOnUse"><stop stop-color="#62883F"/><stop offset=".34" stop-color="#CD3935"/><stop offset=".68" stop-color="#A85551"/><stop offset="1" stop-color="#BF873E"/></linearGradient><clipPath id="a"><rect width="165" height="21" fill="#fff" rx="5"/></clipPath></defs></svg>
-1
docs/public/badges/requires/cozy_vector.svg
··· 1 - <svg xmlns="http://www.w3.org/2000/svg" width="176" height="56" fill="none"><rect width="176" height="56" fill="#000" rx="8"/><rect width="173.9" height="53.9" x="1.05" y="1.05" stroke="url(#a)" stroke-opacity=".88" stroke-width="2.1" rx="6.95"/><g fill="#F9F9F9" filter="url(#b)"><path d="M36.77 44.13a8.44 8.44 0 0 1-1.46-.67c-.44-.27-.88-.57-1.32-.9l-1.76.55a2.48 2.48 0 0 1-2.96-1.1l-.51-.88a2.48 2.48 0 0 1-.33-1.69c.1-.6.37-1.1.84-1.5l1.39-1.2a14.98 14.98 0 0 1 0-3.32l-1.4-1.24a2.43 2.43 0 0 1-.5-3.14l.5-.88a2.5 2.5 0 0 1 2.97-1.1l1.76.55a9.28 9.28 0 0 1 2.78-1.57l.42-1.89a2.46 2.46 0 0 1 2.46-1.98h1.1a2.43 2.43 0 0 1 2.46 1.98l.42 1.9c.52.18 1.01.4 1.46.66.45.26.89.56 1.32.9l1.76-.55a2.48 2.48 0 0 1 2.96 1.1l.51.88a2.47 2.47 0 0 1-.51 3.18l-1.39 1.2a15 15 0 0 1 0 3.32l1.4 1.25a2.45 2.45 0 0 1 .5 3.14l-.5.87a2.48 2.48 0 0 1-2.96 1.1l-1.77-.54a14 14 0 0 1-1.32.9c-.45.26-.94.48-1.46.66l-.42 1.9A2.46 2.46 0 0 1 40.76 48h-1.11a2.43 2.43 0 0 1-2.46-1.98l-.42-1.9Zm3.43-3.51c1.55 0 2.86-.54 3.94-1.62a5.33 5.33 0 0 0 1.62-3.92c0-1.53-.54-2.84-1.62-3.92a5.37 5.37 0 0 0-3.94-1.61 5.4 5.4 0 0 0-3.94 1.61 5.33 5.33 0 0 0-1.62 3.92c0 1.54.54 2.85 1.62 3.92a5.37 5.37 0 0 0 3.94 1.62Zm-22.68-3.9a7.29 7.29 0 0 1-1.85-1l-1.22.37a1.68 1.68 0 0 1-1.89-.71l-.33-.59c-.2-.33-.27-.7-.21-1.08.05-.4.24-.71.54-.96l.93-.8a7.13 7.13 0 0 1 0-2.17l-.93-.8c-.3-.25-.49-.56-.54-.94-.06-.37.01-.73.2-1.06l.39-.63c.2-.3.46-.53.8-.67a1.5 1.5 0 0 1 1.04-.04l1.22.38a7.33 7.33 0 0 1 1.85-1l.25-1.22a1.6 1.6 0 0 1 1.6-1.3h.66c.4 0 .74.13 1.03.38.3.25.48.57.57.96l.25 1.17a7.33 7.33 0 0 1 1.85 1l1.21-.37a1.68 1.68 0 0 1 1.89.71l.34.59c.2.33.26.7.2 1.08-.05.4-.23.71-.54.96l-.92.8a7.11 7.11 0 0 1 0 2.17l.92.8c.31.25.5.56.55.94.05.37-.02.73-.21 1.06l-.38.63c-.2.3-.46.53-.8.67a1.5 1.5 0 0 1-1.05.04l-1.21-.38a7.35 7.35 0 0 1-1.85 1l-.25 1.22a1.6 1.6 0 0 1-1.6 1.3h-.67c-.39 0-.73-.13-1.03-.38a1.7 1.7 0 0 1-.56-.96l-.25-1.17Zm2.18-2.51c.92 0 1.71-.33 2.37-.98.66-.66.99-1.45.99-2.36a3.2 3.2 0 0 0-1-2.37 3.24 3.24 0 0 0-2.36-.98c-.92 0-1.72.33-2.37.98a3.21 3.21 0 0 0-.99 2.37c0 .91.33 1.7.99 2.36.65.65 1.45.98 2.37.98Zm9.85-25.98c-.37.1-.66.3-.88.61-.2.32-.3.67-.29 1.06l.06 1.17a7.17 7.17 0 0 0-1.5 1.42l-1.24-.05a1.6 1.6 0 0 0-1.6 1.15l-.17.64c-.1.36-.08.72.07 1.08.16.35.41.61.77.77l1.07.52a6.98 6.98 0 0 0 .56 2.06l-.68.98c-.22.32-.32.66-.27 1.03.04.37.2.69.47.96l.51.5c.27.23.58.38.93.43.35.04.69-.03 1-.23l1.06-.66a7.23 7.23 0 0 0 2 .48l.55 1.08a1.57 1.57 0 0 0 1.84.83l.64-.17c.37-.1.66-.3.88-.62.21-.31.3-.66.29-1.05l-.06-1.17c.28-.22.54-.44.78-.66.24-.22.48-.47.71-.76l1.25.05a1.64 1.64 0 0 0 1.6-1.15l.17-.64c.1-.37.08-.73-.07-1.08a1.45 1.45 0 0 0-.77-.77l-1.07-.52a6.98 6.98 0 0 0-.56-2.06l.68-.99c.22-.31.32-.65.27-1.02-.04-.37-.2-.7-.47-.96l-.51-.5a1.67 1.67 0 0 0-.93-.43c-.35-.05-.69.03-1 .23l-1.06.66a7.21 7.21 0 0 0-2-.48l-.55-1.09a1.57 1.57 0 0 0-1.84-.82l-.64.17Zm1.6 4.66c.87-.23 1.7-.12 2.5.33a3.2 3.2 0 0 1 1.53 1.99c.23.87.12 1.7-.33 2.48a3.17 3.17 0 0 1-2 1.53c-.87.24-1.7.13-2.5-.33a3.16 3.16 0 0 1-1.53-1.98 3.2 3.2 0 0 1 .33-2.49 3.17 3.17 0 0 1 2-1.53Z"/></g><g filter="url(#c)"><path fill="#E8E8E8" d="M61.28 24.5V12.86h4.15c.9 0 1.65.16 2.24.47.6.31 1.05.74 1.34 1.29.3.54.44 1.18.44 1.9 0 .7-.15 1.33-.44 1.87-.3.54-.75.96-1.35 1.26-.59.3-1.34.45-2.24.45h-3.14v-1.51h2.98c.57 0 1.03-.09 1.39-.25.36-.16.62-.4.79-.7.16-.32.25-.7.25-1.13 0-.44-.09-.82-.26-1.14a1.65 1.65 0 0 0-.79-.74 3.2 3.2 0 0 0-1.4-.26h-2.2V24.5h-1.76Zm5.74-5.25 2.88 5.25h-2l-2.82-5.25h1.94Zm8.09 5.43c-.86 0-1.6-.19-2.23-.55a3.88 3.88 0 0 1-1.43-1.57 5.29 5.29 0 0 1-.5-2.37c0-.9.17-1.68.5-2.36a4 4 0 0 1 1.41-1.6 4.28 4.28 0 0 1 3.56-.33 3.47 3.47 0 0 1 2.11 2.13c.21.55.32 1.22.32 2.02v.6h-6.94v-1.27h5.28c0-.45-.1-.85-.28-1.2A2.06 2.06 0 0 0 75 17.07a2.23 2.23 0 0 0-2.07 1.26c-.19.38-.28.78-.28 1.21v1c0 .58.1 1.08.3 1.49.21.4.5.72.87.93.37.22.8.32 1.3.32a2.14 2.14 0 0 0 1.58-.55c.18-.19.33-.42.43-.7l1.61.3c-.13.47-.36.89-.69 1.24-.33.35-.74.63-1.24.83-.5.19-1.07.29-1.7.29Zm11.52 3.1v-4.64h-.1c-.1.19-.25.4-.45.64a2.46 2.46 0 0 1-2.08.89 3.38 3.38 0 0 1-3.18-2.1 5.76 5.76 0 0 1-.46-2.42c0-.94.15-1.75.47-2.42a3.4 3.4 0 0 1 3.19-2.07c.54 0 .97.09 1.3.27.33.18.6.39.77.63.19.24.33.45.44.63h.14v-1.42h1.66v12h-1.7Zm-2.25-4.56c.49 0 .9-.13 1.24-.38.34-.27.6-.63.77-1.09.18-.46.27-1 .27-1.61 0-.61-.1-1.14-.27-1.6a2.4 2.4 0 0 0-.76-1.06 2.02 2.02 0 0 0-1.25-.38 2 2 0 0 0-1.28.4c-.34.27-.6.63-.77 1.09-.17.45-.26.97-.26 1.55 0 .58.09 1.1.26 1.57.18.47.43.84.78 1.1a2 2 0 0 0 1.27.41Zm11.76-2.34v-5.1h1.7v8.72h-1.67v-1.51h-.09c-.2.46-.52.85-.96 1.16a2.8 2.8 0 0 1-1.65.46 2.6 2.6 0 0 1-2.5-1.48 4.06 4.06 0 0 1-.36-1.8v-5.56h1.7v5.35a2 2 0 0 0 .5 1.42c.32.35.75.53 1.28.53a2.08 2.08 0 0 0 1.74-.96c.2-.32.31-.73.3-1.23Zm3.99 3.62v-8.73h1.7v8.73h-1.7Zm.85-10.07c-.3 0-.55-.1-.76-.3a.96.96 0 0 1-.3-.72.92.92 0 0 1 .3-.71c.22-.2.47-.3.76-.3.3 0 .55.1.76.3a.95.95 0 0 1 0 1.43c-.2.2-.46.3-.76.3Zm3.13 10.07v-8.73h1.64v1.39h.1c.15-.47.43-.84.84-1.1.4-.28.86-.42 1.37-.42a7.24 7.24 0 0 1 .72.04v1.63a3.49 3.49 0 0 0-.89-.1c-.4 0-.75.08-1.07.25a1.9 1.9 0 0 0-1.01 1.71v5.33h-1.7Zm9.59.18a3.75 3.75 0 0 1-3.65-2.12 5.29 5.29 0 0 1-.5-2.37c0-.9.16-1.68.5-2.36a3.9 3.9 0 0 1 1.4-1.6 4.28 4.28 0 0 1 3.56-.33 3.47 3.47 0 0 1 2.12 2.13c.2.55.31 1.22.31 2.02v.6h-6.93v-1.27h5.27c0-.45-.1-.85-.27-1.2a2.06 2.06 0 0 0-1.92-1.12 2.23 2.23 0 0 0-2.07 1.26c-.19.38-.28.78-.28 1.21v1c0 .58.1 1.08.3 1.49.21.4.5.72.88.93.37.22.8.32 1.3.32.32 0 .61-.04.88-.13a1.83 1.83 0 0 0 1.13-1.1l1.6.28a2.9 2.9 0 0 1-.69 1.24c-.33.35-.74.63-1.24.83-.5.19-1.06.29-1.7.29Zm12.17-6.78-1.54.28c-.06-.2-.17-.39-.3-.57a1.52 1.52 0 0 0-.56-.43 2.02 2.02 0 0 0-.89-.17c-.48 0-.88.1-1.2.32-.32.21-.48.49-.48.82 0 .3.1.53.32.7.22.19.57.33 1.05.45l1.38.31c.8.19 1.4.48 1.8.86a2 2 0 0 1 .59 1.5c0 .53-.15 1-.46 1.4-.3.41-.71.73-1.25.96-.53.23-1.15.35-1.86.35a4.2 4.2 0 0 1-2.39-.63c-.62-.42-1-1.02-1.14-1.79l1.64-.25c.1.43.32.75.64.97.31.22.73.33 1.24.33.56 0 1-.12 1.34-.35.33-.23.5-.52.5-.86a.9.9 0 0 0-.31-.69c-.2-.18-.51-.32-.93-.42l-1.48-.32a3.55 3.55 0 0 1-1.8-.89 2.1 2.1 0 0 1-.58-1.54c0-.51.14-.96.43-1.35.29-.38.69-.69 1.2-.9.5-.22 1.08-.33 1.74-.33.94 0 1.68.2 2.23.61.54.4.9.95 1.07 1.63Z"/><path fill="#fff" d="M72.16 35.62h-3.02a2.62 2.62 0 0 0-.25-.84 2.04 2.04 0 0 0-1.22-1.05 2.8 2.8 0 0 0-.93-.14c-.6 0-1.11.14-1.55.44-.43.3-.76.72-1 1.27a5.28 5.28 0 0 0-.34 2.02c0 .82.12 1.5.35 2.06.24.55.57.97 1 1.25.43.28.94.42 1.53.42a3 3 0 0 0 .9-.13 2.04 2.04 0 0 0 1.23-.97c.13-.23.23-.5.28-.8l3.02.02a5.17 5.17 0 0 1-3.24 4.11c-.65.26-1.4.39-2.26.39a6.1 6.1 0 0 1-3-.74 5.3 5.3 0 0 1-2.09-2.16 7.18 7.18 0 0 1-.77-3.45c0-1.36.26-2.52.78-3.46a5.33 5.33 0 0 1 2.1-2.16 6.61 6.61 0 0 1 5.05-.42 4.63 4.63 0 0 1 2.86 2.36c.3.58.5 1.24.57 1.98Zm6.04 8.06a5 5 0 0 1-2.51-.6 4.1 4.1 0 0 1-1.62-1.68 5.36 5.36 0 0 1-.56-2.5c0-.96.19-1.8.56-2.51a4.1 4.1 0 0 1 1.62-1.68c.7-.4 1.53-.6 2.5-.6.98 0 1.82.2 2.51.6.7.4 1.24.96 1.61 1.68.38.71.57 1.55.57 2.5s-.19 1.79-.57 2.5a4.07 4.07 0 0 1-1.6 1.69c-.7.4-1.54.6-2.51.6Zm.01-2.23c.36 0 .66-.11.9-.33.25-.22.44-.52.57-.9.13-.39.2-.84.2-1.34 0-.52-.07-.97-.2-1.36a2.04 2.04 0 0 0-.57-.9 1.3 1.3 0 0 0-.9-.33c-.36 0-.67.11-.92.33-.25.22-.45.52-.58.9-.13.39-.2.84-.2 1.36 0 .5.07.95.2 1.34.13.38.33.68.58.9.25.22.56.33.92.33Zm6.18 2.05v-9.27h2.81v1.7h.1a2.64 2.64 0 0 1 2.62-1.82c.65 0 1.2.16 1.66.5.46.32.75.76.87 1.32h.1c.17-.55.51-1 1.01-1.32.5-.34 1.1-.5 1.78-.5.88 0 1.6.28 2.14.84.55.56.82 1.33.82 2.3v6.25h-2.95v-5.57c0-.46-.12-.81-.36-1.05a1.23 1.23 0 0 0-.92-.37c-.4 0-.73.13-.96.4-.23.26-.34.61-.34 1.05v5.54h-2.84v-5.6c0-.43-.12-.77-.35-1.02a1.2 1.2 0 0 0-.92-.37 1.25 1.25 0 0 0-1.15.7c-.11.23-.16.5-.16.8v5.49h-2.96Zm15.74 0v-9.27h2.8v1.7h.11a2.64 2.64 0 0 1 2.62-1.82c.65 0 1.2.16 1.66.5.46.32.75.76.87 1.32h.1c.17-.55.5-1 1.01-1.32.5-.34 1.1-.5 1.78-.5.88 0 1.6.28 2.14.84.55.56.82 1.33.82 2.3v6.25h-2.95v-5.57c0-.46-.12-.81-.36-1.05a1.23 1.23 0 0 0-.92-.37c-.4 0-.73.13-.96.4-.23.26-.34.61-.34 1.05v5.54h-2.84v-5.6c0-.43-.12-.77-.35-1.02a1.2 1.2 0 0 0-.93-.37 1.25 1.25 0 0 0-1.14.7c-.11.23-.17.5-.17.8v5.49h-2.95Zm18.35.16a3.9 3.9 0 0 1-1.57-.3 2.4 2.4 0 0 1-1.08-.9 2.8 2.8 0 0 1-.4-1.53c0-.52.1-.95.28-1.3.18-.36.43-.65.75-.87.31-.22.68-.39 1.1-.5.4-.12.85-.2 1.32-.24.53-.04.95-.1 1.28-.15.32-.05.55-.13.7-.23a.5.5 0 0 0 .22-.44v-.03c0-.31-.1-.55-.32-.72-.21-.17-.5-.25-.86-.25-.4 0-.7.08-.95.25-.23.17-.38.4-.45.7l-2.72-.1a3.2 3.2 0 0 1 2.01-2.56c.6-.25 1.3-.38 2.13-.38a6 6 0 0 1 1.64.2c.5.14.93.35 1.3.61a2.73 2.73 0 0 1 1.17 2.28v6.3h-2.78v-1.3h-.07a2.64 2.64 0 0 1-1.54 1.3c-.34.1-.72.16-1.16.16Zm.92-1.93c.31 0 .6-.07.85-.2a1.44 1.44 0 0 0 .85-1.33v-.88a5.16 5.16 0 0 1-1.13.3l-.43.08c-.28.04-.51.1-.71.2-.2.1-.34.22-.45.36a.88.88 0 0 0-.15.52c0 .3.1.54.33.7.22.16.5.25.84.25Zm9.38-3.52v5.29h-2.95v-9.27h2.8v1.7h.11a2.6 2.6 0 0 1 1.05-1.33c.5-.33 1.09-.5 1.77-.5a3 3 0 0 1 2.82 1.67c.27.52.4 1.12.4 1.82v5.91h-2.95v-5.33c0-.52-.13-.92-.4-1.2a1.4 1.4 0 0 0-1.09-.44c-.3 0-.58.07-.82.2-.23.14-.42.33-.54.58-.13.25-.2.55-.2.9ZM140 43.63a3.41 3.41 0 0 1-3.2-2.12 6.1 6.1 0 0 1-.5-2.64c0-1.1.17-1.99.52-2.7.34-.7.8-1.22 1.35-1.56a3.47 3.47 0 0 1 3.07-.25c.35.17.63.38.86.64a3 3 0 0 1 .51.81h.06v-4.67h2.95V43.5h-2.92V42h-.09c-.12.28-.3.55-.53.8-.23.25-.52.45-.86.6-.35.16-.75.23-1.22.23Zm1.03-2.3c.36 0 .66-.1.92-.3.25-.2.44-.5.58-.86.14-.37.2-.8.2-1.3 0-.51-.06-.95-.2-1.32a1.8 1.8 0 0 0-.58-.84c-.26-.2-.56-.3-.92-.3-.37 0-.68.1-.93.3-.25.2-.45.49-.58.86-.13.36-.2.8-.2 1.3s.07.93.2 1.3c.14.37.33.66.58.86.25.2.56.3.93.3Zm10.8 2.35c-.98 0-1.81-.2-2.52-.58a3.96 3.96 0 0 1-1.62-1.65 5.45 5.45 0 0 1-.56-2.55c0-.96.2-1.8.57-2.51a4.1 4.1 0 0 1 1.6-1.68c.69-.4 1.5-.6 2.43-.6.66 0 1.27.1 1.81.3a3.89 3.89 0 0 1 2.35 2.4c.22.6.34 1.27.34 2.03v.74h-8.06v-1.72h5.3c0-.31-.07-.6-.22-.84a1.52 1.52 0 0 0-.6-.57 1.72 1.72 0 0 0-.86-.21 1.76 1.76 0 0 0-1.5.82c-.16.25-.24.53-.25.85v1.74c0 .38.08.71.23 1a1.67 1.67 0 0 0 1.61.89c.26 0 .5-.03.71-.1.21-.08.4-.18.55-.32.15-.15.27-.32.34-.52l2.72.07a3.54 3.54 0 0 1-.75 1.6 3.8 3.8 0 0 1-1.49 1.04c-.61.24-1.33.37-2.14.37Zm5.9-.18v-9.27h2.87v1.69h.1c.17-.61.44-1.07.82-1.37.39-.3.83-.45 1.34-.45a3.06 3.06 0 0 1 .83.1v2.57a4.95 4.95 0 0 0-1.16-.16 2 2 0 0 0-.95.24 1.72 1.72 0 0 0-.89 1.62v5.03h-2.96Z"/></g><defs><filter id="b" width="51.43" height="51.43" x="6.29" y="2.29" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2.86"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1_50"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1_50" result="shape"/></filter><filter id="c" width="115.2" height="48.2" x="54.4" y="3.9" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2.8"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1_50"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1_50" result="shape"/></filter><linearGradient id="a" x1="0" x2="176" y1="28" y2="28" gradientUnits="userSpaceOnUse"><stop stop-color="#62883F"/><stop offset=".34" stop-color="#CD3935"/><stop offset=".68" stop-color="#A85551"/><stop offset="1" stop-color="#BF873E"/></linearGradient></defs></svg>
-1
docs/public/badges/supports/compact_vector.svg
··· 1 - <svg xmlns="http://www.w3.org/2000/svg" width="233" height="40" fill="none"><rect width="233" height="40" fill="#000" rx="8"/><rect width="230.86" height="37.86" x="1.07" y="1.07" stroke="url(#a)" stroke-opacity=".88" stroke-width="2.14" rx="6.93"/><g fill="#F9F9F9" filter="url(#b)"><path d="M25.34 31.29a5.9 5.9 0 0 1-1.94-1.1l-1.24.39a1.74 1.74 0 0 1-2.07-.78l-.36-.61a1.7 1.7 0 0 1-.23-1.18c.07-.42.26-.77.59-1.05l.97-.84a10.48 10.48 0 0 1 0-2.32l-.97-.88a1.7 1.7 0 0 1-.36-2.2l.36-.6a1.74 1.74 0 0 1 2.08-.78l1.23.39a5.9 5.9 0 0 1 1.95-1.1l.28-1.32a1.72 1.72 0 0 1 1.72-1.4h.78a1.7 1.7 0 0 1 1.72 1.4l.29 1.32a5.9 5.9 0 0 1 1.95 1.1l1.23-.4a1.74 1.74 0 0 1 2.07.78l.36.62c.22.36.3.76.23 1.18a1.7 1.7 0 0 1-.59 1.05l-.97.84a10.5 10.5 0 0 1 0 2.32l.97.87a1.7 1.7 0 0 1 .36 2.2l-.36.61a1.74 1.74 0 0 1-2.08.78l-1.22-.39c-.3.24-.61.45-.93.63a5.9 5.9 0 0 1-1.02.47l-.3 1.32a1.72 1.72 0 0 1-1.7 1.39h-.78a1.7 1.7 0 0 1-1.72-1.39l-.29-1.32Zm2.4-2.46c1.08 0 2-.37 2.76-1.13a3.73 3.73 0 0 0 1.13-2.74c0-1.08-.38-2-1.13-2.75a3.76 3.76 0 0 0-2.76-1.13c-1.08 0-2 .38-2.76 1.13a3.73 3.73 0 0 0-1.13 2.75c0 1.07.38 1.99 1.13 2.74a3.76 3.76 0 0 0 2.76 1.13ZM11.86 26.1a5.1 5.1 0 0 1-1.3-.7l-.84.26a1.18 1.18 0 0 1-1.32-.5l-.24-.4a1.14 1.14 0 0 1-.15-.77c.04-.27.17-.5.38-.67l.65-.55a4.98 4.98 0 0 1 0-1.52l-.65-.56a1.07 1.07 0 0 1-.23-1.4l.26-.44c.14-.22.33-.37.56-.47.24-.1.48-.1.74-.03l.85.26a5.13 5.13 0 0 1 1.3-.7l.17-.85a1.12 1.12 0 0 1 1.11-.9h.47c.28 0 .52.08.72.26.2.17.34.4.4.67l.18.82.66.3c.2.11.41.25.63.4l.85-.26a1.18 1.18 0 0 1 1.32.5l.24.4c.13.24.18.5.14.77a.96.96 0 0 1-.38.67l-.64.56a4.98 4.98 0 0 1 0 1.52l.64.55c.22.18.35.4.38.66.04.26 0 .51-.14.75l-.27.43a1.1 1.1 0 0 1-.55.47c-.24.1-.48.11-.74.03l-.85-.26a5.14 5.14 0 0 1-1.3.7l-.17.85a1.12 1.12 0 0 1-1.12.9h-.47c-.27 0-.51-.08-.72-.26-.2-.17-.33-.4-.4-.67l-.17-.82Zm1.53-1.75c.65 0 1.2-.23 1.66-.7.46-.45.69-1 .69-1.64 0-.65-.23-1.2-.7-1.66a2.27 2.27 0 0 0-1.65-.68c-.65 0-1.2.22-1.66.68-.46.46-.7 1.01-.7 1.66 0 .64.24 1.19.7 1.65.46.46 1.01.69 1.66.69Zm6.9-18.19c-.26.07-.47.21-.62.43-.15.22-.22.47-.2.74l.04.82a5.03 5.03 0 0 0-1.05 1l-.87-.04c-.26-.01-.5.06-.7.21-.2.16-.35.35-.42.6l-.12.44c-.07.25-.05.5.05.76.11.24.29.42.54.54l.75.36a4.9 4.9 0 0 0 .39 1.44l-.47.69a1 1 0 0 0-.2.72c.03.26.14.48.33.67l.36.35c.19.16.4.27.65.3.25.03.48-.02.7-.16l.74-.46a5.05 5.05 0 0 0 1.4.33l.39.76a1.1 1.1 0 0 0 1.29.58l.44-.12c.26-.07.47-.21.62-.43.15-.22.22-.46.2-.74l-.04-.82a4.97 4.97 0 0 0 1.05-1l.87.05a1.15 1.15 0 0 0 1.12-.8l.12-.46c.07-.25.05-.5-.05-.75s-.29-.43-.54-.54l-.75-.37a4.9 4.9 0 0 0-.39-1.44l.47-.69a1 1 0 0 0 .2-.72 1.1 1.1 0 0 0-.33-.67l-.36-.35a1.3 1.3 0 0 0-.65-.3c-.25-.03-.48.02-.7.16l-.74.46a5.04 5.04 0 0 0-1.4-.33l-.39-.76a1.1 1.1 0 0 0-1.29-.58l-.44.12Zm1.11 3.26c.62-.16 1.2-.08 1.75.24.55.31.91.78 1.08 1.39.16.6.08 1.19-.24 1.74s-.78.9-1.4 1.07c-.6.16-1.19.08-1.74-.24a2.21 2.21 0 0 1-1.08-1.39 2.2 2.2 0 0 1 .24-1.74c.32-.55.78-.9 1.4-1.07Z"/></g><g filter="url(#c)"><path fill="#E8E8E8" d="M50.1 17.38a1.76 1.76 0 0 0-.8-1.32 3.06 3.06 0 0 0-1.76-.48c-.5 0-.94.08-1.31.24-.37.16-.66.38-.87.65-.2.28-.3.59-.3.94 0 .3.07.55.2.76.15.21.33.4.56.54a6.47 6.47 0 0 0 1.49.58l1.2.32c.4.1.8.22 1.22.39.41.16.8.38 1.15.65a2.9 2.9 0 0 1 1.19 2.43c0 .7-.18 1.33-.54 1.87a3.7 3.7 0 0 1-1.57 1.29c-.68.3-1.5.47-2.47.47a6 6 0 0 1-2.4-.44 3.7 3.7 0 0 1-1.59-1.25 3.62 3.62 0 0 1-.63-1.94h1.87c.04.45.18.82.44 1.12.25.3.58.51.98.66.4.14.84.21 1.32.21.53 0 1-.08 1.41-.25.41-.17.74-.4.98-.7.23-.3.35-.65.35-1.05 0-.37-.1-.67-.31-.9a2.2 2.2 0 0 0-.84-.58 8.2 8.2 0 0 0-1.19-.4l-1.46-.4a5.69 5.69 0 0 1-2.35-1.2 2.67 2.67 0 0 1-.87-2.07c0-.71.2-1.34.58-1.87a3.8 3.8 0 0 1 1.57-1.24c.66-.3 1.4-.44 2.24-.44.83 0 1.57.14 2.21.44.65.3 1.15.7 1.52 1.21.37.51.57 1.1.58 1.76h-1.8Zm9.93 5.28v-5.43h1.81v9.27h-1.77v-1.6h-.1a2.9 2.9 0 0 1-2.77 1.73c-.6 0-1.13-.14-1.58-.4a2.76 2.76 0 0 1-1.08-1.18 4.3 4.3 0 0 1-.38-1.92v-5.9h1.8v5.68c0 .63.18 1.13.53 1.5.35.38.8.57 1.36.57a2.21 2.21 0 0 0 1.85-1.02c.22-.34.33-.78.33-1.3Zm4.24 7.32V17.23h1.76v1.5h.15c.1-.2.25-.42.45-.67a2.64 2.64 0 0 1 2.22-.96 3.62 3.62 0 0 1 3.38 2.21c.34.71.5 1.57.5 2.57a6 6 0 0 1-.5 2.58 3.56 3.56 0 0 1-3.37 2.22 2.62 2.62 0 0 1-2.22-.95c-.2-.25-.35-.47-.46-.67h-.1v4.92h-1.81Zm1.76-8.12c0 .66.1 1.23.29 1.72.19.49.46.87.82 1.15.36.28.8.41 1.32.41.53 0 .99-.14 1.35-.43.36-.29.63-.68.82-1.17.19-.5.28-1.06.28-1.68 0-.6-.09-1.16-.28-1.64a2.52 2.52 0 0 0-.82-1.16 2.15 2.15 0 0 0-1.35-.42c-.53 0-.97.14-1.33.4-.36.27-.63.65-.82 1.13a4.71 4.71 0 0 0-.28 1.7Zm8.73 8.12V17.23h1.76v1.5h.15c.1-.2.26-.42.45-.67a2.69 2.69 0 0 1 2.21-.96 3.62 3.62 0 0 1 3.4 2.21c.33.71.5 1.57.5 2.57 0 1-.17 1.86-.5 2.58a3.56 3.56 0 0 1-3.37 2.22c-.57 0-1.03-.1-1.4-.28a2.62 2.62 0 0 1-.82-.67c-.2-.25-.36-.47-.47-.67h-.1v4.92h-1.81Zm1.77-8.12c0 .66.1 1.23.28 1.72.19.49.46.87.82 1.15.36.28.8.41 1.32.41.54 0 .99-.14 1.35-.43.36-.29.64-.68.82-1.17.19-.5.28-1.06.28-1.68 0-.6-.09-1.16-.27-1.64a2.52 2.52 0 0 0-.82-1.16 2.15 2.15 0 0 0-1.36-.42c-.53 0-.97.14-1.33.4-.36.27-.63.65-.82 1.13a4.71 4.71 0 0 0-.27 1.7Zm12.63 4.83a4.03 4.03 0 0 1-3.79-2.27 5.55 5.55 0 0 1-.54-2.51c0-.97.18-1.8.54-2.53a4.02 4.02 0 0 1 3.79-2.27 4.02 4.02 0 0 1 3.79 2.28c.35.71.53 1.55.53 2.52 0 .95-.18 1.8-.54 2.5a4.03 4.03 0 0 1-3.78 2.28Zm0-1.52c.57 0 1.03-.15 1.4-.44.37-.3.65-.7.82-1.2a4.72 4.72 0 0 0 0-3.26c-.17-.5-.45-.9-.82-1.2-.37-.3-.83-.45-1.4-.45s-1.04.15-1.41.45c-.37.3-.65.7-.83 1.2a4.8 4.8 0 0 0-.26 1.63c0 .6.09 1.14.26 1.64.18.5.46.89.83 1.19.37.3.84.44 1.41.44Zm6.33 1.33v-9.27h1.75v1.47h.1c.16-.5.46-.89.89-1.18.43-.29.92-.43 1.46-.43a7.65 7.65 0 0 1 .77.04v1.73l-.4-.07a3.7 3.7 0 0 0-.55-.04c-.42 0-.8.09-1.14.27a2.02 2.02 0 0 0-1.07 1.82v5.66h-1.8Zm11.4-9.27v1.45h-5.06v-1.45h5.06ZM103.2 15h1.8v8.77c0 .35.06.61.16.79.1.17.24.3.4.36a2.1 2.1 0 0 0 .93.06l.26-.05.32 1.49a3.52 3.52 0 0 1-2.5-.06 2.27 2.27 0 0 1-1-.82 2.41 2.41 0 0 1-.37-1.4V15Zm12.5 4.48-1.64.3a1.59 1.59 0 0 0-.32-.6 1.61 1.61 0 0 0-.6-.47 2.14 2.14 0 0 0-.93-.18c-.51 0-.94.11-1.28.34-.34.23-.51.52-.51.88 0 .3.11.56.34.75.23.19.6.34 1.11.46l1.47.34c.86.2 1.5.5 1.91.91.42.41.63.94.63 1.6 0 .56-.16 1.05-.48 1.49-.32.43-.77.76-1.34 1.01-.56.25-1.22.37-1.97.37a4.46 4.46 0 0 1-2.54-.67 2.8 2.8 0 0 1-1.2-1.9l1.73-.26c.11.45.34.8.68 1.03.33.23.77.34 1.32.34.59 0 1.06-.12 1.42-.37.35-.24.53-.55.53-.9 0-.3-.11-.54-.33-.74-.21-.2-.54-.34-.98-.44l-1.57-.35c-.87-.2-1.5-.5-1.92-.94a2.28 2.28 0 0 1-.62-1.64c0-.54.16-1.02.46-1.43a3 3 0 0 1 1.27-.96 4.62 4.62 0 0 1 1.85-.35c1 0 1.8.21 2.37.65.57.43.95 1 1.14 1.73Z"/><path fill="#fff" d="M133.16 18.62h-3.02a2.6 2.6 0 0 0-.25-.84 2.05 2.05 0 0 0-1.22-1.05 2.8 2.8 0 0 0-.92-.14c-.6 0-1.12.14-1.56.44-.43.3-.76.72-1 1.27a5.29 5.29 0 0 0-.34 2.02c0 .82.12 1.5.35 2.06.24.55.57.97 1 1.25.43.28.94.42 1.53.42a3 3 0 0 0 .9-.13 2 2 0 0 0 .71-.37c.21-.16.38-.36.52-.6.13-.23.23-.5.28-.8l3.02.02a5.17 5.17 0 0 1-3.24 4.11c-.65.26-1.4.39-2.25.39a6.1 6.1 0 0 1-3.01-.74 5.3 5.3 0 0 1-2.09-2.16 7.18 7.18 0 0 1-.77-3.45c0-1.36.26-2.51.78-3.46a5.33 5.33 0 0 1 2.11-2.16 6.6 6.6 0 0 1 5.05-.42 4.63 4.63 0 0 1 2.86 2.36c.3.58.49 1.24.56 1.98Zm6.04 8.06a5 5 0 0 1-2.51-.6 4.1 4.1 0 0 1-1.62-1.68 5.36 5.36 0 0 1-.56-2.5c0-.96.2-1.8.56-2.51a4.1 4.1 0 0 1 1.62-1.68c.7-.4 1.53-.6 2.5-.6.98 0 1.82.2 2.51.6.7.4 1.24.96 1.62 1.68.37.72.56 1.55.56 2.5a5.3 5.3 0 0 1-.56 2.51 4.07 4.07 0 0 1-1.62 1.68c-.7.4-1.53.6-2.5.6Zm.02-2.23c.35 0 .65-.11.9-.33.24-.22.43-.52.56-.9.13-.39.2-.84.2-1.34 0-.52-.07-.97-.2-1.36a2.04 2.04 0 0 0-.56-.9 1.31 1.31 0 0 0-.9-.33c-.37 0-.68.11-.93.33-.25.22-.44.52-.58.9a4.3 4.3 0 0 0-.19 1.36c0 .5.06.95.2 1.34.13.38.32.68.57.9.25.22.56.33.93.33Zm6.18 2.05v-9.27h2.8v1.7h.1a2.64 2.64 0 0 1 2.63-1.82c.64 0 1.19.16 1.65.5.46.32.75.77.87 1.32h.1c.17-.55.51-1 1.01-1.32.5-.34 1.1-.5 1.79-.5.87 0 1.59.28 2.13.84.55.56.82 1.33.82 2.3v6.25h-2.95v-5.57c0-.46-.12-.81-.35-1.05a1.23 1.23 0 0 0-.93-.37c-.4 0-.72.13-.96.4-.23.26-.34.61-.34 1.05v5.54h-2.84v-5.6c0-.43-.12-.77-.35-1.02a1.2 1.2 0 0 0-.92-.37 1.25 1.25 0 0 0-1.15.7 2 2 0 0 0-.16.8v5.5h-2.95Zm15.73 0v-9.27h2.81v1.7h.1a2.64 2.64 0 0 1 2.62-1.82c.65 0 1.2.16 1.66.5.46.32.75.77.87 1.32h.1c.17-.55.51-1 1.01-1.32.5-.34 1.1-.5 1.78-.5.88 0 1.6.28 2.14.84.55.56.82 1.33.82 2.3v6.25h-2.95v-5.57c0-.46-.12-.81-.36-1.05a1.23 1.23 0 0 0-.92-.37c-.4 0-.73.13-.96.4-.23.26-.34.61-.34 1.05v5.54h-2.84v-5.6c0-.43-.12-.77-.35-1.02a1.2 1.2 0 0 0-.92-.37 1.25 1.25 0 0 0-1.15.7c-.11.23-.16.5-.16.8v5.5h-2.96Zm18.36.16c-.6 0-1.12-.1-1.58-.3a2.5 2.5 0 0 1-1.08-.9 2.8 2.8 0 0 1-.4-1.53c0-.52.1-.95.28-1.3.18-.36.43-.65.75-.87.31-.22.68-.39 1.1-.5.41-.12.85-.2 1.32-.23.53-.05.96-.1 1.28-.16.32-.05.55-.13.7-.23a.5.5 0 0 0 .22-.44v-.03c0-.31-.1-.55-.32-.72-.21-.17-.5-.25-.86-.25-.4 0-.7.08-.94.25-.24.17-.4.4-.46.7l-2.72-.1a3.23 3.23 0 0 1 2.01-2.56c.6-.25 1.3-.38 2.13-.38a6 6 0 0 1 1.64.2c.5.15.93.35 1.3.61a2.73 2.73 0 0 1 1.17 2.28v6.3h-2.78v-1.3h-.07a2.63 2.63 0 0 1-1.53 1.3c-.35.1-.73.16-1.16.16Zm.9-1.93a1.82 1.82 0 0 0 1.48-.73c.15-.23.23-.5.23-.8v-.88a5.33 5.33 0 0 1-1.13.3c-.15.04-.3.06-.43.08a2.6 2.6 0 0 0-.71.2c-.2.1-.34.22-.45.36a.88.88 0 0 0-.15.52c0 .31.11.54.33.7.22.17.5.25.84.25Zm9.4-3.52v5.3h-2.96v-9.28h2.81v1.7h.1a2.6 2.6 0 0 1 1.05-1.33c.5-.33 1.09-.5 1.77-.5.65 0 1.22.15 1.7.45.48.29.86.7 1.12 1.22.27.52.4 1.12.4 1.82v5.91h-2.95v-5.33c0-.52-.13-.92-.4-1.2a1.4 1.4 0 0 0-1.09-.44c-.3 0-.58.07-.82.2-.23.14-.41.33-.54.58-.13.25-.2.55-.2.9Zm11.2 5.42a3.42 3.42 0 0 1-3.2-2.12 6.1 6.1 0 0 1-.5-2.64c0-1.09.17-1.99.52-2.7.34-.7.8-1.22 1.35-1.56a3.47 3.47 0 0 1 3.07-.25 2.69 2.69 0 0 1 1.37 1.46h.06v-4.68h2.95V26.5h-2.92V25h-.09c-.12.28-.3.55-.53.8-.23.25-.52.45-.86.6-.34.16-.75.23-1.22.23Zm1.03-2.3c.36 0 .66-.1.92-.3.25-.2.45-.5.58-.86.14-.37.2-.8.2-1.3 0-.51-.06-.95-.2-1.32a1.81 1.81 0 0 0-.58-.84c-.26-.2-.56-.3-.92-.3-.37 0-.68.1-.93.3-.25.2-.45.49-.58.86-.13.36-.2.8-.2 1.3s.07.93.2 1.3c.14.37.33.66.58.86.25.2.56.3.93.3Zm10.8 2.35c-.97 0-1.81-.2-2.52-.58a3.97 3.97 0 0 1-1.61-1.65 5.45 5.45 0 0 1-.56-2.55c0-.96.18-1.8.56-2.51a4.1 4.1 0 0 1 1.6-1.68c.69-.4 1.5-.6 2.44-.6.66 0 1.26.1 1.8.3a3.9 3.9 0 0 1 2.35 2.4c.23.6.34 1.27.34 2.03v.74h-8.06v-1.72h5.31c0-.31-.08-.6-.22-.84a1.52 1.52 0 0 0-.6-.57 1.73 1.73 0 0 0-.87-.21 1.76 1.76 0 0 0-1.5.82c-.16.25-.24.53-.25.85v1.75c0 .37.08.7.23 1 .15.27.36.5.63.65.28.16.6.23.98.23.26 0 .5-.03.71-.1.22-.08.4-.18.55-.32.16-.14.27-.32.35-.52l2.7.08c-.1.6-.35 1.13-.74 1.58-.38.45-.87.8-1.49 1.05-.61.24-1.33.37-2.13.37Zm5.9-.18v-9.27h2.87v1.69h.1c.17-.61.44-1.07.82-1.37.39-.3.83-.45 1.34-.45a3.08 3.08 0 0 1 .83.11v2.57a4.9 4.9 0 0 0-1.16-.16 2 2 0 0 0-.95.23 1.72 1.72 0 0 0-.89 1.62v5.03h-2.96Z"/></g><defs><filter id="b" width="39.2" height="39.2" x="2.4" y=".4" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2.8"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1_56"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1_56" result="shape"/></filter><filter id="c" width="194.43" height="32.43" x="36.29" y="3.79" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2.86"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1_56"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1_56" result="shape"/></filter><linearGradient id="a" x1="0" x2="233" y1="20" y2="20" gradientUnits="userSpaceOnUse"><stop stop-color="#62883F"/><stop offset=".34" stop-color="#CD3935"/><stop offset=".68" stop-color="#A85551"/><stop offset="1" stop-color="#BF873E"/></linearGradient></defs></svg>
-1
docs/public/badges/supports/compacter_vector.svg
··· 1 - <svg xmlns="http://www.w3.org/2000/svg" width="168" height="21" fill="none"><g clip-path="url(#a)"><rect width="168" height="21" fill="#000" rx="5"/><g fill="#F9F9F9" filter="url(#b)"><path d="M14.9 16.95a3.37 3.37 0 0 1-1.1-.63l-.7.22a1 1 0 0 1-1.2-.44l-.2-.35a.99.99 0 0 1-.13-.67.96.96 0 0 1 .34-.6l.55-.48a6.01 6.01 0 0 1 0-1.33l-.55-.5a.97.97 0 0 1-.2-1.25l.2-.36a1 1 0 0 1 .52-.43 1 1 0 0 1 .66 0l.7.21a4.72 4.72 0 0 1 1.12-.62l.17-.76a.98.98 0 0 1 .98-.8h.44a.97.97 0 0 1 .98.8l.17.76a3.37 3.37 0 0 1 1.11.62l.7-.22a1 1 0 0 1 1.2.45l.2.35c.12.2.16.43.13.67a.96.96 0 0 1-.34.6l-.55.48a6.01 6.01 0 0 1 0 1.33l.55.5a.97.97 0 0 1 .2 1.25l-.2.35a.96.96 0 0 1-.52.43 1 1 0 0 1-.66.01l-.7-.22a4.7 4.7 0 0 1-1.12.63l-.17.76a.98.98 0 0 1-.98.79h-.44a.97.97 0 0 1-.98-.8l-.17-.75Zm1.38-1.4c.62 0 1.14-.22 1.58-.65.43-.43.64-.95.64-1.57 0-.61-.21-1.13-.64-1.57a2.15 2.15 0 0 0-1.58-.64c-.62 0-1.14.21-1.57.64-.44.44-.65.96-.65 1.57 0 .62.21 1.14.65 1.57.43.43.95.65 1.57.65ZM7.2 13.99a4.78 4.78 0 0 1-.73-.4l-.49.15a.67.67 0 0 1-.76-.28l-.13-.24a.65.65 0 0 1-.08-.44.6.6 0 0 1 .22-.38l.36-.32a2.85 2.85 0 0 1 0-.87l-.36-.32a.58.58 0 0 1-.22-.37c-.02-.15 0-.3.08-.43l.15-.25a.68.68 0 0 1 .32-.27.6.6 0 0 1 .42-.01l.49.15a2.93 2.93 0 0 1 .74-.4l.1-.49a.64.64 0 0 1 .63-.52h.27c.16 0 .3.05.41.15.12.1.2.23.23.39l.1.47a4.76 4.76 0 0 1 .74.4l.49-.15a.67.67 0 0 1 .76.28l.13.23c.08.14.1.28.08.44a.59.59 0 0 1-.22.38l-.37.32a2.84 2.84 0 0 1 0 .87l.37.32c.13.1.2.22.22.37.02.15 0 .3-.08.43l-.15.25a.68.68 0 0 1-.32.27.6.6 0 0 1-.42.02l-.49-.16a2.93 2.93 0 0 1-.74.4l-.1.5a.64.64 0 0 1-.64.5h-.26a.61.61 0 0 1-.42-.14.68.68 0 0 1-.22-.39L7.2 14Zm.88-1c.37 0 .68-.14.95-.4a1.3 1.3 0 0 0 0-1.89 1.3 1.3 0 0 0-.95-.4c-.37 0-.69.14-.95.4a1.3 1.3 0 0 0 0 1.89c.26.26.58.4.95.4ZM12.02 2.6a.6.6 0 0 0-.35.24.66.66 0 0 0-.12.42l.03.47a4.65 4.65 0 0 0-.6.56l-.5-.02a.66.66 0 0 0-.64.46L9.77 5c-.04.15-.03.29.03.43s.16.25.3.31l.44.2a2.8 2.8 0 0 0 .22.83l-.27.4a.57.57 0 0 0-.11.4c.02.16.08.28.19.39l.2.2c.1.1.23.15.37.17a.6.6 0 0 0 .4-.09l.43-.26a2.88 2.88 0 0 0 .8.19l.22.43a.63.63 0 0 0 .73.33l.26-.07a.6.6 0 0 0 .35-.24.66.66 0 0 0 .12-.42l-.03-.47a4.64 4.64 0 0 0 .6-.57l.5.02c.15 0 .28-.03.4-.12.12-.1.2-.2.24-.34l.07-.25a.64.64 0 0 0-.03-.44.58.58 0 0 0-.3-.3l-.44-.21a2.8 2.8 0 0 0-.22-.83l.27-.39c.1-.13.13-.26.11-.41a.62.62 0 0 0-.19-.38l-.2-.2a.67.67 0 0 0-.37-.18.6.6 0 0 0-.4.1l-.43.26a2.89 2.89 0 0 0-.8-.2l-.22-.43a.63.63 0 0 0-.73-.33l-.26.07Zm.64 1.86c.35-.1.68-.05 1 .13.31.18.52.45.61.8.1.34.05.67-.13.99-.18.31-.45.52-.8.6-.35.1-.68.06-1-.12a1.26 1.26 0 0 1-.61-.8 1.29 1.29 0 0 1 .93-1.6Z"/></g><g filter="url(#c)"><path fill="#E8E8E8" d="M29.2 8.53a1.35 1.35 0 0 0-.61-1.02 2.38 2.38 0 0 0-1.35-.36c-.39 0-.73.06-1 .18-.3.12-.51.29-.67.5a1.21 1.21 0 0 0-.07 1.3c.1.16.25.3.42.41.17.1.36.2.56.27l.58.18.92.24c.3.07.61.17.93.3.32.12.61.29.88.5a2.22 2.22 0 0 1 .9 1.86c.01.53-.13 1-.4 1.42-.28.42-.68.75-1.2.99-.52.24-1.15.36-1.89.36-.7 0-1.32-.12-1.84-.34a2.83 2.83 0 0 1-1.21-.96c-.3-.41-.45-.9-.49-1.47h1.44c.02.34.13.62.33.85.2.23.45.4.75.5a2.97 2.97 0 0 0 2.09-.03c.31-.12.56-.3.75-.53.18-.23.27-.5.27-.8a1 1 0 0 0-.24-.7 1.77 1.77 0 0 0-.64-.44 6.26 6.26 0 0 0-.91-.3l-1.12-.31a4.24 4.24 0 0 1-1.8-.91c-.44-.4-.66-.93-.66-1.59 0-.54.15-1.02.44-1.43.3-.4.7-.72 1.2-.94a4.1 4.1 0 0 1 1.71-.34 4 4 0 0 1 1.7.33 3 3 0 0 1 1.16.93c.28.4.43.84.44 1.35H29.2Zm7.59 4.03V8.4h1.38v7.09h-1.36v-1.23h-.07c-.16.38-.42.7-.79.95-.35.25-.8.37-1.33.37a2.1 2.1 0 0 1-2.03-1.2c-.2-.4-.3-.89-.3-1.47V8.4h1.38v4.34c0 .49.14.87.4 1.16.27.28.62.43 1.05.43a1.7 1.7 0 0 0 1.41-.78c.17-.27.26-.6.26-1Zm3.24 5.6V8.4h1.34v1.15h.12c.08-.15.2-.32.35-.51.15-.2.36-.37.62-.51a2.2 2.2 0 0 1 1.06-.22 2.77 2.77 0 0 1 2.6 1.68c.25.55.38 1.2.38 1.97 0 .76-.13 1.42-.38 1.97a2.73 2.73 0 0 1-2.58 1.7c-.43 0-.78-.07-1.06-.22a2 2 0 0 1-.64-.5 4.3 4.3 0 0 1-.35-.52h-.08v3.76h-1.38Zm1.35-6.2c0 .5.07.93.22 1.3.14.38.35.68.62.89.28.2.61.31 1 .31.42 0 .77-.1 1.04-.33.28-.22.49-.52.63-.9.15-.38.22-.8.22-1.28 0-.46-.07-.88-.21-1.26a1.93 1.93 0 0 0-.63-.88 1.64 1.64 0 0 0-1.04-.32c-.4 0-.74.1-1.02.3-.27.21-.48.5-.62.87s-.21.8-.21 1.3Zm6.67 6.2V8.4h1.35v1.15h.11c.08-.15.2-.32.35-.51.15-.2.36-.37.63-.51a2.2 2.2 0 0 1 1.06-.22A2.77 2.77 0 0 1 54.14 10c.26.55.39 1.2.39 1.97 0 .76-.13 1.42-.39 1.97a2.73 2.73 0 0 1-2.58 1.7c-.43 0-.78-.07-1.05-.22a2 2 0 0 1-.64-.5c-.16-.2-.27-.37-.36-.52h-.08v3.76h-1.38Zm1.35-6.2c0 .5.07.93.22 1.3.14.38.35.68.63.89.27.2.6.31 1 .31.42 0 .76-.1 1.04-.33.27-.22.48-.52.62-.9.15-.38.22-.8.22-1.28 0-.46-.07-.88-.21-1.26a1.93 1.93 0 0 0-.63-.88 1.64 1.64 0 0 0-1.04-.32c-.4 0-.74.1-1.01.3-.28.21-.48.5-.63.87-.14.37-.2.8-.2 1.3Zm9.66 3.68a3.07 3.07 0 0 1-2.9-1.73 4.24 4.24 0 0 1-.4-1.92c0-.74.13-1.38.4-1.93a3.08 3.08 0 0 1 2.9-1.74 3 3 0 0 1 2.89 1.74c.28.55.41 1.2.41 1.93s-.13 1.37-.4 1.92a3.08 3.08 0 0 1-2.9 1.73Zm0-1.16c.43 0 .8-.11 1.07-.34a2 2 0 0 0 .63-.9 3.75 3.75 0 0 0 .01-2.5 2.03 2.03 0 0 0-.64-.92 1.64 1.64 0 0 0-1.07-.35c-.43 0-.79.12-1.08.35-.28.23-.49.53-.63.92a3.81 3.81 0 0 0 0 2.49c.14.38.35.68.63.91.29.23.65.34 1.08.34Zm4.85 1.02V8.4h1.33v1.14h.07c.13-.39.36-.69.69-.9.33-.23.7-.34 1.11-.34a5.9 5.9 0 0 1 .59.04v1.32a2.82 2.82 0 0 0-.72-.08c-.33 0-.62.06-.87.2a1.54 1.54 0 0 0-.82 1.39v4.33H63.9Zm8.71-7.1v1.12h-3.87V8.4h3.87ZM69.8 6.72h1.38v6.7c0 .28.04.48.12.61.08.14.18.23.3.28a1.62 1.62 0 0 0 .71.05l.2-.05.25 1.14a2.7 2.7 0 0 1-1.91-.05 1.74 1.74 0 0 1-.76-.62 1.8 1.8 0 0 1-.3-1.07v-7Zm9.56 3.43-1.25.22a1.48 1.48 0 0 0-.25-.46 1.23 1.23 0 0 0-.45-.35c-.2-.1-.43-.14-.72-.14-.39 0-.72.09-.98.26-.26.18-.39.4-.39.67 0 .24.09.43.26.58.18.14.46.26.85.35l1.13.26c.65.15 1.14.38 1.46.7.32.31.48.72.48 1.22 0 .43-.12.8-.37 1.14-.24.33-.58.58-1.02.77-.44.19-.94.28-1.51.28a3.4 3.4 0 0 1-1.94-.5 2.2 2.2 0 0 1-.93-1.46l1.34-.2c.08.35.25.6.5.79.27.17.6.26 1.02.26.45 0 .81-.1 1.08-.28.28-.2.41-.42.41-.7 0-.22-.08-.4-.25-.56a1.63 1.63 0 0 0-.75-.34l-1.2-.26a2.88 2.88 0 0 1-1.47-.72 1.74 1.74 0 0 1-.47-1.25c0-.42.12-.79.35-1.1.23-.32.56-.56.97-.74a3.7 3.7 0 0 1 1.42-.26c.76 0 1.37.16 1.8.5.45.32.74.77.88 1.32Z"/><path fill="#fff" d="M92.3 9.47h-2.31a2 2 0 0 0-.19-.64 1.56 1.56 0 0 0-.94-.8 2.14 2.14 0 0 0-.7-.11c-.46 0-.86.11-1.19.34-.33.22-.58.55-.76.97-.18.43-.26.94-.26 1.54 0 .63.08 1.16.26 1.58.18.42.44.74.76.96.33.21.72.32 1.17.32.26 0 .48-.04.7-.1a1.56 1.56 0 0 0 .94-.74c.1-.18.17-.39.2-.61l2.32.01a3.95 3.95 0 0 1-2.47 3.14c-.5.2-1.08.3-1.73.3-.86 0-1.63-.19-2.3-.56a4.05 4.05 0 0 1-1.6-1.66c-.4-.72-.59-1.6-.59-2.64s.2-1.92.6-2.64a4 4 0 0 1 1.61-1.65 5.05 5.05 0 0 1 3.86-.32 3.54 3.54 0 0 1 2.18 1.8c.24.44.38.94.44 1.51Zm4.62 6.16a3.9 3.9 0 0 1-1.93-.45 3.18 3.18 0 0 1-1.23-1.29 4.1 4.1 0 0 1-.43-1.91c0-.73.15-1.37.43-1.92.3-.55.7-.98 1.23-1.28a3.8 3.8 0 0 1 1.93-.46c.74 0 1.38.15 1.91.46.54.3.95.73 1.23 1.28.3.55.44 1.19.44 1.92 0 .72-.15 1.36-.44 1.91-.28.55-.7.98-1.23 1.29a3.8 3.8 0 0 1-1.91.45Zm0-1.7a1 1 0 0 0 .7-.25c.18-.17.33-.4.43-.7.1-.29.15-.63.15-1.02s-.05-.73-.15-1.03c-.1-.3-.25-.53-.43-.7a1 1 0 0 0-.7-.24c-.27 0-.5.08-.7.25-.2.16-.34.4-.44.69-.1.3-.15.64-.15 1.03 0 .4.05.73.15 1.03.1.3.25.52.44.7.2.16.43.24.7.24Zm4.73 1.57V8.4h2.15v1.31h.08a2.02 2.02 0 0 1 2-1.4c.5 0 .92.13 1.27.39.35.25.57.59.67 1.01h.07c.13-.42.4-.76.78-1.01a2.4 2.4 0 0 1 1.36-.38c.67 0 1.21.21 1.63.64.42.43.63 1.02.63 1.77v4.77h-2.26v-4.26c0-.35-.09-.62-.27-.8a.94.94 0 0 0-.7-.29c-.32 0-.56.1-.74.3-.17.2-.26.48-.26.82v4.23h-2.17v-4.28c0-.33-.1-.59-.27-.78a.92.92 0 0 0-.7-.29.95.95 0 0 0-.88.54 1.4 1.4 0 0 0-.13.61v4.2h-2.26Zm12.04 0V8.4h2.15v1.31h.07a2.02 2.02 0 0 1 2-1.4c.5 0 .92.13 1.27.39.35.25.58.59.67 1.01h.08c.13-.42.39-.76.77-1.01a2.4 2.4 0 0 1 1.36-.38c.67 0 1.22.21 1.64.64.42.43.63 1.02.63 1.77v4.77h-2.26v-4.26c0-.35-.1-.62-.27-.8a.94.94 0 0 0-.71-.29c-.31 0-.56.1-.73.3-.18.2-.27.48-.27.82v4.23h-2.17v-4.28c0-.33-.09-.59-.27-.78a.92.92 0 0 0-.7-.29.95.95 0 0 0-.88.54 1.4 1.4 0 0 0-.12.61v4.2h-2.26Zm14.03.12c-.45 0-.85-.08-1.2-.23a1.88 1.88 0 0 1-.83-.69c-.2-.3-.3-.7-.3-1.17 0-.4.07-.72.21-1 .14-.26.33-.48.57-.65.25-.17.52-.3.84-.39.32-.09.66-.15 1.02-.18.4-.03.73-.07.97-.11.25-.05.43-.1.54-.18a.4.4 0 0 0 .17-.34v-.02a.66.66 0 0 0-.25-.55c-.16-.13-.38-.2-.66-.2-.3 0-.54.07-.72.2a.8.8 0 0 0-.34.53l-2.08-.07a2.46 2.46 0 0 1 1.53-1.96 4.6 4.6 0 0 1 2.88-.13c.39.1.72.26 1 .46a2.1 2.1 0 0 1 .9 1.74v4.82h-2.13v-.99h-.06a2 2 0 0 1-1.17.99c-.26.08-.56.12-.89.12Zm.7-1.48c.24 0 .46-.05.66-.15a1.07 1.07 0 0 0 .65-1.02v-.66l-.24.09a4.1 4.1 0 0 1-.63.14l-.33.05c-.21.04-.4.09-.54.16a.86.86 0 0 0-.34.28.68.68 0 0 0-.12.4c0 .23.08.4.25.53.17.12.38.18.64.18Zm7.18-2.68v4.04h-2.26V8.4h2.15v1.31h.08c.15-.43.42-.77.8-1.02a2.4 2.4 0 0 1 1.35-.37c.5 0 .93.1 1.3.33s.66.54.86.94c.2.4.3.86.3 1.39v4.52h-2.25v-4.08c0-.39-.1-.7-.3-.92-.2-.22-.48-.33-.84-.33-.24 0-.45.05-.63.15-.18.1-.31.25-.41.44-.1.2-.15.43-.15.7Zm8.57 4.14a2.61 2.61 0 0 1-2.45-1.62 4.73 4.73 0 0 1-.38-2.02c0-.83.13-1.52.4-2.06.26-.54.6-.94 1.03-1.2a2.65 2.65 0 0 1 2.35-.19c.26.13.48.29.65.49.17.2.3.4.4.62h.04V6.05h2.26v9.45h-2.24v-1.15h-.06c-.1.22-.24.42-.41.61a1.98 1.98 0 0 1-1.59.64Zm.79-1.76c.27 0 .5-.07.7-.23.2-.16.34-.38.44-.66.11-.28.16-.61.16-1 0-.38-.05-.72-.16-1-.1-.28-.25-.5-.44-.64a1.1 1.1 0 0 0-.7-.23c-.28 0-.52.08-.72.23-.19.15-.34.37-.44.65-.1.28-.15.61-.15 1 0 .38.05.7.16 1 .1.27.24.5.43.65.2.16.44.23.72.23Zm8.25 1.8a4 4 0 0 1-1.92-.44c-.53-.3-.95-.72-1.24-1.26a4.17 4.17 0 0 1-.43-1.96c0-.73.15-1.37.44-1.92.29-.55.7-.98 1.22-1.28a4.14 4.14 0 0 1 3.25-.23 2.97 2.97 0 0 1 1.8 1.84c.16.45.25.96.25 1.55v.56h-6.17v-1.31h4.07c0-.24-.06-.46-.17-.64a1.16 1.16 0 0 0-.46-.44c-.2-.11-.41-.16-.66-.16a1.35 1.35 0 0 0-1.16.62c-.11.2-.18.41-.18.66v1.33c0 .29.06.54.17.76.11.22.27.38.48.5.21.12.46.18.75.18.2 0 .38-.02.55-.08.16-.05.3-.14.42-.24a1 1 0 0 0 .26-.4l2.07.06a2.5 2.5 0 0 1-.56 1.21c-.3.35-.67.61-1.15.8a4.4 4.4 0 0 1-1.63.28Zm4.52-.14V8.4h2.2v1.3h.07c.13-.47.34-.81.63-1.04.3-.23.63-.35 1.02-.35a2.36 2.36 0 0 1 .63.09v1.96a3.76 3.76 0 0 0-.89-.12c-.26 0-.5.06-.72.18a1.3 1.3 0 0 0-.5.5c-.12.2-.18.45-.18.73v3.85h-2.26Z"/></g></g><rect width="166.8" height="19.8" x=".6" y=".6" stroke="url(#d)" stroke-opacity=".88" stroke-width="1.2" rx="4.4"/><defs><filter id="b" width="27.2" height="27.2" x="-.6" y="-3.1" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2.8"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_5_17"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_5_17" result="shape"/></filter><filter id="c" width="151.43" height="27.43" x="17.29" y="-3.21" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2.86"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_5_17"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_5_17" result="shape"/></filter><linearGradient id="d" x1="0" x2="168" y1="10.5" y2="10.5" gradientUnits="userSpaceOnUse"><stop stop-color="#62883F"/><stop offset=".34" stop-color="#CD3935"/><stop offset=".68" stop-color="#A85551"/><stop offset="1" stop-color="#BF873E"/></linearGradient><clipPath id="a"><rect width="168" height="21" fill="#fff" rx="5"/></clipPath></defs></svg>
-1
docs/public/badges/supports/cozy_vector.svg
··· 1 - <svg xmlns="http://www.w3.org/2000/svg" width="176" height="56" fill="none"><rect width="176" height="56" fill="#000" rx="8"/><rect width="173.9" height="53.9" x="1.05" y="1.05" stroke="url(#a)" stroke-opacity=".88" stroke-width="2.1" rx="6.95"/><g fill="#F9F9F9" filter="url(#b)"><path d="M36.77 44.13a8.44 8.44 0 0 1-1.46-.67c-.44-.27-.88-.57-1.32-.9l-1.76.55a2.48 2.48 0 0 1-2.96-1.1l-.51-.88a2.48 2.48 0 0 1-.33-1.69c.1-.6.37-1.1.84-1.5l1.39-1.2a14.98 14.98 0 0 1 0-3.32l-1.4-1.24a2.43 2.43 0 0 1-.5-3.14l.5-.88a2.5 2.5 0 0 1 2.97-1.1l1.76.55a9.28 9.28 0 0 1 2.78-1.57l.42-1.89a2.46 2.46 0 0 1 2.46-1.98h1.1a2.43 2.43 0 0 1 2.46 1.98l.42 1.9c.52.18 1.01.4 1.46.66.45.26.89.56 1.32.9l1.76-.55a2.48 2.48 0 0 1 2.96 1.1l.51.88a2.47 2.47 0 0 1-.51 3.18l-1.39 1.2a15 15 0 0 1 0 3.32l1.4 1.25a2.45 2.45 0 0 1 .5 3.14l-.5.87a2.48 2.48 0 0 1-2.96 1.1l-1.77-.54a14 14 0 0 1-1.32.9c-.45.26-.94.48-1.46.66l-.42 1.9A2.46 2.46 0 0 1 40.76 48h-1.11a2.43 2.43 0 0 1-2.46-1.98l-.42-1.9Zm3.43-3.51c1.55 0 2.86-.54 3.94-1.62a5.33 5.33 0 0 0 1.62-3.92c0-1.53-.54-2.84-1.62-3.92a5.37 5.37 0 0 0-3.94-1.61 5.4 5.4 0 0 0-3.94 1.61 5.33 5.33 0 0 0-1.62 3.92c0 1.54.54 2.85 1.62 3.92a5.37 5.37 0 0 0 3.94 1.62Zm-22.68-3.9a7.29 7.29 0 0 1-1.85-1l-1.22.37a1.68 1.68 0 0 1-1.89-.71l-.33-.59c-.2-.33-.27-.7-.21-1.08.05-.4.24-.71.54-.96l.93-.8a7.13 7.13 0 0 1 0-2.17l-.93-.8c-.3-.25-.49-.56-.54-.94-.06-.37.01-.73.2-1.06l.39-.63c.2-.3.46-.53.8-.67a1.5 1.5 0 0 1 1.04-.04l1.22.38a7.33 7.33 0 0 1 1.85-1l.25-1.22a1.6 1.6 0 0 1 1.6-1.3h.66c.4 0 .74.13 1.03.38.3.25.48.57.57.96l.25 1.17a7.33 7.33 0 0 1 1.85 1l1.21-.37a1.68 1.68 0 0 1 1.89.71l.34.59c.2.33.26.7.2 1.08-.05.4-.23.71-.54.96l-.92.8a7.11 7.11 0 0 1 0 2.17l.92.8c.31.25.5.56.55.94.05.37-.02.73-.21 1.06l-.38.63c-.2.3-.46.53-.8.67a1.5 1.5 0 0 1-1.05.04l-1.21-.38a7.35 7.35 0 0 1-1.85 1l-.25 1.22a1.6 1.6 0 0 1-1.6 1.3h-.67c-.39 0-.73-.13-1.03-.38a1.7 1.7 0 0 1-.56-.96l-.25-1.17Zm2.18-2.51c.92 0 1.71-.33 2.37-.98.66-.66.99-1.45.99-2.36a3.2 3.2 0 0 0-1-2.37 3.24 3.24 0 0 0-2.36-.98c-.92 0-1.72.33-2.37.98a3.21 3.21 0 0 0-.99 2.37c0 .91.33 1.7.99 2.36.65.65 1.45.98 2.37.98Zm9.85-25.98c-.37.1-.66.3-.88.61-.2.32-.3.67-.29 1.06l.06 1.17a7.17 7.17 0 0 0-1.5 1.42l-1.24-.05a1.6 1.6 0 0 0-1.6 1.15l-.17.64c-.1.36-.08.72.07 1.08.16.35.41.61.77.77l1.07.52a6.98 6.98 0 0 0 .56 2.06l-.68.98c-.22.32-.32.66-.27 1.03.04.37.2.69.47.96l.51.5c.27.23.58.38.93.43.35.04.69-.03 1-.23l1.06-.66a7.23 7.23 0 0 0 2 .48l.55 1.08a1.57 1.57 0 0 0 1.84.83l.64-.17c.37-.1.66-.3.88-.62.21-.31.3-.66.29-1.05l-.06-1.17c.28-.22.54-.44.78-.66.24-.22.48-.47.71-.76l1.25.05a1.64 1.64 0 0 0 1.6-1.15l.17-.64c.1-.37.08-.73-.07-1.08a1.45 1.45 0 0 0-.77-.77l-1.07-.52a6.98 6.98 0 0 0-.56-2.06l.68-.99c.22-.31.32-.65.27-1.02-.04-.37-.2-.7-.47-.96l-.51-.5a1.67 1.67 0 0 0-.93-.43c-.35-.05-.69.03-1 .23l-1.06.66a7.21 7.21 0 0 0-2-.48l-.55-1.09a1.57 1.57 0 0 0-1.84-.82l-.64.17Zm1.6 4.66c.87-.23 1.7-.12 2.5.33a3.2 3.2 0 0 1 1.53 1.99c.23.87.12 1.7-.33 2.48a3.17 3.17 0 0 1-2 1.53c-.87.24-1.7.13-2.5-.33a3.16 3.16 0 0 1-1.53-1.98 3.2 3.2 0 0 1 .33-2.49 3.17 3.17 0 0 1 2-1.53Z"/></g><g filter="url(#c)"><path fill="#E8E8E8" d="M67.63 15.92a1.66 1.66 0 0 0-.75-1.25c-.44-.3-1-.45-1.66-.45-.48 0-.9.08-1.24.23a1.9 1.9 0 0 0-.82.61c-.18.26-.28.55-.28.88 0 .28.07.52.2.72.13.2.3.37.51.5.22.14.45.25.7.34.24.09.48.16.7.22l1.14.3c.37.08.75.2 1.14.36.4.16.75.36 1.09.61.33.26.6.57.8.95.21.37.32.82.32 1.34a3.1 3.1 0 0 1-.51 1.76c-.34.51-.83.91-1.48 1.21-.64.3-1.41.44-2.32.44-.87 0-1.62-.14-2.26-.41a3.48 3.48 0 0 1-1.5-1.18 3.4 3.4 0 0 1-.6-1.82h1.77c.03.42.17.77.4 1.06.25.27.56.48.93.61.38.14.8.2 1.25.2.5 0 .94-.07 1.32-.23.4-.16.7-.38.92-.66.23-.28.34-.61.34-1 0-.34-.1-.62-.3-.84-.19-.22-.45-.4-.79-.55-.33-.14-.7-.27-1.12-.38l-1.37-.37A5.35 5.35 0 0 1 61.94 18a2.51 2.51 0 0 1-.8-1.95c0-.68.17-1.26.54-1.76.36-.5.85-.89 1.47-1.16a5.06 5.06 0 0 1 2.1-.43c.8 0 1.49.14 2.1.42.6.28 1.07.66 1.42 1.14.35.48.53 1.04.55 1.66h-1.7Zm9.34 4.96v-5.1h1.7v8.72H77v-1.51h-.09a2.7 2.7 0 0 1-2.61 1.62 2.6 2.6 0 0 1-2.5-1.48 4.06 4.06 0 0 1-.36-1.8v-5.56h1.7v5.35a2 2 0 0 0 .5 1.42c.32.35.75.53 1.28.53a2.08 2.08 0 0 0 1.73-.96c.22-.32.32-.73.32-1.23Zm3.98 6.9v-12h1.66v1.4h.15c.1-.17.24-.38.42-.62.19-.24.44-.45.77-.63a2.7 2.7 0 0 1 1.31-.27 3.4 3.4 0 0 1 3.19 2.07c.32.67.48 1.48.48 2.42a5.7 5.7 0 0 1-.48 2.43 3.36 3.36 0 0 1-3.18 2.1 2.9 2.9 0 0 1-1.3-.28 2.35 2.35 0 0 1-.78-.62 4.72 4.72 0 0 1-.43-.64h-.1v4.63h-1.7Zm1.67-7.64c0 .61.09 1.15.27 1.61.17.46.43.82.77 1.09.34.25.75.38 1.24.38a2 2 0 0 0 1.27-.4c.34-.27.6-.64.77-1.11.18-.47.27-.99.27-1.57 0-.58-.09-1.1-.26-1.55a2.37 2.37 0 0 0-.77-1.09 2 2 0 0 0-1.28-.4c-.5 0-.91.13-1.25.38-.34.26-.6.61-.77 1.07-.17.45-.26.98-.26 1.59Zm8.2 7.63v-12h1.67v1.42h.14c.1-.18.24-.4.43-.63.18-.24.44-.45.77-.63a2.7 2.7 0 0 1 1.3-.27 3.4 3.4 0 0 1 3.2 2.07c.31.67.47 1.48.47 2.42a5.7 5.7 0 0 1-.47 2.43 3.36 3.36 0 0 1-3.18 2.1c-.53-.01-.96-.1-1.3-.28-.33-.17-.6-.38-.78-.62a4.72 4.72 0 0 1-.44-.64h-.1v4.63h-1.7Zm1.67-7.63c0 .61.1 1.15.27 1.61.18.46.44.82.77 1.09.34.25.75.38 1.24.38a2 2 0 0 0 1.28-.4c.34-.27.6-.64.77-1.11.18-.47.27-.99.27-1.57 0-.58-.1-1.1-.27-1.55a2.37 2.37 0 0 0-.77-1.09 2 2 0 0 0-1.28-.4c-.49 0-.9.13-1.25.38-.33.26-.59.61-.76 1.07-.18.45-.27.98-.27 1.59Zm11.9 4.54a3.8 3.8 0 0 1-3.56-2.14 5.22 5.22 0 0 1-.52-2.36c0-.9.17-1.7.5-2.38a3.78 3.78 0 0 1 3.57-2.14 3.78 3.78 0 0 1 3.56 2.14c.34.68.5 1.47.5 2.38 0 .9-.16 1.69-.5 2.36a3.79 3.79 0 0 1-3.56 2.14Zm0-1.43a2 2 0 0 0 1.31-.42c.35-.28.61-.65.78-1.12.17-.47.25-.98.25-1.54s-.08-1.07-.25-1.53a2.5 2.5 0 0 0-.78-1.13 2.02 2.02 0 0 0-1.31-.43 2 2 0 0 0-1.33.43 2.5 2.5 0 0 0-.78 1.13c-.17.46-.25.97-.25 1.53s.08 1.07.25 1.54c.17.47.43.84.78 1.12.35.28.8.42 1.33.42Zm5.95 1.25v-8.73H112v1.39h.09c.16-.47.44-.84.84-1.1.4-.28.86-.42 1.37-.42a7.24 7.24 0 0 1 .73.04v1.63a3.48 3.48 0 0 0-.89-.1c-.4 0-.76.08-1.08.25a1.9 1.9 0 0 0-1 1.71v5.33h-1.7Zm10.73-8.73v1.37h-4.76v-1.37h4.77Zm-3.48-2.09h1.7v8.26c0 .33.04.58.14.74.1.16.23.28.38.34a2 2 0 0 0 .88.06l.24-.05.3 1.4a3.33 3.33 0 0 1-2.35-.06c-.38-.16-.7-.42-.94-.77a2.27 2.27 0 0 1-.35-1.31v-8.6Zm11.76 4.22-1.54.28c-.06-.2-.16-.39-.3-.57a1.52 1.52 0 0 0-.56-.43 2.02 2.02 0 0 0-.88-.17c-.48 0-.88.1-1.2.32-.33.21-.49.49-.49.82 0 .3.11.53.33.7.21.19.56.33 1.04.45l1.39.31c.8.19 1.4.48 1.8.86.39.39.58.89.58 1.5 0 .53-.15 1-.45 1.4-.3.41-.72.73-1.26.96-.53.23-1.15.35-1.85.35a4.2 4.2 0 0 1-2.4-.63c-.61-.42-1-1.02-1.13-1.79l1.64-.25c.1.43.31.75.63.97.32.22.73.33 1.24.33.56 0 1-.12 1.34-.35.33-.23.5-.52.5-.86 0-.27-.1-.5-.3-.69a2 2 0 0 0-.93-.42l-1.48-.32a3.55 3.55 0 0 1-1.8-.89c-.4-.4-.59-.91-.59-1.54 0-.51.15-.96.43-1.35.3-.38.7-.69 1.2-.9.5-.22 1.09-.33 1.74-.33.95 0 1.69.2 2.23.61.54.4.9.95 1.07 1.63Z"/><path fill="#fff" d="M72.16 35.62h-3.02a2.62 2.62 0 0 0-.25-.84 2.04 2.04 0 0 0-1.22-1.05 2.8 2.8 0 0 0-.93-.14c-.6 0-1.11.14-1.55.44-.43.3-.76.72-1 1.27a5.28 5.28 0 0 0-.34 2.02c0 .82.12 1.5.35 2.06.24.55.57.97 1 1.25.43.28.94.42 1.53.42a3 3 0 0 0 .9-.13 2.04 2.04 0 0 0 1.23-.97c.13-.23.23-.5.28-.8l3.02.02a5.17 5.17 0 0 1-3.24 4.11c-.65.26-1.4.39-2.26.39a6.1 6.1 0 0 1-3-.74 5.3 5.3 0 0 1-2.09-2.16 7.18 7.18 0 0 1-.77-3.45c0-1.36.26-2.52.78-3.46a5.33 5.33 0 0 1 2.1-2.16 6.61 6.61 0 0 1 5.05-.42 4.63 4.63 0 0 1 2.86 2.36c.3.58.5 1.24.57 1.98Zm6.04 8.06a5 5 0 0 1-2.51-.6 4.1 4.1 0 0 1-1.62-1.68 5.36 5.36 0 0 1-.56-2.5c0-.96.19-1.8.56-2.51a4.1 4.1 0 0 1 1.62-1.68c.7-.4 1.53-.6 2.5-.6.98 0 1.82.2 2.51.6.7.4 1.24.96 1.61 1.68.38.71.57 1.55.57 2.5s-.19 1.79-.57 2.5a4.07 4.07 0 0 1-1.6 1.69c-.7.4-1.54.6-2.51.6Zm.01-2.23c.36 0 .66-.11.9-.33.25-.22.44-.52.57-.9.13-.39.2-.84.2-1.34 0-.52-.07-.97-.2-1.36a2.04 2.04 0 0 0-.57-.9 1.3 1.3 0 0 0-.9-.33c-.36 0-.67.11-.92.33-.25.22-.45.52-.58.9-.13.39-.2.84-.2 1.36 0 .5.07.95.2 1.34.13.38.33.68.58.9.25.22.56.33.92.33Zm6.18 2.05v-9.27h2.81v1.7h.1a2.64 2.64 0 0 1 2.62-1.82c.65 0 1.2.16 1.66.5.46.32.75.76.87 1.32h.1c.17-.55.51-1 1.01-1.32.5-.34 1.1-.5 1.78-.5.88 0 1.6.28 2.14.84.55.56.82 1.33.82 2.3v6.25h-2.95v-5.57c0-.46-.12-.81-.36-1.05a1.23 1.23 0 0 0-.92-.37c-.4 0-.73.13-.96.4-.23.26-.34.61-.34 1.05v5.54h-2.84v-5.6c0-.43-.12-.77-.35-1.02a1.2 1.2 0 0 0-.92-.37 1.25 1.25 0 0 0-1.15.7c-.11.23-.16.5-.16.8v5.49h-2.96Zm15.74 0v-9.27h2.8v1.7h.11a2.64 2.64 0 0 1 2.62-1.82c.65 0 1.2.16 1.66.5.46.32.75.76.87 1.32h.1c.17-.55.5-1 1.01-1.32.5-.34 1.1-.5 1.78-.5.88 0 1.6.28 2.14.84.55.56.82 1.33.82 2.3v6.25h-2.95v-5.57c0-.46-.12-.81-.36-1.05a1.23 1.23 0 0 0-.92-.37c-.4 0-.73.13-.96.4-.23.26-.34.61-.34 1.05v5.54h-2.84v-5.6c0-.43-.12-.77-.35-1.02a1.2 1.2 0 0 0-.93-.37 1.25 1.25 0 0 0-1.14.7c-.11.23-.17.5-.17.8v5.49h-2.95Zm18.35.16a3.9 3.9 0 0 1-1.57-.3 2.4 2.4 0 0 1-1.08-.9 2.8 2.8 0 0 1-.4-1.53c0-.52.1-.95.28-1.3.18-.36.43-.65.75-.87.31-.22.68-.39 1.1-.5.4-.12.85-.2 1.32-.24.53-.04.95-.1 1.28-.15.32-.05.55-.13.7-.23a.5.5 0 0 0 .22-.44v-.03c0-.31-.1-.55-.32-.72-.21-.17-.5-.25-.86-.25-.4 0-.7.08-.95.25-.23.17-.38.4-.45.7l-2.72-.1a3.2 3.2 0 0 1 2.01-2.56c.6-.25 1.3-.38 2.13-.38a6 6 0 0 1 1.64.2c.5.14.93.35 1.3.61a2.73 2.73 0 0 1 1.17 2.28v6.3h-2.78v-1.3h-.07a2.64 2.64 0 0 1-1.54 1.3c-.34.1-.72.16-1.16.16Zm.92-1.93c.31 0 .6-.07.85-.2a1.44 1.44 0 0 0 .85-1.33v-.88a5.16 5.16 0 0 1-1.13.3l-.43.08c-.28.04-.51.1-.71.2-.2.1-.34.22-.45.36a.88.88 0 0 0-.15.52c0 .3.1.54.33.7.22.16.5.25.84.25Zm9.38-3.52v5.29h-2.95v-9.27h2.8v1.7h.11a2.6 2.6 0 0 1 1.05-1.33c.5-.33 1.09-.5 1.77-.5a3 3 0 0 1 2.82 1.67c.27.52.4 1.12.4 1.82v5.91h-2.95v-5.33c0-.52-.13-.92-.4-1.2a1.4 1.4 0 0 0-1.09-.44c-.3 0-.58.07-.82.2-.23.14-.42.33-.54.58-.13.25-.2.55-.2.9ZM140 43.63a3.41 3.41 0 0 1-3.2-2.12 6.1 6.1 0 0 1-.5-2.64c0-1.1.17-1.99.52-2.7.34-.7.8-1.22 1.35-1.56a3.47 3.47 0 0 1 3.07-.25c.35.17.63.38.86.64a3 3 0 0 1 .51.81h.06v-4.67h2.95V43.5h-2.92V42h-.09c-.12.28-.3.55-.53.8-.23.25-.52.45-.86.6-.35.16-.75.23-1.22.23Zm1.03-2.3c.36 0 .66-.1.92-.3.25-.2.44-.5.58-.86.14-.37.2-.8.2-1.3 0-.51-.06-.95-.2-1.32a1.8 1.8 0 0 0-.58-.84c-.26-.2-.56-.3-.92-.3-.37 0-.68.1-.93.3-.25.2-.45.49-.58.86-.13.36-.2.8-.2 1.3s.07.93.2 1.3c.14.37.33.66.58.86.25.2.56.3.93.3Zm10.8 2.35c-.98 0-1.81-.2-2.52-.58a3.96 3.96 0 0 1-1.62-1.65 5.45 5.45 0 0 1-.56-2.55c0-.96.2-1.8.57-2.51a4.1 4.1 0 0 1 1.6-1.68c.69-.4 1.5-.6 2.43-.6.66 0 1.27.1 1.81.3a3.89 3.89 0 0 1 2.35 2.4c.22.6.34 1.27.34 2.03v.74h-8.06v-1.72h5.3c0-.31-.07-.6-.22-.84a1.52 1.52 0 0 0-.6-.57 1.72 1.72 0 0 0-.86-.21 1.76 1.76 0 0 0-1.5.82c-.16.25-.24.53-.25.85v1.74c0 .38.08.71.23 1a1.67 1.67 0 0 0 1.61.89c.26 0 .5-.03.71-.1.21-.08.4-.18.55-.32.15-.15.27-.32.34-.52l2.72.07a3.54 3.54 0 0 1-.75 1.6 3.8 3.8 0 0 1-1.49 1.04c-.61.24-1.33.37-2.14.37Zm5.9-.18v-9.27h2.87v1.69h.1c.17-.61.44-1.07.82-1.37.39-.3.83-.45 1.34-.45a3.06 3.06 0 0 1 .83.1v2.57a4.95 4.95 0 0 0-1.16-.16 2 2 0 0 0-.95.24 1.72 1.72 0 0 0-.89 1.62v5.03h-2.96Z"/></g><defs><filter id="b" width="51.43" height="51.43" x="6.29" y="2.29" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2.86"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1_50"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1_50" result="shape"/></filter><filter id="c" width="115.2" height="48.2" x="54.4" y="3.9" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2.8"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1_50"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1_50" result="shape"/></filter><linearGradient id="a" x1="0" x2="176" y1="28" y2="28" gradientUnits="userSpaceOnUse"><stop stop-color="#62883F"/><stop offset=".34" stop-color="#CD3935"/><stop offset=".68" stop-color="#A85551"/><stop offset="1" stop-color="#BF873E"/></linearGradient></defs></svg>
docs/public/favicon.png

This is a binary file and will not be displayed.

docs/public/logo.png

This is a binary file and will not be displayed.

-20
docs/public/robots.txt
··· 1 - User-agent: CCBot 2 - Disallow: / 3 - 4 - User-agent: ChatGPT-User 5 - Disallow: / 6 - 7 - User-agent: GPTBot 8 - Disallow: / 9 - 10 - User-agent: Google-Extended 11 - Disallow: / 12 - 13 - User-agent: Omgilibot 14 - Disallow: / 15 - 16 - User-agent: Omgili 17 - Disallow: / 18 - 19 - User-agent: FacebookBot 20 - Disallow: /
-86
docs/zh-cn/BrigadierCommands.md
··· 1 - # ๆ–œๆ ๅ‘ฝไปค 2 - 3 - ๅ‘ฝไปคๅฎ˜ๆจก็ป„ๅผ•ๅ…ฅไบ†ไธ€็ณปๅˆ— `/` ๆ ทๅผ็š„ๅ‘ฝไปคใ€‚่ฟ™ไบ›ๅ‘ฝไปค้ƒฝ่ฆๆฑ‚ๆƒ้™็ญ‰็บง 2. 4 - 5 - ## `cmd:arithmetica` 6 - 7 - ่ฟ™ไธชๅ‘ฝไปคๅฏไปฅ่ฎฉไฝ ๅœจ่Šๅคฉๆ ไธญ่ฟ่กŒ่กจ่พพๅผใ€‚ไธŠไธ‹ๆ–‡๏ผš`minecraft:level`๏ผˆๅญ˜ๆกฃ๏ผ‰๏ผŒ`minecraft:origin`๏ผˆ็ปดๅบฆ๏ผ‰ๅ’Œๅฏ้€‰็š„ `minecraft:this_entity`๏ผˆๅฎžไฝ“๏ผ‰ใ€‚่ฟ™ไธชๅ‘ฝไปค้œ€่ฆไธ€ไธชๅญ—็ฌฆไธฒ็ฑปๅž‹็š„่กจ่พพๅผ๏ผŒไปฅๅŠๅฏ้€‰็š„ๆ•ฐๆฎ็ฑปๅž‹่ฝฌๆขใ€‚ 8 - 9 - ``` 10 - cmd:arithmetica -> 11 - \- expression 12 - \- cast ๏ผˆ้ป˜่ฎค๏ผšๆ— ๏ผ‰ 13 - ``` 14 - 15 - ็คบไพ‹๏ผš`cmd:arithmetica "sin(level.getDayTime)" bool` 16 - 17 - ## `cmd:explode` 18 - 19 - ่ฟ™ไธช็ฎ€ๅ•็š„ๅ‘ฝไปคๅฏไปฅ็”Ÿๆˆ็ˆ†็‚ธใ€‚ๅฎƒ็š„็ป“ๆž„ๆ ‘ๅฆ‚ไธ‹๏ผš 20 - 21 - ``` 22 - cmd:explode -> 23 - \- entity ๏ผˆ็ˆ†็‚ธ็š„้€ ๆˆ่€…๏ผ‰ 24 - \- position 25 - \- [power] ๏ผˆ้ป˜่ฎค๏ผš4๏ผ‰ 26 - \- [fire] ๏ผˆ้ป˜่ฎค๏ผšๅฆ๏ผ‰ 27 - \- position 28 - \- [power] ๏ผˆ้ป˜่ฎค๏ผš4๏ผ‰ 29 - \- [fire] ๏ผˆ้ป˜่ฎค๏ผšๅฆ๏ผ‰ 30 - ``` 31 - 32 - ็คบไพ‹๏ผš`cmd:explode @s ~ ~ ~ 6.4 true` 33 - 34 - ## `cmd:data` 35 - 36 - ่ฟ™ไธ€ๅ‘ฝไปคไฝฟไฝ ่ƒฝๅคŸ่ฏปๅ†™ๆŒไน…ๅž‹ๆ•ฐๆฎ๏ผŒไปฅไพฟๅœจๅŽ็ปญ็š„่กจ่พพๅผไธญไฝฟ็”จใ€‚ๆไพ›็š„ๆ•ฐๆฎๅฟ…้กปๆ˜ฏๆ•ฐๅญ—ๆˆ–ๅญ—็ฌฆไธฒใ€‚ๅฎƒ็š„็ป“ๆž„ๆ ‘ๅฆ‚ไธ‹๏ผš 37 - 38 - ::: details ็ป“ๆž„ๆ ‘ 39 - ``` 40 - cmd:data 41 - \- read ๏ผˆ่ฏปๅ–๏ผ‰ 42 - \- level ๏ผˆๅญ˜ๆกฃ๏ผ‰ 43 - \- key ๏ผˆ้”ฎๅ€ผ๏ผ‰ 44 - \- chunk ๏ผˆๅŒบๅ—๏ผ‰ 45 - \- position ๏ผˆๅๆ ‡ไฝ็ฝฎ๏ผ‰ 46 - \- key ๏ผˆ้”ฎๅ€ผ๏ผ‰ 47 - \- entity ๏ผˆๅฎžไฝ“๏ผ‰ 48 - \- entity ๏ผˆๅฎžไฝ“๏ผ‰ 49 - \- key ๏ผˆ้”ฎๅ€ผ๏ผ‰ 50 - \- block_entity ๏ผˆๆ–นๅ—ๅฎžไฝ“๏ผ‰ 51 - \- position ๏ผˆๅๆ ‡ไฝ็ฝฎ๏ผ‰ 52 - \- key ๏ผˆ้”ฎๅ€ผ๏ผ‰ 53 - \- write ๏ผˆๅ†™ๅ…ฅ๏ผ‰ 54 - \- level ๏ผˆๅญ˜ๆกฃ๏ผ‰ 55 - \- key ๏ผˆ้”ฎๅ€ผ๏ผ‰ 56 - \- data ๏ผˆๆ•ฐๆฎ๏ผ‰ 57 - \- chunk ๏ผˆๅŒบๅ—๏ผ‰ 58 - \- position ๏ผˆๅๆ ‡ไฝ็ฝฎ๏ผ‰ 59 - \- key ๏ผˆ้”ฎๅ€ผ๏ผ‰ 60 - \- data ๏ผˆๆ•ฐๆฎ๏ผ‰ 61 - \- entity ๏ผˆๅฎžไฝ“๏ผ‰ 62 - \- entity ๏ผˆๅฎžไฝ“๏ผ‰ 63 - \- key ๏ผˆ้”ฎๅ€ผ๏ผ‰ 64 - \- data ๏ผˆๆ•ฐๆฎ๏ผ‰ 65 - \- block_entity ๏ผˆๆ–นๅ—ๅฎžไฝ“๏ผ‰ 66 - \- position ๏ผˆๅๆ ‡ไฝ็ฝฎ๏ผ‰ 67 - \- key ๏ผˆ้”ฎๅ€ผ๏ผ‰ 68 - \- data ๏ผˆๆ•ฐๆฎ๏ผ‰ 69 - \- remove ๏ผˆ็งป้™ค๏ผ‰ 70 - \- level ๏ผˆไธ–็•Œ๏ผ‰ 71 - \- key ๏ผˆ้”ฎๅ€ผ๏ผ‰ 72 - \- chunk ๏ผˆๅŒบๅ—๏ผ‰ 73 - \- position ๏ผˆไฝ็ฝฎ๏ผ‰ 74 - \- key ๏ผˆ้”ฎๅ€ผ๏ผ‰ 75 - \- entity ๏ผˆๅฎžไฝ“๏ผ‰ 76 - \- entity ๏ผˆๅฎžไฝ“๏ผ‰ 77 - \- key ๏ผˆ้”ฎๅ€ผ๏ผ‰ 78 - \- block_entity ๏ผˆๆ–นๅ—ๅฎžไฝ“๏ผ‰ 79 - \- position ๏ผˆไฝ็ฝฎ๏ผ‰ 80 - \- key ๏ผˆ้”ฎๅ€ผ๏ผ‰ 81 - ``` 82 - ::: 83 - 84 - ่ฏปๅ†™็คบไพ‹๏ผš`cmd:data write entity @s "my_test_data" "Hello "`๏ผŒ`cmd:data read entity @s "my_test_data"`๏ผŒ`cmd:data remove entity @s "my_test_data"` 85 - 86 - ไฝฟ็”จ็คบไพ‹๏ผš`this_entity.storage.my_test_data + 'World!'`
-167
docs/zh-cn/Commands.md
··· 1 - # ๅ‘ฝไปค 2 - 3 - ๅ‘ฝไปคๅฎ˜ๆจก็ป„ไธญ็š„ๅ‘ฝไปคๆ˜ฏไฝ ็š„่ฎข้˜…ๆ–‡ไปถไธญ็š„ไธป่ง’ใ€‚ 4 - 5 - ๅ‘ฝไปค็›ธๅฝ“ไบŽ JSON ๆ–‡ไปถไธญ็š„ๅฏน่ฑกใ€‚ไบ‹ไปถ่ขซ่งฆๅ‘ๆ—ถ๏ผŒ`type` ๅญ—ๆฎตๅฐ†ๅ†ณๅฎšๅ“ชไบ›ๅ‘ฝไปค่ขซๆ‰ง่กŒใ€‚ 6 - 7 - `condition` ๅญ—ๆฎตๅฏไปฅๅบ”็”จไบŽๆ‰€ๆœ‰็ฑปๅž‹็š„ๅ‘ฝไปค๏ผŒๅฎƒ่ƒฝๅคŸๅœจๅฝ“ไธ‹ไบ‹ไปถ็š„ไธŠไธ‹ๆ–‡ไธญๆ‰ง่กŒใ€‚่ฟ™ไธ€ๅญ—ๆฎตไฝฟ็”จไบ†ๅŽŸ็‰ˆ็š„ๆกไปถ็ณป็ปŸ๏ผŒ่ฟ™ไธชๅทฅๅ…ท [misode.github.io](https://misode.github.io/predicate/) ๅฏไปฅๅธฎไฝ ๅฟซ้€Ÿๅˆ›ๅปบๆกไปถใ€‚ 8 - 9 - ้ƒจๅˆ†็ง็ฑป็š„ๅ‘ฝไปคๆœ‰้ขๅค–ๅฝขๅผๅ‚ๆ•ฐใ€‚ 10 - 11 - ไฝ ่ฟ˜ๅฏไปฅๅœจๆˆๅฐฑ็š„ๅฅ–ๅŠฑไธญไฝฟ็”จๆœฌๆจก็ป„็š„ๅ‘ฝไปค๏ผš 12 - 13 - ```json 14 - { 15 - "rewards": { 16 - "commander:commands": [ 17 - { 18 - "type": "commander:print", 19 - "text": "42" 20 - } 21 - ] 22 - } 23 - } 24 - ``` 25 - 26 - ๅ…ถไป–ๆจก็ป„ไนŸ่ƒฝๅฐ†ๅ‘ฝไปค้›†ๆˆๅˆฐๅ…ถไป–็š„ไธŠไธ‹ๆ–‡ไธญ๏ผ 27 - 28 - [[toc]] 29 - 30 - ## ้€‰ๆ‹ฉๅ™จ 31 - 32 - ไธ€ไบ›ๅ‘ฝไปค้œ€่ฆไฝ ๆŒ‡ๅฎšโ€œ้€‰ๆ‹ฉๅ™จโ€ใ€‚้€‰ๆ‹ฉๅ™จ็”จไบŽ้€‰ๆ‹ฉๆ‰ง่กŒ่€…๏ผŒๅฎƒๅฏไปฅๆ˜ฏไธ€ไธชไฝ็ฝฎ๏ผŒไนŸๅฏไปฅ๏ผˆๅฏ้€‰๏ผ‰ๆ˜ฏไธ€ไธชๅฎžไฝ“ใ€‚ไธ€ไบ›ไพ‹ๅค–้™คๅค–๏ผŒ้€‰ๆ‹ฉๅ™จๆจกไปฟไบ†ๅŽŸ็‰ˆๆˆ˜ๅˆฉๅ“็š„ไธŠไธ‹ๆ–‡ใ€‚ไฝ ๅฏไปฅๅœจ[ๅ†…็ฝฎ้€‰ๆ‹ฉๅ™จ](https://github.com/constellation-mc/commander/blob/main/src/main/java/me/melontini/commander/impl/builtin/BuiltInSelectors.java)้กต้ข๏ผŒไบ†่งฃๆ‰€ๆœ‰็š„ๅ†…็ฝฎ้€‰ๆ‹ฉๅ™จ๏ผšใ€‚ 33 - 34 - ## ๅ†…็ฝฎๅ‘ฝไปค 35 - ๆœฌๆจก็ป„ๅ†…็ฝฎไบ†ๅฐ‘้‡ๅ‘ฝไปค๏ผŒๅ› ไธบๆธธๆˆๅ†…ไบคไบ’ๅบ”่ฏฅไบค็ป™ `/` ๆ ทๅผ็š„ๅ‘ฝไปค๏ผŒๆˆ–่€…ๅ‡ฝๆ•ฐใ€‚ 36 - 37 - ### `commander:commands` 38 - ่ฟ™ๆ˜ฏไฝ ๅœจ่ฎข้˜…ๆ–‡ไปถไธญ๏ผŒไธป่ฆไผš็”จๅˆฐ็š„ๅ‘ฝไปค็ฑปๅž‹ใ€‚ 39 - 40 - ่ฟ™ไธ€็ง็ฑป้œ€่ฆๆœ‰ไธ€ไธช selector๏ผˆ้€‰ๆ‹ฉๅ™จ๏ผ‰๏ผŒไปฅๅŠไธ€็ณปๅˆ—ๅ‘ฝไปค๏ผˆๆˆ–ๅ‡ฝๆ•ฐ๏ผ‰ใ€‚ๆŽจ่ไฝฟ็”จๅ‡ฝๆ•ฐ๏ผŒๅ› ไธบๆธธๆˆ็š„ๅ‡ฝๆ•ฐๅŠ ่ฝฝๅ™จไผšๅœจ้‡่ฝฝๆ—ถๆ ก้ชŒๅฎƒไปฌใ€‚ 41 - 42 - ::: details ็คบไพ‹ 43 - ```json 44 - { 45 - "type": "commander:commands", 46 - "selector": "commander:random_player", 47 - "commands": [ 48 - "/cmd:explode ~ ~1 ~ 2" 49 - ] 50 - } 51 - ``` 52 - ::: 53 - 54 - #### ๅ‘ฝไปคๅฎ 55 - 56 - ๅฏนไบŽๆƒณ่ฆๅœจๅ‘ฝไปคไธญๆ’ๅ…ฅไธ€ไบ›ไธœ่ฅฟ็š„ไบบ๏ผŒๅ‘ฝไปคๅฎๆ˜ฏไธไบŒไน‹้€‰ใ€‚ 57 - 58 - ๅ‘ฝไปคๅฎๅฐฑๆ˜ฏ `${{}}`๏ผŒไธ€ไธช่ƒฝ่ฎฉไฝ ้€š่ฟ‡่กจ่พพๅผ๏ผŒๅŠจๆ€ๆ’ๅ…ฅๅ†…ๅฎน็š„ไปฃ็ ๅ—ใ€‚็คบไพ‹ๅฆ‚ไธ‹๏ผš 59 - ``` 60 - "/say ๆˆ‘็š„ๅๅญ—ๆ˜ฏ${{level.dimension.location}}๏ผ" 61 - ``` 62 - ่ฟ™ๆฎตๅ‘ฝไปคๅœจไธปไธ–็•Œไธญๆ‰ง่กŒ๏ผŒๅฐ†่พ“ๅ‡บ `ๆˆ‘็š„ๅๅญ—ๆ˜ฏminecraft:overworld`ใ€‚ 63 - 64 - ไฝ ๅฏไปฅๅœจ[่กจ่พพๅผ](Expressions)้กต้ขไธญ๏ผŒไบ†่งฃๆ›ดๅคšๅ…ณไบŽๅฎƒ็š„ไฟกๆฏใ€‚ 65 - 66 - ๅ‘ฝไปคๅฎ้ป˜่ฎค่ฟ”ๅ›žๅญ—็ฌฆไธฒ๏ผŒไฝ†ไฝ ๅฏไปฅๅƒ่ฟ™ๆ ทๅฐ†ๅฎƒ่ฝฌๅŒ–ไธบๅ…ถๅฎƒ็ฑปๅž‹๏ผš 67 - 68 - ``` 69 - $(long){{random(0, 34)}} 34.52946 -> 34 70 - $(double){{true}} true -> 1.0 71 - $(bool){{0}} 0 -> false 72 - ``` 73 - 74 - ### `commander:cancel` 75 - ่ฟ™็ง็ฑปๅž‹ๆฏ”่พƒ็‰นๅˆซ๏ผŒๅช่ƒฝๅœจ็‰นๅฎš็ฑปๅž‹็š„ไบ‹ไปถ้‡Œไฝฟ็”จใ€‚ 76 - 77 - ่ฟ™ไธ€็ฑปๅž‹ๅช่ฆๆฑ‚ๅ…ทๅค‡ๆœ‰ๅˆ้€‚่ฟ”ๅ›žๅ€ผ็š„ `value` ๅญ—ๆฎตใ€‚ 78 - 79 - ::: details ็คบไพ‹ 80 - ```json 81 - { 82 - "type": "commander:cancel", 83 - "value": false 84 - } 85 - ``` 86 - 87 - ```json 88 - { 89 - "type": "commander:cancel", 90 - "value": "fail" 91 - } 92 - ``` 93 - ::: 94 - 95 - ### `commander:all_of`, `commander:any_of`, `commander:defaulted` 96 - ่ฟ™ไธ‰็ง็ฑปๅž‹็š„ๅŠŸ่ƒฝ็›ธไผผใ€‚ๅฆ‚ๆžœ condition๏ผˆๆกไปถ๏ผ‰่ฟ”ๅ›ž true๏ผŒๅฐฑๆ‰ง่กŒๅœจๅฏ้€‰็š„ `then` ไปฃ็ ๅ—ไธญ็š„ๅ‘ฝไปคใ€‚ 97 - 98 - `all_of` ่ฆๆฑ‚ๆ‰€ๆœ‰ๅ‘ฝไปคๆˆๅŠŸๆ‰ง่กŒ๏ผŒ`any_of` ่ฆๆฑ‚ๅ…ถไธญไปปไธ€ๅ‘ฝไปคๆˆๅŠŸๆ‰ง่กŒ๏ผŒ`defaulted` ่ฆๆฑ‚ๅ…ถไธญไปปไธ€ๅ‘ฝไปคๆ‰ง่กŒๅคฑ่ดฅใ€‚ 99 - 100 - ๆณจๆ„๏ผŒๅฐฑ็ฎ—ๆกไปถๅœจๅพˆๅ‰้ขๅฐฑ่ฟ”ๅ›ž true ไบ†๏ผŒ่ฟ˜ๆ˜ฏไผšๆ‰ง่กŒๅ…จ้ƒจๅ‘ฝไปค๏ผไฝ†ๆ˜ฏ๏ผŒๅœจๅฏ็”จ `short_circuit` ๅŽ๏ผŒๆกไปถไธๆปก่ถณๆ—ถ๏ผŒๅฐ†็ซ‹ๅณ้€€ๅ‡บๆ‰ง่กŒใ€‚ 101 - 102 - ::: details ็คบไพ‹ 103 - ```json 104 - { 105 - "event": "commander:player_use/item", 106 - "commands": [ 107 - { 108 - "type": "commander:all_of", 109 - "condition": { 110 - "condition": "minecraft:match_tool", 111 - "predicate": { 112 - "items": [ 113 - "minecraft:diamond" 114 - ] 115 - } 116 - }, 117 - "commands": [ 118 - { 119 - "type": "commander:commands", 120 - "selector": "this_entity", 121 - "commands": [ 122 - "/say ๆ˜ฏ้’ป็Ÿณ..." 123 - ] 124 - }, 125 - { 126 - "type": "commander:cancel", 127 - "value": "success" 128 - } 129 - ] 130 - } 131 - ] 132 - } 133 - ``` 134 - ::: 135 - 136 - ### `commander:random` 137 - ่ฟ™ไธ€็ฑปๅž‹ๅฐ†ๆŒ‰ๆƒ้‡้šๆœบๆ‰ง่กŒๅ…ถไธญ็š„ๅ‘ฝไปคใ€‚้ป˜่ฎคๆ‰ง่กŒไธ€่ฝฎใ€‚ไฝ ๅฏไปฅๅœจ้žๅฟ…่ฆ็š„ `rolls` ๅญ—ๆฎตไธญๆŒ‡ๅฎš่ฝฎๆฌก๏ผˆๆ”ฏๆŒ[่กจ่พพๅผ](Expressions)๏ผ‰ใ€‚ 138 - ::: details ็คบไพ‹ 139 - ```json 140 - { 141 - "type": "commander:random", 142 - "rolls": 2, 143 - "commands": [ 144 - { 145 - "weight": 2, 146 - "data": { 147 - "type": "commander:commands", 148 - "selector": "this_entity", 149 - "commands": [ 150 - "/say weight 2" 151 - ] 152 - } 153 - }, 154 - { 155 - "weight": 6, 156 - "data": { 157 - "type": "commander:commands", 158 - "selector": "this_entity", 159 - "commands": [ 160 - "/say weight 6" 161 - ] 162 - } 163 - } 164 - ] 165 - } 166 - ``` 167 - :::
-68
docs/zh-cn/Events.md
··· 1 - # ไบ‹ไปถ 2 - 3 - ไบ‹ไปถๆ˜ฏๆธธๆˆๅ†…๏ผŒ่งฆๅ‘[ๅ‘ฝไปค](Commands)็š„่Š‚็‚นใ€‚ 4 - 5 - ## ่ฎข้˜…ๆ–‡ไปถ็š„ๅผ•ๅ…ฅ 6 - 7 - ๅœจ่ฎข้˜…ๆ–‡ไปถไธญๅฃฐๆ˜Žไธ€ไธชไบ‹ไปถ๏ผŒๆ„ๅ‘ณ็€ๆ ‡่ฏ†ไฝ ๆƒณ่ฆ็›‘ๅฌ็š„ไบ‹ไปถใ€‚ไบ‹ไปถๅฏไปฅๅœจ `parameters` ไปฃ็ ๅ—ไธญๆŽฅๅ—้ขๅค–็š„ๅฝขๅผๅ‚ๆ•ฐ๏ผŒไฝ†๏ผˆ็›ฎๅ‰๏ผ‰ๅ†…็ฝฎไบ‹ไปถๅนถไธ่ฆๆฑ‚ๅ…ทๅค‡ๅฎƒไปฌใ€‚่ฎข้˜…ๆ–‡ไปถๅฐ†ๅœจ `commander/events` ไธ‹่ขซ่ฏปๅ–ใ€‚ 8 - 9 - ``` 10 - |- recipes 11 - |- commander 12 - |- events 13 - |- test_event.json 14 - |- folder 15 - |- nested.json 16 - |- tags 17 - ``` 18 - 19 - ๅฃฐๆ˜Žไบ‹ไปถ็š„ๅ…ธๅž‹ไพ‹ๅญๅฆ‚ไธ‹๏ผš 20 - 21 - ::: details ็คบไพ‹ 22 - ```json 23 - { 24 - "event": "commander:after_killed_by_other" 25 - "commands": [ 26 - 27 - ] 28 - } 29 - ``` 30 - <br/> 31 - 32 - ```json 33 - { 34 - "event": "modid:custom_event", 35 - "parameters": { 36 - }, 37 - "commands": [ 38 - 39 - ] 40 - } 41 - ``` 42 - ::: 43 - 44 - ่ฎข้˜…ๆ–‡ไปถๅฏไปฅ็›‘ๅฌไธๅŒ็š„๏ผˆๆˆ–่€…ไธ€ๆ ท็š„๏ผ‰ไบ‹ไปถใ€‚ 45 - 46 - ::: details ็คบไพ‹ 47 - ```json 48 - { 49 - "events": [ 50 - { 51 - "event": "commander:after_killed_by_other" 52 - }, 53 - { 54 - "event": "commander:allow_damage" 55 - }, 56 - { 57 - "event": "commander:player_attack/block" 58 - } 59 - ] 60 - } 61 - ``` 62 - ::: 63 - 64 - ## ๅ†…็ฝฎไบ‹ไปถ 65 - 66 - ๅœจ `commander` ๅ‘ฝๅ็ฉบ้—ดไธ‹๏ผŒๆœฌๆจก็ป„ๆถต็›–ไบ†็ปๅคง้ƒจๅˆ†ๅ…ผๅฎน็š„ fabric ไบ‹ไปถใ€‚่ฟ™้‡Œ็š„่ฟ”ๅ›žๆŒ‡[ๅ–ๆถˆ็ฑปๅ‘ฝไปค](Commands#commandercancel)ใ€‚ 67 - 68 - ๅฝ“ๅ‰ๅฏ็”จ็š„ไบ‹ไปถ่ฏท่ง๏ผš[ๅฎžไฝ“ไบ‹ไปถ](https://github.com/constellation-mc/commander/blob/main/src/main/java/me/melontini/commander/impl/builtin/events/EntityEvents.java)๏ผŒ[็Žฉๅฎถไบ‹ไปถ](https://github.com/constellation-mc/commander/blob/main/src/main/java/me/melontini/commander/impl/builtin/events/PlayerEvents.java)๏ผŒ[ๆœๅŠกๅ™จ็”Ÿๅ‘ฝๅ‘จๆœŸ](https://github.com/constellation-mc/commander/blob/main/src/main/java/me/melontini/commander/impl/builtin/events/ServerLifecycle.java)๏ผŒ[ๆœๅŠกๅ™จๅˆป](https://github.com/constellation-mc/commander/blob/main/src/main/java/me/melontini/commander/impl/builtin/events/ServerTick.java)ใ€‚
-218
docs/zh-cn/Expressions.md
··· 1 - # ่กจ่พพๅผ 2 - 3 - ๅ‘ฝไปคๅฎ˜ๆจก็ป„ๅ‡ญๅ€ŸๅŠจๆ€ๅฎžไฝ“ๆ•ฐๆฎ๏ผŒๅฎž็Žฐไบ†่ฟ็ฎ—่กจ่พพๅผ็š„ๅŠŸ่ƒฝใ€‚ 4 - 5 - ็ฎ—ๆœฏไฝฟ็”จ [EvalEx](https://ezylang.github.io/EvalEx/) ๆฅๅฎž็Žฐ่ฟ็ฎ—ๅŠŸ่ƒฝใ€‚ๆŽŒๆกๅฎƒๅนถ้žๅฟ…่ฆ๏ผŒไฝ†ๅคšๆœ‰ๅฅฝๅค„ใ€‚่ฟ™้‡Œ็š„ๅŒบๅˆซๆ˜ฏ๏ผŒๆ‰€ๆœ‰ๅ‡ฝๆ•ฐใ€ๅ˜้‡ใ€ๅธธ้‡้ƒฝๅฏนๅคงๅฐๅ†™ๆœ‰ไธฅๆ ผ็š„ๅ‡†็กฎๅบฆ่ฆๆฑ‚๏ผŒๅนถไธ”ๆ‰€ๆœ‰ๅ‡ฝๆ•ฐๅ้ƒฝๆ˜ฏ้ฉผๅณฐๅผๅคงๅฐๅ†™๏ผˆ้™ค็ฌฌไธ€ไธช่ฏ่ฏญ๏ผŒๅ…ถๅฎƒ้ฆ–ๅญ—ๆฏๅคงๅ†™๏ผŒไธๅŠ ๆ ‡็‚น๏ผ‰๏ผŒ่€Œไธๆ˜ฏ่›‡ๅผ๏ผˆๅ…จๅคงๅ†™๏ผŒไธ”็”จ_ๅˆ’ๅˆ†๏ผ‰ใ€‚ 6 - 7 - ## ้ขๅค–ๅŠŸ่ƒฝไธŽ่ฟ็ฎ—็ฌฆ 8 - 9 - ๆœฌๆจก็ป„ๅœจ EvalEx ็š„ๅŸบ็ก€ไธŠ๏ผŒๆทปๅŠ ไบ†ไธ€ไบ›้ขๅค–ๅŠŸ่ƒฝใ€‚ 10 - 11 - ่ฟ™้‡Œ็š„โ€œๅŒฟๅๅ‡ฝๆ•ฐโ€ๆŒ‡ๅธฆๆœ‰ `it` ๅฝขๅผๅ‚ๆ•ฐ็š„ไธ€่ˆฌ่กจ่พพๅผใ€‚ๆฏ”ๅฆ‚๏ผš`arrayFind(arrayOf(0, 1, 2), it == 1)`ใ€‚ๅŒฟๅๅ‡ฝๆ•ฐ็š„ๅฝขๅผๅ‚ๆ•ฐ้ƒฝๆœ‰ `ฮป` ไฝœไธบๆ ‡่ฎฐใ€‚ 12 - 13 - ๅฏๅ˜ๅฎž้™…ๅ‚ๆ•ฐ๏ผˆVarArgs๏ผ‰้ƒฝๆœ‰ `...` ๆ ‡่ฎฐ๏ผˆไฝ ๅฏไปฅๆ— ้™ๆŒ‡ๅฎšๅฎž้™…ๅ‚ๆ•ฐ๏ผ‰ 14 - 15 - ::: details ่ฎก็ฎ—็›ธๅ…ณ 16 - 17 - | ๅ‡ฝๆ•ฐ | ๆ่ฟฐ | ๅฎžๅ‚ | ็คบไพ‹ | 18 - |---|---|---|---| 19 - | `random` | ๅœจๆŒ‡ๅฎš่Œƒๅ›ดๅ†…็”Ÿๆˆ้šๆœบๆ•ฐใ€‚ | `min`, `max` | `random(0, 23)` | 20 - | `clamp` | ๅฐ†ๅ…ถไป–ไธคไธชๅฎž้™…ๅ‚ๆ•ฐ็บฆๆŸๅœจ่Œƒๅ›ดๅ†…ใ€‚ | `value`, `min`, `max` | `clamp(12, 14, 16)` | 21 - | `lerp` | ๅนณๆป‘ๅœฐๅฐ†ๅˆๅ€ผ่ฟ‡ๆธกๅˆฐๆœซๅ€ผใ€‚ | `delta`, `start`, `end` | `lerp(0.5, 10, 16)` | 22 - 23 - ::: 24 - 25 - ::: details ๆ•ฐ็ป„ 26 - 27 - ๆ‰€ๆœ‰็š„ๅ‡ฝๆ•ฐ้ƒฝไผšๆž„ๅปบๆ–ฐ็š„ๆ•ฐ็ป„๏ผŒไธๅฏนๅŽŸๆœฌ็š„้€ ๆˆๅฝฑๅ“ใ€‚ 28 - 29 - | ๅ‡ฝๆ•ฐ | ๆ่ฟฐ | ๅฎžๅ‚ | ็คบไพ‹ | 30 - |---|---|---|---| 31 - | `arrayOf` | ๆŒ‡ๅฎšๅฏน่ฑกๆž„ๅปบๆ•ฐ็ป„ใ€‚ | `args...` | `arrayOf(0, 23)` | 32 - | `arrayMap` | ๅฏนๆ•ฐ็ป„ไธญ็š„ๆ‰€ๆœ‰ๅฏน่ฑกๅบ”็”จๆ›ดๆ”นใ€‚ | `array`, `function(ฮป)` | `arrayMap(arrayOf(0,1,2), sqrt(it))` | 33 - | `arrayFind` | ่ฟ‡ๆปคๆމๆ‰€ๆœ‰ไธ็ฌฆๅˆๆกไปถ็š„ๅฏน่ฑกใ€‚ | `array`, `predicate(ฮป)` | `arrayFind(arrayOf(0,1,2), it == 1)` | 34 - | `arrayAnyMatch` | ๆฃ€ๆŸฅๆ˜ฏๅฆๆ•ฐ็ป„ไธญๅญ˜ๅœจ็ฌฆๅˆๆกไปถ็š„ๅฏน่ฑกใ€‚ | `array`, `predicate(ฮป)` | `arrayAnyMatch(arrayOf(0,1,2), it == 1)` | 35 - | `arrayNoneMatch` | ๆฃ€ๆŸฅๆ˜ฏๅฆๆ•ฐ็ป„ไธญๅฎŒๅ…จๆฒกๆœ‰็ฌฆๅˆๆกไปถ็š„ๅฏน่ฑกใ€‚ | `array`, `predicate(ฮป)` | `arrayNoneMatch(arrayOf(0,1,2), it == 1)` | 36 - | `arrayAllMatch` | ๆฃ€ๆŸฅๆ˜ฏๅฆๆ•ฐ็ป„ไธญ็š„ๅ…จ้ƒจๅฏน่ฑก็ฌฆๅˆๆกไปถใ€‚ | `array`, `predicate(ฮป)` | `arrayAllMatch(arrayOf(0,1,2), it == 1)` | 37 - | `arrayFindFirst` | ่ฟ”ๅ›žๆ•ฐ็ป„ไธญ็š„็ฌฌไธ€ไธชๅฏน่ฑก๏ผŒ่‹ฅไธบ็ฉบๆ•ฐ็ป„๏ผŒ่ฟ”ๅ›ž `null`ใ€‚ | `array` | `arrayFindFirst(arrayOf(0, 23))` | 38 - 39 - ::: 40 - 41 - ::: details ๆณจๅ†Œ่กจ 42 - 43 - | ๅ‡ฝๆ•ฐ | ๆ่ฟฐ | ๅฎžๅ‚ | ็คบไพ‹ | 44 - |---|---|---|---| 45 - | `Registry` | ่ฟ”ๅ›žไธ€ไธช้™ๆ€ๅ†…ๅฎนๆณจๅ†Œ่กจใ€‚ | `identifier` | `Registry('entity_type')` | 46 - | `Item` | ่ฟ”ๅ›žๆณจๅ†Œ่กจไธญ็š„ไธ€ไธช็‰ฉๅ“ใ€‚ | `identifier` | `Item('diamond')` | 47 - | `Block` | ่ฟ”ๅ›žๆณจๅ†Œ่กจไธญ็š„ไธ€ไธชๆ–นๅ—ใ€‚ | `identifier` | `Block('chest')` | 48 - | `DynamicRegistry` | ่ฟ”ๅ›žไธ€ไธชๅŠจๆ€ๅ†…ๅฎนๆณจๅ†Œ่กจใ€‚ | `identifier` | `DynamicRegistry('worldgen/biome')` | 49 - | `Biome` | ่ฟ”ๅ›žๆณจๅ†Œ่กจไธญ็š„ไธ€ไธช็พค็ณปใ€‚ | `identifier` | `Biome('the_void')` | 50 - | `DimensionType` | ่ฟ”ๅ›žๆณจๅ†Œ่กจไธญ็š„ไธ€ไธช็ปดๅบฆใ€‚ | `identifier` | `DimensionType('overworld')` | 51 - 52 - ::: 53 - 54 - ::: details ๆ‚้กน 55 - 56 - | ๅ‡ฝๆ•ฐ | ๆ่ฟฐ | ๅฎžๅ‚ | ็คบไพ‹ | 57 - |---|---|---|---| 58 - | `structContainsKey` | ๆฃ€ๆŸฅๆ˜ฏๅฆ็ป“ๆž„ไธญๅŒ…ๅซๆŒ‡ๅฎš้”ฎๅ€ผใ€‚ | `struct`, `key...` | `structContainsKey(block_state.properties, 'candles')` | 59 - | `hasContext` | ๆฃ€ๆŸฅๆ˜ฏๅฆ่กจ่พพๅผ็š„ๅฝขๅผๅ‚ๆ•ฐๅฏ็”จใ€‚ | `key...` | `hasContext('tool')` | 60 - | `length` | ่ฟ”ๅ›žๆŒ‡ๅฎšๅฏน่ฑก็š„้•ฟๅบฆๆˆ– 0ใ€‚ | `value` | `length('Hello World!')` | 61 - | `strFormat` | ๅฐ†ๅญ—็ฌฆไธฒ่ฝฌๅŒ–ไธบๆŒ‡ๅฎšๆ ผๅผใ€‚ | `pattern`, `args...` | `strFormat('Hello %s World!', 23)` | 62 - | `ifMatches` | ็ฑปไผผไบŽๅ†…็ฝฎ็š„ `if`๏ผŒไฝ†ไป‹ๅ…ฅๅŒฟๅๅ‡ฝๆ•ฐใ€‚ | `value`, `predicate(ฮป)`, `ifTrue(ฮป)`, `ifFalse(ฮป)` | `ifMatches(arrayFind(arrayOf(0,1,2), it == 1), length(it) > 0, it[0], 0)` | 63 - | `chain` | ็”จๅŒฟๅๅ‡ฝๆ•ฐไธฒ่ฟžๅคšไธชๅ‡ฝๆ•ฐ๏ผŒ`it` ๆ˜ฏๆœ€็ปˆ็ป“ๆžœใ€‚ | `value`, `actions(ฮป)...` | `chain(23, it + 3, sqrt(it))` | 64 - 65 - ::: 66 - 67 - ::: details ่ฟ็ฎ—็ฌฆ 68 - 69 - | ๅ‡ฝๆ•ฐ | ๆ่ฟฐ | ๅฎžๅ‚ | ็คบไพ‹ | 70 - |---|---|---|---| 71 - | `?.` | ๅฐ่ฏ•ไปŽ็ป“ๆž„ไธญ่Žทๅ–ๅญ—ๆฎต๏ผŒๆˆ–่ฟ”ๅ›ž `null`ใ€‚ | `struct`, `key` | `struct?.field` | 72 - | `?` | ๅฆ‚ๆžœ `a` ไธบ `null`๏ผŒ่ฟ”ๅ›ž `b`ใ€‚ | `a`, `b` | `struct?.y ? 5` | 73 - 74 - ::: 75 - 76 - ## ่ฏปๅ–ไธŠไธ‹ๆ–‡ๆ•ฐๆฎ 77 - 78 - ๅ› ไธบ็ฎ—ๆœฏ็š„ไธŠไธ‹ๆ–‡ไธบๅŽŸ็‰ˆ็š„ๆˆ˜ๅˆฉๅ“็ณป็ปŸ๏ผŒไฝ ๅฏไปฅ้€š่ฟ‡ไธ€็งๅซ"ๆๅ–"็š„็‰นๆฎŠๅฅๆณ•ๆฅ่ฏปๅ–ๆบๆ•ฐๆฎใ€‚ 79 - ``` 80 - ๆ ‡่ฏ†็ฌฆ.ๅญ—ๆฎต 81 - ๆ ‡่ฏ†็ฌฆ.ๆ–นๆณ• 82 - ๆ ‡่ฏ†็ฌฆ.ๆ–นๆณ•.ๅญ—ๆฎต.ๆ–นๆณ• 83 - ``` 84 - ๆณจๆ„๏ผŒไฝ ็š„็”จๅˆฐ็š„ๅญ—ๆฎตไธŽไธŠไธ‹ๆ–‡**ๅฟ…้กป**ๅญ˜ๅœจ๏ผŒไธ็„ถ่กจ่พพๅผๆ˜ฏๆ— ๆ•ˆ็š„ใ€‚ 85 - 86 - ็คบไพ‹๏ผš 87 - ``` 88 - minecraft:this_entity.getX 89 - this_entity.getHealth 90 - minecraft:this_entity.isInWaterOrRain 91 - this_entity.blockPosition.getX 92 - origin.x 93 - origin.reverse.y 94 - minecraft:level.getDayTime 95 - ``` 96 - 97 - ### ๅ‘ฝไปคๅฎ˜ๆ‹“ๅฑ•๏ผš 98 - 99 - ไธบๆ‹“ๅฑ•็”จ้€”๏ผŒๆœฌๆจก็ป„ไธบๅฏน่ฑกๆ–ฐๅขžไบ†ไธ€ไบ›็‰นๅˆซ็š„ๅญ—ๆฎตใ€‚ 100 - 101 - ::: details `nbt` ๏ผˆ็‰ฉๅ“๏ผŒๅฎžไฝ“๏ผŒๆ–นๅ—ๅฎžไฝ“๏ผ‰ 102 - 103 - ๅฏ็”จไบŽ็‰ฉๅ“๏ผŒๅฎžไฝ“ๅ’Œๆ–นๅ—ๅฎžไฝ“ใ€‚ไฝ ่ƒฝๅคŸ็”จๅฎƒ่ฏปๅ–ๅฏน่ฑก็š„ NBT ๆ•ฐๆฎใ€‚ๅฐฝ็ฎกไพฟๆท๏ผŒ่ฟ™ไธชๆฃ€ๆŸฅ็š„ๆ€ง่ƒฝไธๅฐฝไบบๆ„๏ผŒ้ข‘็น่ฐƒ็”จ๏ผˆๆฏ”ๅฆ‚ๆฏๅˆป๏ผ‰ๅฏ่ƒฝไผšๅฏผ่‡ดๆธธๆˆๅก้กฟใ€‚ 104 - 105 - ็คบไพ‹๏ผš`this_entity.nbt.Air` 106 - 107 - ::: 108 - 109 - ::: details `properties`๏ผˆๆ–นๅ—็Šถๆ€๏ผ‰ 110 - 111 - ๅฏ็”จไบŽ็Šถๆ€๏ผŒๆฏ”ๅฆ‚ๆ–นๅ—็Šถๆ€ใ€‚ 112 - 113 - ็คบไพ‹๏ผš`block_state.properties.candles` 114 - 115 - ::: 116 - 117 - ::: details `attributes`๏ผˆๆดปไฝ“๏ผ‰ 118 - 119 - ไฝ ๅฏไปฅ็”จๅฎƒ่Žทๅ–ๆดปไฝ“็š„ๆ•ฐๆฎใ€‚่ฟ™ไธช้”ฎๅ€ผๆ˜ฏๆ ‡่ฏ†็ฌฆ๏ผŒๆ‰€ไปฅๅฏไปฅๅ†™ `generic.luck`๏ผŒไนŸๅฏไปฅๅ†™ `minecraft:generic.luck`ใ€‚ 120 - 121 - ็คบไพ‹๏ผš`this_entity.attributes.'generic.luck'` 122 - 123 - ::: 124 - 125 - ::: details `storage`๏ผˆๅญ˜ๆกฃ๏ผŒๅŒบๅ—๏ผŒๅฎžไฝ“๏ผŒๆ–นๅ—ๅฎžไฝ“๏ผ‰ 126 - 127 - ไฝ ๅฏไปฅ็”จๅฎƒ่Žทๅ–ๆŒไน…ๅž‹ๆ•ฐๆฎใ€‚่ฟ™ไธ€ๆ•ฐๆฎๅฏไปฅ้€š่ฟ‡ [`/cmd:data`](/zh-cn/BrigadierCommands#cmd-data) ๆฅ่ฏปๅ–ๆˆ–ไฟฎๆ”นใ€‚ 128 - 129 - ::: 130 - 131 - ### P.S. 132 - 133 - ๅฆ‚ๆžœไฝ ไธๅพ—ไธๅœจ่กจ่พพๅผไธญ่ฟ›่กŒๅคๆ‚็š„ๆฃ€ๆŸฅ๏ผŒไฝ ๅฏไปฅ่€ƒ่™‘ๅปถ่ฟŸๆ‰ง่กŒๅฎƒ๏ผŒๅฆ‚ๆžœๅฏไปฅ๏ผŒไฝ ่ฟ˜ๅฏไปฅ้€š่ฟ‡ `level.getDayTime % 20 == 0` ไธบ่กจ่พพๅผๆทปๅŠ  1 ็ง’็š„โ€œๅ†ทๅดโ€ใ€‚ๅŒ็†๏ผŒ`% 40` ๅฏนๅบ” 2 ็ง’๏ผŒ`% 10` ๅฏนๅบ” 0.5 ็ง’ใ€‚ 134 - 135 - ่กจ่พพๅผไฝฟ็”จ็š„ๆ˜ฏ Mojang ็š„ๆ˜ ๅฐ„๏ผˆ้ฆ–ๆฌก่ฝฝๅ…ฅๆ—ถไธ‹่ฝฝ๏ผ‰๏ผŒ่ฟ™ๆ„ๅ‘ณ็€ๅ‡ ไนŽๆ‰€ๆœ‰ๅ…ฌๅ…ฑ็š„ๅญ—ๆฎตๅ’Œ get ็ฑปๅž‹็š„ๆ–นๆณ•้ƒฝๅฏไปฅๅœจ่กจ่พพๅผ้‡Œไฝฟ็”จใ€‚ๅฆ‚ๆžœๆœฌๆจก็ป„ๆ— ๆณ•่ฎพ็ฝฎๆ˜ ๅฐ„๏ผŒไฝ ๅฏ่ƒฝ้œ€่ฆไพ่ต–ไบŽๅนณๅฐ็š„ๅ็งฐ๏ผˆๆฏ”ๅฆ‚ Fabric ็š„ไธญ้—ดๆ˜ ๅฐ„๏ผ‰ใ€‚ 136 - 137 - ::: details ไธ€ไบ›ไพ‹ๅญ 138 - 139 - ่ฟ™ๆ˜ฏๅŽŸ็‰ˆ็Ÿฟ่ฝฆ้€Ÿๅบฆ็š„่ฎก็ฎ—ๅ…ฌๅผ๏ผš 140 - 141 - `if(this_entity.isInWater, 4, 8) / 20`, and furnace: `if(this_entity.isInWater, 3, 4) / 20` 142 - 143 - *** 144 - 145 - ่ฟ™ไธช่กจ่พพๅผ่ƒฝๅคŸๅฐ†ๆธธๆˆๆ—ถ้—ด่ฝฌๅŒ–ไธบ็Žฐๅฎžๆ—ถ้—ดใ€‚https://bukkit.org/threads/how-can-i-convert-minecraft-long-time-to-real-hours-and-minutes.122912/ 146 - 147 - `strFormat('%02.0f:%02.0f', floor((level.getDayTime / 1000 + 8) % 24), floor(60 * (level.getDayTime % 1000) / 1000))` 148 - 149 - ๆ‰€ไปฅๅฎƒ็š„็ป“ๆžœไผšๆ˜ฏ๏ผš`00:00`๏ผŒๆˆ– `13:45` ่ฟ™ๆ ท็š„ใ€‚ 150 - 151 - ::: 152 - 153 - ## ไฝฟ็”จ่กจ่พพๅผ 154 - 155 - ้€š่ฟ‡ไฝฟ็”จ `cmd:arithmetica` ๅ‘ฝไปค๏ผŒไฝ ๅฏไปฅๅฟซ้€ŸไธŠๆ‰‹ใ€‚่ฎฐๅพ—็”จ `"` ๆ‹ฌไฝ่กจ่พพๅผๆฅๆปก่ถณๆ ผๅผ่ฆๆฑ‚ใ€‚ 156 - 157 - ๅ…ถไป–ไฝฟ็”จๆƒ…ๅขƒ่ง๏ผš 158 - 159 - ::: details JSON ๅ‘ฝไปคๅฎ 160 - 161 - ่ฏฆๆƒ…่งๅ‘ฝไปค้กต้ขไธญ๏ผŒๅ‘ฝไปคๅฎ็š„็›ธๅ…ณๅ†…ๅฎนใ€‚[้“พๆŽฅ](/zh-cn/Commands#command-macros) 162 - 163 - ::: 164 - 165 - ::: details ๆˆ˜ๅˆฉๅ“ๆ•ฐๅญ—ๆไพ›ๅ™จ 166 - 167 - ๅœจๆกไปถไธญไฝฟ็”จ `commander:arithmetica` ๆไพ›ๅ™จ๏ผš 168 - 169 - ```json 170 - { 171 - "condition": "minecraft:value_check", 172 - "value": { 173 - "type": "commander:arithmetica", 174 - "value": "round(random(5, 15))" 175 - }, 176 - "range": { 177 - "min": 12.3, 178 - "max": 32 179 - } 180 - } 181 - ``` 182 - 183 - ::: 184 - 185 - ::: details ๆˆ˜ๅˆฉๅ“ๆกไปถ 186 - 187 - ไฝฟ็”จ `commander:expression` ๆกไปถ๏ผš 188 - 189 - ```json 190 - { 191 - "condition": "commander:expression", 192 - "value": "level.isDay" 193 - } 194 - ``` 195 - 196 - ::: 197 - 198 - ::: details `execute` ๅ‘ฝไปค 199 - 200 - ไฝ ๅฏไปฅๅฐ†่กจ่พพๅผไฝœไธบ `execute if` ๅ‘ฝไปค็š„ๆกไปถใ€‚ 201 - 202 - ``` 203 - /execute if cmd:expression "level.isDay" run say ็Žฐๅœจๆ˜ฏ็™ฝๅคฉ๏ผ 204 - ``` 205 - 206 - ::: 207 - 208 - ::: details `scoreboard players` ๅ‘ฝไปค 209 - 210 - ไฝ ่ƒฝๅคŸไปฅ่กจ่พพๅผไธบไฟฎ้ฅฐ๏ผŒไฝœไธบ็Žฉๅฎถ่ฐ“่ฏใ€‚ไฝ ๅฏไปฅ้€š่ฟ‡ `score` ๅ˜้‡่Žทๅ–ๅฝ“ๅ‰ๅˆ†ๆ•ฐใ€‚ 211 - 212 - ๅฝ“ๅ‰็‰ˆๆœฌ่ฟ˜ๆฒกๆœ‰ๆไพ›ๅฎžไฝ“ไธŠไธ‹ๆ–‡ใ€‚ 213 - 214 - ``` 215 - /scoreboard players cmd:operate @s test_objective "(score * 1.5) + level.storage.my_score_modifier" 216 - ``` 217 - 218 - :::
-58
docs/zh-cn/develop/Commands.md
··· 1 - # ๅ‘ฝไปค 2 - 3 - ๅคงๅคšๆƒ…ๅ†ตไธ‹๏ผŒไฝ ๅบ”่ฏฅไฝฟ็”จ `/` ็ฑปๅž‹็š„ๅ‘ฝไปค๏ผŒไฝ†ๆœ‰ๆ—ถ๏ผŒๆˆ‘ไปฌไผš้œ€่ฆไธ€ไบ›้ขๅค–็š„ๅ‘ๆŒฅ็ฉบ้—ดใ€‚ 4 - 5 - ## ๅˆ›ๅปบๅ‘ฝไปค 6 - 7 - ๅˆ›ๅปบๆ–ฐ็š„ๅ‘ฝไปคๅพˆ็ฎ€ๅ•๏ผŒไฝ ่ฆๅš็š„ๆ˜ฏ๏ผšๅฎž็Žฐ `Command` ๆŽฅๅฃ๏ผŒ้€š่ฟ‡ๅˆ›ๅปบไธ€ไธช[ๆ˜ ๅฐ„็ผ–่งฃ็ ๅ™จ](https://forge.gemwire.uk/wiki/Codecs)ๆฅๅบๅˆ—ๅŒ–ๆˆ–ๅๅบๅˆ—ๅŒ–ๅ‘ฝไปค๏ผŒ็„ถๅŽ็”จ `CommandTypes.register()` ๆฅๆณจๅ†Œ็ผ–่งฃ็ ๅ™จใ€‚ 8 - 9 - ่ฎฉๆˆ‘ไปฌๅˆ›ๅปบไธ€ไธช่ƒฝๅคŸๅฐ†ๅญ—็ฌฆไธฒ่ฝฌๅŒ–ไธบๆ ‡ๅ‡†่พ“ๅ‡บ็š„ๅ‘ฝไปคใ€‚ 10 - 11 - ```java 12 - public record DummyCommand(String text) implements Command { 13 - 14 - @Override 15 - public boolean execute(EventContext context) { 16 - System.out.println(text()); 17 - return true; //ๆ‰ง่กŒๆˆๅŠŸๅˆ™่ฟ”ๅ›ž 18 - } 19 - 20 - @Override 21 - public CommandType type() { 22 - return null; 23 - } 24 - } 25 - ``` 26 - 27 - ๆŽฅไธ‹ๆฅ๏ผŒๆˆ‘ไปฌๅ†ไธบ่ฟ™ไธชๅ‘ฝไปค[็ผ–่งฃ็ ๅ™จ](https://forge.gemwire.uk/wiki/Codecs)ๅˆ›ๅปบไธ€ไธช็ผ–่งฃ็ ๅ™จใ€‚ 28 - 29 - ```java 30 - public static final MapCodec<DummyCommand> CODEC = Codec.STRING.fieldOf("text").xmap(DummyCommand::new, DummyCommand::text); 31 - ``` 32 - 33 - ๅ†็„ถๅŽ๏ผŒๆˆ‘ไปฌ้œ€่ฆๆณจๅ†Œ่ฟ™ไธ€ๅ‘ฝไปคๆฅ่Žทๅ– `CommandType`ใ€‚ 34 - 35 - ```java 36 - public static final CommandType DUMMY = CommandType.register(new Identifier("modid", "print"), DummyCommand.CODEC); 37 - ``` 38 - 39 - ๆœ€ๅŽ๏ผŒๅœจ `type()` ่ฟ”ๅ›ž่ฟ™ไธช็ฑปๅž‹ใ€‚ 40 - 41 - ```java 42 - @Override 43 - public CommandType type() { 44 - return MyModInit.DUMMY; 45 - } 46 - ``` 47 - 48 - ## ไบ‹ไปถ็š„ไธŠไธ‹ๆ–‡ 49 - 50 - ไฝ ๅฏไปฅ้€š่ฟ‡ไบ‹ไปถ็š„ไธŠไธ‹ๆ–‡๏ผŒๆฃ€็ดขไธŽไบ‹ไปถ็ฑปๅž‹ไธ€่ตทไผ ้€’็š„ `LootContext`ใ€‚ 51 - 52 - ```java 53 - context.lootContext().getWorld(); //่ฟ”ๅ›žๆœๅŠก็ซฏไธ–็•Œ 54 - 55 - context.lootContext().get(LootContextParameters.TOOL); //ๅฆ‚ๆžœไธๅญ˜ๅœจ๏ผŒ่ฟ”ๅ›žๅฝขๅผๅ‚ๆ•ฐๆˆ–็ฉบๅ€ผใ€‚ 56 - 57 - context.lootContext().requireParameter(LootContextParameters.TOOL); //ๅฆ‚ๆžœไธๅญ˜ๅœจ๏ผŒ่ฟ”ๅ›žๅฝขๅผๅ‚ๆ•ฐๆˆ–ๆŠ›ๅ‡บๅผ‚ๅธธใ€‚ 58 - ```
-69
docs/zh-cn/develop/Events.md
··· 1 - # ไบ‹ไปถ 2 - 3 - ๆœฌๆจก็ป„ๅผ•ๅ…ฅไบ†ๅ…จๆ–ฐ็š„ๆ•ฐๆฎๅŒ…ไบ‹ไปถ็ณป็ปŸ๏ผŒไฝฟๆ•ฐๆฎๅŒ…่ƒฝๅคŸๅƒๆจก็ป„ไธ€ๆ ท็›‘ๅฌไบ‹ไปถใ€‚ 4 - 5 - ๆœ€ๅฅฝ็š„ๅฎž่ทตๅŠžๆณ•ๅฐฑๆ˜ฏๆ‰พๅˆฐไธ€ไธช Fabric ็š„ไบ‹ไปถ๏ผŒ่ฎฉๆœฌๆจก็ป„ๅฏนๅฎƒ่ฟ›่กŒ็›‘ๅฌใ€‚ 6 - 7 - ## ๅˆ›ๅปบไบ‹ไปถ็ฑปๅž‹ 8 - 9 - ไธบไบ†ๅฎž็Žฐๆจก็ป„็š„ไบ‹ไปถๆ”ฏๆŒ๏ผŒไฝ ้œ€่ฆๅ…ˆๆณจๅ†Œไธ€ไธชไบ‹ไปถ็ฑปๅž‹๏ผŒไปฅไพฟๆœฌๆจก็ป„่ฟ›่กŒๅˆ†ๅ‘ใ€‚ไฝ ๅฏไปฅ้€š่ฟ‡ `EventType.Builder` ็ฑปๆฅๆž„ๅปบๅนถๆณจๅ†Œไบ‹ไปถใ€‚ 10 - 11 - ```java 12 - public static final EventType CUSTOM_EVENT = EventType.builder().build(new Identifier("modid", "custom_event")); 13 - ``` 14 - 15 - ๅฆ‚ๆžœไฝ ็š„ไบ‹ไปถ้œ€่ฆ่ฟ”ๅ›ž็ฑปๅž‹๏ผŒไฝ ๅฏไปฅ้€š่ฟ‡ `cancelTerm()` ๆฅๆŒ‡ๅฎšๅ–ๆถˆๆกไปถ็ผ–่งฃ็ ๅ™จใ€‚ 16 - 17 - ```java 18 - EventType.builder() 19 - .cancelTerm(Codec.INT) 20 - .build(new Identifier("modid", "custom_event")); 21 - ``` 22 - 23 - ้€š่ฟ‡ไฝฟ็”จ `extension()`๏ผŒไบ‹ไปถๅฏไปฅๆŽฅๅ—้ขๅค–็š„ๅฝขๅผๅ‚ๆ•ฐใ€‚ๅœจๆŒ‡ๅฎšๆ‰ฉๅฑ•ๅŽ๏ผŒไฝ ๅฏไปฅ่ฟ”ๅ›ž่‡ชๅฎšไน‰็ฑปๅž‹ใ€‚ 24 - 25 - ```java 26 - EventType.builder() 27 - .extension(Codec.STRING, subscriptions -> { 28 - //ๅค„็†ๆ•ฐๆฎ 29 - return /*่ฟ”ๅ›ž็›‘ๅฌ่€…*/; 30 - }) 31 - .build(new Identifier("modid", "custom_event")); 32 - ``` 33 - 34 - ้ป˜่ฎค็š„่ฟ”ๅ›ž็ฑปๅž‹ๆ˜ฏ `List<Command.Conditioned>`ใ€‚ 35 - 36 - ## ่ฐƒ็”จไบ‹ไปถ 37 - 38 - ๅฆ‚ๆžœไฝ ๆฒกๆœ‰ๆŒ‡ๅฎšๆ‰ฉๅฑ•๏ผŒๆˆ–้€‰ๆ‹ฉ่ฟ”ๅ›ž `List<Command.Conditioned>`๏ผŒไฝ ๅฏไปฅไฝฟ็”จ้™„ๅธฆ็š„ `EventExecutors` ่พ…ๅŠฉๅ•ๅ…ƒใ€‚ 39 - 40 - ไธบไบ†ไฝฟ็”จ่ฟ™ไธ€่พ…ๅŠฉๅ•ๅ…ƒ๏ผŒไฝ ้œ€่ฆไผ ้€’๏ผšไบ‹ไปถ็ฑปๅž‹ใ€ๆ‰ง่กŒ็š„ไธ–็•Œ๏ผŒไปฅๅŠๆˆ˜ๅˆฉๅ“็š„ไธŠไธ‹ๆ–‡ๆไพ›่€…ใ€‚ 41 - 42 - ```java 43 - CustomEvent.EVENT.register((world, entity) -> runVoid(CUSTOM_EVENT, world, () -> makeContext(world, entity, entity.getPos()))); 44 - ``` 45 - 46 - ```java 47 - private static LootContext makeContext(ServerWorld world, Entity entity, Vec3d origin) { 48 - LootContextParameterSet.Builder builder = new LootContextParameterSet.Builder(world); 49 - builder.add(THIS_ENTITY, entity).add(ORIGIN, origin); 50 - return new LootContext.Builder(builder.build(LootContextTypes.COMMAND)).build(null /*ๅœจ 1.20.4๏ผŒ.empty()ๅฏ้€‰*/); 51 - } 52 - ``` 53 - 54 - ๅฆ‚ๆžœไฝ ๆŒ‡ๅฎšไบ†ๆ‰ฉๅฑ•๏ผŒๆˆ–่€…ไฝฟ็”จไบ†ไธๅ—ๆ”ฏๆŒ็š„่ฟ”ๅ›ž็ฑปๅž‹๏ผŒๅฐฑ้œ€่ฆ็ผ–ๅ†™่‡ชๅฎšไน‰็š„่งฃๆž้€ป่พ‘ใ€‚ 55 - 56 - ไธบไบ†็ผ–ๅ†™่ฟ™ไธ€้€ป่พ‘๏ผŒไฝ ้œ€่ฆๅˆ›ๅปบ `EventContext`ใ€‚`EventContext` ็”จไบŽๆŠŠๆ‰ง่กŒๅฝขๅผๅ‚ๆ•ฐไผ ้€’็ป™ๅ‘ฝไปคใ€‚ 57 - 58 - ```java 59 - EventContext context = EventContext.builder(type) 60 - .addParameter(EventKey.LOOT_CONTEXT, /*ๆˆ˜ๅˆฉๅ“็š„ไธŠไธ‹ๆ–‡็š„ๅฎžไพ‹*/) 61 - .build(); 62 - for (Command.Conditioned subscriber : subscribers) subscriber.execute(context); 63 - ``` 64 - ไฝ ๅฏไปฅ้€š่ฟ‡่ฐƒ็”จ `getReturnValue(def)` ๆฅ่Žทๅ–่ฟ”ๅ›ž็š„ๅ€ผ๏ผŒ้ป˜่ฎคๅ€ผๅฏไปฅไธบ็ฉบๅ€ผใ€‚่ฟ”ๅ›ž็š„็ฑปๅž‹ๆ˜ฏ genericใ€‚ 65 - 66 - ```java 67 - boolean val = context.getReturnValue(def); 68 - if (val != def) return val; 69 - ```
-170
docs/zh-cn/develop/Expressions.md
··· 1 - # ่กจ่พพๅผ 2 - 3 - ๆญฃๅฆ‚ๅœจ[่กจ่พพๅผ](/zh-cn/Expressions)๏ผŒ็ซ ่Š‚ๆ‰€ไป‹็ป็š„้‚ฃๆ ท๏ผŒๅ‘ฝไปคๅฎ˜ๆจก็ป„ๅผ•ๅ…ฅไบ†ไธ€ไธชๅ…ทๆœ‰้ซ˜ๅบฆๅฏๆ‹“ๅฑ•ๆ€ง็š„่กจ่พพๅผ็ณป็ปŸใ€‚่ฟ™ไธช็ณป็ปŸๅฏไปฅ้€š่ฟ‡ๆŽฅๅฃไปŽๅค–้ƒจ่ฐƒ็”จใ€‚ๅœจ่ฟ™ไธช้กต้ขไธญ๏ผŒๆˆ‘ไปฌๅฐ†ไธ€่ตทๅˆ›ๅปบ็คบไพ‹๏ผŒๅนถๆŽข็ดขไฝฟ่กจ่พพๅผๆˆไธบๅฏ้€‰้›†ๆˆ็š„ๆ–นๅผใ€‚ 4 - 5 - ## ็›ดๆŽฅไฝฟ็”จ่กจ่พพๅผ 6 - 7 - ๅ†…็ฝฎ็š„ `Expression` ็ฑปๆไพ›ไบ†ๅฐ†ๅญ—็ฌฆไธฒ่ฝฌๅŒ–ไธบ่กจ่พพๅผ็š„็ผ–็ ๅ™จ๏ผŒไปฅๅŠ `parse` ๆ–นๆณ•ใ€‚ไธ€ไธช่กจ่พพๅผ็›ธๅฝ“ไบŽ `LootContext -> Expression.Result` ็š„ๅ‡ฝๆ•ฐใ€‚`Expression.Result` ไปฃ่กจๅฏไปฅ่ขซ่ฝฌๅŒ–ไธบ `BigDecimal`๏ผˆ้ซ˜็ฒพๅบฆๅฐๆ•ฐ๏ผ‰๏ผŒ`boolean`๏ผˆๅธƒๅฐ”ๅž‹๏ผ‰๏ผŒ`String`๏ผˆๅญ—็ฌฆไธฒ๏ผ‰๏ผŒ`Instant`๏ผˆๆ—ถ้—ดๆˆณ๏ผ‰ๅ’Œ `Duration`๏ผˆๆŒ็ปญๆ—ถ้—ด๏ผ‰็š„่ฟ”ๅ›žๅ€ผใ€‚ 8 - 9 - ๅœจๅบ”็”จ่กจ่พพ่กจ่พพๅผๅ‡ฝๆ•ฐๅ‰๏ผŒไฝ ๅฟ…้กปๆไพ› `LootContext` ็š„ๅฎžไพ‹ใ€‚ 10 - 11 - ```java 12 - public static final Expression EXP = Expression.parse("strFormat('%02.0f:%02.0f', floor((level.getDayTime / 1000 + 8) % 24), floor(60 * (level.getDayTime % 1000) / 1000))").result().orElseThrow(); 13 - 14 - public String worldTimeInHumanTime(LootContext context) { 15 - return EXP.apply(context).getAsString(); 16 - } 17 - ``` 18 - 19 - ## ็‰นๆฎŠๅ‡ฝๆ•ฐ 20 - 21 - ๅ‘ฝไปคๅฎ˜ๆจก็ป„้™„ๅธฆไบ† `Arithmetica` ๅ’Œ `BooleanExpression`ใ€‚ไฝœไธบ `double` ๅ’Œ `boolean` ๅ‡ฝๆ•ฐ๏ผŒๅฎƒไปฌๅฏไปฅ่ขซ็ผ–็ ไธบๅธธ้‡ๆˆ–่กจ่พพๅผใ€‚ๅ€ผๅพ—ๆณจๆ„็š„ๆ˜ฏ๏ผŒ`"true"` ไผš่ขซๅฝ“ๆˆ่กจ่พพๅผ่งฃ็ ๏ผŒ่€Œ `true` ไธไผšใ€‚ 22 - 23 - ```json 24 - 2.2, "random(0, 3)" //Arithmetica๏ผˆ็ฎ—ๆœฏ๏ผ‰ 25 - 26 - true, "level.isDay" //BooleanExpression๏ผˆๅธƒๅฐ”ๅž‹่กจ่พพๅผ๏ผ‰ 27 - ``` 28 - 29 - ## ่ฎฉ่กจ่พพๅผไฝœไธบๅฏ้€‰้›†ๆˆใ€‚ 30 - 31 - ๅœจๅฎž็Žฐๅฏนๅ‘ฝไปคๅฎ˜ๆจก็ป„็š„ๆ”ฏๆŒๆ—ถ๏ผŒไฝ ๅฏ่ƒฝๅธŒๆœ›่ฎฉ่ฟ™็ง้›†ๆˆๆ˜ฏ้žๅผบๅˆถ็š„ใ€‚ๆœ€็ฎ€ๅ•็š„ๆ–นๆณ•ๆ˜ฏ๏ผŒๅˆ›ๅปบไธ€ไธช้€š็”จๆŽฅๅฃ๏ผŒ็„ถๅŽๅง”ๆ‰˜็ป™ๅ…ถไธญไธ€ไธชๅฎž็Žฐใ€‚ๅฎž้™…ไธŠ๏ผŒ่ฟ™ๅฐฑๆ˜ฏ่กจ่พพๅผๅœจ็พคๆ˜Ÿๆจก็ป„็š„ๆ–ฐ้…็ฝฎ็ณป็ปŸ็š„้…็ฝฎๆ–นๅผใ€‚ 32 - 33 - ่ฎฉๆˆ‘ไปฌไปŽๅฎšไน‰ไธ€ไธช็ฎ€ๅ•็š„ๅธƒๅฐ”ๅž‹ไธญไป‹ๆŽฅๅฃๅผ€ๅง‹ใ€ๆˆ‘ๅปบ่ฎฎไฝฟ็”จ supplier ไฝœไธบๅฝขๅผๅ‚ๆ•ฐ๏ผŒๅ› ไธบ่ฟ™ๆ ทๅฐฑไธ็”จไธบๅธธ้‡ๆž„ๅปบ `LootContext` ไบ†ใ€‚ 34 - 35 - ```java 36 - public interface BooleanIntermediary { 37 - boolean asBoolean(Supplier<LootContext> supplier); 38 - } 39 - ``` 40 - 41 - ๆŽฅไธ‹ๆฅ๏ผŒ่ฎฉๆˆ‘ไปฌๅฎž็Žฐๅธธ้‡ๅง”ๆ‰˜ใ€‚ 42 - ```java 43 - public record ConstantBooleanIntermediary(boolean value) implements BooleanIntermediary { 44 - 45 - @Override 46 - public boolean asBoolean(Supplier<LootContext> supplier) { 47 - return this.value; 48 - } 49 - } 50 - ``` 51 - 52 - ไปฅๅŠๅ‘ฝไปคๅฎ˜ๅง”ๆ‰˜ใ€‚ 53 - 54 - ```java 55 - public final class CommanderBooleanIntermediary implements BooleanIntermediary { 56 - 57 - private final BooleanExpression expression; 58 - private final boolean constant; 59 - 60 - public CommanderBooleanIntermediary(BooleanExpression expression) { 61 - this.expression = expression; 62 - this.constant = expression.toSource().left().isPresent(); //micro optimization 63 - } 64 - 65 - @Override 66 - public boolean asBoolean(Supplier<LootContext> supplier) { 67 - return this.expression.applyAsBoolean(constant ? null : supplier.get()); 68 - } 69 - 70 - public BooleanExpression getExpression() { 71 - return this.expression; 72 - } 73 - } 74 - ``` 75 - 76 - ๅง”ๆ‰˜ๅšๅฅฝๅŽ๏ผŒๆŽฅไธ‹ๆฅ๏ผŒๆˆ‘ไปฌ้œ€่ฆๅŠจๆ€ๅœฐ้€‰ๆ‹ฉๅ…ถไธญไธ€ไธชใ€‚่ฎฉๆˆ‘ไปฌๅ›žๅˆฐไธญไป‹ๆŽฅๅฃ๏ผŒไธบๅธธ้‡ๅ€ผๅˆ›ๅปบไธ€ไธช factory๏ผˆๅทฅๅŽ‚๏ผ‰ใ€‚ๅœจ่ฟ™้‡Œ๏ผŒๆˆ‘็”จ็š„ๆ˜ฏ Dark Matter ็š„ `Support`๏ผŒไฝ ไนŸๅฏไปฅ็›ดๆŽฅๅคๅˆถ่ฟ™ไธชๆ–นๆณ•๏ผš 77 - 78 - ```java 79 - public static <T, F extends T, S extends T> T support(String mod, Supplier<F> expected, Supplier<S> fallback) { 80 - return FabricLoader.getInstance().isModLoaded(mod) ? expected.get() : fallback.get(); 81 - } 82 - ``` 83 - 84 - ่ฟ™ไธชๆ–นๆณ•ๅฐ†ๆฃ€ๆŸฅๆ˜ฏๅฆๅ‘ฝไปคๅฎ˜ๆจก็ป„ๅทฒ่ขซๅฎ‰่ฃ…๏ผŒๅนถ้€‰ๆ‹ฉๆญฃ็กฎ็š„ๅทฅๅŽ‚ใ€‚ 85 - 86 - ```java 87 - public interface BooleanIntermediary { 88 - Function<Boolean, BooleanIntermediary> FACTORY = Support.support("commander", 89 - () -> b -> new CommanderBooleanIntermediary(BooleanExpression.constant(b)), 90 - () -> ConstantBooleanIntermediary::new); 91 - 92 - static BooleanIntermediary of(boolean value) { 93 - return FACTORY.apply(value); 94 - } 95 - //... 96 - } 97 - ``` 98 - 99 - ๅพˆๆฃ’๏ผŒ็Žฐๅœจๆˆ‘ไปฌๅฏไปฅๅœจ้…็ฝฎๆ–‡ไปถไธญๅฎšไน‰่กจ่พพๅผไบ†๏ผ 100 - 101 - ```java 102 - public class Config { 103 - public BooleanIntermediary value = BooleanIntermediary.of(true); 104 - } 105 - ``` 106 - 107 - ็ญ‰็ญ‰๏ผŒ็ผ–็ ๅ’Œ่งฃ็ ่ฏฅๆ€ŽไนˆๅŠž๏ผŸ่ฟ™ๆญฃๆ˜ฏๆˆ‘ไปฌๆŽฅไธ‹ๆฅ่ฆไบ†่งฃ็š„ใ€‚ๅœจ่ฟ™้‡Œ๏ผŒๆˆ‘ไปฌๅฏไปฅไธบๆˆ‘ไปฌ็š„ๅง”ๆ‰˜ๅˆ›ๅปบไธ€ไธช็ผ–่งฃ็ ๅ™จ๏ผŒๅนถไฝฟ็”จ `support` ๆฅ่ฟ›่กŒๆญฃ็กฎ้€‰ๆ‹ฉใ€‚ 108 - 109 - ```java 110 - public record ConstantBooleanIntermediary(boolean value) implements BooleanIntermediary { 111 - public static final Codec<ConstantBooleanIntermediary> CODEC = Codec.BOOL.xmap(ConstantBooleanIntermediary::new, ConstantBooleanIntermediary::value); 112 - //... 113 - } 114 - 115 - public final class CommanderBooleanIntermediary implements BooleanIntermediary { 116 - public static final Codec<CommanderBooleanIntermediary> CODEC = BooleanExpression.CODEC.xmap(CommanderBooleanIntermediary::new, CommanderBooleanIntermediary::getExpression); 117 - //... 118 - } 119 - ``` 120 - 121 - ็Žฐๅœจๅฏไปฅไฝฟ็”จๆˆ‘ไปฌ็š„็ผ–่งฃ็ ๅ™จไบ†ใ€‚ 122 - 123 - ```java 124 - Codec<BooleanIntermediary> codec = (Codec<BooleanIntermediary>) Support.fallback("commander", () -> CommanderBooleanIntermediary.CODEC, () -> ConstantBooleanIntermediary.CODEC); 125 - ``` 126 - 127 - ### ๅœจ้…็ฝฎๆ–‡ไปถไธญไฝฟ็”จไธญไป‹ใ€‚ 128 - 129 - ::: info ๆ็คบ 130 - 131 - ๆœฌ่Š‚ๅชๅœจไฝ ไฝฟ็”จ Gson ๆฅ่ฏปๅ†™้…็ฝฎๆ–‡ไปถๆ—ถๆœ‰็”จใ€‚ 132 - 133 - ๅฆ‚ๆžœไฝ ไฝฟ็”จไบ†็ฌฌไธ‰ๆ–นๅบ“๏ผŒไธ”่ฏฅๅบ“ไธๆ”ฏๆŒไผ ้€’่‡ชๅฎšไน‰ Gson ็คบไพ‹๏ผŒไฝ ๅฏไปฅ้€š่ฟ‡ mixin ๆฅไฟฎๆ”นๅฎƒใ€‚ 134 - ::: 135 - ๅœจ Gson ไธญ๏ผŒไฝ ๅฏไปฅ้€š่ฟ‡ๆไพ›่‡ชๅฎšไน‰ JsonSerializers๏ผˆJson ๅบๅˆ—ๅŒ–ๅ™จ๏ผ‰ๆˆ– JsonDeserializers๏ผˆJson ๅๅบๅˆ—ๅŒ–ๅ™จ๏ผ‰๏ผŒไฝฟ็”จ็ผ–่งฃ็ ๅ™จๆฅๅฏนไธญไป‹่ฟ›่กŒ็ผ–็ ใ€‚ 136 - ๆŽฅไธ‹ๆฅๅฐ†ๅˆ›ๅปบๆˆ‘ไปฌ็š„ `CodecSerializer` ็ฑปใ€‚ 137 - 138 - ::: details ็ผ–่งฃ็ ๅ™จ 139 - ```java 140 - public record CodecSerializer<C>(Codec<C> codec) implements JsonSerializer<C>, JsonDeserializer<C> { 141 - 142 - public static <C> CodecSerializer<C> of(Codec<C> codec) { 143 - return new CodecSerializer<>(codec); 144 - } 145 - 146 - @Override 147 - public C deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 148 - var r = this.codec.parse(JsonOps.INSTANCE, json); 149 - if (r.error().isPresent()) throw new JsonParseException(r.error().orElseThrow().message()); 150 - return r.result().orElseThrow(); 151 - } 152 - 153 - @Override 154 - public JsonElement serialize(C src, Type typeOfSrc, JsonSerializationContext context) { 155 - var r = codec.encodeStart(JsonOps.INSTANCE, src); 156 - if (r.error().isPresent()) throw new IllegalStateException(r.error().orElseThrow().message()); 157 - return r.result().orElseThrow(); 158 - } 159 - } 160 - ``` 161 - 162 - ::: 163 - 164 - ็Žฐๅœจๅฏไปฅ้€š่ฟ‡ๆˆ‘ไปฌ็š„ GsonBuilder ๆฅๆณจๅ†Œ็ฑปๅž‹ๅฑ‚ๆฌก้€‚้…ๅ™จไบ†ใ€‚ 165 - 166 - ```java 167 - builder.registerTypeHierarchyAdapter(BooleanIntermediary.class, CodecSerializer.of(codec)); 168 - ``` 169 - 170 - ๅคงๅŠŸๅ‘Šๆˆ๏ผ
-29
docs/zh-cn/index.md
··· 1 - --- 2 - comment: false 3 - --- 4 - # ๅ‘ฝไปคๅฎ˜ <Badge type="warning" text="beta" /> 5 - 6 - <div class="badge-holder"> 7 - 8 - [![Available on Modrinth](https://raw.githubusercontent.com/melontini/mini-badges/main/minecraft/modrinth.svg)](https://modrinth.com/mod/cmd) 9 - [![Available on CurseForge](https://raw.githubusercontent.com/melontini/mini-badges/main/minecraft/curseforge.svg)](https://www.curseforge.com/minecraft/mc-mods/cmd-project) 10 - 11 - </div> 12 - 13 - ๅ‘ฝไปคๅฎ˜ๆจก็ป„ๆ˜ฏๅฏนๅŽŸ็‰ˆๆ•ฐๆฎๅŒ…็ณป็ปŸ็š„่กฅๅ……ใ€‚ 14 - 15 - ๅฎƒๅผ•ๅ…ฅไบ†ๅ…จๆ–ฐ็š„ไบ‹ไปถ็ณป็ปŸ๏ผŒ็ตๆดป็š„ json ๅ‘ฝไปค๏ผŒๆ–ฐ็š„ `/` ็ฑปๅ‘ฝไปค๏ผŒๅฏ่ฏปๅ–ๆ•ฐๆฎ็š„่ฟ›้˜ถๆ•ฐๅญฆ่กจ่พพๅผๆ”ฏๆŒ๏ผŒ่ฟ˜ๆœ‰ๆ›ดๅคš๏ผ 16 - 17 - ๆœฌๆจก็ป„ไธไผšๆœ‰ๅฎขๆˆท็ซฏ็‰นๆ€ง๏ผŒไธ€ๅˆ‡้ƒฝไผšๅœจๆœๅŠก็ซฏๆ‰ง่กŒใ€‚ 18 - 19 - ::: danger ๆณจๆ„ 20 - ่ฟ™ไธชๆจก็ป„ๅช่ƒฝๅœจ Modrinth๏ผŒCurseForge ๅ’Œ GitHub ็š„ Actions ไธŠ่ขซไธ‹่ฝฝๅˆฐใ€‚ๅฆ‚ๆžœไฝ ไปŽ*ๅ…ถไป–*ไปปไฝ•ๅœฐๆ–นไธ‹่ฝฝๅˆฐไบ†่ฟ™ไธชๆจก็ป„๏ผŒไฝ ็š„็”ต่„‘ๅพˆๅฏ่ƒฝ้ขไธดๆ„ŸๆŸ“ๅƒ [fractureiser](https://github.com/fractureiser-investigation/fractureiser) ็—…ๆฏ’็š„้ฃŽ้™ฉใ€‚ 21 - 22 - ่ฟ™ไธช้กต้ข [ๆ‹’็ปๆจก็ป„ๅ†ๅˆ†ๅ‘](https://stopmodreposts.org/) ๅฑ•็คบไบ†ๆ›ด่ฏฆ็ป†็š„ไฟกๆฏใ€‚ 23 - 24 - ๅˆ‡่ฎฐ๏ผ 25 - ::: 26 - 27 - *** 28 - 29 - <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Creative Commons ่ฎธๅฏ่ฏ" style="border-width:0" src="https://raw.githubusercontent.com/melontini/mini-badges/main/licenses/cc/CC-BY-NC-SA-4.0.svg" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">ๅ‘ฝไปคๅฎ˜ๆจก็ป„็ปดๅŸบ</span>็”ฑ <a xmlns:cc="http://creativecommons.org/ns#" href="https://github.com/constellation-mc/commander/tree/documentation" property="cc:attributionName" rel="cc:attributionURL">constellation ๅŠ่ดก็Œฎ่€…ๅˆ›ไฝœ</a>๏ผŒๅœจ <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International ่ฎธๅฏ่ฏ</a>ไธ‹ๅˆ†ๅ‘ใ€‚
+24
gradle/libs.versions.toml
··· 1 + [versions] 2 + dark-matter = "4.1.1-1.21-build.93" 3 + 4 + [libraries] 5 + fabric-loader = { group = "net.fabricmc", name = "fabric-loader", version = "0.16.0" } 6 + fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version = "0.100.7+1.21" } 7 + evalx = { group = "me.melontini", name = "evalex", version = "4.0.0-build.7" } 8 + mapping-io = { group = "net.fabricmc", name = "mapping-io", version = "0.6.1" } 9 + dark-matter-base = { group = "me.melontini", name = "dark-matter-base", version.ref = "dark-matter" } 10 + dark-matter-mixin = { group = "me.melontini", name = "dark-matter-mixin", version.ref = "dark-matter" } 11 + dark-matter-minecraft = { group = "me.melontini", name = "dark-matter-minecraft", version.ref = "dark-matter" } 12 + dark-matter-data = { group = "me.melontini", name = "dark-matter-data", version.ref = "dark-matter" } 13 + 14 + handy-tests = { group = "me.melontini", name = "handy-tests", version = "0.3.0-1.21-build.19" } 15 + assertj-core = { module = "org.assertj:assertj-core", version = "3.26.3" } 16 + mockito-core = { module = "org.mockito:mockito-core", version = "5.12.0" } 17 + 18 + [plugins] 19 + fabric-loom = { id = "fabric-loom", version = "1.7.3" } 20 + lombok = { id = "io.freefair.lombok", version = "8.6"} 21 + shadow = { id = "com.github.johnrengelman.shadow", version = "8.1.1" } 22 + spotbugs-base = { id = "com.github.spotbugs-base", version = "6.0.19" } 23 + spotless = { id = "com.diffplug.spotless", version = "6.25.0" } 24 + modmuss50-publish = { id = "me.modmuss50.mod-publish-plugin", version = "0.6.3" }
gradle/wrapper/gradle-wrapper.jar

This is a binary file and will not be displayed.

+7
gradle/wrapper/gradle-wrapper.properties
··· 1 + distributionBase=GRADLE_USER_HOME 2 + distributionPath=wrapper/dists 3 + distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip 4 + networkTimeout=10000 5 + validateDistributionUrl=true 6 + zipStoreBase=GRADLE_USER_HOME 7 + zipStorePath=wrapper/dists
+12
gradle.properties
··· 1 + # Done to increase the memory available to gradle. 2 + org.gradle.jvmargs=-Xmx1G 3 + org.gradle.daemon=true 4 + org.gradle.caching=true 5 + # Fabric Properties 6 + # check these on https://modmuss50.me/fabric.html 7 + minecraft_version=1.21 8 + yarn_mappings=1.21+build.9 9 + # Mod Properties 10 + mod_version=0.9.0 11 + maven_group=me.melontini 12 + archives_base_name=commander
+249
gradlew
··· 1 + #!/bin/sh 2 + 3 + # 4 + # Copyright ยฉ 2015-2021 the original authors. 5 + # 6 + # Licensed under the Apache License, Version 2.0 (the "License"); 7 + # you may not use this file except in compliance with the License. 8 + # You may obtain a copy of the License at 9 + # 10 + # https://www.apache.org/licenses/LICENSE-2.0 11 + # 12 + # Unless required by applicable law or agreed to in writing, software 13 + # distributed under the License is distributed on an "AS IS" BASIS, 14 + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + # See the License for the specific language governing permissions and 16 + # limitations under the License. 17 + # 18 + 19 + ############################################################################## 20 + # 21 + # Gradle start up script for POSIX generated by Gradle. 22 + # 23 + # Important for running: 24 + # 25 + # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 + # noncompliant, but you have some other compliant shell such as ksh or 27 + # bash, then to run this script, type that shell name before the whole 28 + # command line, like: 29 + # 30 + # ksh Gradle 31 + # 32 + # Busybox and similar reduced shells will NOT work, because this script 33 + # requires all of these POSIX shell features: 34 + # * functions; 35 + # * expansions ยซ$varยป, ยซ${var}ยป, ยซ${var:-default}ยป, ยซ${var+SET}ยป, 36 + # ยซ${var#prefix}ยป, ยซ${var%suffix}ยป, and ยซ$( cmd )ยป; 37 + # * compound commands having a testable exit status, especially ยซcaseยป; 38 + # * various built-in commands including ยซcommandยป, ยซsetยป, and ยซulimitยป. 39 + # 40 + # Important for patching: 41 + # 42 + # (2) This script targets any POSIX shell, so it avoids extensions provided 43 + # by Bash, Ksh, etc; in particular arrays are avoided. 44 + # 45 + # The "traditional" practice of packing multiple parameters into a 46 + # space-separated string is a well documented source of bugs and security 47 + # problems, so this is (mostly) avoided, by progressively accumulating 48 + # options in "$@", and eventually passing that to Java. 49 + # 50 + # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 + # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 + # see the in-line comments for details. 53 + # 54 + # There are tweaks for specific operating systems such as AIX, CygWin, 55 + # Darwin, MinGW, and NonStop. 56 + # 57 + # (3) This script is generated from the Groovy template 58 + # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 + # within the Gradle project. 60 + # 61 + # You can find Gradle at https://github.com/gradle/gradle/. 62 + # 63 + ############################################################################## 64 + 65 + # Attempt to set APP_HOME 66 + 67 + # Resolve links: $0 may be a link 68 + app_path=$0 69 + 70 + # Need this for daisy-chained symlinks. 71 + while 72 + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 + [ -h "$app_path" ] 74 + do 75 + ls=$( ls -ld "$app_path" ) 76 + link=${ls#*' -> '} 77 + case $link in #( 78 + /*) app_path=$link ;; #( 79 + *) app_path=$APP_HOME$link ;; 80 + esac 81 + done 82 + 83 + # This is normally unused 84 + # shellcheck disable=SC2034 85 + APP_BASE_NAME=${0##*/} 86 + # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 + APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 + 89 + # Use the maximum available, or set MAX_FD != -1 to use that value. 90 + MAX_FD=maximum 91 + 92 + warn () { 93 + echo "$*" 94 + } >&2 95 + 96 + die () { 97 + echo 98 + echo "$*" 99 + echo 100 + exit 1 101 + } >&2 102 + 103 + # OS specific support (must be 'true' or 'false'). 104 + cygwin=false 105 + msys=false 106 + darwin=false 107 + nonstop=false 108 + case "$( uname )" in #( 109 + CYGWIN* ) cygwin=true ;; #( 110 + Darwin* ) darwin=true ;; #( 111 + MSYS* | MINGW* ) msys=true ;; #( 112 + NONSTOP* ) nonstop=true ;; 113 + esac 114 + 115 + CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 + 117 + 118 + # Determine the Java command to use to start the JVM. 119 + if [ -n "$JAVA_HOME" ] ; then 120 + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 + # IBM's JDK on AIX uses strange locations for the executables 122 + JAVACMD=$JAVA_HOME/jre/sh/java 123 + else 124 + JAVACMD=$JAVA_HOME/bin/java 125 + fi 126 + if [ ! -x "$JAVACMD" ] ; then 127 + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 + 129 + Please set the JAVA_HOME variable in your environment to match the 130 + location of your Java installation." 131 + fi 132 + else 133 + JAVACMD=java 134 + if ! command -v java >/dev/null 2>&1 135 + then 136 + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 + 138 + Please set the JAVA_HOME variable in your environment to match the 139 + location of your Java installation." 140 + fi 141 + fi 142 + 143 + # Increase the maximum file descriptors if we can. 144 + if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 + case $MAX_FD in #( 146 + max*) 147 + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 + # shellcheck disable=SC2039,SC3045 149 + MAX_FD=$( ulimit -H -n ) || 150 + warn "Could not query maximum file descriptor limit" 151 + esac 152 + case $MAX_FD in #( 153 + '' | soft) :;; #( 154 + *) 155 + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 + # shellcheck disable=SC2039,SC3045 157 + ulimit -n "$MAX_FD" || 158 + warn "Could not set maximum file descriptor limit to $MAX_FD" 159 + esac 160 + fi 161 + 162 + # Collect all arguments for the java command, stacking in reverse order: 163 + # * args from the command line 164 + # * the main class name 165 + # * -classpath 166 + # * -D...appname settings 167 + # * --module-path (only if needed) 168 + # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 + 170 + # For Cygwin or MSYS, switch paths to Windows format before running java 171 + if "$cygwin" || "$msys" ; then 172 + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 + 175 + JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 + 177 + # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 + for arg do 179 + if 180 + case $arg in #( 181 + -*) false ;; # don't mess with options #( 182 + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 + [ -e "$t" ] ;; #( 184 + *) false ;; 185 + esac 186 + then 187 + arg=$( cygpath --path --ignore --mixed "$arg" ) 188 + fi 189 + # Roll the args list around exactly as many times as the number of 190 + # args, so each arg winds up back in the position where it started, but 191 + # possibly modified. 192 + # 193 + # NB: a `for` loop captures its iteration list before it begins, so 194 + # changing the positional parameters here affects neither the number of 195 + # iterations, nor the values presented in `arg`. 196 + shift # remove old arg 197 + set -- "$@" "$arg" # push replacement arg 198 + done 199 + fi 200 + 201 + 202 + # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 + DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 + 205 + # Collect all arguments for the java command: 206 + # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 + # and any embedded shellness will be escaped. 208 + # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 + # treated as '${Hostname}' itself on the command line. 210 + 211 + set -- \ 212 + "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 + -classpath "$CLASSPATH" \ 214 + org.gradle.wrapper.GradleWrapperMain \ 215 + "$@" 216 + 217 + # Stop when "xargs" is not available. 218 + if ! command -v xargs >/dev/null 2>&1 219 + then 220 + die "xargs is not available" 221 + fi 222 + 223 + # Use "xargs" to parse quoted args. 224 + # 225 + # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 + # 227 + # In Bash we could simply go: 228 + # 229 + # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 + # set -- "${ARGS[@]}" "$@" 231 + # 232 + # but POSIX shell has neither arrays nor command substitution, so instead we 233 + # post-process each arg (as a line of input to sed) to backslash-escape any 234 + # character that might be a shell metacharacter, then use eval to reverse 235 + # that process (while maintaining the separation between arguments), and wrap 236 + # the whole thing up as a single "set" statement. 237 + # 238 + # This will of course break if any of these variables contains a newline or 239 + # an unmatched quote. 240 + # 241 + 242 + eval "set -- $( 243 + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 + xargs -n1 | 245 + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 + tr '\n' ' ' 247 + )" '"$@"' 248 + 249 + exec "$JAVACMD" "$@"
+92
gradlew.bat
··· 1 + @rem 2 + @rem Copyright 2015 the original author or authors. 3 + @rem 4 + @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 + @rem you may not use this file except in compliance with the License. 6 + @rem You may obtain a copy of the License at 7 + @rem 8 + @rem https://www.apache.org/licenses/LICENSE-2.0 9 + @rem 10 + @rem Unless required by applicable law or agreed to in writing, software 11 + @rem distributed under the License is distributed on an "AS IS" BASIS, 12 + @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 + @rem See the License for the specific language governing permissions and 14 + @rem limitations under the License. 15 + @rem 16 + 17 + @if "%DEBUG%"=="" @echo off 18 + @rem ########################################################################## 19 + @rem 20 + @rem Gradle startup script for Windows 21 + @rem 22 + @rem ########################################################################## 23 + 24 + @rem Set local scope for the variables with windows NT shell 25 + if "%OS%"=="Windows_NT" setlocal 26 + 27 + set DIRNAME=%~dp0 28 + if "%DIRNAME%"=="" set DIRNAME=. 29 + @rem This is normally unused 30 + set APP_BASE_NAME=%~n0 31 + set APP_HOME=%DIRNAME% 32 + 33 + @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 + for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 + 36 + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 + set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 + 39 + @rem Find java.exe 40 + if defined JAVA_HOME goto findJavaFromJavaHome 41 + 42 + set JAVA_EXE=java.exe 43 + %JAVA_EXE% -version >NUL 2>&1 44 + if %ERRORLEVEL% equ 0 goto execute 45 + 46 + echo. 47 + echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 + echo. 49 + echo Please set the JAVA_HOME variable in your environment to match the 50 + echo location of your Java installation. 51 + 52 + goto fail 53 + 54 + :findJavaFromJavaHome 55 + set JAVA_HOME=%JAVA_HOME:"=% 56 + set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 + 58 + if exist "%JAVA_EXE%" goto execute 59 + 60 + echo. 61 + echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 + echo. 63 + echo Please set the JAVA_HOME variable in your environment to match the 64 + echo location of your Java installation. 65 + 66 + goto fail 67 + 68 + :execute 69 + @rem Setup the command line 70 + 71 + set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 + 73 + 74 + @rem Execute Gradle 75 + "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 + 77 + :end 78 + @rem End local scope for the variables with windows NT shell 79 + if %ERRORLEVEL% equ 0 goto mainEnd 80 + 81 + :fail 82 + rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 + rem the _cmd.exe /c_ return code! 84 + set EXIT_CODE=%ERRORLEVEL% 85 + if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 + if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 + exit /b %EXIT_CODE% 88 + 89 + :mainEnd 90 + if "%OS%"=="Windows_NT" endlocal 91 + 92 + :omega
+2
jitpack.yml
··· 1 + jdk: 2 + - openjdk17
+1
lombok.config
··· 1 + lombok.accessors.fluent=true
-8
package.json
··· 1 - { 2 - "dependencies": { "vitepress": "^1.0.2", "vitepress-plugin-comment-with-giscus": "^1.1.12" }, 3 - "scripts": { 4 - "docs:dev": "vitepress dev docs", 5 - "docs:build": "vitepress build docs", 6 - "docs:preview": "vitepress preview docs" 7 - } 8 - }
+9
settings.gradle
··· 1 + pluginManagement { 2 + repositories { 3 + maven { 4 + name = 'Fabric' 5 + url = 'https://maven.fabricmc.net/' 6 + } 7 + gradlePluginPortal() 8 + } 9 + }
+17
spotbugs.xml
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <FindBugsFilter> 3 + <Match> 4 + <!-- Must be a full match --> 5 + <Class name="~.*\.mixin\..*Mixin" /> 6 + <Bug pattern="BC_IMPOSSIBLE_CAST, MS_SHOULD_BE_FINAL" /> 7 + </Match> 8 + <Match> 9 + <Bug pattern="EI_EXPOSE_REP, EI_EXPOSE_REP2, PA_PUBLIC_PRIMITIVE_ATTRIBUTE, BC_UNCONFIRMED_CAST" /> 10 + </Match> 11 + 12 + <Match> 13 + <Class name="me.melontini.commander.impl.expression.EvalUtils" /> 14 + <Method name="parseExpression" /> 15 + <Bug pattern="BC_IMPOSSIBLE_CAST" /> 16 + </Match> 17 + </FindBugsFilter>
+56
src/main/java/me/melontini/commander/api/command/Command.java
··· 1 + package me.melontini.commander.api.command; 2 + 3 + import com.mojang.serialization.Codec; 4 + import com.mojang.serialization.DataResult; 5 + import com.mojang.serialization.MapCodec; 6 + import me.melontini.commander.api.event.EventContext; 7 + import me.melontini.commander.api.event.EventType; 8 + import me.melontini.commander.impl.command.ConditionedCommand; 9 + import net.minecraft.util.Identifier; 10 + 11 + /** 12 + * Base command interface to be implemented on your command classes. 13 + * <p>Commands along with their {@link Codec} must be registered with {@link CommandType#register(Identifier, MapCodec)}.</p> 14 + */ 15 + public interface Command { 16 + 17 + MapCodec<Conditioned> CODEC = (MapCodec<Conditioned>) ConditionedCommand.CODEC; 18 + 19 + /** 20 + * Executes the command with the provided event context. 21 + * @param context {@link EventContext} 22 + * @return If the execution was successful. 23 + */ 24 + boolean execute(EventContext context); 25 + 26 + /** 27 + * @return The registered command type. 28 + * @see CommandType#register(Identifier, MapCodec) 29 + */ 30 + CommandType type(); 31 + 32 + /** 33 + * Validates that a command can used with the event type. 34 + * Internally this is only used for the cancel command. 35 + * @param type The {@link EventType} expected to for this command. 36 + * @return {@link DataResult}. 37 + */ 38 + default DataResult<Void> validate(EventType type) { 39 + return DataResult.success(null); 40 + } 41 + 42 + /** 43 + * Executable command proxy. This interface is to be used when you nest additional commands in your base command. 44 + */ 45 + interface Conditioned { 46 + /** 47 + * @see Command#execute(EventContext) 48 + */ 49 + boolean execute(EventContext context); 50 + 51 + /** 52 + * @see Command#validate(EventType) 53 + */ 54 + DataResult<Void> validate(EventType type); 55 + } 56 + }
+23
src/main/java/me/melontini/commander/api/command/CommandType.java
··· 1 + package me.melontini.commander.api.command; 2 + 3 + import com.mojang.serialization.MapCodec; 4 + import me.melontini.commander.impl.event.data.types.CommandTypes; 5 + import net.minecraft.util.Identifier; 6 + import org.jetbrains.annotations.ApiStatus; 7 + 8 + @ApiStatus.NonExtendable 9 + public interface CommandType { 10 + 11 + /** 12 + * Registers a command to be used in events. 13 + * @param identifier The command identifier. 14 + * @param codec The codec to decode the command from JSON. 15 + * @return The command type to be returned in {@link Command#type()} 16 + * @see Command#type() 17 + */ 18 + static CommandType register(Identifier identifier, MapCodec<? extends Command> codec) { 19 + return CommandTypes.register(identifier, () -> codec); 20 + } 21 + 22 + MapCodec<? extends Command> codec(); 23 + }
+52
src/main/java/me/melontini/commander/api/command/Selector.java
··· 1 + package me.melontini.commander.api.command; 2 + 3 + import com.mojang.serialization.Codec; 4 + import java.util.Optional; 5 + import java.util.function.Function; 6 + import me.melontini.commander.api.event.EventContext; 7 + import me.melontini.commander.impl.command.ConditionedSelector; 8 + import me.melontini.commander.impl.event.data.types.SelectorTypes; 9 + import net.minecraft.loot.context.LootContext; 10 + import net.minecraft.server.command.ServerCommandSource; 11 + import net.minecraft.util.Identifier; 12 + import org.jetbrains.annotations.Nullable; 13 + 14 + /** 15 + * Base selector function. Selectors transform input {@link LootContext} parameters into {@link ServerCommandSource}. 16 + * <p>Selectors must be registered with {@link Selector#register(Identifier, Selector)}</p> 17 + */ 18 + public interface Selector extends Function<LootContext, ServerCommandSource> { 19 + 20 + Codec<Conditioned> CODEC = (Codec<Conditioned>) ConditionedSelector.CODEC; 21 + 22 + /** 23 + * Registers a selector to be used with {@link Command}. 24 + * @param identifier The selector identifier. 25 + * @param selector Selector to be registered. 26 + * @return the provided selector instance. 27 + */ 28 + static Selector register(Identifier identifier, Selector selector) { 29 + return SelectorTypes.register(identifier, selector); 30 + } 31 + 32 + @Override 33 + default @Nullable ServerCommandSource apply(LootContext context) { 34 + return this.select(context); 35 + } 36 + 37 + /** 38 + * Selects a {@link ServerCommandSource} based on the provided {@link LootContext}. 39 + * @return {@link ServerCommandSource} extracted from the context or null. 40 + */ 41 + @Nullable ServerCommandSource select(LootContext context); 42 + 43 + /** 44 + * Executable selector proxy. 45 + */ 46 + interface Conditioned { 47 + /** 48 + * @see Selector#select(LootContext) 49 + */ 50 + Optional<ServerCommandSource> select(EventContext context); 51 + } 52 + }
+37
src/main/java/me/melontini/commander/api/event/EventContext.java
··· 1 + package me.melontini.commander.api.event; 2 + 3 + import java.util.IdentityHashMap; 4 + import me.melontini.commander.impl.event.EventContextImpl; 5 + import net.minecraft.loot.context.LootContext; 6 + import org.jetbrains.annotations.ApiStatus; 7 + import org.jetbrains.annotations.Contract; 8 + import org.jetbrains.annotations.NotNull; 9 + 10 + @ApiStatus.NonExtendable 11 + public interface EventContext { 12 + 13 + @Contract("_ -> new") 14 + static EventContext.@NotNull Builder builder(EventType type) { 15 + return new EventContextImpl.Builder(type); 16 + } 17 + 18 + @NotNull EventType type(); 19 + 20 + <T> @NotNull T getParameter(@NotNull EventKey<T> key); 21 + 22 + @NotNull LootContext lootContext(); 23 + 24 + void setReturnValue(Object value); 25 + 26 + <T> T getReturnValue(T def); 27 + 28 + @Contract("_ -> new") 29 + @NotNull EventContext with(@NotNull IdentityHashMap<EventKey<?>, Object> parameters); 30 + 31 + interface Builder { 32 + @Contract("_, _ -> this") 33 + <T> Builder addParameter(EventKey<T> key, T value); 34 + 35 + EventContext build(); 36 + } 37 + }
+27
src/main/java/me/melontini/commander/api/event/EventKey.java
··· 1 + package me.melontini.commander.api.event; 2 + 3 + import java.util.concurrent.atomic.AtomicReference; 4 + import lombok.AccessLevel; 5 + import lombok.Getter; 6 + import lombok.RequiredArgsConstructor; 7 + import me.melontini.commander.impl.Commander; 8 + import net.minecraft.loot.context.LootContext; 9 + import net.minecraft.util.Identifier; 10 + import org.jetbrains.annotations.Contract; 11 + import org.jetbrains.annotations.NotNull; 12 + 13 + @Getter 14 + @RequiredArgsConstructor(access = AccessLevel.PRIVATE) 15 + public final class EventKey<T> { 16 + 17 + public static final EventKey<LootContext> LOOT_CONTEXT = create(Commander.id("loot_context")); 18 + public static final EventKey<AtomicReference<Object>> RETURN_VALUE = 19 + create(Commander.id("return_value")); 20 + 21 + private final Identifier id; 22 + 23 + @Contract("_ -> new") 24 + public static <T> @NotNull EventKey<T> create(Identifier id) { 25 + return new EventKey<>(id); 26 + } 27 + }
+100
src/main/java/me/melontini/commander/api/event/EventType.java
··· 1 + package me.melontini.commander.api.event; 2 + 3 + import static me.melontini.commander.impl.Commander.id; 4 + 5 + import com.mojang.serialization.Codec; 6 + import com.mojang.serialization.MapCodec; 7 + import java.util.List; 8 + import java.util.Optional; 9 + import java.util.function.BiConsumer; 10 + import java.util.function.Function; 11 + import me.melontini.commander.impl.event.EventTypeImpl; 12 + import me.melontini.dark_matter.api.base.util.Context; 13 + import net.minecraft.util.Identifier; 14 + import org.jetbrains.annotations.ApiStatus; 15 + import org.jetbrains.annotations.Contract; 16 + import org.jetbrains.annotations.Nullable; 17 + 18 + /** 19 + * Event types define basic information about events, like the return type, additional parameters and the finalizer. 20 + * 21 + * <p>- {@link EventType.Builder#extension(Codec, Function)} allows you to pass additional parameters to the subscription and process those parameters.</p> 22 + * <p>- {@link EventType.Builder#cancelTerm(Codec)} defines if the {@code commander:cancel} command is supported by this event.</p> 23 + */ 24 + @ApiStatus.NonExtendable 25 + public interface EventType extends Context { 26 + 27 + /** 28 + * Dummy event type. Can be used to execute commands outside of events, or to replace subscriptions in JSON. 29 + */ 30 + EventType NULL = EventType.builder().extension(null, subscriptions -> null).build(id("none")); 31 + 32 + /** 33 + * @deprecated No replacement 34 + */ 35 + @Deprecated 36 + Context.Key<Codec<?>> EXTENSION = EventTypeImpl.EXTENSION; 37 + /** 38 + * @deprecated No replacement 39 + */ 40 + @Deprecated 41 + Context.Key<Function<List<Subscription<?>>, ?>> FINALIZER = EventTypeImpl.FINALIZER; 42 + /** 43 + * @deprecated No replacement 44 + */ 45 + @Deprecated 46 + Context.Key<Codec<?>> CANCEL_TERM = EventTypeImpl.CANCEL_TERM; 47 + 48 + /** 49 + * @deprecated No replacement 50 + */ 51 + @Deprecated 52 + @Override 53 + <T> Optional<T> get(Key<T> key); 54 + 55 + /** 56 + * @deprecated No replacement 57 + */ 58 + @Deprecated 59 + @Override 60 + void forEach(BiConsumer<Key<?>, Object> consumer); 61 + 62 + /** 63 + * @deprecated No replacement 64 + */ 65 + @Deprecated 66 + @Override 67 + default <T> T orThrow(Key<T> key) { 68 + return Context.super.orThrow(key); 69 + } 70 + 71 + static EventType.Builder builder() { 72 + return new EventTypeImpl.Builder(); 73 + } 74 + 75 + interface Builder { 76 + /** 77 + * Adds parameters to event declarations. Prefer using a {@link MapCodec} to avoid conflicts in the future. 78 + * @param extension The codec to decode parameters from JSON. 79 + * @param finalizer The function to process event {@link Subscription}s with parameters. 80 + */ 81 + @Contract("_, _ -> this") 82 + <T, C> Builder extension( 83 + @Nullable Codec<T> extension, Function<List<Subscription<T>>, C> finalizer); 84 + 85 + /** 86 + * Adds a "cancel term" to the event. 87 + * This allows invoking the {@code commander:cancel} command from JSON to modify the return type. 88 + * @param returnCodec The codec to decode the object from JSON. 89 + */ 90 + @Contract("_ -> this") 91 + <R> Builder cancelTerm(Codec<R> returnCodec); 92 + 93 + /** 94 + * Builds and registers the {@link EventType}. 95 + * @param identifier The event type identifier. 96 + * @return Newly constructed {@link EventType} instance. 97 + */ 98 + EventType build(Identifier identifier); 99 + } 100 + }
+39
src/main/java/me/melontini/commander/api/event/Subscription.java
··· 1 + package me.melontini.commander.api.event; 2 + 3 + import com.mojang.serialization.Codec; 4 + import java.util.List; 5 + import java.util.function.Function; 6 + import me.melontini.commander.api.command.Command; 7 + import me.melontini.commander.impl.event.data.DynamicEventManager; 8 + import net.minecraft.server.MinecraftServer; 9 + import org.jetbrains.annotations.ApiStatus; 10 + import org.jetbrains.annotations.Nullable; 11 + 12 + @ApiStatus.NonExtendable 13 + public interface Subscription<E> { 14 + 15 + /** 16 + * Returns all data for the event from the current server. 17 + * @param server The current server instance. 18 + * @param type The event type. 19 + * @return Data associated with the event or null if the {@link EventType.Builder#extension(Codec, Function)} returns null. 20 + */ 21 + static <T> @Nullable T getData(MinecraftServer server, EventType type) { 22 + return DynamicEventManager.getData(server, type); 23 + } 24 + 25 + /** 26 + * @return Subscribed event type. 27 + */ 28 + EventType type(); 29 + 30 + /** 31 + * @return Event parameters or null if {@link EventType.Builder#extension(Codec, Function)} was not specified. 32 + */ 33 + @Nullable E parameters(); 34 + 35 + /** 36 + * @return {@link Command.Conditioned} parsed from the subscription file. 37 + */ 38 + List<Command.Conditioned> list(); 39 + }
+84
src/main/java/me/melontini/commander/api/expression/Arithmetica.java
··· 1 + package me.melontini.commander.api.expression; 2 + 3 + import com.mojang.datafixers.util.Either; 4 + import com.mojang.serialization.Codec; 5 + import com.mojang.serialization.DataResult; 6 + import java.util.Map; 7 + import java.util.function.ToDoubleBiFunction; 8 + import java.util.function.ToDoubleFunction; 9 + import me.melontini.commander.impl.expression.intermediaries.ConstantArithmetica; 10 + import me.melontini.commander.impl.expression.intermediaries.DynamicArithmetica; 11 + import net.minecraft.loot.context.LootContext; 12 + import org.jetbrains.annotations.ApiStatus; 13 + import org.jetbrains.annotations.Contract; 14 + import org.jetbrains.annotations.NotNull; 15 + import org.jetbrains.annotations.Nullable; 16 + 17 + /** 18 + * A simple {@code context -> double} functions, which is encoded as either a double or an expression. 19 + * <p>Can be used as a substitute for {@link Codec#DOUBLE} if {@link LootContext} is available</p> 20 + * 21 + * @see Expression 22 + */ 23 + @ApiStatus.NonExtendable 24 + public interface Arithmetica 25 + extends ToDoubleFunction<LootContext>, 26 + ToDoubleBiFunction<LootContext, @Nullable Map<String, ?>> { 27 + 28 + Codec<Arithmetica> CODEC = Codec.either(Codec.DOUBLE, Codec.STRING) 29 + .comapFlatMap( 30 + (either) -> either.map( 31 + b -> DataResult.success(constant(b)), s -> Expression.parse(s).map(Arithmetica::of)), 32 + Arithmetica::toSource); 33 + 34 + default long asLong(LootContext context) { 35 + return (long) this.asDouble(context); 36 + } 37 + 38 + default int asInt(LootContext context) { 39 + return (int) this.asDouble(context); 40 + } 41 + 42 + default float asFloat(LootContext context) { 43 + return (float) this.asDouble(context); 44 + } 45 + 46 + default double asDouble(LootContext context) { 47 + return this.asDouble(context, null); 48 + } 49 + 50 + default long asLong(LootContext context, @Nullable Map<String, ?> parameters) { 51 + return (long) this.asDouble(context, parameters); 52 + } 53 + 54 + default int asInt(LootContext context, @Nullable Map<String, ?> parameters) { 55 + return (int) this.asDouble(context, parameters); 56 + } 57 + 58 + default float asFloat(LootContext context, @Nullable Map<String, ?> parameters) { 59 + return (float) this.asDouble(context, parameters); 60 + } 61 + 62 + double asDouble(LootContext context, @Nullable Map<String, ?> parameters); 63 + 64 + Either<Double, String> toSource(); 65 + 66 + @Contract("_ -> new") 67 + static @NotNull Arithmetica constant(double d) { 68 + return new ConstantArithmetica(Either.left(d), d); 69 + } 70 + 71 + static @NotNull Arithmetica of(Expression expression) { 72 + return new DynamicArithmetica(Either.right(expression.original()), expression); 73 + } 74 + 75 + @Override 76 + default double applyAsDouble(LootContext context, @Nullable Map<String, ?> parameters) { 77 + return this.asDouble(context, parameters); 78 + } 79 + 80 + @Override 81 + default double applyAsDouble(LootContext context) { 82 + return this.asDouble(context); 83 + } 84 + }
+67
src/main/java/me/melontini/commander/api/expression/BooleanExpression.java
··· 1 + package me.melontini.commander.api.expression; 2 + 3 + import com.mojang.datafixers.util.Either; 4 + import com.mojang.serialization.Codec; 5 + import com.mojang.serialization.DataResult; 6 + import java.util.Map; 7 + import java.util.function.BiPredicate; 8 + import java.util.function.Predicate; 9 + import me.melontini.commander.impl.expression.intermediaries.ConstantBooleanExpression; 10 + import me.melontini.commander.impl.expression.intermediaries.DynamicBooleanExpression; 11 + import me.melontini.commander.impl.expression.intermediaries.NegatedBooleanExpression; 12 + import net.minecraft.loot.context.LootContext; 13 + import org.jetbrains.annotations.ApiStatus; 14 + import org.jetbrains.annotations.Contract; 15 + import org.jetbrains.annotations.NotNull; 16 + import org.jetbrains.annotations.Nullable; 17 + 18 + /** 19 + * A simple {@code context -> boolean} functions, which is encoded as either a boolean or an expression. 20 + * <p>Can be used as a substitute for {@link Codec#BOOL} if {@link LootContext} is available</p> 21 + * 22 + * @see Expression 23 + */ 24 + @ApiStatus.NonExtendable 25 + public interface BooleanExpression 26 + extends Predicate<LootContext>, BiPredicate<LootContext, @Nullable Map<String, ?>> { 27 + 28 + Codec<BooleanExpression> CODEC = Codec.either(Codec.BOOL, Codec.STRING) 29 + .comapFlatMap( 30 + (either) -> either.map(b -> DataResult.success(constant(b)), s -> Expression.parse(s) 31 + .map(BooleanExpression::of)), 32 + BooleanExpression::toSource); 33 + 34 + default boolean asBoolean(LootContext context) { 35 + return this.asBoolean(context, null); 36 + } 37 + 38 + boolean asBoolean(LootContext context, @Nullable Map<String, ?> parameters); 39 + 40 + Either<Boolean, String> toSource(); 41 + 42 + @Contract("_ -> new") 43 + static @NotNull BooleanExpression constant(boolean b) { 44 + return b ? ConstantBooleanExpression.TRUE : ConstantBooleanExpression.FALSE; 45 + } 46 + 47 + @Contract("_ -> new") 48 + static @NotNull BooleanExpression of(Expression expression) { 49 + return new DynamicBooleanExpression(Either.right(expression.original()), expression); 50 + } 51 + 52 + @Override 53 + default boolean test(LootContext context, @Nullable Map<String, ?> parameters) { 54 + return this.asBoolean(context, parameters); 55 + } 56 + 57 + @Override 58 + default boolean test(LootContext context) { 59 + return this.asBoolean(context); 60 + } 61 + 62 + @Override 63 + default @NotNull BooleanExpression negate() { 64 + if (this instanceof ConstantBooleanExpression cbe) return constant(!cbe.asBoolean(null)); 65 + return new NegatedBooleanExpression(this); 66 + } 67 + }
+50
src/main/java/me/melontini/commander/api/expression/BrigadierMacro.java
··· 1 + package me.melontini.commander.api.expression; 2 + 3 + import com.mojang.serialization.Codec; 4 + import com.mojang.serialization.DataResult; 5 + import java.util.Map; 6 + import java.util.function.Function; 7 + import me.melontini.commander.impl.expression.macro.PatternParser; 8 + import net.minecraft.loot.context.LootContext; 9 + import org.jetbrains.annotations.ApiStatus; 10 + import org.jetbrains.annotations.Nullable; 11 + 12 + /** 13 + * A special type of string function with support for {@code ${{}}} macros. 14 + * <p>The main purpose is to enable command macros in {@code commander:commands}, but can be used anywhere else.</p> 15 + */ 16 + @ApiStatus.NonExtendable 17 + public interface BrigadierMacro extends Function<LootContext, String> { 18 + 19 + Codec<BrigadierMacro> CODEC = 20 + Codec.STRING.comapFlatMap(BrigadierMacro::parse, BrigadierMacro::original); 21 + 22 + static DataResult<BrigadierMacro> parse(String input) { 23 + return PatternParser.parse(input); 24 + } 25 + 26 + /** 27 + * @return The command string with expression results inserted as strings. 28 + * @see Expression#eval(LootContext) 29 + */ 30 + default String build(LootContext context) { 31 + return this.build(context, null); 32 + } 33 + 34 + /** 35 + * @see #build(LootContext) 36 + * @see Expression#eval(LootContext, Map) 37 + */ 38 + @ApiStatus.Experimental 39 + String build(LootContext context, @Nullable Map<String, Object> params); 40 + 41 + /** 42 + * @return The original input string. 43 + */ 44 + String original(); 45 + 46 + @Override 47 + default String apply(LootContext context) { 48 + return build(context); 49 + } 50 + }
+104
src/main/java/me/melontini/commander/api/expression/Expression.java
··· 1 + package me.melontini.commander.api.expression; 2 + 3 + import com.ezylang.evalex.data.types.*; 4 + import com.mojang.serialization.Codec; 5 + import com.mojang.serialization.DataResult; 6 + import java.math.BigDecimal; 7 + import java.time.Duration; 8 + import java.time.Instant; 9 + import java.util.Map; 10 + import java.util.function.Function; 11 + import me.melontini.commander.impl.expression.EvalUtils; 12 + import me.melontini.commander.impl.expression.extensions.ReflectiveValueConverter; 13 + import net.minecraft.loot.context.LootContext; 14 + import org.jetbrains.annotations.ApiStatus; 15 + import org.jetbrains.annotations.NotNull; 16 + import org.jetbrains.annotations.Nullable; 17 + 18 + @ApiStatus.NonExtendable 19 + public interface Expression extends Function<LootContext, Expression.Result> { 20 + 21 + Codec<Expression> CODEC = Codec.STRING.comapFlatMap(Expression::parse, Expression::original); 22 + 23 + static DataResult<Expression> parse(String expression) { 24 + return (DataResult<Expression>) (Object) EvalUtils.parseExpression(expression); 25 + } 26 + 27 + default Result eval(LootContext context) { 28 + return this.eval(context, null); 29 + } 30 + 31 + /** 32 + * Evaluates expressions with additional parameters. 33 + * Parameters must be consistent, if something is unavailable - use {@link Result#NULL}. 34 + * Otherwise, expressions could start failing and using the {@code ?} operator will be impossible. 35 + * 36 + * @return The evaluation {@link Result}. 37 + * @see #eval(LootContext) 38 + */ 39 + @NotNull Result eval(LootContext context, @Nullable Map<String, ?> parameters); 40 + 41 + /** 42 + * @return The original expression string. 43 + */ 44 + String original(); 45 + 46 + @ApiStatus.NonExtendable 47 + interface Result { 48 + 49 + Result NULL = (Result) (Object) NullValue.of(); 50 + 51 + static Result convert(Object o) { 52 + return (Result) (Object) ReflectiveValueConverter.convert(o); 53 + } 54 + 55 + static Result convert(BigDecimal decimal) { 56 + return (Result) (Object) NumberValue.of(decimal); 57 + } 58 + 59 + static Result convert(boolean bool) { 60 + return (Result) (Object) BooleanValue.of(bool); 61 + } 62 + 63 + static Result convert(String string) { 64 + return (Result) (Object) StringValue.of(string); 65 + } 66 + 67 + static Result convert(Instant instant) { 68 + return (Result) (Object) DateTimeValue.of(instant); 69 + } 70 + 71 + static Result convert(Duration duration) { 72 + return (Result) (Object) DurationValue.of(duration); 73 + } 74 + 75 + @Nullable BigDecimal getAsDecimal(); 76 + 77 + boolean getAsBoolean(); 78 + 79 + @Nullable String getAsString(); 80 + 81 + @Nullable Instant getAsInstant(); 82 + 83 + @Nullable Duration getAsDuration(); 84 + 85 + boolean isDecimalValue(); 86 + 87 + boolean isBooleanValue(); 88 + 89 + boolean isStringValue(); 90 + 91 + boolean isInstantValue(); 92 + 93 + boolean isDurationValue(); 94 + 95 + boolean isNullValue(); 96 + 97 + @Nullable Object getValue(); 98 + } 99 + 100 + @Override 101 + default Result apply(LootContext context) { 102 + return this.eval(context); 103 + } 104 + }
+34
src/main/java/me/melontini/commander/api/expression/ExpressionLibrary.java
··· 1 + package me.melontini.commander.api.expression; 2 + 3 + import java.util.Map; 4 + import me.melontini.commander.impl.expression.library.ExpressionLibraryLoader; 5 + import net.minecraft.server.MinecraftServer; 6 + import net.minecraft.util.Identifier; 7 + import org.jetbrains.annotations.ApiStatus; 8 + import org.jetbrains.annotations.Nullable; 9 + import org.jetbrains.annotations.UnmodifiableView; 10 + 11 + /** 12 + * Provides access to user defined expression library. 13 + */ 14 + @ApiStatus.NonExtendable 15 + public interface ExpressionLibrary { 16 + 17 + static ExpressionLibrary get(MinecraftServer server) { 18 + return server.dm$getReloader(ExpressionLibraryLoader.RELOADER); 19 + } 20 + 21 + /** 22 + * Internally used by the {@code library} container and brigadier commands. 23 + * @param id The expression identifier. 24 + * @return {@link Expression} with the following ID or null if not present. 25 + */ 26 + @Nullable Expression getExpression(Identifier id); 27 + 28 + /** 29 + * Internally used to append command suggestions. 30 + * @return All expressions in the user library. 31 + */ 32 + @UnmodifiableView 33 + Map<Identifier, Expression> allExpressions(); 34 + }
+67
src/main/java/me/melontini/commander/api/expression/LongExpression.java
··· 1 + package me.melontini.commander.api.expression; 2 + 3 + import com.mojang.datafixers.util.Either; 4 + import com.mojang.serialization.Codec; 5 + import com.mojang.serialization.DataResult; 6 + import java.util.Map; 7 + import java.util.function.ToLongBiFunction; 8 + import java.util.function.ToLongFunction; 9 + import me.melontini.commander.impl.expression.intermediaries.ConstantLongExpression; 10 + import me.melontini.commander.impl.expression.intermediaries.DynamicLongExpression; 11 + import net.minecraft.loot.context.LootContext; 12 + import org.jetbrains.annotations.ApiStatus; 13 + import org.jetbrains.annotations.Contract; 14 + import org.jetbrains.annotations.NotNull; 15 + import org.jetbrains.annotations.Nullable; 16 + 17 + /** 18 + * A simple {@code context -> long} functions, which is encoded as either a long or an expression. 19 + * <p>Can be used as a substitute for {@link Codec#LONG} if {@link LootContext} is available</p> 20 + * 21 + * @see Expression 22 + */ 23 + @ApiStatus.NonExtendable 24 + public interface LongExpression 25 + extends ToLongFunction<LootContext>, ToLongBiFunction<LootContext, @Nullable Map<String, ?>> { 26 + 27 + Codec<LongExpression> CODEC = Codec.either(Codec.LONG, Codec.STRING) 28 + .comapFlatMap( 29 + (either) -> either.map(b -> DataResult.success(constant(b)), s -> Expression.parse(s) 30 + .map(LongExpression::of)), 31 + LongExpression::toSource); 32 + 33 + default int asInt(LootContext context) { 34 + return (int) this.asLong(context); 35 + } 36 + 37 + default long asLong(LootContext context) { 38 + return this.asLong(context, null); 39 + } 40 + 41 + default int asInt(LootContext context, @Nullable Map<String, ?> parameters) { 42 + return (int) this.asLong(context, parameters); 43 + } 44 + 45 + long asLong(LootContext context, @Nullable Map<String, ?> parameters); 46 + 47 + Either<Long, String> toSource(); 48 + 49 + @Contract("_ -> new") 50 + static @NotNull LongExpression constant(long j) { 51 + return new ConstantLongExpression(Either.left(j), j); 52 + } 53 + 54 + static @NotNull LongExpression of(Expression expression) { 55 + return new DynamicLongExpression(Either.right(expression.original()), expression); 56 + } 57 + 58 + @Override 59 + default long applyAsLong(LootContext context, @Nullable Map<String, ?> parameters) { 60 + return this.asLong(context, parameters); 61 + } 62 + 63 + @Override 64 + default long applyAsLong(LootContext context) { 65 + return this.asLong(context); 66 + } 67 + }
+19
src/main/java/me/melontini/commander/api/expression/LootContextParameterRegistry.java
··· 1 + package me.melontini.commander.api.expression; 2 + 3 + import lombok.NonNull; 4 + import me.melontini.commander.impl.event.data.types.ExtractionTypes; 5 + import net.minecraft.loot.context.LootContextParameter; 6 + import org.jetbrains.annotations.ApiStatus; 7 + 8 + /** 9 + * A registry for {@link LootContextParameter}. Parameters already come with an identifier, so specifying it separately is unnecessary. 10 + */ 11 + @ApiStatus.Experimental 12 + public class LootContextParameterRegistry { 13 + 14 + public static void register(LootContextParameter<?> @NonNull ... parameters) { 15 + for (LootContextParameter<?> parameter : parameters) { 16 + ExtractionTypes.register(parameter); 17 + } 18 + } 19 + }
+19
src/main/java/me/melontini/commander/api/expression/extensions/AbstractProxyMap.java
··· 1 + package me.melontini.commander.api.expression.extensions; 2 + 3 + import java.util.AbstractMap; 4 + import me.melontini.commander.api.expression.Expression; 5 + import org.jetbrains.annotations.ApiStatus; 6 + 7 + /** 8 + * @see ProxyMap 9 + */ 10 + @ApiStatus.Experimental 11 + public abstract class AbstractProxyMap extends AbstractMap<String, Expression.Result> 12 + implements ProxyMap { 13 + 14 + @Override 15 + public abstract boolean containsKey(Object key); 16 + 17 + @Override 18 + public abstract Expression.Result get(Object key); 19 + }
+28
src/main/java/me/melontini/commander/api/expression/extensions/CustomDataAccessor.java
··· 1 + package me.melontini.commander.api.expression.extensions; 2 + 3 + import me.melontini.commander.api.expression.Expression; 4 + import net.minecraft.loot.context.LootContext; 5 + import org.jetbrains.annotations.ApiStatus; 6 + import org.jetbrains.annotations.Nullable; 7 + 8 + /** 9 + * A contextual data accessor. Can be implemented on objects to automatically support expression access.<br/> 10 + * You might prefer using {@link ProxyMap} if your implementation is backed by a map. 11 + */ 12 + @ApiStatus.OverrideOnly 13 + @ApiStatus.Experimental 14 + public interface CustomDataAccessor { 15 + 16 + /** 17 + * Returns {@link Expression.Result} or null if there's no such field. 18 + * {@code null} and {@link Expression.Result#NULL} mean different things here: 19 + * <ul> 20 + * <li>{@link Expression.Result#NULL} represents a <b>present</b> value which is null.</li> 21 + * <li>{@code null} represents no value.</li> 22 + * </ul> 23 + * @param variable The requested variable or field. 24 + * @param context The current loot context. 25 + * @return {@link Expression.Result} or null if there's no such field. 26 + */ 27 + @Nullable Expression.Result getExpressionData(String variable, LootContext context) throws Exception; 28 + }
+42
src/main/java/me/melontini/commander/api/expression/extensions/CustomFields.java
··· 1 + package me.melontini.commander.api.expression.extensions; 2 + 3 + import java.util.function.BiFunction; 4 + import java.util.function.Function; 5 + import lombok.experimental.UtilityClass; 6 + import me.melontini.commander.impl.expression.extensions.ReflectiveMapStructure; 7 + import me.melontini.commander.impl.expression.extensions.ReflectiveValueConverter; 8 + import net.minecraft.loot.context.LootContext; 9 + import org.jetbrains.annotations.ApiStatus; 10 + 11 + @ApiStatus.Experimental 12 + @UtilityClass 13 + public class CustomFields { 14 + 15 + /** 16 + * Adds virtual fields to objects. This works only if the object is not handled by object converters. <br/> 17 + * For better UX, it's best to return types which are handled by converters. 18 + */ 19 + public static <C> void addVirtualField( 20 + Class<C> cls, String name, BiFunction<C, LootContext, Object> accessor) { 21 + ReflectiveMapStructure.addField(cls, name, accessor); 22 + } 23 + 24 + public static <C> void addVirtualField(Class<C> cls, String name, Function<C, Object> accessor) { 25 + ReflectiveMapStructure.addField(cls, name, (c, lootContext) -> accessor.apply(c)); 26 + } 27 + 28 + /** 29 + * Registers an {@link ObjectConverter} to convert objects to types supported by expressions. <br/> 30 + * This is useful for wrappers and complex map-like objects. 31 + */ 32 + public static void registerConverter(ObjectConverter converter) { 33 + ReflectiveValueConverter.registerConverter(Integer.MAX_VALUE, converter); 34 + } 35 + 36 + /** 37 + * @see CustomFields#registerConverter(ObjectConverter) 38 + */ 39 + public static void registerConverter(int priority, ObjectConverter converter) { 40 + ReflectiveValueConverter.registerConverter(priority, converter); 41 + } 42 + }
+61
src/main/java/me/melontini/commander/api/expression/extensions/ObjectConverter.java
··· 1 + package me.melontini.commander.api.expression.extensions; 2 + 3 + import java.util.function.Function; 4 + import me.melontini.commander.api.expression.Expression; 5 + import org.jetbrains.annotations.ApiStatus; 6 + import org.jetbrains.annotations.NotNull; 7 + 8 + /** 9 + * An object converter converts objects to types supported by expressions. 10 + */ 11 + @ApiStatus.OverrideOnly 12 + @ApiStatus.Experimental 13 + public interface ObjectConverter { 14 + 15 + static ObjectConverter ofClasses( 16 + Function<Object, Expression.Result> function, Class<?>... classes) { 17 + return new ObjectConverter() { 18 + @Override 19 + public Expression.@NotNull Result convert(@NotNull Object object) { 20 + return function.apply(object); 21 + } 22 + 23 + @Override 24 + public boolean canConvert(@NotNull Object object) { 25 + for (Class<?> aClass : classes) { 26 + if (aClass.isInstance(object)) return true; 27 + } 28 + return false; 29 + } 30 + }; 31 + } 32 + 33 + static <C> ObjectConverter ofClass(Class<C> cls, Function<C, Expression.Result> function) { 34 + return new ObjectConverter() { 35 + @Override 36 + public Expression.@NotNull Result convert(@NotNull Object object) { 37 + return function.apply((C) object); 38 + } 39 + 40 + @Override 41 + public boolean canConvert(@NotNull Object object) { 42 + return cls.isInstance(object); 43 + } 44 + }; 45 + } 46 + 47 + Expression.@NotNull Result convert(@NotNull Object object); 48 + 49 + /** 50 + * You must be absolutely sure that this object is convertible. 51 + * Do not return {@code null} in {@link #convert(Object)}. 52 + * @param object Non-null object of unspecified type. 53 + * @return Whenever the object can be converted by the converted. 54 + */ 55 + boolean canConvert(@NotNull Object object); 56 + 57 + default IllegalArgumentException illegalArgument(Object object) { 58 + return new IllegalArgumentException( 59 + "Unsupported data type '" + object.getClass().getName() + "'"); 60 + } 61 + }
+11
src/main/java/me/melontini/commander/api/expression/extensions/ProxyMap.java
··· 1 + package me.melontini.commander.api.expression.extensions; 2 + 3 + import java.util.Map; 4 + import me.melontini.commander.api.expression.Expression; 5 + 6 + /** 7 + * A special type of map which guarantees the type of {@code <String, Expression.Resul>}.<br/> 8 + * These maps must implement 3 methods: {@link #containsKey(Object)}, {@link #get(Object)} and {@link #entrySet()}. 9 + * It's recommended to lazily convert map entries, especially if the map is large. 10 + */ 11 + public interface ProxyMap extends Map<String, Expression.Result> {}
+94
src/main/java/me/melontini/commander/api/util/EventExecutors.java
··· 1 + package me.melontini.commander.api.util; 2 + 3 + import java.util.List; 4 + import java.util.Objects; 5 + import java.util.function.Supplier; 6 + import lombok.NonNull; 7 + import lombok.experimental.UtilityClass; 8 + import me.melontini.commander.api.command.Command; 9 + import me.melontini.commander.api.event.EventContext; 10 + import me.melontini.commander.api.event.EventKey; 11 + import me.melontini.commander.api.event.EventType; 12 + import me.melontini.commander.api.event.Subscription; 13 + import me.melontini.commander.impl.event.data.types.EventTypes; 14 + import me.melontini.dark_matter.api.base.util.MakeSure; 15 + import net.fabricmc.fabric.api.util.TriState; 16 + import net.minecraft.loot.context.LootContext; 17 + import net.minecraft.util.ActionResult; 18 + import net.minecraft.world.World; 19 + import org.jetbrains.annotations.Nullable; 20 + 21 + /** 22 + * A utility to execute generic event types. 23 + * Cannot be used if the {@link EventType} specifies parameters. 24 + */ 25 + @UtilityClass 26 + public class EventExecutors { 27 + public static void runVoid(EventType type, @NonNull World world, Supplier<LootContext> supplier) { 28 + if (world.isClient()) return; 29 + 30 + List<Command.Conditioned> subscribers = Objects.requireNonNull( 31 + Subscription.getData(MakeSure.notNull(world.getServer()), type), 32 + () -> "Failed to get subscribers for event %s!".formatted(EventTypes.getId(type))); 33 + if (subscribers.isEmpty()) return; 34 + 35 + EventContext context = EventContext.builder(type) 36 + .addParameter(EventKey.LOOT_CONTEXT, supplier.get()) 37 + .build(); 38 + for (Command.Conditioned subscriber : subscribers) subscriber.execute(context); 39 + } 40 + 41 + public static boolean runBoolean( 42 + EventType type, boolean def, @NonNull World world, Supplier<LootContext> supplier) { 43 + if (world.isClient()) return def; 44 + 45 + List<Command.Conditioned> subscribers = Objects.requireNonNull( 46 + Subscription.getData(MakeSure.notNull(world.getServer()), type), 47 + () -> "Failed to get subscribers for event %s!".formatted(EventTypes.getId(type))); 48 + if (subscribers.isEmpty()) return def; 49 + 50 + EventContext context = EventContext.builder(type) 51 + .addParameter(EventKey.LOOT_CONTEXT, supplier.get()) 52 + .build(); 53 + for (Command.Conditioned subscriber : subscribers) { 54 + subscriber.execute(context); 55 + boolean val = Boolean.TRUE.equals(context.getReturnValue(def)); 56 + if (val != def) return val; 57 + } 58 + return def; 59 + } 60 + 61 + public static boolean runBoolean( 62 + EventType type, @NonNull World world, Supplier<LootContext> supplier) { 63 + return runBoolean(type, true, world, supplier); 64 + } 65 + 66 + public static <T extends Enum<T>> T runEnum( 67 + EventType type, @Nullable T def, @NonNull World world, Supplier<LootContext> supplier) { 68 + if (world.isClient()) return def; 69 + 70 + List<Command.Conditioned> subscribers = Objects.requireNonNull( 71 + Subscription.getData(MakeSure.notNull(world.getServer()), type), 72 + () -> "Failed to get subscribers for event %s!".formatted(EventTypes.getId(type))); 73 + if (subscribers.isEmpty()) return def; 74 + 75 + var context = EventContext.builder(type) 76 + .addParameter(EventKey.LOOT_CONTEXT, supplier.get()) 77 + .build(); 78 + for (Command.Conditioned subscriber : subscribers) { 79 + subscriber.execute(context); 80 + T r = context.getReturnValue(def); 81 + if (r != def) return r; 82 + } 83 + return def; 84 + } 85 + 86 + public static ActionResult runActionResult( 87 + EventType type, World world, Supplier<LootContext> supplier) { 88 + return runEnum(type, ActionResult.PASS, world, supplier); 89 + } 90 + 91 + public static TriState runTriState(EventType type, World world, Supplier<LootContext> supplier) { 92 + return runEnum(type, TriState.DEFAULT, world, supplier); 93 + } 94 + }
+181
src/main/java/me/melontini/commander/impl/Commander.java
··· 1 + package me.melontini.commander.impl; 2 + 3 + import static java.util.concurrent.CompletableFuture.*; 4 + import static net.minecraft.loot.context.LootContextParameters.*; 5 + 6 + import com.google.gson.JsonObject; 7 + import com.google.gson.JsonParser; 8 + import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 9 + import java.io.IOException; 10 + import java.io.InputStreamReader; 11 + import java.nio.charset.StandardCharsets; 12 + import java.nio.file.*; 13 + import java.util.function.Supplier; 14 + import lombok.Getter; 15 + import lombok.experimental.Accessors; 16 + import lombok.extern.log4j.Log4j2; 17 + import me.melontini.commander.api.expression.LootContextParameterRegistry; 18 + import me.melontini.commander.impl.builtin.BuiltInCommands; 19 + import me.melontini.commander.impl.builtin.BuiltInEvents; 20 + import me.melontini.commander.impl.builtin.BuiltInSelectors; 21 + import me.melontini.commander.impl.event.data.DynamicEventManager; 22 + import me.melontini.commander.impl.expression.EvalUtils; 23 + import me.melontini.commander.impl.expression.extensions.convert.RegistryAccessStruct; 24 + import me.melontini.commander.impl.expression.library.ExpressionLibraryLoader; 25 + import me.melontini.commander.impl.util.NbtCodecs; 26 + import me.melontini.commander.impl.util.loot.ArithmeticaLootNumberProvider; 27 + import me.melontini.commander.impl.util.loot.ExpressionLootCondition; 28 + import me.melontini.commander.impl.util.mappings.AmbiguousRemapper; 29 + import me.melontini.commander.impl.util.mappings.MappingKeeper; 30 + import me.melontini.commander.impl.util.mappings.MinecraftDownloader; 31 + import me.melontini.dark_matter.api.base.util.Exceptions; 32 + import me.melontini.dark_matter.api.base.util.Result; 33 + import me.melontini.dark_matter.api.data.loading.ServerReloadersEvent; 34 + import me.melontini.dark_matter.api.minecraft.util.TextUtil; 35 + import net.fabricmc.fabric.api.attachment.v1.AttachmentRegistry; 36 + import net.fabricmc.fabric.api.attachment.v1.AttachmentType; 37 + import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; 38 + import net.minecraft.loot.condition.LootConditionType; 39 + import net.minecraft.loot.provider.number.LootNumberProviderType; 40 + import net.minecraft.nbt.NbtCompound; 41 + import net.minecraft.registry.Registries; 42 + import net.minecraft.registry.Registry; 43 + import net.minecraft.util.Identifier; 44 + import net.minecraft.util.Util; 45 + 46 + @SuppressWarnings("UnstableApiUsage") 47 + @Accessors(fluent = true) 48 + @Log4j2 49 + public class Commander { 50 + 51 + public static final LootNumberProviderType ARITHMETICA_PROVIDER = Registry.register( 52 + Registries.LOOT_NUMBER_PROVIDER_TYPE, 53 + id("arithmetica"), 54 + new LootNumberProviderType(ArithmeticaLootNumberProvider.CODEC)); 55 + public static final LootConditionType EXPRESSION_CONDITION = Registry.register( 56 + Registries.LOOT_CONDITION_TYPE, 57 + id("expression"), 58 + new LootConditionType(ExpressionLootCondition.CODEC)); 59 + 60 + private static final Path BASE_PATH = 61 + Path.of(System.getProperty("user.home")).resolve(".commander"); 62 + public static final String MINECRAFT_VERSION = getVersion(); 63 + public static final Path COMMANDER_PATH = BASE_PATH.resolve(MINECRAFT_VERSION); 64 + 65 + public static final AttachmentType<NbtCompound> DATA_ATTACHMENT = 66 + AttachmentRegistry.<NbtCompound>builder() 67 + .initializer(NbtCompound::new) 68 + .persistent(NbtCodecs.COMPOUND_CODEC) 69 + .buildAndRegister(id("persistent")); 70 + 71 + public static final DynamicCommandExceptionType EXPRESSION_EXCEPTION = 72 + new DynamicCommandExceptionType(object -> TextUtil.literal("Failed to evaluate: " + object)); 73 + 74 + @Getter 75 + private AmbiguousRemapper mappingKeeper; 76 + 77 + public static Identifier id(String path) { 78 + return Identifier.of("commander", path); 79 + } 80 + 81 + private static Supplier<Commander> instance = () -> { 82 + throw new NullPointerException("Commander instance requested too early!"); 83 + }; 84 + 85 + public static void init() { 86 + var cmd = new Commander(); 87 + cmd.onInitialize(); 88 + instance = () -> cmd; 89 + } 90 + 91 + public static Commander get() { 92 + return instance.get(); 93 + } 94 + 95 + public void onInitialize() { 96 + if (!Files.exists(COMMANDER_PATH)) { 97 + Exceptions.run(() -> Files.createDirectories(COMMANDER_PATH)); 98 + try { 99 + // Some users don't like junk in their home folder and windows is very special. 100 + if (BASE_PATH.getFileSystem().supportedFileAttributeViews().contains("dos")) 101 + Files.setAttribute(BASE_PATH, "dos:hidden", Boolean.TRUE, LinkOption.NOFOLLOW_LINKS); 102 + } catch (IOException ignored) { 103 + log.warn("Failed to hide the .commander folder"); 104 + } 105 + } 106 + 107 + ServerReloadersEvent.EVENT.register(context -> { 108 + this.resetCaches(); 109 + context.register(new ExpressionLibraryLoader()); 110 + context.register(new DynamicEventManager()); 111 + }); 112 + 113 + ServerLifecycleEvents.SERVER_STOPPING.register(server -> this.resetCaches()); 114 + 115 + EvalUtils.init(); 116 + this.loadMappings(); 117 + 118 + // Init built-ins 119 + BuiltInEvents.init(); 120 + BuiltInCommands.init(); 121 + BuiltInSelectors.init(); 122 + 123 + // Register vanilla parameter types 124 + LootContextParameterRegistry.register( 125 + ORIGIN, 126 + TOOL, 127 + THIS_ENTITY, 128 + LAST_DAMAGE_PLAYER, 129 + ATTACKING_ENTITY, 130 + DIRECT_ATTACKING_ENTITY, 131 + DAMAGE_SOURCE, 132 + EXPLOSION_RADIUS, 133 + BLOCK_STATE, 134 + BLOCK_ENTITY, 135 + ENCHANTMENT_LEVEL, 136 + ENCHANTMENT_ACTIVE); 137 + } 138 + 139 + private void resetCaches() { 140 + EvalUtils.resetCache(); 141 + RegistryAccessStruct.resetCache(); 142 + } 143 + 144 + private void loadMappings() { 145 + if (MappingKeeper.NAMESPACE.equals("mojang")) { 146 + mappingKeeper = (cls, name) -> name; // Nothing to remap. 147 + return; 148 + } 149 + 150 + var offTarget = supplyAsync(MappingKeeper::loadOffTarget, Util.getMainWorkerExecutor()); 151 + var offMojmap = runAsync(MinecraftDownloader::downloadMappings, Util.getMainWorkerExecutor()) 152 + .thenApplyAsync(unused -> MappingKeeper.loadOffMojmap(), Util.getMainWorkerExecutor()); 153 + 154 + mappingKeeper = Exceptions.<AmbiguousRemapper>supplyAsResult(() -> 155 + new MappingKeeper(MappingKeeper.loadMojmapTarget(offMojmap.join(), offTarget.join()))) 156 + .ifErrPresent(t -> log.error( 157 + "Failed to download and prepare mappings! Data access remapping will not work!!!", 158 + Exceptions.unwrap(t))) 159 + .flatmapErr(t -> Result.ok((cls, name) -> name)) 160 + .value() 161 + .orElseThrow(); 162 + } 163 + 164 + // Returns the current MC version parsed from included version.json 165 + private static String getVersion() { 166 + return Exceptions.supplyAsResult(() -> { 167 + try (var stream = new InputStreamReader( 168 + MinecraftDownloader.class.getResourceAsStream("/version.json"), 169 + StandardCharsets.UTF_8)) { 170 + JsonObject o = JsonParser.parseReader(stream).getAsJsonObject(); 171 + return o.getAsJsonPrimitive("id").getAsString(); 172 + } 173 + }) 174 + .ifErrPresent(e -> { 175 + throw new IllegalStateException( 176 + "Failed to read 'version.json' included in the Minecraft jar!"); 177 + }) 178 + .value() 179 + .orElseThrow(); 180 + } 181 + }
+19
src/main/java/me/melontini/commander/impl/builtin/BrigadierCommands.java
··· 1 + package me.melontini.commander.impl.builtin; 2 + 3 + import lombok.experimental.UtilityClass; 4 + import me.melontini.commander.impl.builtin.brigadier.ArithmeticaCommand; 5 + import me.melontini.commander.impl.builtin.brigadier.DataCommand; 6 + import me.melontini.commander.impl.builtin.brigadier.ExplodeCommand; 7 + import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; 8 + 9 + @UtilityClass 10 + public class BrigadierCommands { 11 + 12 + public static void init() { 13 + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { 14 + ExplodeCommand.register(dispatcher); 15 + ArithmeticaCommand.register(dispatcher); 16 + DataCommand.register(dispatcher); 17 + }); 18 + } 19 + }
+35
src/main/java/me/melontini/commander/impl/builtin/BuiltInCommands.java
··· 1 + package me.melontini.commander.impl.builtin; 2 + 3 + import static me.melontini.commander.impl.Commander.id; 4 + 5 + import lombok.experimental.UtilityClass; 6 + import me.melontini.commander.api.command.CommandType; 7 + import me.melontini.commander.impl.builtin.commands.action.CancelCommand; 8 + import me.melontini.commander.impl.builtin.commands.action.CommandCommand; 9 + import me.melontini.commander.impl.builtin.commands.action.PrintArithmeticaCommand; 10 + import me.melontini.commander.impl.builtin.commands.action.PrintCommand; 11 + import me.melontini.commander.impl.builtin.commands.logic.AllOfCommand; 12 + import me.melontini.commander.impl.builtin.commands.logic.AnyOfCommand; 13 + import me.melontini.commander.impl.builtin.commands.logic.DefaultedCommand; 14 + import me.melontini.commander.impl.builtin.commands.logic.RandomCommand; 15 + 16 + @UtilityClass 17 + public class BuiltInCommands { 18 + 19 + public static final CommandType RANDOM = CommandType.register(id("random"), RandomCommand.CODEC); 20 + public static final CommandType ALL_OF = CommandType.register(id("all_of"), AllOfCommand.CODEC); 21 + public static final CommandType ANY_OF = CommandType.register(id("any_of"), AnyOfCommand.CODEC); 22 + public static final CommandType DEFAULTED = 23 + CommandType.register(id("defaulted"), DefaultedCommand.CODEC); 24 + 25 + public static final CommandType CANCEL = CommandType.register(id("cancel"), CancelCommand.CODEC); 26 + public static final CommandType COMMANDS = 27 + CommandType.register(id("commands"), CommandCommand.CODEC); 28 + public static final CommandType PRINT = CommandType.register(id("print"), PrintCommand.CODEC); 29 + public static final CommandType PRINT_ARITHMETICA = 30 + CommandType.register(id("print_arithmetica"), PrintArithmeticaCommand.CODEC); 31 + 32 + public static void init() { 33 + BrigadierCommands.init(); 34 + } 35 + }
+18
src/main/java/me/melontini/commander/impl/builtin/BuiltInEvents.java
··· 1 + package me.melontini.commander.impl.builtin; 2 + 3 + import lombok.experimental.UtilityClass; 4 + import me.melontini.commander.impl.builtin.events.EntityEvents; 5 + import me.melontini.commander.impl.builtin.events.PlayerEvents; 6 + import me.melontini.commander.impl.builtin.events.ServerLifecycle; 7 + import me.melontini.commander.impl.builtin.events.ServerTick; 8 + 9 + @UtilityClass 10 + public class BuiltInEvents { 11 + 12 + public static void init() { 13 + ServerLifecycle.init(); 14 + ServerTick.init(); 15 + EntityEvents.init(); 16 + PlayerEvents.init(); 17 + } 18 + }
+100
src/main/java/me/melontini/commander/impl/builtin/BuiltInSelectors.java
··· 1 + package me.melontini.commander.impl.builtin; 2 + 3 + import static me.melontini.commander.impl.Commander.id; 4 + 5 + import lombok.experimental.UtilityClass; 6 + import me.melontini.commander.api.command.Selector; 7 + import me.melontini.dark_matter.api.base.util.Utilities; 8 + import me.melontini.dark_matter.api.minecraft.util.TextUtil; 9 + import net.minecraft.entity.Entity; 10 + import net.minecraft.loot.context.LootContextParameters; 11 + import net.minecraft.server.command.ServerCommandSource; 12 + import net.minecraft.server.world.ServerWorld; 13 + import net.minecraft.util.Identifier; 14 + import net.minecraft.util.math.Vec2f; 15 + import net.minecraft.util.math.Vec3d; 16 + 17 + @UtilityClass 18 + @SuppressWarnings("unused") 19 + public final class BuiltInSelectors { 20 + 21 + public static final Selector SERVER = 22 + Selector.register(mc("server"), context -> context.getWorld().getServer().getCommandSource()); 23 + public static final Selector ORIGIN = Selector.register(mc("origin"), context -> { 24 + var world = context.getWorld(); 25 + var o = context.requireParameter(LootContextParameters.ORIGIN); 26 + 27 + return new ServerCommandSource( 28 + world.getServer(), 29 + o, 30 + Vec2f.ZERO, 31 + world, 32 + 4, 33 + world.getRegistryKey().getValue().toString(), 34 + TextUtil.literal(world.getRegistryKey().getValue().toString()), 35 + world.getServer(), 36 + null); 37 + }); 38 + public static final Selector THIS_ENTITY = Selector.register( 39 + mc("this_entity"), 40 + context -> forEntity(context.requireParameter(LootContextParameters.THIS_ENTITY))); 41 + public static final Selector KILLER_ENTITY = Selector.register( 42 + mc("attacking_entity"), 43 + context -> forEntity(context.requireParameter(LootContextParameters.ATTACKING_ENTITY))); 44 + public static final Selector DIRECT_KILLER_ENTITY = Selector.register( 45 + mc("direct_attacking_entity"), 46 + context -> 47 + forEntity(context.requireParameter(LootContextParameters.DIRECT_ATTACKING_ENTITY))); 48 + public static final Selector LAST_DAMAGE_PLAYER = Selector.register( 49 + mc("last_damage_player"), 50 + context -> forEntity(context.requireParameter(LootContextParameters.LAST_DAMAGE_PLAYER))); 51 + public static final Selector BLOCK_ENTITY = Selector.register(mc("block_entity"), context -> { 52 + var be = context.requireParameter(LootContextParameters.BLOCK_ENTITY); 53 + return new ServerCommandSource( 54 + context.getWorld().getServer(), 55 + Vec3d.ofCenter(be.getPos()), 56 + Vec2f.ZERO, 57 + (ServerWorld) be.getWorld(), 58 + 4, 59 + "BlockEntity", 60 + TextUtil.literal("BlockEntity"), 61 + context.getWorld().getServer(), 62 + null); 63 + }); 64 + 65 + public static final Selector DAMAGE_SOURCE_SOURCE = 66 + Selector.register(id("damage_source/source"), context -> { 67 + var s = context.requireParameter(LootContextParameters.DAMAGE_SOURCE).getSource(); 68 + return s != null ? forEntity(s) : null; 69 + }); 70 + public static final Selector DAMAGE_SOURCE_ATTACKER = 71 + Selector.register(id("damage_source/attacker"), context -> { 72 + var s = context.requireParameter(LootContextParameters.DAMAGE_SOURCE).getAttacker(); 73 + return s != null ? forEntity(s) : null; 74 + }); 75 + 76 + public static final Selector RANDOM_PLAYER = Selector.register(id("random_player"), context -> { 77 + var l = context.getWorld().getServer().getPlayerManager().getPlayerList(); 78 + if (l == null || l.isEmpty()) return null; 79 + return forEntity(Utilities.pickAtRandom(l)); 80 + }); 81 + 82 + public static void init() {} 83 + 84 + public static Identifier mc(String string) { 85 + return Identifier.of("minecraft", string); 86 + } 87 + 88 + public static ServerCommandSource forEntity(Entity entity) { 89 + return new ServerCommandSource( 90 + entity.getWorld().getServer(), 91 + entity.getPos(), 92 + new Vec2f(entity.getPitch(), entity.getYaw()), 93 + (ServerWorld) entity.getWorld(), 94 + 4, 95 + entity.getNameForScoreboard(), 96 + entity.getName(), 97 + entity.getWorld().getServer(), 98 + entity); 99 + } 100 + }
+63
src/main/java/me/melontini/commander/impl/builtin/brigadier/ArithmeticaCommand.java
··· 1 + package me.melontini.commander.impl.builtin.brigadier; 2 + 3 + import com.mojang.brigadier.CommandDispatcher; 4 + import com.mojang.brigadier.arguments.StringArgumentType; 5 + import com.mojang.brigadier.context.CommandContext; 6 + import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 + import me.melontini.commander.impl.Commander; 8 + import me.melontini.commander.impl.expression.CmdEvalException; 9 + import me.melontini.commander.impl.expression.EvalUtils; 10 + import me.melontini.commander.impl.expression.macro.PatternParser; 11 + import me.melontini.commander.impl.util.loot.LootUtil; 12 + import net.minecraft.loot.context.LootContextParameterSet; 13 + import net.minecraft.loot.context.LootContextParameters; 14 + import net.minecraft.loot.context.LootContextTypes; 15 + import net.minecraft.server.command.CommandManager; 16 + import net.minecraft.server.command.ServerCommandSource; 17 + import net.minecraft.text.Text; 18 + import org.jetbrains.annotations.Nullable; 19 + 20 + public class ArithmeticaCommand { 21 + 22 + public static void register(CommandDispatcher<ServerCommandSource> dispatcher) { 23 + var cmd = CommandManager.argument("expression", StringArgumentType.string()) 24 + .executes( 25 + context -> execute(context, StringArgumentType.getString(context, "expression"), null)); 26 + 27 + for (String cast : PatternParser.CONVERTERS.keySet()) { 28 + cmd.then(CommandManager.literal(cast) 29 + .executes(context -> 30 + execute(context, StringArgumentType.getString(context, "expression"), cast))); 31 + } 32 + 33 + dispatcher.register(CommandManager.literal("cmd:arithmetica") 34 + .requires(source -> source.hasPermissionLevel(2)) 35 + .then(cmd)); 36 + } 37 + 38 + private static int execute( 39 + CommandContext<ServerCommandSource> context, String expression, @Nullable String cast) 40 + throws CommandSyntaxException { 41 + try { 42 + var r = PatternParser.parseExpression(expression, cast); 43 + if (r.error().isPresent()) 44 + throw Commander.EXPRESSION_EXCEPTION.create(r.error().get().message()); 45 + 46 + context 47 + .getSource() 48 + .sendMessage(Text.literal(EvalUtils.prettyToString(r.result() 49 + .orElseThrow() 50 + .apply( 51 + LootUtil.build(new LootContextParameterSet.Builder( 52 + context.getSource().getWorld()) 53 + .add(LootContextParameters.ORIGIN, context.getSource().getPosition()) 54 + .addOptional( 55 + LootContextParameters.THIS_ENTITY, context.getSource().getEntity()) 56 + .build(LootContextTypes.COMMAND)), 57 + null)))); 58 + return 1; 59 + } catch (CmdEvalException e) { 60 + throw Commander.EXPRESSION_EXCEPTION.create(e.getMessage()); 61 + } 62 + } 63 + }
+160
src/main/java/me/melontini/commander/impl/builtin/brigadier/DataCommand.java
··· 1 + package me.melontini.commander.impl.builtin.brigadier; 2 + 3 + import com.mojang.brigadier.CommandDispatcher; 4 + import com.mojang.brigadier.arguments.StringArgumentType; 5 + import com.mojang.brigadier.builder.LiteralArgumentBuilder; 6 + import com.mojang.brigadier.builder.RequiredArgumentBuilder; 7 + import com.mojang.brigadier.context.CommandContext; 8 + import com.mojang.brigadier.exceptions.CommandSyntaxException; 9 + import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 10 + import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 11 + import java.util.Locale; 12 + import java.util.Map; 13 + import java.util.function.BiFunction; 14 + import java.util.function.Supplier; 15 + import me.melontini.commander.impl.Commander; 16 + import me.melontini.commander.impl.util.DataTarget; 17 + import me.melontini.dark_matter.api.minecraft.util.TextUtil; 18 + import net.fabricmc.fabric.api.attachment.v1.AttachmentTarget; 19 + import net.minecraft.command.argument.BlockPosArgumentType; 20 + import net.minecraft.command.argument.EntityArgumentType; 21 + import net.minecraft.command.argument.NbtElementArgumentType; 22 + import net.minecraft.nbt.AbstractNbtNumber; 23 + import net.minecraft.nbt.NbtElement; 24 + import net.minecraft.nbt.NbtString; 25 + import net.minecraft.server.command.CommandManager; 26 + import net.minecraft.server.command.ServerCommandSource; 27 + 28 + public class DataCommand { 29 + 30 + private static final DynamicCommandExceptionType NO_BE_EXCEPTION = 31 + new DynamicCommandExceptionType( 32 + (a) -> TextUtil.literal("No block entity at position %s".formatted(a))); 33 + private static final DynamicCommandExceptionType NO_KEY_EXCEPTION = 34 + new DynamicCommandExceptionType( 35 + (a) -> TextUtil.literal("No such key '%s' in target".formatted(a))); 36 + private static final SimpleCommandExceptionType WRONG_DATA_TYPE_EXCEPTION = 37 + new SimpleCommandExceptionType(TextUtil.literal("Nbt element must be a string or a number!")); 38 + 39 + private static final Map<DataTarget, Supplier<RequiredArgumentBuilder<ServerCommandSource, ?>>> 40 + ARGS = Map.of( 41 + DataTarget.LEVEL, () -> null, 42 + DataTarget.CHUNK, 43 + () -> CommandManager.argument("position", BlockPosArgumentType.blockPos()), 44 + DataTarget.ENTITY, () -> CommandManager.argument("entity", EntityArgumentType.entity()), 45 + DataTarget.BLOCK_ENTITY, 46 + () -> CommandManager.argument("position", BlockPosArgumentType.blockPos())); 47 + 48 + private static final Map< 49 + DataTarget, PseudoFunction<CommandContext<ServerCommandSource>, AttachmentTarget>> 50 + TO_TARGET = Map.of( 51 + DataTarget.LEVEL, ctx -> ctx.getSource().getWorld(), 52 + DataTarget.CHUNK, 53 + ctx -> ctx.getSource() 54 + .getWorld() 55 + .getChunk(BlockPosArgumentType.getBlockPos(ctx, "position")), 56 + DataTarget.ENTITY, ctx -> EntityArgumentType.getEntity(ctx, "entity"), 57 + DataTarget.BLOCK_ENTITY, 58 + ctx -> { 59 + var pos = BlockPosArgumentType.getBlockPos(ctx, "position"); 60 + var be = ctx.getSource().getWorld().getBlockEntity(pos); 61 + if (be == null) 62 + throw NO_BE_EXCEPTION.create( 63 + "[%s, %s, %s]".formatted(pos.getX(), pos.getY(), pos.getZ())); 64 + return be; 65 + }); 66 + 67 + public static void register(CommandDispatcher<ServerCommandSource> dispatcher) { 68 + var cmd = CommandManager.literal("cmd:data").requires(source -> source.hasPermissionLevel(2)); 69 + 70 + attachCommand( 71 + cmd, 72 + "read", 73 + (target, builder) -> builder.executes(context -> readValue( 74 + context.getSource(), 75 + TO_TARGET.get(target).apply(context), 76 + StringArgumentType.getString(context, "key")))); 77 + attachCommand( 78 + cmd, 79 + "write", 80 + (target, builder) -> 81 + builder.then(CommandManager.argument("data", NbtElementArgumentType.nbtElement()) 82 + .executes(context -> writeValue( 83 + context.getSource(), 84 + TO_TARGET.get(target).apply(context), 85 + StringArgumentType.getString(context, "key"), 86 + NbtElementArgumentType.getNbtElement(context, "data"))))); 87 + attachCommand( 88 + cmd, 89 + "remove", 90 + (target, builder) -> builder.executes(context -> removeValue( 91 + context.getSource(), 92 + TO_TARGET.get(target).apply(context), 93 + StringArgumentType.getString(context, "key")))); 94 + 95 + dispatcher.register(cmd); 96 + } 97 + 98 + private static void attachCommand( 99 + LiteralArgumentBuilder<ServerCommandSource> base, 100 + String name, 101 + BiFunction< 102 + DataTarget, 103 + RequiredArgumentBuilder<ServerCommandSource, ?>, 104 + RequiredArgumentBuilder<ServerCommandSource, ?>> 105 + keyAttachment) { 106 + var command = CommandManager.literal(name); 107 + for (DataTarget value : DataTarget.values()) { 108 + var start = CommandManager.literal(value.name().toLowerCase(Locale.ROOT)); 109 + var keyArg = 110 + keyAttachment.apply(value, CommandManager.argument("key", StringArgumentType.string())); 111 + 112 + var args = ARGS.get(value).get(); 113 + if (args != null) args.then(keyArg); 114 + else args = keyArg; 115 + 116 + command.then(start.then(args)); 117 + } 118 + base.then(command); 119 + } 120 + 121 + private static int removeValue(ServerCommandSource source, AttachmentTarget target, String key) { 122 + var nbt = target.getAttachedOrCreate(Commander.DATA_ATTACHMENT); 123 + if (nbt.contains(key)) { 124 + nbt.remove(key); 125 + source.sendFeedback( 126 + () -> TextUtil.literal("Successfully removed key %s from target!".formatted(key)), false); 127 + return 1; 128 + } 129 + source.sendFeedback(() -> TextUtil.literal("Target has no %s key!".formatted(key)), false); 130 + return 0; 131 + } 132 + 133 + public static int writeValue( 134 + ServerCommandSource source, AttachmentTarget target, String key, NbtElement element) 135 + throws CommandSyntaxException { 136 + var r = target.getAttachedOrCreate(Commander.DATA_ATTACHMENT); 137 + if (element instanceof AbstractNbtNumber || element instanceof NbtString) { 138 + r.put(key, element); 139 + source.sendFeedback( 140 + () -> TextUtil.literal("Successfully wrote value %s to target!".formatted(element)), 141 + false); 142 + return 1; 143 + } 144 + throw WRONG_DATA_TYPE_EXCEPTION.create(); 145 + } 146 + 147 + public static int readValue(ServerCommandSource source, AttachmentTarget target, String key) 148 + throws CommandSyntaxException { 149 + var r = target.getAttachedOrCreate(Commander.DATA_ATTACHMENT).get(key); 150 + if (r == null) { 151 + throw NO_KEY_EXCEPTION.create(key); 152 + } 153 + source.sendFeedback(() -> TextUtil.literal(r.asString()), false); 154 + return 1; 155 + } 156 + 157 + private interface PseudoFunction<T, R> { 158 + R apply(T t) throws CommandSyntaxException; 159 + } 160 + }
+87
src/main/java/me/melontini/commander/impl/builtin/brigadier/ExplodeCommand.java
··· 1 + package me.melontini.commander.impl.builtin.brigadier; 2 + 3 + import com.mojang.brigadier.CommandDispatcher; 4 + import com.mojang.brigadier.arguments.BoolArgumentType; 5 + import com.mojang.brigadier.arguments.FloatArgumentType; 6 + import net.minecraft.command.argument.EntityArgumentType; 7 + import net.minecraft.command.argument.Vec3ArgumentType; 8 + import net.minecraft.entity.Entity; 9 + import net.minecraft.server.command.CommandManager; 10 + import net.minecraft.server.command.ServerCommandSource; 11 + import net.minecraft.util.math.Vec3d; 12 + import net.minecraft.world.World; 13 + import org.jetbrains.annotations.Nullable; 14 + 15 + public class ExplodeCommand { 16 + 17 + public static void register(CommandDispatcher<ServerCommandSource> dispatcher) { 18 + dispatcher.register(CommandManager.literal("cmd:explode") 19 + .requires(source -> source.hasPermissionLevel(2)) 20 + .executes(context -> execute( 21 + context.getSource().getWorld(), null, context.getSource().getPosition(), 4, false)) 22 + .then(CommandManager.argument("pos", Vec3ArgumentType.vec3()) 23 + .executes(context -> execute( 24 + context.getSource().getWorld(), 25 + null, 26 + Vec3ArgumentType.getVec3(context, "pos"), 27 + 4, 28 + false)) 29 + .then(CommandManager.argument("power", FloatArgumentType.floatArg(0)) 30 + .executes(context -> execute( 31 + context.getSource().getWorld(), 32 + null, 33 + Vec3ArgumentType.getVec3(context, "pos"), 34 + FloatArgumentType.getFloat(context, "power"), 35 + false)) 36 + .then(CommandManager.argument("fire", BoolArgumentType.bool()) 37 + .executes(context -> execute( 38 + context.getSource().getWorld(), 39 + null, 40 + Vec3ArgumentType.getVec3(context, "pos"), 41 + FloatArgumentType.getFloat(context, "power"), 42 + BoolArgumentType.getBool(context, "fire")))))) 43 + .then(CommandManager.argument("entity", EntityArgumentType.entity()) 44 + .executes(context -> execute( 45 + context.getSource().getWorld(), 46 + EntityArgumentType.getEntity(context, "entity"), 47 + EntityArgumentType.getEntity(context, "entity").getPos(), 48 + 4, 49 + false)) 50 + .then(CommandManager.argument("pos", Vec3ArgumentType.vec3()) 51 + .executes(context -> execute( 52 + context.getSource().getWorld(), 53 + EntityArgumentType.getEntity(context, "entity"), 54 + Vec3ArgumentType.getVec3(context, "pos"), 55 + 4, 56 + false)) 57 + .then(CommandManager.argument("power", FloatArgumentType.floatArg(0)) 58 + .executes(context -> execute( 59 + context.getSource().getWorld(), 60 + EntityArgumentType.getEntity(context, "entity"), 61 + Vec3ArgumentType.getVec3(context, "pos"), 62 + FloatArgumentType.getFloat(context, "power"), 63 + false)) 64 + .then(CommandManager.argument("fire", BoolArgumentType.bool()) 65 + .executes(context -> execute( 66 + context.getSource().getWorld(), 67 + EntityArgumentType.getEntity(context, "entity"), 68 + Vec3ArgumentType.getVec3(context, "pos"), 69 + FloatArgumentType.getFloat(context, "power"), 70 + BoolArgumentType.getBool(context, "fire")))))))); 71 + } 72 + 73 + private static int execute( 74 + World world, @Nullable Entity entity, Vec3d vec, float power, boolean fire) { 75 + world.createExplosion( 76 + null, 77 + world.getDamageSources().explosion(null, entity), 78 + null, 79 + vec.getX(), 80 + vec.getY(), 81 + vec.getZ(), 82 + power, 83 + fire, 84 + World.ExplosionSourceType.TNT); 85 + return 1; 86 + } 87 + }
+51
src/main/java/me/melontini/commander/impl/builtin/commands/action/CancelCommand.java
··· 1 + package me.melontini.commander.impl.builtin.commands.action; 2 + 3 + import com.google.gson.JsonElement; 4 + import com.mojang.serialization.DataResult; 5 + import com.mojang.serialization.JsonOps; 6 + import com.mojang.serialization.MapCodec; 7 + import lombok.Getter; 8 + import lombok.RequiredArgsConstructor; 9 + import lombok.experimental.Accessors; 10 + import me.melontini.commander.api.command.Command; 11 + import me.melontini.commander.api.command.CommandType; 12 + import me.melontini.commander.api.event.EventContext; 13 + import me.melontini.commander.api.event.EventType; 14 + import me.melontini.commander.impl.builtin.BuiltInCommands; 15 + import me.melontini.commander.impl.event.EventTypeImpl; 16 + import net.minecraft.util.dynamic.Codecs; 17 + 18 + @Getter 19 + @Accessors(fluent = true) 20 + @RequiredArgsConstructor 21 + public final class CancelCommand implements Command { 22 + 23 + public static final MapCodec<CancelCommand> CODEC = 24 + Codecs.JSON_ELEMENT.fieldOf("value").xmap(CancelCommand::new, CancelCommand::element); 25 + 26 + private final JsonElement element; 27 + private Object value; // This is not great 28 + 29 + @Override 30 + public boolean execute(EventContext context) { 31 + context.setReturnValue(value); 32 + return true; 33 + } 34 + 35 + @Override 36 + public CommandType type() { 37 + return BuiltInCommands.CANCEL; 38 + } 39 + 40 + @Override 41 + public DataResult<Void> validate(EventType type) { 42 + if (type.get(EventTypeImpl.CANCEL_TERM).isEmpty()) 43 + return DataResult.error(() -> "Event '%s' does not support cancellation"); 44 + 45 + var val = type.get(EventTypeImpl.CANCEL_TERM).orElseThrow().parse(JsonOps.INSTANCE, element); 46 + if (val.error().isPresent()) return val.map(object -> null); 47 + this.value = val.result().orElseThrow(); 48 + 49 + return Command.super.validate(type); 50 + } 51 + }
+104
src/main/java/me/melontini/commander/impl/builtin/commands/action/CommandCommand.java
··· 1 + package me.melontini.commander.impl.builtin.commands.action; 2 + 3 + import com.mojang.datafixers.util.Either; 4 + import com.mojang.serialization.MapCodec; 5 + import com.mojang.serialization.codecs.RecordCodecBuilder; 6 + import java.util.List; 7 + import me.melontini.commander.api.command.Command; 8 + import me.melontini.commander.api.command.CommandType; 9 + import me.melontini.commander.api.command.Selector; 10 + import me.melontini.commander.api.event.EventContext; 11 + import me.melontini.commander.api.expression.BrigadierMacro; 12 + import me.melontini.commander.impl.builtin.BuiltInCommands; 13 + import me.melontini.commander.impl.util.ServerHelper; 14 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 15 + import net.minecraft.loot.context.LootContext; 16 + import net.minecraft.server.command.ServerCommandSource; 17 + import net.minecraft.text.Text; 18 + import net.minecraft.util.Formatting; 19 + import net.minecraft.util.Identifier; 20 + 21 + public record CommandCommand(Selector.Conditioned selector, InterFunction commands) 22 + implements Command { 23 + 24 + public static final MapCodec<CommandCommand> CODEC = 25 + RecordCodecBuilder.mapCodec(data -> data.group( 26 + Selector.CODEC.fieldOf("selector").forGetter(CommandCommand::selector), 27 + ExtraCodecs.either(BrigadierMacro.CODEC.listOf(), Identifier.CODEC) 28 + .<InterFunction>xmap( 29 + e -> e.map(MacroedFunction::new, FunctionFunction::new), 30 + InterFunction::origin) 31 + .fieldOf("commands") 32 + .forGetter(CommandCommand::commands)) 33 + .apply(data, CommandCommand::new)); 34 + 35 + @Override 36 + public boolean execute(EventContext context) { 37 + var opt = selector().select(context).map(ServerCommandSource::withSilent); 38 + if (opt.isEmpty()) return false; 39 + return commands().execute(context.lootContext(), opt.orElseThrow()); 40 + } 41 + 42 + @Override 43 + public CommandType type() { 44 + return BuiltInCommands.COMMANDS; 45 + } 46 + 47 + private interface InterFunction { 48 + boolean execute(LootContext context, ServerCommandSource source); 49 + 50 + Either<List<BrigadierMacro>, Identifier> origin(); 51 + } 52 + 53 + private record MacroedFunction(List<BrigadierMacro> macros) implements InterFunction { 54 + 55 + @Override 56 + public boolean execute(LootContext context, ServerCommandSource source) { 57 + var server = context.getWorld().getServer(); 58 + 59 + for (BrigadierMacro command : macros()) { 60 + try { 61 + server.getCommandManager().executeWithPrefix(source, command.build(context)); 62 + } catch (Throwable e) { 63 + ServerHelper.broadcastToOps( 64 + server, 65 + Text.literal(command.original()) 66 + .append(Text.literal(" failed execution! ")) 67 + .append("%s: %s".formatted(e.getClass().getSimpleName(), e.getLocalizedMessage())) 68 + .formatted(Formatting.RED)); 69 + return false; 70 + } 71 + } 72 + return true; 73 + } 74 + 75 + @Override 76 + public Either<List<BrigadierMacro>, Identifier> origin() { 77 + return Either.left(macros()); 78 + } 79 + } 80 + 81 + private record FunctionFunction(Identifier identifier) implements InterFunction { 82 + 83 + @Override 84 + public boolean execute(LootContext context, ServerCommandSource source) { 85 + var server = context.getWorld().getServer(); 86 + 87 + var func = server.getCommandFunctionManager().getFunction(identifier()); 88 + if (func.isPresent()) { 89 + server.getCommandFunctionManager().execute(func.orElseThrow(), source); 90 + return true; 91 + } else { 92 + ServerHelper.broadcastToOps( 93 + server, 94 + Text.literal("Unknown function %s!".formatted(identifier())).formatted(Formatting.RED)); 95 + return false; 96 + } 97 + } 98 + 99 + @Override 100 + public Either<List<BrigadierMacro>, Identifier> origin() { 101 + return Either.right(identifier()); 102 + } 103 + } 104 + }
+26
src/main/java/me/melontini/commander/impl/builtin/commands/action/PrintArithmeticaCommand.java
··· 1 + package me.melontini.commander.impl.builtin.commands.action; 2 + 3 + import com.mojang.serialization.MapCodec; 4 + import me.melontini.commander.api.command.Command; 5 + import me.melontini.commander.api.command.CommandType; 6 + import me.melontini.commander.api.event.EventContext; 7 + import me.melontini.commander.api.expression.Arithmetica; 8 + import me.melontini.commander.impl.builtin.BuiltInCommands; 9 + 10 + public record PrintArithmeticaCommand(Arithmetica arithmetica) implements Command { 11 + 12 + public static final MapCodec<PrintArithmeticaCommand> CODEC = Arithmetica.CODEC 13 + .xmap(PrintArithmeticaCommand::new, PrintArithmeticaCommand::arithmetica) 14 + .fieldOf("value"); 15 + 16 + @Override 17 + public boolean execute(EventContext context) { 18 + System.out.println(arithmetica().asDouble(context.lootContext())); 19 + return true; 20 + } 21 + 22 + @Override 23 + public CommandType type() { 24 + return BuiltInCommands.PRINT_ARITHMETICA; 25 + } 26 + }
+25
src/main/java/me/melontini/commander/impl/builtin/commands/action/PrintCommand.java
··· 1 + package me.melontini.commander.impl.builtin.commands.action; 2 + 3 + import com.mojang.serialization.Codec; 4 + import com.mojang.serialization.MapCodec; 5 + import me.melontini.commander.api.command.Command; 6 + import me.melontini.commander.api.command.CommandType; 7 + import me.melontini.commander.api.event.EventContext; 8 + import me.melontini.commander.impl.builtin.BuiltInCommands; 9 + 10 + public record PrintCommand(String text) implements Command { 11 + 12 + public static final MapCodec<PrintCommand> CODEC = 13 + Codec.STRING.fieldOf("text").xmap(PrintCommand::new, PrintCommand::text); 14 + 15 + @Override 16 + public boolean execute(EventContext context) { 17 + System.out.println(text()); 18 + return true; 19 + } 20 + 21 + @Override 22 + public CommandType type() { 23 + return BuiltInCommands.PRINT; 24 + } 25 + }
+50
src/main/java/me/melontini/commander/impl/builtin/commands/logic/AllOfCommand.java
··· 1 + package me.melontini.commander.impl.builtin.commands.logic; 2 + 3 + import com.mojang.serialization.Codec; 4 + import com.mojang.serialization.MapCodec; 5 + import com.mojang.serialization.codecs.RecordCodecBuilder; 6 + import java.util.List; 7 + import java.util.Optional; 8 + import me.melontini.commander.api.command.Command; 9 + import me.melontini.commander.api.command.CommandType; 10 + import me.melontini.commander.api.event.EventContext; 11 + import me.melontini.commander.impl.builtin.BuiltInCommands; 12 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 13 + 14 + public record AllOfCommand( 15 + List<Command.Conditioned> commands, Optional<Command.Conditioned> then, boolean shortCircuit) 16 + implements Command { 17 + 18 + public static final MapCodec<AllOfCommand> CODEC = RecordCodecBuilder.mapCodec(data -> data.group( 19 + ExtraCodecs.list(Command.CODEC.codec()) 20 + .fieldOf("commands") 21 + .forGetter(AllOfCommand::commands), 22 + ExtraCodecs.optional("then", Command.CODEC.codec()).forGetter(AllOfCommand::then), 23 + ExtraCodecs.optional("short_circuit", Codec.BOOL, false) 24 + .forGetter(AllOfCommand::shortCircuit)) 25 + .apply(data, AllOfCommand::new)); 26 + 27 + @Override 28 + public boolean execute(EventContext context) { 29 + if (!shortCircuit) { 30 + boolean b = true; 31 + for (Conditioned command : commands()) { 32 + b &= command.execute(context); 33 + } 34 + if (b) { 35 + return then().map(cc -> cc.execute(context)).orElse(true); 36 + } 37 + return false; 38 + } 39 + 40 + for (Conditioned command : commands()) { 41 + if (!command.execute(context)) return false; 42 + } 43 + return then().map(cc -> cc.execute(context)).orElse(true); 44 + } 45 + 46 + @Override 47 + public CommandType type() { 48 + return BuiltInCommands.ALL_OF; 49 + } 50 + }
+50
src/main/java/me/melontini/commander/impl/builtin/commands/logic/AnyOfCommand.java
··· 1 + package me.melontini.commander.impl.builtin.commands.logic; 2 + 3 + import com.mojang.serialization.Codec; 4 + import com.mojang.serialization.MapCodec; 5 + import com.mojang.serialization.codecs.RecordCodecBuilder; 6 + import java.util.List; 7 + import java.util.Optional; 8 + import me.melontini.commander.api.command.Command; 9 + import me.melontini.commander.api.command.CommandType; 10 + import me.melontini.commander.api.event.EventContext; 11 + import me.melontini.commander.impl.builtin.BuiltInCommands; 12 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 13 + 14 + public record AnyOfCommand( 15 + List<Command.Conditioned> commands, Optional<Command.Conditioned> then, boolean shortCircuit) 16 + implements Command { 17 + 18 + public static final MapCodec<AnyOfCommand> CODEC = RecordCodecBuilder.mapCodec(data -> data.group( 19 + ExtraCodecs.list(Command.CODEC.codec()) 20 + .fieldOf("commands") 21 + .forGetter(AnyOfCommand::commands), 22 + ExtraCodecs.optional("then", Command.CODEC.codec()).forGetter(AnyOfCommand::then), 23 + ExtraCodecs.optional("short_circuit", Codec.BOOL, false) 24 + .forGetter(AnyOfCommand::shortCircuit)) 25 + .apply(data, AnyOfCommand::new)); 26 + 27 + @Override 28 + public boolean execute(EventContext context) { 29 + if (!shortCircuit) { 30 + boolean b = false; 31 + for (Command.Conditioned command : commands()) { 32 + b |= command.execute(context); 33 + } 34 + if (b) { 35 + return then().map(cc -> cc.execute(context)).orElse(true); 36 + } 37 + return false; 38 + } 39 + 40 + for (Command.Conditioned command : commands()) { 41 + if (command.execute(context)) return then().map(cc -> cc.execute(context)).orElse(true); 42 + } 43 + return false; 44 + } 45 + 46 + @Override 47 + public CommandType type() { 48 + return BuiltInCommands.ANY_OF; 49 + } 50 + }
+52
src/main/java/me/melontini/commander/impl/builtin/commands/logic/DefaultedCommand.java
··· 1 + package me.melontini.commander.impl.builtin.commands.logic; 2 + 3 + import com.mojang.serialization.Codec; 4 + import com.mojang.serialization.MapCodec; 5 + import com.mojang.serialization.codecs.RecordCodecBuilder; 6 + import java.util.List; 7 + import java.util.Optional; 8 + import me.melontini.commander.api.command.Command; 9 + import me.melontini.commander.api.command.CommandType; 10 + import me.melontini.commander.api.event.EventContext; 11 + import me.melontini.commander.impl.builtin.BuiltInCommands; 12 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 13 + 14 + public record DefaultedCommand( 15 + List<Command.Conditioned> commands, Optional<Command.Conditioned> then, boolean shortCircuit) 16 + implements Command { 17 + 18 + public static final MapCodec<DefaultedCommand> CODEC = 19 + RecordCodecBuilder.mapCodec(data -> data.group( 20 + ExtraCodecs.list(Command.CODEC.codec()) 21 + .fieldOf("commands") 22 + .forGetter(DefaultedCommand::commands), 23 + ExtraCodecs.optional("then", Command.CODEC.codec()).forGetter(DefaultedCommand::then), 24 + ExtraCodecs.optional("short_circuit", Codec.BOOL, false) 25 + .forGetter(DefaultedCommand::shortCircuit)) 26 + .apply(data, DefaultedCommand::new)); 27 + 28 + @Override 29 + public boolean execute(EventContext context) { 30 + if (!shortCircuit) { 31 + boolean b = false; 32 + for (Command.Conditioned command : commands()) { 33 + b |= command.execute(context); 34 + } 35 + if (!b) { 36 + return then().map(cc -> cc.execute(context)).orElse(false); 37 + } 38 + return true; 39 + } 40 + 41 + for (Command.Conditioned command : commands()) { 42 + if (!command.execute(context)) 43 + return then().map(cc -> cc.execute(context)).orElse(false); 44 + } 45 + return true; 46 + } 47 + 48 + @Override 49 + public CommandType type() { 50 + return BuiltInCommands.DEFAULTED; 51 + } 52 + }
+40
src/main/java/me/melontini/commander/impl/builtin/commands/logic/RandomCommand.java
··· 1 + package me.melontini.commander.impl.builtin.commands.logic; 2 + 3 + import com.mojang.serialization.MapCodec; 4 + import com.mojang.serialization.codecs.RecordCodecBuilder; 5 + import me.melontini.commander.api.command.Command; 6 + import me.melontini.commander.api.command.CommandType; 7 + import me.melontini.commander.api.event.EventContext; 8 + import me.melontini.commander.api.expression.Arithmetica; 9 + import me.melontini.commander.impl.builtin.BuiltInCommands; 10 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 11 + import net.minecraft.util.collection.WeightedList; 12 + 13 + public record RandomCommand(WeightedList<Command.Conditioned> commands, Arithmetica rolls) 14 + implements Command { 15 + 16 + public static final MapCodec<RandomCommand> CODEC = 17 + RecordCodecBuilder.mapCodec(data -> data.group( 18 + ExtraCodecs.weightedList(Command.CODEC.codec()) 19 + .fieldOf("commands") 20 + .forGetter(RandomCommand::commands), 21 + ExtraCodecs.optional("rolls", Arithmetica.CODEC, Arithmetica.constant(1)) 22 + .forGetter(RandomCommand::rolls)) 23 + .apply(data, RandomCommand::new)); 24 + 25 + @Override 26 + public boolean execute(EventContext context) { 27 + boolean b = false; 28 + int rolls = rolls().asInt(context.lootContext()); 29 + for (int i = 0; i < rolls; i++) { 30 + var itr = this.commands().shuffle().iterator(); 31 + if (itr.hasNext()) b |= itr.next().execute(context); 32 + } 33 + return b; 34 + } 35 + 36 + @Override 37 + public CommandType type() { 38 + return BuiltInCommands.RANDOM; 39 + } 40 + }
+129
src/main/java/me/melontini/commander/impl/builtin/events/EntityEvents.java
··· 1 + package me.melontini.commander.impl.builtin.events; 2 + 3 + import static me.melontini.commander.api.util.EventExecutors.*; 4 + import static me.melontini.commander.impl.Commander.id; 5 + import static net.minecraft.loot.context.LootContextParameters.*; 6 + 7 + import com.mojang.serialization.Codec; 8 + import java.util.Objects; 9 + import lombok.experimental.UtilityClass; 10 + import me.melontini.commander.api.event.EventType; 11 + import me.melontini.commander.impl.util.loot.LootUtil; 12 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 13 + import net.fabricmc.fabric.api.entity.event.v1.EntityElytraEvents; 14 + import net.fabricmc.fabric.api.entity.event.v1.EntitySleepEvents; 15 + import net.fabricmc.fabric.api.entity.event.v1.ServerEntityCombatEvents; 16 + import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents; 17 + import net.minecraft.entity.Entity; 18 + import net.minecraft.entity.damage.DamageSource; 19 + import net.minecraft.entity.player.PlayerEntity; 20 + import net.minecraft.loot.context.LootContext; 21 + import net.minecraft.loot.context.LootContextParameterSet; 22 + import net.minecraft.loot.context.LootContextTypes; 23 + import net.minecraft.server.world.ServerWorld; 24 + import net.minecraft.util.ActionResult; 25 + import net.minecraft.util.math.Vec3d; 26 + import org.jetbrains.annotations.Nullable; 27 + 28 + @UtilityClass 29 + public class EntityEvents { 30 + 31 + public static final EventType ALLOW_DAMAGE = 32 + EventType.builder().cancelTerm(Codec.BOOL).build(id("allow_damage")); 33 + public static final EventType ALLOW_DEATH = 34 + EventType.builder().cancelTerm(Codec.BOOL).build(id("allow_death")); 35 + public static final EventType AFTER_DEATH = EventType.builder().build(id("after_death")); 36 + 37 + public static final EventType START_SLEEPING = EventType.builder().build(id("sleeping/start")); 38 + public static final EventType STOP_SLEEPING = EventType.builder().build(id("sleeping/stop")); 39 + public static final EventType ALLOW_SLEEPING = EventType.builder() 40 + .cancelTerm(ExtraCodecs.enumCodec(PlayerEntity.SleepFailureReason.class)) 41 + .build(id("sleeping/allow")); 42 + public static final EventType ALLOW_SLEEP_TIME = EventType.builder() 43 + .cancelTerm(ExtraCodecs.enumCodec(ActionResult.class)) 44 + .build(id("sleeping/allow_time")); 45 + public static final EventType ALLOW_NEARBY_MONSTERS = EventType.builder() 46 + .cancelTerm(ExtraCodecs.enumCodec(ActionResult.class)) 47 + .build(id("sleeping/allow_nearby_monsters")); 48 + 49 + public static final EventType ALLOW_ELYTRA = 50 + EventType.builder().cancelTerm(Codec.BOOL).build(id("elytra_flight/allow")); 51 + // public static final EventType CUSTOM_ELYTRA = 52 + // EventType.builder().cancelTerm(Codec.BOOL).build(id("elytra_flight/custom")); 53 + 54 + public static final EventType AFTER_KILLED_BY_OTHER = 55 + EventType.builder().build(id("after_killed_by_other")); 56 + 57 + public static void init() { 58 + ServerLivingEntityEvents.ALLOW_DAMAGE.register((entity, source, amount) -> runBoolean( 59 + ALLOW_DAMAGE, 60 + entity.getWorld(), 61 + () -> makeContext( 62 + entity, Objects.requireNonNullElse(source.getPosition(), entity.getPos()), source))); 63 + ServerLivingEntityEvents.ALLOW_DEATH.register((entity, source, damageAmount) -> runBoolean( 64 + ALLOW_DEATH, 65 + entity.getWorld(), 66 + () -> makeContext( 67 + entity, Objects.requireNonNullElse(source.getPosition(), entity.getPos()), source))); 68 + 69 + ServerLivingEntityEvents.AFTER_DEATH.register((entity, source) -> runVoid( 70 + AFTER_DEATH, 71 + entity.getWorld(), 72 + () -> makeContext( 73 + entity, Objects.requireNonNullElse(source.getPosition(), entity.getPos()), source))); 74 + 75 + EntitySleepEvents.START_SLEEPING.register((entity, sleepingPos) -> runVoid( 76 + START_SLEEPING, 77 + entity.getWorld(), 78 + () -> makeContext(entity, Vec3d.ofCenter(sleepingPos), null))); 79 + EntitySleepEvents.STOP_SLEEPING.register((entity, sleepingPos) -> runVoid( 80 + STOP_SLEEPING, 81 + entity.getWorld(), 82 + () -> makeContext(entity, Vec3d.ofCenter(sleepingPos), null))); 83 + EntitySleepEvents.ALLOW_SLEEPING.register((player, sleepingPos) -> runEnum( 84 + ALLOW_SLEEPING, 85 + null, 86 + player.getWorld(), 87 + () -> makeContext(player, Vec3d.ofCenter(sleepingPos), null))); 88 + EntitySleepEvents.ALLOW_SLEEP_TIME.register( 89 + (player, sleepingPos, vanillaResult) -> runActionResult( 90 + ALLOW_SLEEP_TIME, 91 + player.getWorld(), 92 + () -> makeContext(player, Vec3d.ofCenter(sleepingPos), null))); 93 + EntitySleepEvents.ALLOW_NEARBY_MONSTERS.register( 94 + (player, sleepingPos, vanillaResult) -> runActionResult( 95 + ALLOW_NEARBY_MONSTERS, 96 + player.getWorld(), 97 + () -> makeContext(player, Vec3d.ofCenter(sleepingPos), null))); 98 + 99 + EntityElytraEvents.ALLOW.register(entity -> runBoolean( 100 + ALLOW_ELYTRA, entity.getWorld(), () -> makeContext(entity, entity.getPos(), null))); 101 + 102 + ServerEntityCombatEvents.AFTER_KILLED_OTHER_ENTITY.register( 103 + (world, entity, killedEntity) -> runVoid( 104 + AFTER_KILLED_BY_OTHER, 105 + world, 106 + () -> 107 + LootUtil.build(new LootContextParameterSet.Builder((ServerWorld) entity.getWorld()) 108 + .add(THIS_ENTITY, killedEntity) 109 + .add(ORIGIN, killedEntity.getPos()) 110 + .add(DAMAGE_SOURCE, world.getDamageSources().generic()) 111 + .add(ATTACKING_ENTITY, entity) 112 + .build(LootContextTypes.ENTITY)))); 113 + } 114 + 115 + private static LootContext makeContext( 116 + Entity entity, Vec3d origin, @Nullable DamageSource source) { 117 + LootContextParameterSet.Builder builder = 118 + new LootContextParameterSet.Builder((ServerWorld) entity.getWorld()); 119 + builder.add(THIS_ENTITY, entity).add(ORIGIN, origin); 120 + if (source != null) { 121 + builder 122 + .add(DAMAGE_SOURCE, source) 123 + .addOptional(DIRECT_ATTACKING_ENTITY, source.getAttacker()) 124 + .addOptional(ATTACKING_ENTITY, source.getSource()); 125 + } 126 + return LootUtil.build( 127 + builder.build(source == null ? LootContextTypes.COMMAND : LootContextTypes.ENTITY)); 128 + } 129 + }
+150
src/main/java/me/melontini/commander/impl/builtin/events/PlayerEvents.java
··· 1 + package me.melontini.commander.impl.builtin.events; 2 + 3 + import static me.melontini.commander.impl.Commander.id; 4 + import static net.minecraft.loot.context.LootContextParameters.*; 5 + 6 + import com.mojang.serialization.Codec; 7 + import lombok.experimental.UtilityClass; 8 + import me.melontini.commander.api.event.EventType; 9 + import me.melontini.commander.api.util.EventExecutors; 10 + import me.melontini.commander.impl.util.loot.LootUtil; 11 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 12 + import net.fabricmc.fabric.api.event.player.*; 13 + import net.minecraft.block.BlockState; 14 + import net.minecraft.block.entity.BlockEntity; 15 + import net.minecraft.entity.Entity; 16 + import net.minecraft.entity.damage.DamageSource; 17 + import net.minecraft.entity.player.PlayerEntity; 18 + import net.minecraft.item.ItemStack; 19 + import net.minecraft.loot.context.LootContext; 20 + import net.minecraft.loot.context.LootContextParameterSet; 21 + import net.minecraft.loot.context.LootContextTypes; 22 + import net.minecraft.server.world.ServerWorld; 23 + import net.minecraft.util.ActionResult; 24 + import net.minecraft.util.Hand; 25 + import net.minecraft.util.TypedActionResult; 26 + import net.minecraft.util.math.BlockPos; 27 + import net.minecraft.util.math.Vec3d; 28 + import net.minecraft.world.World; 29 + import org.jetbrains.annotations.Nullable; 30 + 31 + @UtilityClass 32 + public class PlayerEvents { 33 + 34 + public static final EventType ATTACK_BLOCK = EventType.builder() 35 + .cancelTerm(ExtraCodecs.enumCodec(ActionResult.class)) 36 + .build(id("player_attack/block")); 37 + public static final EventType USE_BLOCK = EventType.builder() 38 + .cancelTerm(ExtraCodecs.enumCodec(ActionResult.class)) 39 + .build(id("player_use/block")); 40 + 41 + public static final EventType ATTACK_ENTITY = EventType.builder() 42 + .cancelTerm(ExtraCodecs.enumCodec(ActionResult.class)) 43 + .build(id("player_attack/entity")); 44 + public static final EventType USE_ENTITY = EventType.builder() 45 + .cancelTerm(ExtraCodecs.enumCodec(ActionResult.class)) 46 + .build(id("player_use/entity")); 47 + 48 + public static final EventType USE_ITEM = EventType.builder() 49 + .cancelTerm(ExtraCodecs.enumCodec(ActionResult.class)) 50 + .build(id("player_use/item")); 51 + 52 + public static final EventType BEFORE_BREAK = 53 + EventType.builder().cancelTerm(Codec.BOOL).build(id("player_break_block/before")); 54 + public static final EventType AFTER_BREAK = 55 + EventType.builder().build(id("player_break_block/after")); 56 + public static final EventType CANCELLED_BREAK = 57 + EventType.builder().build(id("player_break_block/cancelled")); 58 + 59 + public static void init() { 60 + AttackBlockCallback.EVENT.register((player, world, hand, pos, direction) -> blockCallback( 61 + ATTACK_BLOCK, world, player, player.getStackInHand(hand), Vec3d.ofCenter(pos), pos)); 62 + UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> blockCallback( 63 + USE_BLOCK, 64 + world, 65 + player, 66 + player.getStackInHand(hand), 67 + hitResult.getPos(), 68 + hitResult.getBlockPos())); 69 + 70 + AttackEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> 71 + entityCallback(ATTACK_ENTITY, world, player, hand, entity)); 72 + UseEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> 73 + entityCallback(USE_ENTITY, world, player, hand, entity)); 74 + 75 + UseItemCallback.EVENT.register((player, world, hand) -> new TypedActionResult<>( 76 + EventExecutors.runActionResult( 77 + USE_ITEM, 78 + world, 79 + () -> LootUtil.build(new LootContextParameterSet.Builder((ServerWorld) world) 80 + .add(THIS_ENTITY, player) 81 + .add(ORIGIN, player.getPos()) 82 + .add(TOOL, player.getStackInHand(hand)) 83 + .build(LootContextTypes.FISHING))), 84 + player.getStackInHand(hand))); 85 + 86 + PlayerBlockBreakEvents.BEFORE.register( 87 + (world, player, pos, state, blockEntity) -> EventExecutors.runBoolean( 88 + BEFORE_BREAK, world, () -> breakContext(world, player, pos, state, blockEntity))); 89 + PlayerBlockBreakEvents.AFTER.register( 90 + (world, player, pos, state, blockEntity) -> EventExecutors.runVoid( 91 + AFTER_BREAK, world, () -> breakContext(world, player, pos, state, blockEntity))); 92 + PlayerBlockBreakEvents.CANCELED.register( 93 + (world, player, pos, state, blockEntity) -> EventExecutors.runVoid( 94 + CANCELLED_BREAK, world, () -> breakContext(world, player, pos, state, blockEntity))); 95 + } 96 + 97 + private static LootContext breakContext( 98 + World world, 99 + PlayerEntity player, 100 + BlockPos pos, 101 + BlockState state, 102 + @Nullable BlockEntity blockEntity) { 103 + return LootUtil.build(new LootContextParameterSet.Builder((ServerWorld) world) 104 + .add(THIS_ENTITY, player) 105 + .add(ORIGIN, Vec3d.ofCenter(pos)) 106 + .add(BLOCK_STATE, state) 107 + .add(TOOL, ItemStack.EMPTY) 108 + .addOptional(BLOCK_ENTITY, blockEntity) 109 + .build(LootContextTypes.BLOCK)); 110 + } 111 + 112 + private static ActionResult entityCallback( 113 + EventType type, World world, PlayerEntity player, Hand hand, Entity entity) { 114 + if (world.isClient()) return ActionResult.PASS; 115 + return EventExecutors.runActionResult(type, world, () -> { 116 + ItemStack tool = player.getStackInHand(hand); 117 + DamageSource source = world.getDamageSources().generic(); 118 + 119 + return LootUtil.build(new LootContextParameterSet.Builder((ServerWorld) world) 120 + .add(THIS_ENTITY, entity) 121 + .add(ORIGIN, entity.getPos()) 122 + .add(TOOL, tool) 123 + .add(LAST_DAMAGE_PLAYER, player) 124 + .add(DAMAGE_SOURCE, source) 125 + .build(LootContextTypes.ENTITY)); 126 + }); 127 + } 128 + 129 + private static ActionResult blockCallback( 130 + EventType type, 131 + World world, 132 + PlayerEntity player, 133 + ItemStack tool, 134 + Vec3d origin, 135 + BlockPos pos) { 136 + if (world.isClient()) return ActionResult.PASS; 137 + return EventExecutors.runActionResult(type, world, () -> { 138 + BlockState state = world.getBlockState(pos); 139 + BlockEntity blockEntity = world.getBlockEntity(pos); 140 + 141 + return LootUtil.build(new LootContextParameterSet.Builder((ServerWorld) world) 142 + .add(THIS_ENTITY, player) 143 + .add(ORIGIN, origin) 144 + .add(BLOCK_STATE, state) 145 + .add(TOOL, tool) 146 + .addOptional(BLOCK_ENTITY, blockEntity) 147 + .build(LootContextTypes.BLOCK)); 148 + }); 149 + } 150 + }
+78
src/main/java/me/melontini/commander/impl/builtin/events/ServerLifecycle.java
··· 1 + package me.melontini.commander.impl.builtin.events; 2 + 3 + import static me.melontini.commander.api.util.EventExecutors.runVoid; 4 + import static me.melontini.commander.impl.Commander.id; 5 + 6 + import me.melontini.commander.api.event.EventType; 7 + import me.melontini.commander.impl.util.loot.LootUtil; 8 + import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents; 9 + import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents; 10 + import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; 11 + import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents; 12 + import net.minecraft.entity.Entity; 13 + import net.minecraft.loot.context.LootContext; 14 + import net.minecraft.loot.context.LootContextParameterSet; 15 + import net.minecraft.loot.context.LootContextParameters; 16 + import net.minecraft.loot.context.LootContextTypes; 17 + import net.minecraft.server.world.ServerWorld; 18 + import net.minecraft.util.math.Vec3d; 19 + 20 + public class ServerLifecycle { 21 + 22 + public static final EventType SERVER_STARTED = EventType.builder().build(id("server/started")); 23 + public static final EventType SERVER_STOPPING = EventType.builder().build(id("server/stopping")); 24 + 25 + public static final EventType WORLD_LOAD = EventType.builder().build(id("world/load")); 26 + public static final EventType WORLD_UNLOAD = EventType.builder().build(id("world/unload")); 27 + 28 + public static final EventType CHUNK_LOAD = EventType.builder().build(id("chunk/load")); 29 + public static final EventType CHUNK_UNLOAD = EventType.builder().build(id("chunk/unload")); 30 + 31 + public static final EventType ENTITY_LOAD = EventType.builder().build(id("entity/load")); 32 + public static final EventType ENTITY_UNLOAD = EventType.builder().build(id("entity/unload")); 33 + 34 + public static void init() { 35 + ServerLifecycleEvents.SERVER_STARTED.register(server -> 36 + runVoid(SERVER_STARTED, server.getOverworld(), () -> forWorld(server.getOverworld()))); 37 + ServerLifecycleEvents.SERVER_STOPPING.register(server -> { 38 + if (server.getOverworld() 39 + != null) // If the server crashes at a specific moment, this event may be triggered, but 40 + // the worlds are not yet loaded. 41 + runVoid(SERVER_STOPPING, server.getOverworld(), () -> forWorld(server.getOverworld())); 42 + }); 43 + 44 + ServerWorldEvents.LOAD.register( 45 + (server, world) -> runVoid(WORLD_LOAD, world, () -> forWorld(world))); 46 + ServerWorldEvents.UNLOAD.register( 47 + (server, world) -> runVoid(WORLD_UNLOAD, world, () -> forWorld(world))); 48 + 49 + ServerChunkEvents.CHUNK_LOAD.register((world, chunk) -> 50 + runVoid(CHUNK_LOAD, world, () -> forWorld(world, Vec3d.of(chunk.getPos().getStartPos())))); 51 + ServerChunkEvents.CHUNK_UNLOAD.register((world, chunk) -> runVoid( 52 + CHUNK_UNLOAD, world, () -> forWorld(world, Vec3d.of(chunk.getPos().getStartPos())))); 53 + 54 + ServerEntityEvents.ENTITY_LOAD.register( 55 + (entity, world) -> runVoid(ENTITY_LOAD, world, () -> forEntity(world, entity))); 56 + ServerEntityEvents.ENTITY_UNLOAD.register( 57 + (entity, world) -> runVoid(ENTITY_UNLOAD, world, () -> forEntity(world, entity))); 58 + } 59 + 60 + private static LootContext forEntity(ServerWorld world, Entity entity) { 61 + return LootUtil.build(new LootContextParameterSet.Builder(world) 62 + .add(LootContextParameters.ORIGIN, entity.getPos()) 63 + .add(LootContextParameters.THIS_ENTITY, entity) 64 + .build(LootContextTypes.COMMAND)); 65 + } 66 + 67 + private static LootContext forWorld(ServerWorld world, Vec3d origin) { 68 + return LootUtil.build(new LootContextParameterSet.Builder(world) 69 + .add(LootContextParameters.ORIGIN, origin) 70 + .build(LootContextTypes.COMMAND)); 71 + } 72 + 73 + private static LootContext forWorld(ServerWorld world) { 74 + return LootUtil.build(new LootContextParameterSet.Builder(world) 75 + .add(LootContextParameters.ORIGIN, Vec3d.ZERO) 76 + .build(LootContextTypes.COMMAND)); 77 + } 78 + }
+43
src/main/java/me/melontini/commander/impl/builtin/events/ServerTick.java
··· 1 + package me.melontini.commander.impl.builtin.events; 2 + 3 + import static me.melontini.commander.api.util.EventExecutors.runVoid; 4 + import static me.melontini.commander.impl.Commander.id; 5 + 6 + import lombok.experimental.UtilityClass; 7 + import me.melontini.commander.api.event.EventType; 8 + import me.melontini.commander.impl.util.loot.LootUtil; 9 + import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; 10 + import net.minecraft.loot.context.LootContext; 11 + import net.minecraft.loot.context.LootContextParameterSet; 12 + import net.minecraft.loot.context.LootContextParameters; 13 + import net.minecraft.loot.context.LootContextTypes; 14 + import net.minecraft.server.world.ServerWorld; 15 + import net.minecraft.util.math.Vec3d; 16 + 17 + @UtilityClass 18 + public class ServerTick { 19 + 20 + public static final EventType START_TICK = EventType.builder().build(id("server_tick/start")); 21 + public static final EventType END_TICK = EventType.builder().build(id("server_tick/end")); 22 + public static final EventType START_WORLD_TICK = 23 + EventType.builder().build(id("world_tick/start")); 24 + public static final EventType END_WORLD_TICK = EventType.builder().build(id("world_tick/end")); 25 + 26 + public static void init() { 27 + ServerTickEvents.START_SERVER_TICK.register(server -> 28 + runVoid(START_TICK, server.getOverworld(), () -> forWorld(server.getOverworld()))); 29 + ServerTickEvents.END_SERVER_TICK.register( 30 + server -> runVoid(END_TICK, server.getOverworld(), () -> forWorld(server.getOverworld()))); 31 + 32 + ServerTickEvents.START_WORLD_TICK.register( 33 + (world) -> runVoid(START_WORLD_TICK, world, () -> forWorld(world))); 34 + ServerTickEvents.END_WORLD_TICK.register( 35 + (world) -> runVoid(END_WORLD_TICK, world, () -> forWorld(world))); 36 + } 37 + 38 + private static LootContext forWorld(ServerWorld world) { 39 + return LootUtil.build(new LootContextParameterSet.Builder(world) 40 + .add(LootContextParameters.ORIGIN, Vec3d.ZERO) 41 + .build(LootContextTypes.COMMAND)); 42 + } 43 + }
+61
src/main/java/me/melontini/commander/impl/command/ConditionedCommand.java
··· 1 + package me.melontini.commander.impl.command; 2 + 3 + import com.mojang.serialization.*; 4 + import java.util.Optional; 5 + import java.util.function.Function; 6 + import java.util.stream.Stream; 7 + import me.melontini.commander.api.command.Command; 8 + import me.melontini.commander.api.event.EventContext; 9 + import me.melontini.commander.api.event.EventType; 10 + import me.melontini.commander.impl.event.data.types.CommandTypes; 11 + import net.minecraft.loot.condition.LootCondition; 12 + 13 + public record ConditionedCommand(Optional<LootCondition> condition, Command other) 14 + implements Command.Conditioned { 15 + 16 + public static final MapCodec<? extends Command.Conditioned> CODEC = 17 + new MapCodec<ConditionedCommand>() { 18 + @Override 19 + public <T> RecordBuilder<T> encode( 20 + ConditionedCommand input, DynamicOps<T> ops, RecordBuilder<T> prefix) { 21 + var r = ((MapCodecCodec<Command>) CommandTypes.CODEC) 22 + .codec() 23 + .encode(input.other(), ops, prefix); 24 + input 25 + .condition() 26 + .map(condition1 -> LootCondition.CODEC.encodeStart(ops, condition1)) 27 + .ifPresent(tDataResult -> r.add("condition", tDataResult)); 28 + return r; 29 + } 30 + 31 + @Override 32 + public <T> DataResult<ConditionedCommand> decode(DynamicOps<T> ops, MapLike<T> input) { 33 + var r = ((MapCodecCodec<Command>) CommandTypes.CODEC).codec().decode(ops, input); 34 + T condition = input.get("condition"); 35 + if (condition == null) 36 + return r.map(command -> new ConditionedCommand(Optional.empty(), command)); 37 + return r.map(command -> LootCondition.CODEC 38 + .parse(ops, condition) 39 + .map(condition1 -> new ConditionedCommand(Optional.of(condition1), command))) 40 + .flatMap(Function.identity()); 41 + } 42 + 43 + @Override 44 + public <T> Stream<T> keys(DynamicOps<T> ops) { 45 + return Stream.of("condition").map(ops::createString); 46 + } 47 + }; 48 + 49 + @Override 50 + public boolean execute(EventContext context) { 51 + if (!this.condition 52 + .map(condition1 -> condition1.test(context.lootContext())) 53 + .orElse(true)) return false; 54 + return other().execute(context); 55 + } 56 + 57 + @Override 58 + public DataResult<Void> validate(EventType type) { 59 + return this.other().validate(type); 60 + } 61 + }
+47
src/main/java/me/melontini/commander/impl/command/ConditionedSelector.java
··· 1 + package me.melontini.commander.impl.command; 2 + 3 + import com.mojang.datafixers.util.Either; 4 + import com.mojang.serialization.Codec; 5 + import com.mojang.serialization.codecs.RecordCodecBuilder; 6 + import java.util.Optional; 7 + import java.util.function.Function; 8 + import me.melontini.commander.api.command.Selector; 9 + import me.melontini.commander.api.event.EventContext; 10 + import me.melontini.commander.impl.event.data.types.SelectorTypes; 11 + import me.melontini.commander.impl.util.loot.LootUtil; 12 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 13 + import net.minecraft.loot.condition.LootCondition; 14 + import net.minecraft.loot.context.LootContextParameterSet; 15 + import net.minecraft.loot.context.LootContextParameters; 16 + import net.minecraft.loot.context.LootContextTypes; 17 + import net.minecraft.server.command.ServerCommandSource; 18 + 19 + public record ConditionedSelector(Optional<LootCondition> condition, Selector other) 20 + implements Selector.Conditioned { 21 + 22 + public static final Codec<? extends Selector.Conditioned> CODEC = ExtraCodecs.either( 23 + RecordCodecBuilder.<ConditionedSelector>create(data -> data.group( 24 + ExtraCodecs.optional("condition", LootCondition.CODEC) 25 + .forGetter(ConditionedSelector::condition), 26 + SelectorTypes.CODEC.fieldOf("value").forGetter(ConditionedSelector::other)) 27 + .apply(data, ConditionedSelector::new)), 28 + SelectorTypes.CODEC) 29 + .xmap( 30 + e -> e.map( 31 + Function.identity(), selector -> new ConditionedSelector(Optional.empty(), selector)), 32 + Either::left); 33 + 34 + @Override 35 + public Optional<ServerCommandSource> select(EventContext context) { 36 + var source = other.select(context.lootContext()); 37 + if (source == null) return Optional.empty(); 38 + return condition 39 + .filter(lootCondition -> !lootCondition.test(LootUtil.build( 40 + new LootContextParameterSet.Builder(context.lootContext().getWorld()) 41 + .add(LootContextParameters.ORIGIN, source.getPosition()) 42 + .addOptional(LootContextParameters.THIS_ENTITY, source.getEntity()) 43 + .build(LootContextTypes.COMMAND)))) 44 + .<Optional<ServerCommandSource>>map(lootCondition -> Optional.empty()) 45 + .orElseGet(() -> Optional.of(source)); 46 + } 47 + }
+82
src/main/java/me/melontini/commander/impl/event/EventContextImpl.java
··· 1 + package me.melontini.commander.impl.event; 2 + 3 + import java.util.IdentityHashMap; 4 + import java.util.concurrent.atomic.AtomicReference; 5 + import lombok.Getter; 6 + import lombok.NonNull; 7 + import lombok.experimental.Accessors; 8 + import me.melontini.commander.api.event.EventContext; 9 + import me.melontini.commander.api.event.EventKey; 10 + import me.melontini.commander.api.event.EventType; 11 + import net.minecraft.loot.context.LootContext; 12 + import org.jetbrains.annotations.NotNull; 13 + 14 + @Accessors(fluent = true) 15 + public final class EventContextImpl implements EventContext { 16 + 17 + @Getter 18 + private final EventType type; 19 + 20 + private final IdentityHashMap<EventKey<?>, Object> parameters; 21 + 22 + private EventContextImpl(EventType type, IdentityHashMap<EventKey<?>, Object> parameters) { 23 + this.parameters = parameters; 24 + this.type = type; 25 + } 26 + 27 + @Override 28 + public @NotNull EventContextImpl with(@NonNull IdentityHashMap<EventKey<?>, Object> parameters) { 29 + IdentityHashMap<EventKey<?>, Object> map = new IdentityHashMap<>(this.parameters); 30 + map.putAll(parameters); 31 + return new EventContextImpl(type, map); 32 + } 33 + 34 + @Override 35 + public <T> @NotNull T getParameter(@NonNull EventKey<T> key) { 36 + var r = parameters.get(key); 37 + if (r == null) 38 + throw new IllegalStateException("Missing required parameter key %s".formatted(key.id())); 39 + return (T) r; 40 + } 41 + 42 + @Override 43 + public @NotNull LootContext lootContext() { 44 + return getParameter(EventKey.LOOT_CONTEXT); 45 + } 46 + 47 + @Override 48 + public void setReturnValue(Object value) { 49 + getParameter(EventKey.RETURN_VALUE).set(value); 50 + } 51 + 52 + @Override 53 + public <T> T getReturnValue(T def) { 54 + var r = getParameter(EventKey.RETURN_VALUE).get(); 55 + if (r == null) return def; 56 + return (T) r; 57 + } 58 + 59 + public static final class Builder implements EventContext.Builder { 60 + private final IdentityHashMap<EventKey<?>, Object> map = new IdentityHashMap<>(); 61 + private final EventType type; 62 + 63 + public Builder(EventType type) { 64 + this.type = type; 65 + 66 + if (this.type.get(EventTypeImpl.CANCEL_TERM).isPresent()) { 67 + map.put(EventKey.RETURN_VALUE, new AtomicReference<>()); 68 + } 69 + } 70 + 71 + @Override 72 + public <T> Builder addParameter(EventKey<T> key, T value) { 73 + this.map.put(key, value); 74 + return this; 75 + } 76 + 77 + @Override 78 + public EventContextImpl build() { 79 + return new EventContextImpl(type, map); 80 + } 81 + } 82 + }
+62
src/main/java/me/melontini/commander/impl/event/EventTypeImpl.java
··· 1 + package me.melontini.commander.impl.event; 2 + 3 + import com.mojang.serialization.Codec; 4 + import java.util.List; 5 + import java.util.Optional; 6 + import java.util.function.BiConsumer; 7 + import java.util.function.Function; 8 + import me.melontini.commander.api.event.EventType; 9 + import me.melontini.commander.api.event.Subscription; 10 + import me.melontini.commander.impl.event.data.types.EventTypes; 11 + import me.melontini.dark_matter.api.base.util.Context; 12 + import me.melontini.dark_matter.api.base.util.Utilities; 13 + import net.minecraft.util.Identifier; 14 + import org.jetbrains.annotations.Nullable; 15 + 16 + public record EventTypeImpl(Context context) implements EventType { 17 + 18 + public static final Context.Key<Codec<?>> EXTENSION = Context.key("extension"); 19 + public static final Context.Key<Function<List<Subscription<?>>, ?>> FINALIZER = 20 + Context.key("finalizer"); 21 + public static final Context.Key<Codec<?>> CANCEL_TERM = Context.key("cancel_term"); 22 + 23 + @Override 24 + public <T> Optional<T> get(Key<T> key) { 25 + return context.get(key); 26 + } 27 + 28 + @Override 29 + public void forEach(BiConsumer<Key<?>, Object> consumer) { 30 + context.forEach(consumer); 31 + } 32 + 33 + @Override 34 + public boolean equals(Object obj) { 35 + return obj == this; 36 + } 37 + 38 + public static class Builder implements EventType.Builder { 39 + private final Context.Builder builder = Context.builder(); 40 + 41 + @Override 42 + public <T, C> Builder extension( 43 + @Nullable Codec<T> extension, Function<List<Subscription<T>>, C> finalizer) { 44 + if (extension != null) builder.put(EXTENSION, extension); 45 + builder.put(FINALIZER, Utilities.cast(finalizer)); 46 + return this; 47 + } 48 + 49 + @Override 50 + public <R> Builder cancelTerm(Codec<R> returnCodec) { 51 + builder.put(CANCEL_TERM, returnCodec); 52 + return this; 53 + } 54 + 55 + @Override 56 + public EventTypeImpl build(Identifier identifier) { 57 + var type = new EventTypeImpl(this.builder.build()); 58 + EventTypes.register(identifier, type); 59 + return type; 60 + } 61 + } 62 + }
+66
src/main/java/me/melontini/commander/impl/event/data/DynamicEventManager.java
··· 1 + package me.melontini.commander.impl.event.data; 2 + 3 + import com.google.common.collect.Sets; 4 + import java.util.*; 5 + import java.util.stream.Collectors; 6 + import lombok.experimental.Accessors; 7 + import me.melontini.commander.api.event.EventType; 8 + import me.melontini.commander.api.event.Subscription; 9 + import me.melontini.commander.impl.event.EventTypeImpl; 10 + import me.melontini.commander.impl.event.data.types.EventTypes; 11 + import me.melontini.dark_matter.api.data.codecs.JsonCodecDataLoader; 12 + import me.melontini.dark_matter.api.data.loading.ReloaderType; 13 + import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; 14 + import net.minecraft.resource.ResourceManager; 15 + import net.minecraft.server.MinecraftServer; 16 + import net.minecraft.util.Identifier; 17 + import org.jetbrains.annotations.Nullable; 18 + 19 + @Accessors(fluent = true) 20 + public class DynamicEventManager extends JsonCodecDataLoader<List<? extends Subscription<?>>> 21 + implements IdentifiableResourceReloadListener { 22 + 23 + public static final ReloaderType<DynamicEventManager> RELOADER = 24 + ReloaderType.create(Identifier.of("commander:events")); 25 + 26 + final IdentityHashMap<EventType, Object> customData = new IdentityHashMap<>(); 27 + 28 + public DynamicEventManager() { 29 + super(RELOADER.identifier(), SubscriptionImpl.CODEC); 30 + } 31 + 32 + public static <T> @Nullable T getData(MinecraftServer server, EventType type) { 33 + return server.dm$getReloader(DynamicEventManager.RELOADER).getData(type); 34 + } 35 + 36 + public <T> @Nullable T getData(EventType type) { 37 + return (T) this.customData.get(type); 38 + } 39 + 40 + @Override 41 + protected void apply( 42 + Map<Identifier, List<? extends Subscription<?>>> parsed, ResourceManager manager) { 43 + parsed.values().stream() 44 + .flatMap(Collection::stream) 45 + .collect(Collectors.groupingBy(Subscription::type)) 46 + .forEach((eventType, subscriptions) -> { 47 + var finalizer = eventType.get(EventTypeImpl.FINALIZER); 48 + if (finalizer.isPresent()) { 49 + this.customData.put( 50 + eventType, finalizer.get().apply(Collections.unmodifiableList(subscriptions))); 51 + return; 52 + } 53 + this.customData.put( 54 + eventType, subscriptions.stream().flatMap(s -> s.list().stream()).toList()); 55 + }); 56 + 57 + Sets.difference(EventTypes.types(), customData.keySet()).forEach(type -> { 58 + var finalizer = type.get(EventTypeImpl.FINALIZER); 59 + if (finalizer.isPresent()) { 60 + this.customData.put(type, finalizer.get().apply(Collections.emptyList())); 61 + return; 62 + } 63 + this.customData.put(type, Collections.emptyList()); 64 + }); 65 + } 66 + }
+97
src/main/java/me/melontini/commander/impl/event/data/SubscriptionImpl.java
··· 1 + package me.melontini.commander.impl.event.data; 2 + 3 + import com.mojang.datafixers.util.Either; 4 + import com.mojang.serialization.*; 5 + import java.util.Collections; 6 + import java.util.List; 7 + import java.util.Optional; 8 + import java.util.function.Function; 9 + import java.util.stream.Stream; 10 + import me.melontini.commander.api.command.Command; 11 + import me.melontini.commander.api.event.EventType; 12 + import me.melontini.commander.api.event.Subscription; 13 + import me.melontini.commander.impl.event.EventTypeImpl; 14 + import me.melontini.commander.impl.event.data.types.EventTypes; 15 + import me.melontini.dark_matter.api.base.util.Utilities; 16 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 17 + import org.jetbrains.annotations.Nullable; 18 + 19 + public record SubscriptionImpl<E>( 20 + EventType type, @Nullable E parameters, List<Command.Conditioned> list) 21 + implements Subscription<E> { 22 + 23 + private static final Codec<List<Command.Conditioned>> LIST_CODEC = 24 + ExtraCodecs.list(Command.CODEC.codec()); 25 + public static final MapCodec<? extends Subscription<?>> BASE_CODEC = 26 + new MapCodec<SubscriptionImpl<?>>() { 27 + @Override 28 + public <T> RecordBuilder<T> encode( 29 + SubscriptionImpl<?> input, DynamicOps<T> ops, RecordBuilder<T> prefix) { 30 + var map = ops.mapBuilder(); 31 + var r = EventTypes.CODEC.encodeStart(ops, input.type()); 32 + map.add("event", r); 33 + if (!input.list().isEmpty()) 34 + map.add("commands", LIST_CODEC.encodeStart(ops, input.list())); 35 + if (input.parameters() != null) { 36 + var opt = input.type().get(EventTypeImpl.EXTENSION); 37 + opt.ifPresent(codec -> 38 + map.add("parameters", codec.encodeStart(ops, Utilities.cast(input.parameters())))); 39 + } 40 + return map; 41 + } 42 + 43 + @Override 44 + public <T> DataResult<SubscriptionImpl<?>> decode(DynamicOps<T> ops, MapLike<T> input) { 45 + T type = input.get("event"); 46 + if (type == null) 47 + return DataResult.error(() -> "Missing 'event' field in %s".formatted(input)); 48 + DataResult<EventType> event = EventTypes.CODEC.parse(ops, type); 49 + 50 + T parameters = input.get("parameters"); 51 + return event 52 + .flatMap(eventType -> { 53 + var opt = eventType.get(EventTypeImpl.EXTENSION); 54 + if (opt.isPresent()) { 55 + if (parameters == null) 56 + return DataResult.error( 57 + () -> "Missing required 'parameters' field in %s".formatted(input)); 58 + return opt.get().parse(ops, parameters).map(Optional::ofNullable); 59 + } 60 + return DataResult.success(Optional.empty()); 61 + }) 62 + .map(o -> event.map(eventType -> { 63 + T commands = input.get("commands"); 64 + if (commands != null) 65 + return LIST_CODEC 66 + .parse(ops, commands) 67 + .flatMap(list1 -> { 68 + for (Command.Conditioned conditionedCommand : list1) { 69 + var r = conditionedCommand.validate(eventType); 70 + if (r.error().isPresent()) 71 + return r.map(unused -> Collections.<Command.Conditioned>emptyList()); 72 + } 73 + return DataResult.success(list1); 74 + }) 75 + .map(list1 -> new SubscriptionImpl<>( 76 + eventType, o.orElse(null), (List<Command.Conditioned>) list1)); 77 + return DataResult.success( 78 + new SubscriptionImpl<>(eventType, o.orElse(null), Collections.emptyList())); 79 + })) 80 + .flatMap(Function.identity()) 81 + .map(Function.identity()) 82 + .flatMap(r -> r) 83 + .map(Function.identity()); 84 + } 85 + 86 + @Override 87 + public <T> Stream<T> keys(DynamicOps<T> ops) { 88 + return Stream.of("event", "parameters", "commands").map(ops::createString); 89 + } 90 + }; 91 + 92 + public static final Codec<List<? extends Subscription<?>>> CODEC = ExtraCodecs.either( 93 + ExtraCodecs.list(BASE_CODEC.codec()).fieldOf("events").codec(), BASE_CODEC.codec()) 94 + .xmap( 95 + e -> e.map(Function.identity(), Collections::singletonList), 96 + subscriptions -> Utilities.cast(Either.left(subscriptions))); 97 + }
+38
src/main/java/me/melontini/commander/impl/event/data/types/CommandTypes.java
··· 1 + package me.melontini.commander.impl.event.data.types; 2 + 3 + import com.google.common.collect.BiMap; 4 + import com.google.common.collect.HashBiMap; 5 + import com.mojang.serialization.Codec; 6 + import java.util.Objects; 7 + import lombok.experimental.UtilityClass; 8 + import me.melontini.commander.api.command.Command; 9 + import me.melontini.commander.api.command.CommandType; 10 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 11 + import net.minecraft.util.Identifier; 12 + 13 + @UtilityClass 14 + public final class CommandTypes { 15 + 16 + private static final BiMap<Identifier, CommandType> COMMANDS = HashBiMap.create(); 17 + private static final Codec<CommandType> TYPE_CODEC = 18 + ExtraCodecs.mapLookup(Identifier.CODEC, COMMANDS); 19 + public static final Codec<Command> CODEC = 20 + TYPE_CODEC.dispatch("type", Command::type, CommandType::codec); 21 + 22 + public static Identifier getId(CommandType type) { 23 + return Objects.requireNonNull( 24 + COMMANDS.inverse().get(type), () -> "Unregistered CommandType %s!".formatted(type)); 25 + } 26 + 27 + public static CommandType getType(Identifier identifier) { 28 + return Objects.requireNonNull( 29 + COMMANDS.get(identifier), () -> "Unknown CommandType %s!".formatted(identifier)); 30 + } 31 + 32 + public static CommandType register(Identifier identifier, CommandType type) { 33 + var old = COMMANDS.put(identifier, type); 34 + if (old != null) 35 + throw new IllegalStateException("Already registered command %s".formatted(identifier)); 36 + return type; 37 + } 38 + }
+41
src/main/java/me/melontini/commander/impl/event/data/types/EventTypes.java
··· 1 + package me.melontini.commander.impl.event.data.types; 2 + 3 + import com.google.common.collect.BiMap; 4 + import com.google.common.collect.HashBiMap; 5 + import com.mojang.serialization.Codec; 6 + import java.util.Collections; 7 + import java.util.Objects; 8 + import java.util.Set; 9 + import lombok.experimental.UtilityClass; 10 + import me.melontini.commander.api.event.EventType; 11 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 12 + import net.minecraft.util.Identifier; 13 + import org.jetbrains.annotations.UnmodifiableView; 14 + 15 + @UtilityClass 16 + public final class EventTypes { 17 + 18 + private static final BiMap<Identifier, EventType> EVENTS = HashBiMap.create(); 19 + public static final Codec<EventType> CODEC = ExtraCodecs.mapLookup(Identifier.CODEC, EVENTS); 20 + 21 + public static Identifier getId(EventType type) { 22 + return Objects.requireNonNull( 23 + EVENTS.inverse().get(type), () -> "Unregistered EventType %s!".formatted(type)); 24 + } 25 + 26 + public static EventType getType(Identifier identifier) { 27 + return Objects.requireNonNull( 28 + EVENTS.get(identifier), "Unknown EventType %s!".formatted(identifier)); 29 + } 30 + 31 + public static @UnmodifiableView Set<EventType> types() { 32 + return Collections.unmodifiableSet(EVENTS.values()); 33 + } 34 + 35 + public static EventType register(Identifier identifier, EventType type) { 36 + var old = EVENTS.put(identifier, type); 37 + if (old != null) 38 + throw new IllegalStateException("Already registered event %s".formatted(identifier)); 39 + return type; 40 + } 41 + }
+28
src/main/java/me/melontini/commander/impl/event/data/types/ExtractionTypes.java
··· 1 + package me.melontini.commander.impl.event.data.types; 2 + 3 + import com.google.common.collect.BiMap; 4 + import com.google.common.collect.HashBiMap; 5 + import com.mojang.serialization.Codec; 6 + import lombok.NonNull; 7 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 8 + import net.minecraft.loot.context.LootContextParameter; 9 + import net.minecraft.util.Identifier; 10 + import org.jetbrains.annotations.Nullable; 11 + 12 + public class ExtractionTypes { 13 + 14 + private static final BiMap<Identifier, LootContextParameter<?>> KNOWN_PARAMETERS = 15 + HashBiMap.create(); 16 + public static final Codec<LootContextParameter<?>> CODEC = 17 + ExtraCodecs.mapLookup(Identifier.CODEC, KNOWN_PARAMETERS); 18 + 19 + public static @Nullable LootContextParameter<?> getParameter(Identifier identifier) { 20 + return KNOWN_PARAMETERS.get(identifier); 21 + } 22 + 23 + public static void register(@NonNull LootContextParameter<?> parameter) { 24 + var old = KNOWN_PARAMETERS.put(parameter.getId(), parameter); 25 + if (old != null) 26 + throw new IllegalStateException("Already registered context %s".formatted(parameter.getId())); 27 + } 28 + }
+24
src/main/java/me/melontini/commander/impl/event/data/types/SelectorTypes.java
··· 1 + package me.melontini.commander.impl.event.data.types; 2 + 3 + import com.google.common.collect.BiMap; 4 + import com.google.common.collect.HashBiMap; 5 + import com.mojang.serialization.Codec; 6 + import lombok.experimental.UtilityClass; 7 + import me.melontini.commander.api.command.Selector; 8 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 9 + import net.minecraft.util.Identifier; 10 + 11 + @UtilityClass 12 + public final class SelectorTypes { 13 + 14 + private static final BiMap<Identifier, Selector> SELECTORS = HashBiMap.create(); 15 + 16 + public static final Codec<Selector> CODEC = ExtraCodecs.mapLookup(Identifier.CODEC, SELECTORS); 17 + 18 + public static Selector register(Identifier identifier, Selector selector) { 19 + var old = SELECTORS.put(identifier, selector); 20 + if (old != null) 21 + throw new IllegalStateException("Already registered selector %s".formatted(identifier)); 22 + return selector; 23 + } 24 + }
+6
src/main/java/me/melontini/commander/impl/expression/CmdEvalException.java
··· 1 + package me.melontini.commander.impl.expression; 2 + 3 + import lombok.experimental.StandardException; 4 + 5 + @StandardException 6 + public class CmdEvalException extends RuntimeException {}
+262
src/main/java/me/melontini/commander/impl/expression/EvalUtils.java
··· 1 + package me.melontini.commander.impl.expression; 2 + 3 + import com.ezylang.evalex.BaseException; 4 + import com.ezylang.evalex.EvaluationContext; 5 + import com.ezylang.evalex.EvaluationException; 6 + import com.ezylang.evalex.Expression; 7 + import com.ezylang.evalex.config.ExpressionConfiguration; 8 + import com.ezylang.evalex.data.EvaluationValue; 9 + import com.ezylang.evalex.data.types.BooleanValue; 10 + import com.ezylang.evalex.data.types.NullValue; 11 + import com.ezylang.evalex.data.types.NumberValue; 12 + import com.ezylang.evalex.data.types.StringValue; 13 + import com.ezylang.evalex.parser.ASTNode; 14 + import com.ezylang.evalex.parser.ExpressionParser; 15 + import com.ezylang.evalex.parser.InlinedASTNode; 16 + import com.google.common.base.CaseFormat; 17 + import com.google.common.collect.ImmutableMap; 18 + import com.google.common.collect.ImmutableSet; 19 + import com.mojang.serialization.DataResult; 20 + import it.unimi.dsi.fastutil.objects.Object2ReferenceMap; 21 + import it.unimi.dsi.fastutil.objects.Object2ReferenceMaps; 22 + import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; 23 + import java.math.BigDecimal; 24 + import java.math.MathContext; 25 + import java.util.*; 26 + import java.util.function.Function; 27 + import lombok.extern.log4j.Log4j2; 28 + import me.melontini.commander.impl.expression.extensions.ReflectiveValueConverter; 29 + import me.melontini.commander.impl.expression.functions.*; 30 + import me.melontini.commander.impl.expression.functions.arrays.*; 31 + import me.melontini.commander.impl.expression.functions.math.ClampFunction; 32 + import me.melontini.commander.impl.expression.functions.math.LerpFunction; 33 + import me.melontini.commander.impl.expression.functions.math.RangedRandomFunction; 34 + import me.melontini.commander.impl.expression.functions.registry.DynamicRegistryFunction; 35 + import me.melontini.commander.impl.expression.functions.registry.DynamicRegistryRegistryFunction; 36 + import me.melontini.commander.impl.expression.functions.registry.RegistryFunction; 37 + import me.melontini.commander.impl.expression.operators.SafeOrOperator; 38 + import me.melontini.commander.impl.expression.operators.SafeStructureOperator; 39 + import me.melontini.commander.impl.util.ThrowingOptional; 40 + import me.melontini.dark_matter.api.base.util.Exceptions; 41 + import me.melontini.dark_matter.api.base.util.functions.Memoize; 42 + import net.minecraft.loot.context.LootContext; 43 + import net.minecraft.nbt.NbtString; 44 + import net.minecraft.registry.Registries; 45 + import net.minecraft.registry.RegistryKeys; 46 + import org.jetbrains.annotations.Nullable; 47 + 48 + @Log4j2 49 + public class EvalUtils { 50 + 51 + public static final ExpressionConfiguration CONFIGURATION; 52 + public static final ExpressionParser PARSER; 53 + public static final Object2ReferenceMap<String, EvaluationValue> CONSTANTS = 54 + Object2ReferenceMaps.unmodifiable(new Object2ReferenceOpenHashMap<>(ImmutableMap.of( 55 + "true", BooleanValue.TRUE, 56 + "false", BooleanValue.FALSE, 57 + "PI", 58 + NumberValue.of( 59 + new BigDecimal( 60 + "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679")), 61 + "E", 62 + NumberValue.of(new BigDecimal( 63 + "2.71828182845904523536028747135266249775724709369995957496696762772407663")), 64 + "null", NullValue.of(), 65 + "DT_FORMAT_ISO_DATE_TIME", StringValue.of("yyyy-MM-dd'T'HH:mm:ss[.SSS][XXX]['['VV']']"), 66 + "DT_FORMAT_LOCAL_DATE_TIME", StringValue.of("yyyy-MM-dd'T'HH:mm:ss[.SSS]"), 67 + "DT_FORMAT_LOCAL_DATE", StringValue.of("yyyy-MM-dd")))); 68 + 69 + static { 70 + LootContextDataAccessor dataAccessor = new LootContextDataAccessor(); 71 + var builder = ExpressionConfiguration.builder() 72 + .dataAccessorSupplier(() -> dataAccessor) 73 + .parameterMapSupplier(Object2ReferenceOpenHashMap::new) 74 + .evaluationValueConverter(new ReflectiveValueConverter()) 75 + .allowOverwriteConstants(false) 76 + .additionalAllowedIdentifierChars(new char[] {':'}) 77 + .singleQuoteStringLiteralsAllowed(true) 78 + .mathContext(MathContext.DECIMAL64) 79 + .constants(CONSTANTS); 80 + 81 + Set<String> toCache = ImmutableSet.of( 82 + "fact", 83 + "sqrt", 84 + "acos", 85 + "acosh", 86 + "acosr", 87 + "acot", 88 + "acoth", 89 + "acotr", 90 + "asin", 91 + "asinh", 92 + "asinr", 93 + "atan", 94 + "atan2", 95 + "atan2r", 96 + "atanh", 97 + "atanr", 98 + "cosh", 99 + "cosr", 100 + "cot", 101 + "coth", 102 + "cotr", 103 + "csc", 104 + "csch", 105 + "cscr", 106 + "sinh", 107 + "sinr", 108 + "sec", 109 + "sech", 110 + "secr", 111 + "tanh", 112 + "tanr", 113 + "strContains", 114 + "strEndsWith", 115 + "strLower", 116 + "strStartsWith", 117 + "strTrim", 118 + "strUpper"); 119 + 120 + var fd = ExpressionConfiguration.defaultConfiguration().getFunctionDictionary(); 121 + var functions = fd.toBuilder(Object2ReferenceOpenHashMap::new); 122 + fd.forEach((string, functionIfc) -> { 123 + var newName = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, string); 124 + functions.add( 125 + newName, toCache.contains(newName) ? LruCachingFunction.of(functionIfc) : functionIfc); 126 + }); 127 + 128 + functions.add("random", new RangedRandomFunction()); 129 + functions.add("lerp", new LerpFunction()); 130 + functions.add("clamp", new ClampFunction()); 131 + functions.add("ifMatches", new MatchesFunction()); 132 + functions.add("chain", new ChainFunction()); 133 + functions.add("length", new LengthFunction()); 134 + 135 + functions.add("arrayOf", new ArrayOf()); 136 + functions.add("arrayMap", new ArrayMap()); 137 + functions.add("arrayFind", new ArrayFind()); 138 + functions.add("arrayFindAny", new ArrayFindAny()); 139 + functions.add("arrayFindFirst", new ArrayFindFirst()); 140 + functions.add("arrayAnyMatch", new ArrayAnyMatch()); 141 + functions.add("arrayNoneMatch", new ArrayNoneMatch()); 142 + functions.add("arrayAllMatch", new ArrayAllMatch()); 143 + 144 + functions.add("structContainsKey", new StructContainsKeyFunction()); 145 + functions.add("hasContext", new HasContextFunction()); 146 + 147 + functions.add("Registry", new RegistryFunction(Registries.REGISTRIES)); 148 + functions.add("Item", LruCachingFunction.of(new RegistryFunction(Registries.ITEM))); 149 + functions.add("Block", LruCachingFunction.of(new RegistryFunction(Registries.BLOCK))); 150 + 151 + functions.add("DynamicRegistry", new DynamicRegistryRegistryFunction()); 152 + functions.add("Biome", new DynamicRegistryFunction(RegistryKeys.BIOME)); 153 + functions.add("DimensionType", new DynamicRegistryFunction(RegistryKeys.DIMENSION_TYPE)); 154 + builder.functionDictionary(functions.build()); 155 + 156 + var operators = 157 + ExpressionConfiguration.defaultConfiguration().getOperatorDictionary().toBuilder( 158 + Object2ReferenceOpenHashMap::new); 159 + 160 + operators.infix("?.", new SafeStructureOperator()); 161 + operators.infix("?", new SafeOrOperator()); 162 + 163 + builder.operatorDictionary(operators.build()); 164 + 165 + CONFIGURATION = builder.build(); 166 + PARSER = new ExpressionParser(CONFIGURATION); 167 + } 168 + 169 + public static String toMacroString(EvaluationValue value) { 170 + if (value.isStringValue()) return value.getStringValue(); 171 + 172 + if (value.isArrayValue()) { 173 + StringJoiner joiner = new StringJoiner(", ", "[", "]"); 174 + value.getArrayValue().forEach(value1 -> joiner.add(toMacroString(value1))); 175 + return joiner.toString(); 176 + } 177 + 178 + if (value.isStructureValue()) { 179 + var map = value.getStructureValue(); 180 + StringJoiner joiner = new StringJoiner(", ", "{", "}"); 181 + map.forEach((string, value1) -> joiner.add(string + "=" + toMacroString(value1))); 182 + return joiner.toString(); 183 + } 184 + 185 + return prettyToString(value); 186 + } 187 + 188 + public static String prettyToString(EvaluationValue value) { 189 + if (value.isNumberValue()) return value.getStringValue(); 190 + if (value.isBooleanValue()) return value.getBooleanValue() ? "true" : "false"; 191 + if (value.isStringValue()) return NbtString.escape(value.getStringValue()); 192 + 193 + if (value.isArrayValue()) { 194 + StringJoiner joiner = new StringJoiner(", ", "[", "]"); 195 + value.getArrayValue().forEach(value1 -> joiner.add(prettyToString(value1))); 196 + return joiner.toString(); 197 + } 198 + 199 + if (value.isStructureValue()) { 200 + var map = value.getStructureValue(); 201 + StringJoiner joiner = new StringJoiner(", ", "{", "}"); 202 + map.forEach((string, value1) -> joiner.add(string + "=" + prettyToString(value1))); 203 + return joiner.toString(); 204 + } 205 + 206 + if (value.isNullValue()) return "null"; 207 + return Objects.toString(value.getValue()); 208 + } 209 + 210 + public static ThrowingOptional<EvaluationValue> valueOrEmpty(ASTNode node) { 211 + if (node instanceof InlinedASTNode inlined) return ThrowingOptional.of(inlined.value()); 212 + return ThrowingOptional.empty(); 213 + } 214 + 215 + // Quickly evaluate a fake "lambda". 216 + public static EvaluationValue runLambda( 217 + EvaluationContext context, EvaluationValue value, ASTNode predicate) 218 + throws EvaluationException { 219 + return context.expression().evaluateSubtree(predicate, context.withParameter("it", value)); 220 + } 221 + 222 + public static EvaluationValue evaluate( 223 + LootContext context, Expression exp, @Nullable Map<String, ?> params) { 224 + try { 225 + var builder = EvaluationContext.builder(exp).context(context); 226 + if (params != null && !params.isEmpty()) builder.parameters(params); 227 + return exp.evaluate(builder.build()); 228 + } catch (BaseException e) { 229 + throw new CmdEvalException( 230 + Objects.requireNonNullElseGet(e.getMessage(), () -> "Failed to evaluate expression %s" 231 + .formatted(exp.getExpressionString())), 232 + e); 233 + } 234 + } 235 + 236 + private static final Object CACHE_LOCK = new Object(); 237 + private static Function<String, Expression> EXPRESSION_CACHE; 238 + 239 + static { 240 + resetCache(); 241 + } 242 + 243 + public static void resetCache() { 244 + synchronized (CACHE_LOCK) { 245 + EXPRESSION_CACHE = Memoize.lruFunction(Exceptions.function(PARSER::parseAndInline), 60); 246 + } 247 + } 248 + 249 + public static DataResult<Expression> parseExpression(String expression) { 250 + try { 251 + Expression result; 252 + synchronized (CACHE_LOCK) { 253 + result = EXPRESSION_CACHE.apply(expression); 254 + } 255 + return DataResult.success(result.copy()); 256 + } catch (Throwable throwable) { 257 + return DataResult.error(Exceptions.unwrap(throwable)::getMessage); 258 + } 259 + } 260 + 261 + public static void init() {} 262 + }
+65
src/main/java/me/melontini/commander/impl/expression/LootContextDataAccessor.java
··· 1 + package me.melontini.commander.impl.expression; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.data.DataAccessorIfc; 6 + import com.ezylang.evalex.data.EvaluationValue; 7 + import com.ezylang.evalex.parser.Token; 8 + import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; 9 + import java.util.Collections; 10 + import java.util.Map; 11 + import java.util.function.Function; 12 + import me.melontini.commander.api.expression.ExpressionLibrary; 13 + import me.melontini.commander.impl.event.data.types.ExtractionTypes; 14 + import me.melontini.commander.impl.expression.extensions.ReflectiveValueConverter; 15 + import net.minecraft.loot.context.LootContext; 16 + import net.minecraft.util.Identifier; 17 + 18 + public class LootContextDataAccessor implements DataAccessorIfc { 19 + 20 + private static final Map<Identifier, Function<LootContext, Object>> overrides = 21 + Collections.unmodifiableMap(new Object2ReferenceOpenHashMap<>(Map.of( 22 + Identifier.of("level"), LootContext::getWorld, 23 + Identifier.of("luck"), LootContext::getLuck, 24 + Identifier.of("library"), 25 + context -> ExpressionLibrary.get(context.getWorld().getServer())))); 26 + // Cache the way we got the variable. 27 + // We use the same instance for all expressions, so this can help save some overhead. 28 + private final Map<String, Function<LootContext, EvaluationValue>> varCache = 29 + new Object2ReferenceOpenHashMap<>(); 30 + 31 + @Override 32 + public EvaluationValue getData(String variable, Token token, EvaluationContext context) 33 + throws EvaluationException { 34 + var supplier = varCache.get(variable); 35 + if (supplier != null) return supplier.apply((LootContext) context.context()[0]); 36 + 37 + var r = Identifier.validate(variable); 38 + if (r.error().isPresent()) { 39 + throw new EvaluationException(token, r.error().orElseThrow().message()); 40 + } 41 + 42 + var id = r.result().orElseThrow(); 43 + var func = overrides.get(id); 44 + if (func != null) { 45 + varCache.put( 46 + variable, 47 + supplier = (lootContext) -> ReflectiveValueConverter.convert(func.apply(lootContext))); 48 + return supplier.apply((LootContext) context.context()[0]); 49 + } 50 + 51 + var param = ExtractionTypes.getParameter(id); 52 + if (param == null) { 53 + throw new EvaluationException( 54 + token, "%s is not a registered loot context parameter!".formatted(id)); 55 + } 56 + varCache.put( 57 + variable, 58 + supplier = (lootContext) -> { 59 + var object = lootContext.get(param); 60 + if (object == null) return null; 61 + return ReflectiveValueConverter.convert(object); 62 + }); 63 + return supplier.apply((LootContext) context.context()[0]); 64 + } 65 + }
+46
src/main/java/me/melontini/commander/impl/expression/extensions/DefaultCustomFields.java
··· 1 + package me.melontini.commander.impl.expression.extensions; 2 + 3 + import me.melontini.commander.api.expression.extensions.CustomFields; 4 + import me.melontini.commander.impl.Commander; 5 + import me.melontini.commander.impl.expression.extensions.convert.RegistryAccessStruct; 6 + import me.melontini.commander.impl.expression.extensions.convert.attributes.EntityAttributesStruct; 7 + import me.melontini.commander.impl.expression.extensions.convert.components.ComponentStruct; 8 + import me.melontini.commander.impl.expression.extensions.convert.states.StateStruct; 9 + import net.fabricmc.fabric.api.attachment.v1.AttachmentTarget; 10 + import net.minecraft.block.entity.BlockEntity; 11 + import net.minecraft.entity.Entity; 12 + import net.minecraft.entity.LivingEntity; 13 + import net.minecraft.item.ItemStack; 14 + import net.minecraft.nbt.NbtCompound; 15 + import net.minecraft.predicate.NbtPredicate; 16 + import net.minecraft.registry.Registry; 17 + import net.minecraft.state.State; 18 + 19 + class DefaultCustomFields { 20 + 21 + private static final NbtCompound EMPTY = new NbtCompound(); 22 + 23 + static void init() { 24 + CustomFields.addVirtualField(ItemStack.class, "nbt", (object, context) -> { 25 + if (object.isEmpty()) return EMPTY; 26 + return object.encode(context.getWorld().getRegistryManager()); 27 + }); 28 + CustomFields.addVirtualField(Entity.class, "nbt", NbtPredicate::entityToNbt); 29 + CustomFields.addVirtualField( 30 + BlockEntity.class, 31 + "nbt", 32 + (be, context) -> be.createNbtWithIdentifyingData(context.getWorld().getRegistryManager())); 33 + 34 + CustomFields.addVirtualField(State.class, "properties", StateStruct::new); 35 + CustomFields.addVirtualField( 36 + LivingEntity.class, "attributes", e -> new EntityAttributesStruct(e.getAttributes())); 37 + CustomFields.addVirtualField( 38 + ItemStack.class, "components", stack -> new ComponentStruct(stack.getComponents())); 39 + 40 + CustomFields.addVirtualField( 41 + AttachmentTarget.class, 42 + "storage", 43 + target -> target.getAttachedOrCreate(Commander.DATA_ATTACHMENT)); 44 + CustomFields.addVirtualField(Registry.class, "access", RegistryAccessStruct::forRegistry); 45 + } 46 + }
+227
src/main/java/me/melontini/commander/impl/expression/extensions/ReflectiveMapStructure.java
··· 1 + package me.melontini.commander.impl.expression.extensions; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.data.DataAccessorIfc; 6 + import com.ezylang.evalex.data.EvaluationValue; 7 + import com.ezylang.evalex.parser.Token; 8 + import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; 9 + import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; 10 + import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; 11 + import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; 12 + import java.lang.invoke.CallSite; 13 + import java.lang.invoke.LambdaMetafactory; 14 + import java.lang.invoke.MethodHandles; 15 + import java.lang.invoke.MethodType; 16 + import java.lang.reflect.Field; 17 + import java.lang.reflect.Method; 18 + import java.lang.reflect.Modifier; 19 + import java.util.*; 20 + import java.util.function.BiFunction; 21 + import java.util.function.Function; 22 + import lombok.EqualsAndHashCode; 23 + import lombok.NonNull; 24 + import lombok.Synchronized; 25 + import lombok.extern.log4j.Log4j2; 26 + import me.melontini.commander.impl.Commander; 27 + import me.melontini.dark_matter.api.base.util.tuple.Tuple; 28 + import net.minecraft.loot.context.LootContext; 29 + import org.jetbrains.annotations.Nullable; 30 + 31 + @Log4j2 32 + @EqualsAndHashCode(callSuper = false) 33 + public class ReflectiveMapStructure implements DataAccessorIfc { 34 + 35 + private static final Map<Class<?>, Struct> MAPPINGS = new Reference2ReferenceOpenHashMap<>(); 36 + private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 37 + 38 + static { 39 + DefaultCustomFields.init(); 40 + } 41 + 42 + @EqualsAndHashCode.Exclude 43 + private final Struct mappings; 44 + 45 + private final Object object; 46 + 47 + public ReflectiveMapStructure(Object object) { 48 + this.object = object; 49 + this.mappings = getAccessors(object.getClass()); 50 + } 51 + 52 + // API made from an implementation side effect. 53 + public static <C> void addField( 54 + Class<C> cls, String name, BiFunction<C, LootContext, Object> accessor) { 55 + ReflectiveMapStructure.getAccessors(cls) 56 + .addAccessor(name, (BiFunction<Object, LootContext, Object>) accessor); 57 + } 58 + 59 + private static Struct getAccessors(Class<?> cls) { 60 + Struct map = MAPPINGS.get(cls); 61 + if (map != null) return map; 62 + 63 + synchronized (MAPPINGS) { 64 + Struct struct = new Struct(); 65 + 66 + // When we create a new struct, we must guarantee that all changes from 67 + // interfaces and superclasses propagate downstream. 68 + for (Class<?> anInterface : cls.getInterfaces()) { 69 + getAccessors(anInterface).addListener(struct); 70 + } 71 + Class<?> target = cls; 72 + while ((target = target.getSuperclass()) != null) { 73 + getAccessors(target).addListener(struct); 74 + for (Class<?> anInterface : cls.getInterfaces()) { 75 + getAccessors(anInterface).addListener(struct); 76 + } 77 + } 78 + 79 + MAPPINGS.put(cls, struct); 80 + return struct; 81 + } 82 + } 83 + 84 + // It is believed that LambdaMetafactory is faster than reflection, so I'll blindly believe that 85 + // too. 86 + private static BiFunction<Object, LootContext, Object> methodAccessor(Method method) { 87 + try { 88 + var handle = LOOKUP.unreflect(method); 89 + CallSite getterSite = LambdaMetafactory.metafactory( 90 + LOOKUP, 91 + "apply", 92 + MethodType.methodType(Function.class), 93 + MethodType.methodType(Object.class, Object.class), 94 + handle, 95 + handle.type().wrap()); 96 + var getter = (Function<Object, Object>) getterSite.getTarget().invoke(); 97 + return (object1, lootContext) -> getter.apply(object1); 98 + } catch (Throwable e) { 99 + throw new RuntimeException(e); 100 + } 101 + } 102 + 103 + @Override 104 + public @Nullable EvaluationValue getData(String variable, Token token, EvaluationContext context) 105 + throws EvaluationException { 106 + // We cache invalid values to avoid bad look up overhead. 107 + if (this.mappings.isInvalid(variable)) return null; 108 + 109 + // We lazily look up accessors, as doing so eagerly would be extremely slow. 110 + var cache = this.mappings.getAccessor(variable); 111 + if (cache != null) 112 + return ReflectiveValueConverter.convert( 113 + cache.apply(this.object, (LootContext) context.context()[0])); 114 + 115 + var accessor = findFieldOrMethod(this.object.getClass(), variable); 116 + if (accessor == null) { 117 + this.mappings.invalidate(variable); 118 + return null; 119 + } 120 + 121 + synchronized (MAPPINGS) { 122 + getAccessors(accessor.left()).addAccessor(variable, accessor.right()); 123 + } 124 + return ReflectiveValueConverter.convert( 125 + accessor.right().apply(this.object, (LootContext) context.context()[0])); 126 + } 127 + 128 + // This method scans the class, all superclasses and interfaces to _maybe_ find a member. 129 + // We cannot skip remapping based on package due to subclasses of vanilla classes. 130 + private static @Nullable Tuple<Class<?>, BiFunction<Object, LootContext, Object>> 131 + findFieldOrMethod(Class<?> cls, String name) { 132 + var keeper = Commander.get().mappingKeeper(); 133 + String mapped; 134 + Class<?> target = cls; 135 + do { 136 + if ((mapped = keeper.getFieldOrMethod(target, name)) != null) 137 + return findAccessor(target, mapped); 138 + var targetItfs = target.getInterfaces(); 139 + if (targetItfs.length == 0) continue; 140 + 141 + Queue<Class<?>> interfaces = new ArrayDeque<>(List.of(targetItfs)); 142 + while (!interfaces.isEmpty()) { 143 + var itf = interfaces.poll(); 144 + 145 + if ((mapped = keeper.getFieldOrMethod(itf, name)) != null) return findAccessor(itf, mapped); 146 + if ((targetItfs = itf.getInterfaces()).length > 0) interfaces.addAll(List.of(targetItfs)); 147 + } 148 + } while ((target = target.getSuperclass()) != null); 149 + return findAccessor(cls, name); 150 + } 151 + 152 + // This method attempts to look up a member based on its name. 153 + // Iterating here to avoid generating a throwable. 154 + @Nullable private static Tuple<Class<?>, BiFunction<Object, LootContext, Object>> findAccessor( 155 + @NonNull Class<?> cls, String mapped) { 156 + for (Method method : cls.getMethods()) { 157 + if (!method.getName().equals(mapped)) continue; 158 + if (Modifier.isStatic(method.getModifiers())) continue; 159 + if (method.getParameterCount() > 0) continue; 160 + if (method.getReturnType() == void.class) continue; 161 + 162 + return Tuple.of(method.getDeclaringClass(), methodAccessor(method)); 163 + } 164 + 165 + for (Field field : cls.getFields()) { 166 + if (!field.getName().equals(mapped)) continue; 167 + if (Modifier.isStatic(field.getModifiers())) continue; 168 + return Tuple.of(field.getDeclaringClass(), (o, context) -> { 169 + try { 170 + return field.get(o); 171 + } catch (IllegalAccessException e) { 172 + throw new RuntimeException(e); 173 + } 174 + }); 175 + } 176 + 177 + return null; 178 + } 179 + 180 + @Override 181 + public String toString() { 182 + return String.valueOf(this.object); 183 + } 184 + 185 + static final class Struct { 186 + private Map<String, BiFunction<Object, LootContext, Object>> accessors; 187 + private Set<String> invalid; 188 + private Set<Struct> consumers; 189 + 190 + public boolean isInvalid(String key) { 191 + return this.invalid != null && this.invalid.contains(key); 192 + } 193 + 194 + @Synchronized 195 + public void invalidate(String key) { 196 + if (this.invalid == null) this.invalid = new ObjectOpenHashSet<>(); 197 + this.invalid.add(key); 198 + } 199 + 200 + public @Nullable BiFunction<Object, LootContext, Object> getAccessor(String key) { 201 + return this.accessors == null ? null : this.accessors.get(key); 202 + } 203 + 204 + @Synchronized 205 + public void addAccessor(String key, BiFunction<Object, LootContext, Object> accessor) { 206 + if (this.accessors == null) this.accessors = new Object2ReferenceOpenHashMap<>(); 207 + this.accessors.put(key, accessor); 208 + 209 + if (this.consumers == null) return; 210 + for (Struct consumer : consumers) { 211 + consumer.addAccessor(key, accessor); 212 + } 213 + } 214 + 215 + @Synchronized 216 + public void addListener(Struct other) { 217 + if (this.consumers == null) this.consumers = new ReferenceOpenHashSet<>(); 218 + this.consumers.add(other); 219 + if (this.accessors != null) this.accessors.forEach(other::addAccessor); 220 + } 221 + 222 + @Override 223 + public String toString() { 224 + return String.valueOf(this.accessors); 225 + } 226 + } 227 + }
+75
src/main/java/me/melontini/commander/impl/expression/extensions/ReflectiveValueConverter.java
··· 1 + package me.melontini.commander.impl.expression.extensions; 2 + 3 + import com.ezylang.evalex.config.ExpressionConfiguration; 4 + import com.ezylang.evalex.data.EvaluationValue; 5 + import com.ezylang.evalex.data.conversion.*; 6 + import com.ezylang.evalex.data.types.DataAccessorValue; 7 + import com.ezylang.evalex.data.types.NullValue; 8 + import com.ezylang.evalex.data.types.StructureValue; 9 + import com.google.common.collect.Lists; 10 + import java.util.List; 11 + import java.util.Map; 12 + import me.melontini.commander.api.expression.extensions.ObjectConverter; 13 + import me.melontini.commander.api.expression.extensions.ProxyMap; 14 + import me.melontini.commander.impl.expression.EvalUtils; 15 + import me.melontini.commander.impl.expression.extensions.convert.gson.GsonConverter; 16 + import me.melontini.commander.impl.expression.extensions.convert.misc.DataAccessorConverter; 17 + import me.melontini.commander.impl.expression.extensions.convert.misc.IdentifierConverter; 18 + import me.melontini.commander.impl.expression.extensions.convert.misc.OptionalConverter; 19 + import me.melontini.commander.impl.expression.extensions.convert.nbt.NbtConverter; 20 + import me.melontini.dark_matter.api.base.util.MathUtil; 21 + 22 + public class ReflectiveValueConverter implements EvaluationValueConverterIfc { 23 + 24 + private static final List<ConverterIfc> converters = Lists.newArrayList( 25 + new NumberConverter(), 26 + new StringConverter(), 27 + new BooleanConverter(), 28 + new DataAccessorConverter(), 29 + new ExpressionNodeConverter(), 30 + new IdentifierConverter(), 31 + new NbtConverter(), 32 + new GsonConverter(), 33 + new ArrayConverter(), 34 + new OptionalConverter(), 35 + new DateTimeConverter(), 36 + new DurationConverter()); 37 + 38 + public static void registerConverter(int priority, ObjectConverter converter) { 39 + priority = MathUtil.clamp(priority, 0, converters.size()); 40 + 41 + converters.add(priority, new ConverterIfc() { 42 + @Override 43 + public EvaluationValue convert(Object object, ExpressionConfiguration configuration) { 44 + return (EvaluationValue) (Object) converter.convert(object); 45 + } 46 + 47 + @Override 48 + public boolean canConvert(Object object) { 49 + return converter.canConvert(object); 50 + } 51 + }); 52 + } 53 + 54 + public static EvaluationValue convert(Object o) { 55 + return EvalUtils.CONFIGURATION 56 + .getEvaluationValueConverter() 57 + .convertObject(o, EvalUtils.CONFIGURATION); 58 + } 59 + 60 + @Override 61 + public EvaluationValue convertObject(Object object, ExpressionConfiguration configuration) { 62 + if (object == null) return NullValue.of(); 63 + if (object instanceof EvaluationValue value) return value; 64 + 65 + // Proxy maps convert all of their outputs. 66 + if (object instanceof ProxyMap map) 67 + return StructureValue.of((Map<String, EvaluationValue>) (Object) map); 68 + 69 + for (ConverterIfc converter : converters) { 70 + if (converter.canConvert(object)) return converter.convert(object, configuration); 71 + } 72 + 73 + return DataAccessorValue.of(new ReflectiveMapStructure(object)); 74 + } 75 + }
+45
src/main/java/me/melontini/commander/impl/expression/extensions/convert/LazyMapEntry.java
··· 1 + package me.melontini.commander.impl.expression.extensions.convert; 2 + 3 + import java.util.Map; 4 + import java.util.Objects; 5 + import java.util.function.Function; 6 + import lombok.AllArgsConstructor; 7 + import me.melontini.commander.api.expression.Expression; 8 + 9 + @AllArgsConstructor 10 + public class LazyMapEntry<K, V> implements Map.Entry<String, Expression.Result> { 11 + 12 + private final K key; 13 + private V value; 14 + 15 + private final transient Function<K, String> toString; 16 + 17 + @Override 18 + public String getKey() { 19 + return toString.apply(key); 20 + } 21 + 22 + @Override 23 + public Expression.Result getValue() { 24 + return Expression.Result.convert(value); 25 + } 26 + 27 + @Override 28 + public Expression.Result setValue(Expression.Result value) { 29 + throw new UnsupportedOperationException(); 30 + } 31 + 32 + @Override 33 + public boolean equals(Object object) { 34 + if (!(object instanceof Map.Entry<?, ?> that)) return false; 35 + if (that instanceof LazyMapEntry<?, ?> lazy) { 36 + return Objects.equals(key, lazy.key) && Objects.equals(value, lazy.value); 37 + } 38 + return Objects.equals(getKey(), that.getKey()) && Objects.equals(getValue(), that.getValue()); 39 + } 40 + 41 + @Override 42 + public int hashCode() { 43 + return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); 44 + } 45 + }
+84
src/main/java/me/melontini/commander/impl/expression/extensions/convert/RegistryAccessStruct.java
··· 1 + package me.melontini.commander.impl.expression.extensions.convert; 2 + 3 + import java.util.Set; 4 + import java.util.function.Function; 5 + import java.util.stream.Collectors; 6 + import lombok.EqualsAndHashCode; 7 + import lombok.Getter; 8 + import lombok.Synchronized; 9 + import me.melontini.commander.api.expression.Expression; 10 + import me.melontini.commander.api.expression.extensions.AbstractProxyMap; 11 + import me.melontini.commander.impl.util.Identity; 12 + import me.melontini.dark_matter.api.base.util.functions.Memoize; 13 + import net.minecraft.registry.Registry; 14 + import net.minecraft.util.Identifier; 15 + import org.jetbrains.annotations.NotNull; 16 + 17 + @EqualsAndHashCode(callSuper = false) 18 + public class RegistryAccessStruct extends AbstractProxyMap { 19 + 20 + private static final Object CACHE_LOCK = new Object(); 21 + private static Function<Identity<Registry<?>>, RegistryAccessStruct> CACHE; 22 + 23 + static { 24 + resetCache(); 25 + } 26 + 27 + public static void resetCache() { 28 + synchronized (CACHE_LOCK) { 29 + CACHE = Memoize.lruFunction(identity -> new RegistryAccessStruct(identity.value()), 10); 30 + } 31 + } 32 + 33 + public static synchronized RegistryAccessStruct forRegistry(Registry<?> registry) { 34 + synchronized (CACHE_LOCK) { 35 + return CACHE.apply(new Identity<>(registry)); 36 + } 37 + } 38 + 39 + private final Registry<?> registry; 40 + 41 + @Getter 42 + private final Function<String, Object> cache; 43 + 44 + private Set<Entry<String, Expression.Result>> entries; 45 + 46 + private RegistryAccessStruct(Registry<?> registry) { 47 + this.registry = registry; 48 + this.cache = Memoize.lruFunction(key -> registry.get(Identifier.of(key)), 20); 49 + } 50 + 51 + @Override 52 + public boolean containsKey(Object o) { 53 + if (!(o instanceof String key)) return false; 54 + return this.cache.apply(key) != null; 55 + } 56 + 57 + @Override 58 + public Expression.Result get(Object o) { 59 + if (!(o instanceof String key)) return null; 60 + return Expression.Result.convert(this.cache.apply(key)); 61 + } 62 + 63 + @Override 64 + public int size() { 65 + return registry.size(); 66 + } 67 + 68 + @Synchronized 69 + @NotNull @Override 70 + public Set<Entry<String, Expression.Result>> entrySet() { 71 + if (entries == null) { 72 + this.entries = registry.getEntrySet().stream() 73 + .map(entry -> new LazyMapEntry<>( 74 + entry.getKey(), entry.getValue(), key -> key.getValue().toString())) 75 + .collect(Collectors.toUnmodifiableSet()); 76 + } 77 + return entries; 78 + } 79 + 80 + @Override 81 + public String toString() { 82 + return String.valueOf(registry); 83 + } 84 + }
+41
src/main/java/me/melontini/commander/impl/expression/extensions/convert/attributes/EntityAttributesStruct.java
··· 1 + package me.melontini.commander.impl.expression.extensions.convert.attributes; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.data.DataAccessorIfc; 6 + import com.ezylang.evalex.data.EvaluationValue; 7 + import com.ezylang.evalex.data.types.NumberValue; 8 + import com.ezylang.evalex.parser.Token; 9 + import java.math.BigDecimal; 10 + import lombok.EqualsAndHashCode; 11 + import net.minecraft.entity.attribute.AttributeContainer; 12 + import net.minecraft.registry.Registries; 13 + import net.minecraft.util.Identifier; 14 + import org.jetbrains.annotations.Nullable; 15 + 16 + @EqualsAndHashCode(callSuper = false) 17 + public final class EntityAttributesStruct implements DataAccessorIfc { 18 + 19 + private final AttributeContainer container; 20 + 21 + public EntityAttributesStruct(AttributeContainer container) { 22 + this.container = container; 23 + } 24 + 25 + @Override 26 + public @Nullable EvaluationValue getData(String variable, Token token, EvaluationContext context) 27 + throws EvaluationException { 28 + var attr = Registries.ATTRIBUTE.getEntry(Identifier.of(variable)); 29 + if (attr.isEmpty()) return null; 30 + return attr.filter(container::hasAttribute) 31 + .map(container::getValue) 32 + .map(BigDecimal::valueOf) 33 + .map(NumberValue::of) 34 + .orElse(null); 35 + } 36 + 37 + @Override 38 + public String toString() { 39 + return String.valueOf(container.toNbt()); 40 + } 41 + }
+39
src/main/java/me/melontini/commander/impl/expression/extensions/convert/components/ComponentStruct.java
··· 1 + package me.melontini.commander.impl.expression.extensions.convert.components; 2 + 3 + import lombok.EqualsAndHashCode; 4 + import me.melontini.commander.api.expression.Expression; 5 + import me.melontini.commander.api.expression.extensions.CustomDataAccessor; 6 + import net.minecraft.component.ComponentMap; 7 + import net.minecraft.loot.context.LootContext; 8 + import net.minecraft.registry.RegistryKeys; 9 + import net.minecraft.util.Identifier; 10 + import org.jetbrains.annotations.Nullable; 11 + 12 + @EqualsAndHashCode(callSuper = false) 13 + public class ComponentStruct implements CustomDataAccessor { 14 + 15 + private final ComponentMap map; 16 + 17 + public ComponentStruct(ComponentMap map) { 18 + this.map = map; 19 + } 20 + 21 + @Override 22 + public String toString() { 23 + return String.valueOf(map); 24 + } 25 + 26 + @Override 27 + public @Nullable Expression.Result getExpressionData(String variable, LootContext context) 28 + throws Exception { 29 + var component = context 30 + .getWorld() 31 + .getRegistryManager() 32 + .get(RegistryKeys.DATA_COMPONENT_TYPE) 33 + .get(Identifier.of(variable)); 34 + if (component == null) return null; 35 + var result = map.get(component); 36 + if (result == null) return null; 37 + return Expression.Result.convert(result); 38 + } 39 + }
+48
src/main/java/me/melontini/commander/impl/expression/extensions/convert/gson/GsonConverter.java
··· 1 + package me.melontini.commander.impl.expression.extensions.convert.gson; 2 + 3 + import com.ezylang.evalex.config.ExpressionConfiguration; 4 + import com.ezylang.evalex.data.EvaluationValue; 5 + import com.ezylang.evalex.data.conversion.ConverterIfc; 6 + import com.ezylang.evalex.data.types.*; 7 + import com.ezylang.evalex.data.util.LazyListWrapper; 8 + import com.google.common.collect.Maps; 9 + import com.google.gson.JsonArray; 10 + import com.google.gson.JsonElement; 11 + import com.google.gson.JsonObject; 12 + import com.google.gson.JsonPrimitive; 13 + import me.melontini.commander.impl.expression.extensions.ReflectiveValueConverter; 14 + 15 + public class GsonConverter implements ConverterIfc { 16 + 17 + @Override 18 + public EvaluationValue convert(Object object, ExpressionConfiguration configuration) { 19 + JsonElement element = (JsonElement) object; 20 + if (element.isJsonNull()) return NullValue.of(); 21 + 22 + if (element.isJsonPrimitive()) { 23 + JsonPrimitive primitive = element.getAsJsonPrimitive(); 24 + if (primitive.isString()) return StringValue.of(primitive.getAsString()); 25 + if (primitive.isBoolean()) return BooleanValue.of(primitive.getAsBoolean()); 26 + if (primitive.isNumber()) return NumberValue.of(primitive.getAsBigDecimal()); 27 + throw illegalArgument(object); 28 + } 29 + 30 + if (element.isJsonArray()) { 31 + JsonArray array = element.getAsJsonArray(); 32 + return ArrayValue.of(new LazyListWrapper(array.asList(), configuration)); 33 + } 34 + 35 + if (element.isJsonObject()) { 36 + JsonObject jsonObject = element.getAsJsonObject(); 37 + return StructureValue.of( 38 + Maps.transformValues(jsonObject.asMap(), ReflectiveValueConverter::convert)); 39 + } 40 + 41 + throw illegalArgument(object); 42 + } 43 + 44 + @Override 45 + public boolean canConvert(Object object) { 46 + return object instanceof JsonElement; 47 + } 48 + }
+46
src/main/java/me/melontini/commander/impl/expression/extensions/convert/misc/DataAccessorConverter.java
··· 1 + package me.melontini.commander.impl.expression.extensions.convert.misc; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.config.ExpressionConfiguration; 6 + import com.ezylang.evalex.data.DataAccessorIfc; 7 + import com.ezylang.evalex.data.EvaluationValue; 8 + import com.ezylang.evalex.data.conversion.ConverterIfc; 9 + import com.ezylang.evalex.data.types.DataAccessorValue; 10 + import com.ezylang.evalex.parser.Token; 11 + import me.melontini.commander.api.expression.extensions.CustomDataAccessor; 12 + import me.melontini.dark_matter.api.base.util.Exceptions; 13 + import net.minecraft.loot.context.LootContext; 14 + import org.jetbrains.annotations.Nullable; 15 + 16 + public class DataAccessorConverter implements ConverterIfc { 17 + 18 + @Override 19 + public EvaluationValue convert(Object object, ExpressionConfiguration configuration) { 20 + if (object instanceof DataAccessorIfc accessor) return DataAccessorValue.of(accessor); 21 + 22 + if (object instanceof CustomDataAccessor accessor) 23 + return DataAccessorValue.of(new AccessorWrapper(accessor)); 24 + 25 + throw illegalArgument(object); 26 + } 27 + 28 + @Override 29 + public boolean canConvert(Object object) { 30 + return object instanceof DataAccessorIfc || object instanceof CustomDataAccessor; 31 + } 32 + 33 + public record AccessorWrapper(CustomDataAccessor accessor) implements DataAccessorIfc { 34 + 35 + @Override 36 + public @Nullable EvaluationValue getData( 37 + String variable, Token token, EvaluationContext context) throws EvaluationException { 38 + try { 39 + return (EvaluationValue) 40 + accessor().getExpressionData(variable, (LootContext) context.context()[0]); 41 + } catch (Exception e) { 42 + throw new EvaluationException(token, Exceptions.unwrap(e).getMessage()); 43 + } 44 + } 45 + } 46 + }
+19
src/main/java/me/melontini/commander/impl/expression/extensions/convert/misc/IdentifierConverter.java
··· 1 + package me.melontini.commander.impl.expression.extensions.convert.misc; 2 + 3 + import com.ezylang.evalex.config.ExpressionConfiguration; 4 + import com.ezylang.evalex.data.EvaluationValue; 5 + import com.ezylang.evalex.data.conversion.ConverterIfc; 6 + import com.ezylang.evalex.data.types.StringValue; 7 + import net.minecraft.util.Identifier; 8 + 9 + public class IdentifierConverter implements ConverterIfc { 10 + @Override 11 + public EvaluationValue convert(Object object, ExpressionConfiguration configuration) { 12 + return StringValue.of(object.toString()); 13 + } 14 + 15 + @Override 16 + public boolean canConvert(Object object) { 17 + return object instanceof Identifier; 18 + } 19 + }
+23
src/main/java/me/melontini/commander/impl/expression/extensions/convert/misc/OptionalConverter.java
··· 1 + package me.melontini.commander.impl.expression.extensions.convert.misc; 2 + 3 + import com.ezylang.evalex.config.ExpressionConfiguration; 4 + import com.ezylang.evalex.data.EvaluationValue; 5 + import com.ezylang.evalex.data.conversion.ConverterIfc; 6 + import com.ezylang.evalex.data.types.NullValue; 7 + import java.util.Optional; 8 + 9 + public class OptionalConverter implements ConverterIfc { 10 + 11 + @Override 12 + public EvaluationValue convert(Object object, ExpressionConfiguration configuration) { 13 + Optional<?> optional = (Optional<?>) object; 14 + return optional.isEmpty() 15 + ? NullValue.of() 16 + : configuration.getEvaluationValueConverter().convertObject(optional.get(), configuration); 17 + } 18 + 19 + @Override 20 + public boolean canConvert(Object object) { 21 + return object instanceof Optional<?>; 22 + } 23 + }
+55
src/main/java/me/melontini/commander/impl/expression/extensions/convert/nbt/NbtCompoundStruct.java
··· 1 + package me.melontini.commander.impl.expression.extensions.convert.nbt; 2 + 3 + import com.google.common.collect.Maps; 4 + import java.util.Map; 5 + import java.util.Set; 6 + import lombok.EqualsAndHashCode; 7 + import lombok.Synchronized; 8 + import me.melontini.commander.api.expression.Expression; 9 + import me.melontini.commander.api.expression.extensions.AbstractProxyMap; 10 + import me.melontini.commander.impl.mixin.NbtCompoundAccessor; 11 + import net.minecraft.nbt.NbtCompound; 12 + import org.jetbrains.annotations.NotNull; 13 + 14 + @EqualsAndHashCode(callSuper = false) 15 + public final class NbtCompoundStruct extends AbstractProxyMap { 16 + 17 + private final NbtCompound compound; 18 + private Map<String, Expression.Result> view; 19 + 20 + public NbtCompoundStruct(NbtCompound compound) { 21 + this.compound = compound; 22 + } 23 + 24 + @Override 25 + public boolean containsKey(Object o) { 26 + if (!(o instanceof String key)) return false; 27 + return compound.contains(key); 28 + } 29 + 30 + @Override 31 + public Expression.Result get(Object o) { 32 + if (!(o instanceof String key)) return null; 33 + return Expression.Result.convert(compound.get(key)); 34 + } 35 + 36 + @Synchronized 37 + @NotNull @Override 38 + public Set<Entry<String, Expression.Result>> entrySet() { 39 + if (this.view == null) { 40 + this.view = Maps.transformValues( 41 + ((NbtCompoundAccessor) compound).commander$toMap(), Expression.Result::convert); 42 + } 43 + return this.view.entrySet(); 44 + } 45 + 46 + @Override 47 + public int size() { 48 + return compound.getSize(); 49 + } 50 + 51 + @Override 52 + public String toString() { 53 + return String.valueOf(compound); 54 + } 55 + }
+38
src/main/java/me/melontini/commander/impl/expression/extensions/convert/nbt/NbtConverter.java
··· 1 + package me.melontini.commander.impl.expression.extensions.convert.nbt; 2 + 3 + import com.ezylang.evalex.config.ExpressionConfiguration; 4 + import com.ezylang.evalex.data.EvaluationValue; 5 + import com.ezylang.evalex.data.conversion.ConverterIfc; 6 + import com.ezylang.evalex.data.conversion.NumberConverter; 7 + import com.ezylang.evalex.data.types.ArrayValue; 8 + import com.ezylang.evalex.data.types.StringValue; 9 + import com.ezylang.evalex.data.types.StructureValue; 10 + import com.ezylang.evalex.data.util.LazyListWrapper; 11 + import java.util.Map; 12 + import net.minecraft.nbt.*; 13 + 14 + public class NbtConverter implements ConverterIfc { 15 + 16 + private final NumberConverter converter = new NumberConverter(); 17 + 18 + @Override 19 + public EvaluationValue convert(Object object, ExpressionConfiguration configuration) { 20 + 21 + if (object instanceof AbstractNbtNumber n) { 22 + return converter.convert(n.numberValue(), configuration); 23 + } else if (object instanceof NbtString n) { 24 + return StringValue.of(n.asString()); 25 + } else if (object instanceof AbstractNbtList<?> n) { 26 + return ArrayValue.of(new LazyListWrapper(n, configuration)); 27 + } else if (object instanceof NbtCompound n) { 28 + return StructureValue.of((Map<String, EvaluationValue>) (Object) new NbtCompoundStruct(n)); 29 + } 30 + 31 + throw illegalArgument(object); 32 + } 33 + 34 + @Override 35 + public boolean canConvert(Object object) { 36 + return object instanceof NbtElement; 37 + } 38 + }
+56
src/main/java/me/melontini/commander/impl/expression/extensions/convert/states/StateStruct.java
··· 1 + package me.melontini.commander.impl.expression.extensions.convert.states; 2 + 3 + import java.util.Set; 4 + import java.util.stream.Collectors; 5 + import lombok.EqualsAndHashCode; 6 + import lombok.Synchronized; 7 + import me.melontini.commander.api.expression.Expression; 8 + import me.melontini.commander.api.expression.extensions.AbstractProxyMap; 9 + import me.melontini.commander.impl.expression.extensions.convert.LazyMapEntry; 10 + import net.minecraft.state.State; 11 + import net.minecraft.state.property.Property; 12 + 13 + @EqualsAndHashCode(callSuper = false) 14 + public final class StateStruct extends AbstractProxyMap { 15 + 16 + private final State<?, ?> state; 17 + private Set<Entry<String, Expression.Result>> entries; 18 + 19 + public StateStruct(State<?, ?> state) { 20 + this.state = state; 21 + } 22 + 23 + @Synchronized 24 + @Override 25 + public Set<Entry<String, Expression.Result>> entrySet() { 26 + if (entries == null) { 27 + this.entries = this.state.getEntries().entrySet().stream() 28 + .map(entry -> new LazyMapEntry<>(entry.getKey(), entry.getValue(), Property::getName)) 29 + .collect(Collectors.toUnmodifiableSet()); 30 + } 31 + return this.entries; 32 + } 33 + 34 + @Override 35 + public int size() { 36 + return state.getEntries().size(); 37 + } 38 + 39 + @Override 40 + public boolean containsKey(Object o) { 41 + if (!(o instanceof String key)) return false; 42 + for (Entry<Property<?>, Comparable<?>> e : state.getEntries().entrySet()) { 43 + if (e.getKey().getName().equals(key)) return true; 44 + } 45 + return false; 46 + } 47 + 48 + @Override 49 + public Expression.Result get(Object o) { 50 + if (!(o instanceof String key)) return null; 51 + for (Entry<Property<?>, Comparable<?>> e : state.getEntries().entrySet()) { 52 + if (e.getKey().getName().equals(key)) return Expression.Result.convert(e.getValue()); 53 + } 54 + return null; 55 + } 56 + }
+35
src/main/java/me/melontini/commander/impl/expression/functions/ChainFunction.java
··· 1 + package me.melontini.commander.impl.expression.functions; 2 + 3 + import static me.melontini.commander.impl.expression.EvalUtils.runLambda; 4 + 5 + import com.ezylang.evalex.EvaluationContext; 6 + import com.ezylang.evalex.EvaluationException; 7 + import com.ezylang.evalex.Expression; 8 + import com.ezylang.evalex.data.EvaluationValue; 9 + import com.ezylang.evalex.functions.AbstractFunction; 10 + import com.ezylang.evalex.functions.FunctionParameter; 11 + import com.ezylang.evalex.parser.ASTNode; 12 + import com.ezylang.evalex.parser.Token; 13 + import java.util.List; 14 + import org.jetbrains.annotations.Nullable; 15 + 16 + @FunctionParameter(name = "value") 17 + @FunctionParameter(name = "actions", isLazy = true, isVarArg = true) 18 + public class ChainFunction extends AbstractFunction { 19 + 20 + @Override 21 + public EvaluationValue evaluate(EvaluationContext context, Token token, EvaluationValue... par) 22 + throws EvaluationException { 23 + var value = par[0]; 24 + for (int i = 1; i < par.length; i++) { 25 + value = runLambda(context, value, par[i].getExpressionNode()); 26 + } 27 + return value; 28 + } 29 + 30 + @Override 31 + public @Nullable EvaluationValue inlineFunction( 32 + Expression expression, Token token, List<ASTNode> parameters) throws EvaluationException { 33 + return null; 34 + } 35 + }
+40
src/main/java/me/melontini/commander/impl/expression/functions/HasContextFunction.java
··· 1 + package me.melontini.commander.impl.expression.functions; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.Expression; 6 + import com.ezylang.evalex.data.EvaluationValue; 7 + import com.ezylang.evalex.data.types.BooleanValue; 8 + import com.ezylang.evalex.functions.AbstractFunction; 9 + import com.ezylang.evalex.functions.FunctionParameter; 10 + import com.ezylang.evalex.parser.ASTNode; 11 + import com.ezylang.evalex.parser.Token; 12 + import java.util.List; 13 + import org.jetbrains.annotations.Nullable; 14 + 15 + @FunctionParameter(name = "key", isVarArg = true) 16 + public class HasContextFunction extends AbstractFunction { 17 + @Override 18 + public EvaluationValue evaluate(EvaluationContext context, Token token, EvaluationValue... par) 19 + throws EvaluationException { 20 + return switch (par.length) { 21 + case 0 -> BooleanValue.TRUE; 22 + case 1 -> BooleanValue.of( 23 + context.expression().getDataAccessor().getData(par[0].getStringValue(), token, context) 24 + != null); 25 + default -> { 26 + var da = context.expression().getDataAccessor(); 27 + for (EvaluationValue value : par) { 28 + if (da.getData(value.getStringValue(), token, context) == null) yield BooleanValue.FALSE; 29 + } 30 + yield BooleanValue.TRUE; 31 + } 32 + }; 33 + } 34 + 35 + @Override 36 + public @Nullable EvaluationValue inlineFunction( 37 + Expression expression, Token token, List<ASTNode> parameters) throws EvaluationException { 38 + return null; 39 + } 40 + }
+51
src/main/java/me/melontini/commander/impl/expression/functions/LengthFunction.java
··· 1 + package me.melontini.commander.impl.expression.functions; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.Expression; 6 + import com.ezylang.evalex.data.EvaluationValue; 7 + import com.ezylang.evalex.data.types.NumberValue; 8 + import com.ezylang.evalex.functions.AbstractFunction; 9 + import com.ezylang.evalex.functions.FunctionParameter; 10 + import com.ezylang.evalex.parser.ASTNode; 11 + import com.ezylang.evalex.parser.Token; 12 + import java.math.BigDecimal; 13 + import java.util.List; 14 + import me.melontini.commander.impl.expression.EvalUtils; 15 + import org.jetbrains.annotations.Nullable; 16 + 17 + @FunctionParameter(name = "value") 18 + public class LengthFunction extends AbstractFunction { 19 + @Override 20 + public EvaluationValue evaluate( 21 + EvaluationContext context, Token functionToken, EvaluationValue... par) 22 + throws EvaluationException { 23 + EvaluationValue value = par[0]; 24 + 25 + if (value.isStringValue()) 26 + return context.expression().convertValue(value.getStringValue().length()); 27 + if (value.isArrayValue()) 28 + return context.expression().convertValue(value.getArrayValue().size()); 29 + if (value.isStructureValue()) 30 + return context.expression().convertValue(value.getStructureValue().size()); 31 + if (value.isDurationValue()) 32 + return context.expression().convertValue(value.getDurationValue().toMillis()); 33 + 34 + return NumberValue.of(BigDecimal.ZERO); 35 + } 36 + 37 + // We can safely inline constant string and duration values. 38 + @Override 39 + public @Nullable EvaluationValue inlineFunction( 40 + Expression expression, Token token, List<ASTNode> parameters) throws EvaluationException { 41 + return EvalUtils.valueOrEmpty(parameters.get(0)) 42 + .map(value -> { 43 + if (value.isStringValue()) 44 + return expression.convertValue(value.getStringValue().length()); 45 + if (value.isDurationValue()) 46 + return expression.convertValue(value.getDurationValue().toMillis()); 47 + return null; 48 + }) 49 + .orElse(null); 50 + } 51 + }
+91
src/main/java/me/melontini/commander/impl/expression/functions/LruCachingFunction.java
··· 1 + package me.melontini.commander.impl.expression.functions; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.data.EvaluationValue; 6 + import com.ezylang.evalex.functions.FunctionIfc; 7 + import com.ezylang.evalex.functions.FunctionParameterDefinition; 8 + import com.ezylang.evalex.parser.Token; 9 + import java.util.*; 10 + import java.util.function.Function; 11 + 12 + public class LruCachingFunction implements FunctionIfc { 13 + 14 + public static FunctionIfc of(FunctionIfc delegate) { 15 + return new LruCachingFunction(delegate); 16 + } 17 + 18 + private final FunctionIfc delegate; 19 + private final Function<EvaluationValue[], Object> keyGetter; 20 + private final Map<Object, EvaluationValue> cache = 21 + Collections.synchronizedMap(new LinkedHashMap<>(128 + 1, 0.75f, true) { 22 + @Override 23 + protected boolean removeEldestEntry(Map.Entry<Object, EvaluationValue> eldest) { 24 + return size() > 128; 25 + } 26 + }); 27 + 28 + public LruCachingFunction(FunctionIfc delegate) { 29 + this.delegate = delegate; 30 + 31 + if (this.delegate.getFunctionParameterDefinitions().size() == 1) { 32 + this.keyGetter = vals -> vals[0].getValue(); 33 + } else { 34 + this.keyGetter = ArrayEqualityWrapper::new; 35 + } 36 + } 37 + 38 + @Override 39 + public List<FunctionParameterDefinition> getFunctionParameterDefinitions() { 40 + return delegate.getFunctionParameterDefinitions(); 41 + } 42 + 43 + @Override 44 + public EvaluationValue evaluate( 45 + EvaluationContext context, Token functionToken, EvaluationValue... parameterValues) 46 + throws EvaluationException { 47 + var key = this.keyGetter.apply(parameterValues); 48 + var r = this.cache.get(key); 49 + if (r == null) { 50 + r = delegate.evaluate(context, functionToken, parameterValues); 51 + this.cache.put(key, r); 52 + return r; 53 + } 54 + return r; 55 + } 56 + 57 + @Override 58 + public void validatePreEvaluation(Token token, EvaluationValue... parameterValues) 59 + throws EvaluationException { 60 + delegate.validatePreEvaluation(token, parameterValues); 61 + } 62 + 63 + @Override 64 + public boolean hasVarArgs() { 65 + return delegate.hasVarArgs(); 66 + } 67 + 68 + @Override 69 + public boolean isParameterLazy(int parameterIndex) { 70 + return delegate.isParameterLazy(parameterIndex); 71 + } 72 + 73 + @Override 74 + public int getCountOfNonVarArgParameters() { 75 + return delegate.getCountOfNonVarArgParameters(); 76 + } 77 + 78 + private record ArrayEqualityWrapper(EvaluationValue[] arr) { 79 + 80 + @Override 81 + public boolean equals(Object obj) { 82 + if (!(obj instanceof ArrayEqualityWrapper o)) return false; 83 + return Arrays.equals(arr, o.arr); 84 + } 85 + 86 + @Override 87 + public int hashCode() { 88 + return Arrays.hashCode(arr); 89 + } 90 + } 91 + }
+37
src/main/java/me/melontini/commander/impl/expression/functions/MatchesFunction.java
··· 1 + package me.melontini.commander.impl.expression.functions; 2 + 3 + import static me.melontini.commander.impl.expression.EvalUtils.runLambda; 4 + 5 + import com.ezylang.evalex.EvaluationContext; 6 + import com.ezylang.evalex.EvaluationException; 7 + import com.ezylang.evalex.Expression; 8 + import com.ezylang.evalex.data.EvaluationValue; 9 + import com.ezylang.evalex.functions.AbstractFunction; 10 + import com.ezylang.evalex.functions.FunctionParameter; 11 + import com.ezylang.evalex.parser.ASTNode; 12 + import com.ezylang.evalex.parser.Token; 13 + import java.util.List; 14 + import org.jetbrains.annotations.Nullable; 15 + 16 + @FunctionParameter(name = "value") 17 + @FunctionParameter(name = "predicate", isLazy = true) 18 + @FunctionParameter(name = "ifTrue", isLazy = true) 19 + @FunctionParameter(name = "ifFalse", isLazy = true) 20 + public class MatchesFunction extends AbstractFunction { 21 + 22 + @Override 23 + public EvaluationValue evaluate( 24 + EvaluationContext context, Token functionToken, EvaluationValue... par) 25 + throws EvaluationException { 26 + EvaluationValue value = par[0]; 27 + boolean predicate = runLambda(context, value, par[1].getExpressionNode()).getBooleanValue(); 28 + 29 + return runLambda(context, value, par[predicate ? 2 : 3].getExpressionNode()); 30 + } 31 + 32 + @Override 33 + public @Nullable EvaluationValue inlineFunction( 34 + Expression expression, Token token, List<ASTNode> parameters) throws EvaluationException { 35 + return null; 36 + } 37 + }
+42
src/main/java/me/melontini/commander/impl/expression/functions/StructContainsKeyFunction.java
··· 1 + package me.melontini.commander.impl.expression.functions; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.Expression; 6 + import com.ezylang.evalex.data.EvaluationValue; 7 + import com.ezylang.evalex.data.types.BooleanValue; 8 + import com.ezylang.evalex.functions.AbstractFunction; 9 + import com.ezylang.evalex.functions.FunctionParameter; 10 + import com.ezylang.evalex.parser.ASTNode; 11 + import com.ezylang.evalex.parser.Token; 12 + import java.util.List; 13 + import org.jetbrains.annotations.Nullable; 14 + 15 + @FunctionParameter(name = "struct") 16 + @FunctionParameter(name = "key", isVarArg = true) 17 + public class StructContainsKeyFunction extends AbstractFunction { 18 + 19 + @Override 20 + public EvaluationValue evaluate( 21 + EvaluationContext context, Token functionToken, EvaluationValue... par) 22 + throws EvaluationException { 23 + return switch (par.length) { 24 + case 1 -> BooleanValue.TRUE; 25 + case 2 -> BooleanValue.of(par[0].getStructureValue().containsKey(par[1].getStringValue())); 26 + default -> { 27 + var struct = par[0].getStructureValue(); 28 + for (int i = 1; i < par.length; i++) { 29 + var key = par[i].getStringValue(); 30 + if (!struct.containsKey(key)) yield BooleanValue.FALSE; 31 + } 32 + yield BooleanValue.TRUE; 33 + } 34 + }; 35 + } 36 + 37 + @Override 38 + public @Nullable EvaluationValue inlineFunction( 39 + Expression expression, Token token, List<ASTNode> parameters) throws EvaluationException { 40 + return null; 41 + } 42 + }
+37
src/main/java/me/melontini/commander/impl/expression/functions/arrays/ArrayAllMatch.java
··· 1 + package me.melontini.commander.impl.expression.functions.arrays; 2 + 3 + import static me.melontini.commander.impl.expression.EvalUtils.runLambda; 4 + 5 + import com.ezylang.evalex.EvaluationContext; 6 + import com.ezylang.evalex.EvaluationException; 7 + import com.ezylang.evalex.Expression; 8 + import com.ezylang.evalex.data.EvaluationValue; 9 + import com.ezylang.evalex.data.types.BooleanValue; 10 + import com.ezylang.evalex.functions.AbstractFunction; 11 + import com.ezylang.evalex.functions.FunctionParameter; 12 + import com.ezylang.evalex.parser.ASTNode; 13 + import com.ezylang.evalex.parser.Token; 14 + import java.util.List; 15 + import me.melontini.dark_matter.api.base.util.Exceptions; 16 + 17 + @FunctionParameter(name = "array") 18 + @FunctionParameter(name = "predicate", isLazy = true) 19 + public class ArrayAllMatch extends AbstractFunction { 20 + 21 + @Override 22 + public EvaluationValue evaluate( 23 + EvaluationContext context, Token functionToken, EvaluationValue... par) 24 + throws EvaluationException { 25 + List<EvaluationValue> array = par[0].getArrayValue(); 26 + ASTNode predicate = par[1].getExpressionNode(); 27 + return BooleanValue.of(array.stream() 28 + .allMatch(value -> 29 + Exceptions.supply(() -> runLambda(context, value, predicate)).getBooleanValue())); 30 + } 31 + 32 + @Override 33 + public EvaluationValue inlineFunction( 34 + Expression expression, Token token, List<ASTNode> parameters) throws EvaluationException { 35 + return null; 36 + } 37 + }
+38
src/main/java/me/melontini/commander/impl/expression/functions/arrays/ArrayAnyMatch.java
··· 1 + package me.melontini.commander.impl.expression.functions.arrays; 2 + 3 + import static me.melontini.commander.impl.expression.EvalUtils.runLambda; 4 + 5 + import com.ezylang.evalex.EvaluationContext; 6 + import com.ezylang.evalex.EvaluationException; 7 + import com.ezylang.evalex.Expression; 8 + import com.ezylang.evalex.data.EvaluationValue; 9 + import com.ezylang.evalex.data.types.BooleanValue; 10 + import com.ezylang.evalex.functions.AbstractFunction; 11 + import com.ezylang.evalex.functions.FunctionParameter; 12 + import com.ezylang.evalex.parser.ASTNode; 13 + import com.ezylang.evalex.parser.Token; 14 + import java.util.List; 15 + import me.melontini.dark_matter.api.base.util.Exceptions; 16 + 17 + @FunctionParameter(name = "array") 18 + @FunctionParameter(name = "predicate", isLazy = true) 19 + public class ArrayAnyMatch extends AbstractFunction { 20 + 21 + @Override 22 + public EvaluationValue evaluate( 23 + EvaluationContext context, Token functionToken, EvaluationValue... par) 24 + throws EvaluationException { 25 + List<EvaluationValue> array = par[0].getArrayValue(); 26 + ASTNode predicate = par[1].getExpressionNode(); 27 + 28 + return BooleanValue.of(array.stream() 29 + .anyMatch(value -> 30 + Exceptions.supply(() -> runLambda(context, value, predicate)).getBooleanValue())); 31 + } 32 + 33 + @Override 34 + public EvaluationValue inlineFunction( 35 + Expression expression, Token token, List<ASTNode> parameters) throws EvaluationException { 36 + return null; 37 + } 38 + }
+39
src/main/java/me/melontini/commander/impl/expression/functions/arrays/ArrayFind.java
··· 1 + package me.melontini.commander.impl.expression.functions.arrays; 2 + 3 + import static me.melontini.commander.impl.expression.EvalUtils.runLambda; 4 + 5 + import com.ezylang.evalex.EvaluationContext; 6 + import com.ezylang.evalex.EvaluationException; 7 + import com.ezylang.evalex.Expression; 8 + import com.ezylang.evalex.data.EvaluationValue; 9 + import com.ezylang.evalex.data.types.ArrayValue; 10 + import com.ezylang.evalex.functions.AbstractFunction; 11 + import com.ezylang.evalex.functions.FunctionParameter; 12 + import com.ezylang.evalex.parser.ASTNode; 13 + import com.ezylang.evalex.parser.Token; 14 + import java.util.List; 15 + import me.melontini.dark_matter.api.base.util.Exceptions; 16 + 17 + @FunctionParameter(name = "array") 18 + @FunctionParameter(name = "predicate", isLazy = true) 19 + public class ArrayFind extends AbstractFunction { 20 + 21 + @Override 22 + public EvaluationValue evaluate( 23 + EvaluationContext context, Token functionToken, EvaluationValue... par) 24 + throws EvaluationException { 25 + List<EvaluationValue> array = par[0].getArrayValue(); 26 + ASTNode predicate = par[1].getExpressionNode(); 27 + 28 + return ArrayValue.of(array.stream() 29 + .filter(value -> 30 + Exceptions.supply(() -> runLambda(context, value, predicate)).getBooleanValue()) 31 + .toList()); 32 + } 33 + 34 + @Override 35 + public EvaluationValue inlineFunction( 36 + Expression expression, Token token, List<ASTNode> parameters) throws EvaluationException { 37 + return null; 38 + } 39 + }
+20
src/main/java/me/melontini/commander/impl/expression/functions/arrays/ArrayFindAny.java
··· 1 + package me.melontini.commander.impl.expression.functions.arrays; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.data.EvaluationValue; 6 + import com.ezylang.evalex.data.types.NullValue; 7 + import com.ezylang.evalex.functions.AbstractFunction; 8 + import com.ezylang.evalex.functions.FunctionParameter; 9 + import com.ezylang.evalex.parser.Token; 10 + 11 + @FunctionParameter(name = "array") 12 + public class ArrayFindAny extends AbstractFunction { 13 + 14 + @Override 15 + public EvaluationValue evaluate( 16 + EvaluationContext context, Token functionToken, EvaluationValue... par) 17 + throws EvaluationException { 18 + return par[0].getArrayValue().stream().findAny().orElse(NullValue.of()); 19 + } 20 + }
+20
src/main/java/me/melontini/commander/impl/expression/functions/arrays/ArrayFindFirst.java
··· 1 + package me.melontini.commander.impl.expression.functions.arrays; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.data.EvaluationValue; 6 + import com.ezylang.evalex.data.types.NullValue; 7 + import com.ezylang.evalex.functions.AbstractFunction; 8 + import com.ezylang.evalex.functions.FunctionParameter; 9 + import com.ezylang.evalex.parser.Token; 10 + 11 + @FunctionParameter(name = "array") 12 + public class ArrayFindFirst extends AbstractFunction { 13 + 14 + @Override 15 + public EvaluationValue evaluate( 16 + EvaluationContext context, Token functionToken, EvaluationValue... par) 17 + throws EvaluationException { 18 + return par[0].getArrayValue().stream().findFirst().orElse(NullValue.of()); 19 + } 20 + }
+38
src/main/java/me/melontini/commander/impl/expression/functions/arrays/ArrayMap.java
··· 1 + package me.melontini.commander.impl.expression.functions.arrays; 2 + 3 + import static me.melontini.commander.impl.expression.EvalUtils.runLambda; 4 + 5 + import com.ezylang.evalex.EvaluationContext; 6 + import com.ezylang.evalex.EvaluationException; 7 + import com.ezylang.evalex.Expression; 8 + import com.ezylang.evalex.data.EvaluationValue; 9 + import com.ezylang.evalex.data.types.ArrayValue; 10 + import com.ezylang.evalex.functions.AbstractFunction; 11 + import com.ezylang.evalex.functions.FunctionParameter; 12 + import com.ezylang.evalex.parser.ASTNode; 13 + import com.ezylang.evalex.parser.Token; 14 + import com.google.common.collect.Lists; 15 + import java.util.List; 16 + import me.melontini.dark_matter.api.base.util.Exceptions; 17 + 18 + @FunctionParameter(name = "array") 19 + @FunctionParameter(name = "function", isLazy = true) 20 + public class ArrayMap extends AbstractFunction { 21 + 22 + @Override 23 + public EvaluationValue evaluate( 24 + EvaluationContext expression, Token functionToken, EvaluationValue... par) 25 + throws EvaluationException { 26 + List<EvaluationValue> array = par[0].getArrayValue(); 27 + ASTNode function = par[1].getExpressionNode(); 28 + 29 + return ArrayValue.of(Lists.transform( 30 + array, input -> Exceptions.supply(() -> runLambda(expression, input, function)))); 31 + } 32 + 33 + @Override 34 + public EvaluationValue inlineFunction( 35 + Expression expression, Token token, List<ASTNode> parameters) throws EvaluationException { 36 + return null; 37 + } 38 + }
+38
src/main/java/me/melontini/commander/impl/expression/functions/arrays/ArrayNoneMatch.java
··· 1 + package me.melontini.commander.impl.expression.functions.arrays; 2 + 3 + import static me.melontini.commander.impl.expression.EvalUtils.runLambda; 4 + 5 + import com.ezylang.evalex.EvaluationContext; 6 + import com.ezylang.evalex.EvaluationException; 7 + import com.ezylang.evalex.Expression; 8 + import com.ezylang.evalex.data.EvaluationValue; 9 + import com.ezylang.evalex.data.types.BooleanValue; 10 + import com.ezylang.evalex.functions.AbstractFunction; 11 + import com.ezylang.evalex.functions.FunctionParameter; 12 + import com.ezylang.evalex.parser.ASTNode; 13 + import com.ezylang.evalex.parser.Token; 14 + import java.util.List; 15 + import me.melontini.dark_matter.api.base.util.Exceptions; 16 + 17 + @FunctionParameter(name = "array") 18 + @FunctionParameter(name = "predicate", isLazy = true) 19 + public class ArrayNoneMatch extends AbstractFunction { 20 + 21 + @Override 22 + public EvaluationValue evaluate( 23 + EvaluationContext context, Token functionToken, EvaluationValue... par) 24 + throws EvaluationException { 25 + List<EvaluationValue> array = par[0].getArrayValue(); 26 + ASTNode predicate = par[1].getExpressionNode(); 27 + 28 + return BooleanValue.of(array.stream() 29 + .noneMatch(value -> 30 + Exceptions.supply(() -> runLambda(context, value, predicate)).getBooleanValue())); 31 + } 32 + 33 + @Override 34 + public EvaluationValue inlineFunction( 35 + Expression expression, Token token, List<ASTNode> parameters) throws EvaluationException { 36 + return null; 37 + } 38 + }
+20
src/main/java/me/melontini/commander/impl/expression/functions/arrays/ArrayOf.java
··· 1 + package me.melontini.commander.impl.expression.functions.arrays; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.data.EvaluationValue; 6 + import com.ezylang.evalex.data.types.ArrayValue; 7 + import com.ezylang.evalex.functions.AbstractFunction; 8 + import com.ezylang.evalex.functions.FunctionParameter; 9 + import com.ezylang.evalex.parser.Token; 10 + import java.util.Arrays; 11 + 12 + @FunctionParameter(name = "args", isVarArg = true) 13 + public class ArrayOf extends AbstractFunction { 14 + @Override 15 + public EvaluationValue evaluate( 16 + EvaluationContext context, Token functionToken, EvaluationValue... par) 17 + throws EvaluationException { 18 + return ArrayValue.of(Arrays.asList(par)); 19 + } 20 + }
+25
src/main/java/me/melontini/commander/impl/expression/functions/math/ClampFunction.java
··· 1 + package me.melontini.commander.impl.expression.functions.math; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.data.EvaluationValue; 6 + import com.ezylang.evalex.data.types.NumberValue; 7 + import com.ezylang.evalex.functions.AbstractFunction; 8 + import com.ezylang.evalex.functions.FunctionParameter; 9 + import com.ezylang.evalex.parser.Token; 10 + import java.math.BigDecimal; 11 + 12 + @FunctionParameter(name = "value") 13 + @FunctionParameter(name = "min") 14 + @FunctionParameter(name = "max") 15 + public class ClampFunction extends AbstractFunction { 16 + @Override 17 + public EvaluationValue evaluate( 18 + EvaluationContext context, Token functionToken, EvaluationValue... par) 19 + throws EvaluationException { 20 + BigDecimal value = par[0].getNumberValue(); 21 + BigDecimal min = par[1].getNumberValue(); 22 + BigDecimal max = par[2].getNumberValue(); 23 + return NumberValue.of(value.compareTo(min) < 0 ? min : value.min(max)); 24 + } 25 + }
+25
src/main/java/me/melontini/commander/impl/expression/functions/math/LerpFunction.java
··· 1 + package me.melontini.commander.impl.expression.functions.math; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.data.EvaluationValue; 6 + import com.ezylang.evalex.data.types.NumberValue; 7 + import com.ezylang.evalex.functions.AbstractFunction; 8 + import com.ezylang.evalex.functions.FunctionParameter; 9 + import com.ezylang.evalex.parser.Token; 10 + import java.math.BigDecimal; 11 + 12 + @FunctionParameter(name = "delta") 13 + @FunctionParameter(name = "start") 14 + @FunctionParameter(name = "end") 15 + public class LerpFunction extends AbstractFunction { 16 + @Override 17 + public EvaluationValue evaluate( 18 + EvaluationContext context, Token functionToken, EvaluationValue... par) 19 + throws EvaluationException { 20 + BigDecimal start = par[0].getNumberValue(); 21 + BigDecimal delta = par[1].getNumberValue(); 22 + BigDecimal end = par[2].getNumberValue(); 23 + return NumberValue.of(start.add(delta.multiply(end.subtract(start)))); 24 + } 25 + }
+40
src/main/java/me/melontini/commander/impl/expression/functions/math/RangedRandomFunction.java
··· 1 + package me.melontini.commander.impl.expression.functions.math; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.Expression; 6 + import com.ezylang.evalex.data.EvaluationValue; 7 + import com.ezylang.evalex.functions.AbstractFunction; 8 + import com.ezylang.evalex.functions.FunctionParameter; 9 + import com.ezylang.evalex.parser.ASTNode; 10 + import com.ezylang.evalex.parser.Token; 11 + import java.math.BigDecimal; 12 + import java.util.List; 13 + import me.melontini.dark_matter.api.base.util.MathUtil; 14 + import org.jetbrains.annotations.Nullable; 15 + 16 + @FunctionParameter(name = "min") 17 + @FunctionParameter(name = "max") 18 + public class RangedRandomFunction extends AbstractFunction { 19 + @Override 20 + public EvaluationValue evaluate( 21 + EvaluationContext context, Token functionToken, EvaluationValue... par) 22 + throws EvaluationException { 23 + BigDecimal min = par[0].getNumberValue(); 24 + BigDecimal max = par[1].getNumberValue(); 25 + return context 26 + .expression() 27 + .convertValue( 28 + min.compareTo(max) >= 0 29 + ? min 30 + : BigDecimal.valueOf(MathUtil.threadRandom().nextDouble()) 31 + .multiply(max.subtract(min)) 32 + .add(min)); 33 + } 34 + 35 + @Override 36 + public @Nullable EvaluationValue inlineFunction( 37 + Expression expression, Token token, List<ASTNode> parameters) throws EvaluationException { 38 + return null; 39 + } 40 + }
+52
src/main/java/me/melontini/commander/impl/expression/functions/registry/DynamicRegistryFunction.java
··· 1 + package me.melontini.commander.impl.expression.functions.registry; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.Expression; 6 + import com.ezylang.evalex.data.EvaluationValue; 7 + import com.ezylang.evalex.functions.AbstractFunction; 8 + import com.ezylang.evalex.functions.FunctionParameter; 9 + import com.ezylang.evalex.parser.ASTNode; 10 + import com.ezylang.evalex.parser.Token; 11 + import java.util.List; 12 + import java.util.NoSuchElementException; 13 + import me.melontini.commander.impl.expression.extensions.ReflectiveValueConverter; 14 + import net.minecraft.loot.context.LootContext; 15 + import net.minecraft.registry.Registry; 16 + import net.minecraft.registry.RegistryKey; 17 + import net.minecraft.util.Identifier; 18 + import org.jetbrains.annotations.Nullable; 19 + 20 + @FunctionParameter(name = "identifier") 21 + public class DynamicRegistryFunction extends AbstractFunction { 22 + 23 + protected final RegistryKey<? extends Registry<?>> registry; 24 + 25 + public DynamicRegistryFunction(RegistryKey<? extends Registry<?>> registry) { 26 + this.registry = registry; 27 + } 28 + 29 + @Override 30 + public EvaluationValue evaluate( 31 + EvaluationContext context, Token functionToken, EvaluationValue... par) 32 + throws EvaluationException { 33 + try { 34 + var id = Identifier.of(par[0].getStringValue()); 35 + var server = ((LootContext) context.context()[0]).getWorld().getServer(); 36 + return ReflectiveValueConverter.convert(server 37 + .getRegistryManager() 38 + .get(registry) 39 + .getOrEmpty(id) 40 + .orElseThrow(() -> 41 + new NoSuchElementException("No such %s: %s".formatted(registry.getValue(), id)))); 42 + } catch (Exception e) { 43 + throw new EvaluationException(functionToken, e.getMessage()); 44 + } 45 + } 46 + 47 + @Override 48 + public @Nullable EvaluationValue inlineFunction( 49 + Expression expression, Token token, List<ASTNode> parameters) throws EvaluationException { 50 + return null; 51 + } 52 + }
+44
src/main/java/me/melontini/commander/impl/expression/functions/registry/DynamicRegistryRegistryFunction.java
··· 1 + package me.melontini.commander.impl.expression.functions.registry; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.Expression; 6 + import com.ezylang.evalex.data.EvaluationValue; 7 + import com.ezylang.evalex.functions.AbstractFunction; 8 + import com.ezylang.evalex.functions.FunctionParameter; 9 + import com.ezylang.evalex.parser.ASTNode; 10 + import com.ezylang.evalex.parser.Token; 11 + import java.util.List; 12 + import java.util.NoSuchElementException; 13 + import me.melontini.commander.impl.expression.extensions.ReflectiveValueConverter; 14 + import net.minecraft.loot.context.LootContext; 15 + import net.minecraft.registry.RegistryKey; 16 + import net.minecraft.util.Identifier; 17 + import org.jetbrains.annotations.Nullable; 18 + 19 + @FunctionParameter(name = "identifier") 20 + public class DynamicRegistryRegistryFunction extends AbstractFunction { 21 + 22 + @Override 23 + public EvaluationValue evaluate( 24 + EvaluationContext context, Token functionToken, EvaluationValue... par) 25 + throws EvaluationException { 26 + try { 27 + var id = Identifier.of(par[0].getStringValue()); 28 + var server = ((LootContext) context.context()[0]).getWorld().getServer(); 29 + return ReflectiveValueConverter.convert(server 30 + .getRegistryManager() 31 + .getOptional(RegistryKey.ofRegistry(id)) 32 + .orElseThrow( 33 + () -> new NoSuchElementException("No such %s: %s".formatted("minecraft:root", id)))); 34 + } catch (Exception e) { 35 + throw new EvaluationException(functionToken, e.getMessage()); 36 + } 37 + } 38 + 39 + @Override 40 + public @Nullable EvaluationValue inlineFunction( 41 + Expression expression, Token token, List<ASTNode> parameters) throws EvaluationException { 42 + return null; 43 + } 44 + }
+38
src/main/java/me/melontini/commander/impl/expression/functions/registry/RegistryFunction.java
··· 1 + package me.melontini.commander.impl.expression.functions.registry; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.data.EvaluationValue; 6 + import com.ezylang.evalex.functions.AbstractFunction; 7 + import com.ezylang.evalex.functions.FunctionParameter; 8 + import com.ezylang.evalex.parser.Token; 9 + import java.util.NoSuchElementException; 10 + import me.melontini.commander.impl.expression.extensions.ReflectiveValueConverter; 11 + import me.melontini.dark_matter.api.base.util.Exceptions; 12 + import net.minecraft.registry.Registry; 13 + import net.minecraft.util.Identifier; 14 + 15 + @FunctionParameter(name = "identifier") 16 + public class RegistryFunction extends AbstractFunction { 17 + 18 + private final Registry<?> registry; 19 + 20 + public RegistryFunction(Registry<?> registry) { 21 + this.registry = registry; 22 + } 23 + 24 + @Override 25 + public EvaluationValue evaluate( 26 + EvaluationContext context, Token functionToken, EvaluationValue... par) 27 + throws EvaluationException { 28 + try { 29 + var id = Identifier.of(par[0].getStringValue()); 30 + return ReflectiveValueConverter.convert(this.registry 31 + .getOrEmpty(id) 32 + .orElseThrow(() -> new NoSuchElementException( 33 + "No such %s: %s".formatted(registry.getKey().getValue(), id)))); 34 + } catch (Exception e) { 35 + throw new EvaluationException(functionToken, Exceptions.unwrap(e).getMessage()); 36 + } 37 + } 38 + }
+35
src/main/java/me/melontini/commander/impl/expression/intermediaries/ConstantArithmetica.java
··· 1 + package me.melontini.commander.impl.expression.intermediaries; 2 + 3 + import com.mojang.datafixers.util.Either; 4 + import java.util.Map; 5 + import lombok.EqualsAndHashCode; 6 + import me.melontini.commander.api.expression.Arithmetica; 7 + import net.minecraft.loot.context.LootContext; 8 + import org.jetbrains.annotations.Nullable; 9 + 10 + @EqualsAndHashCode 11 + public final class ConstantArithmetica implements Arithmetica { 12 + @EqualsAndHashCode.Exclude 13 + private final Either<Double, String> either; 14 + 15 + private final double value; 16 + 17 + public ConstantArithmetica(Either<Double, String> either, double value) { 18 + this.either = either; 19 + this.value = value; 20 + } 21 + 22 + @Override 23 + public Either<Double, String> toSource() { 24 + return either; 25 + } 26 + 27 + @Override 28 + public double asDouble(LootContext context, @Nullable Map<String, ?> parameters) { 29 + return value; 30 + } 31 + 32 + public String toString() { 33 + return "Arithmetica(value=" + this.value + ')'; 34 + } 35 + }
+41
src/main/java/me/melontini/commander/impl/expression/intermediaries/ConstantBooleanExpression.java
··· 1 + package me.melontini.commander.impl.expression.intermediaries; 2 + 3 + import com.mojang.datafixers.util.Either; 4 + import java.util.Map; 5 + import lombok.EqualsAndHashCode; 6 + import me.melontini.commander.api.expression.BooleanExpression; 7 + import net.minecraft.loot.context.LootContext; 8 + import org.jetbrains.annotations.Nullable; 9 + 10 + @EqualsAndHashCode 11 + public final class ConstantBooleanExpression implements BooleanExpression { 12 + 13 + public static final ConstantBooleanExpression TRUE = 14 + new ConstantBooleanExpression(Either.left(true), true); 15 + public static final ConstantBooleanExpression FALSE = 16 + new ConstantBooleanExpression(Either.left(false), false); 17 + 18 + @EqualsAndHashCode.Exclude 19 + private final Either<Boolean, String> either; 20 + 21 + private final boolean value; 22 + 23 + private ConstantBooleanExpression(Either<Boolean, String> either, boolean value) { 24 + this.either = either; 25 + this.value = value; 26 + } 27 + 28 + @Override 29 + public Either<Boolean, String> toSource() { 30 + return either; 31 + } 32 + 33 + @Override 34 + public boolean asBoolean(LootContext context, @Nullable Map<String, ?> parameters) { 35 + return value; 36 + } 37 + 38 + public String toString() { 39 + return "BooleanExpression(value=" + this.value + ')'; 40 + } 41 + }
+36
src/main/java/me/melontini/commander/impl/expression/intermediaries/ConstantLongExpression.java
··· 1 + package me.melontini.commander.impl.expression.intermediaries; 2 + 3 + import com.mojang.datafixers.util.Either; 4 + import java.util.Map; 5 + import lombok.EqualsAndHashCode; 6 + import me.melontini.commander.api.expression.LongExpression; 7 + import net.minecraft.loot.context.LootContext; 8 + import org.jetbrains.annotations.Nullable; 9 + 10 + @EqualsAndHashCode 11 + public final class ConstantLongExpression implements LongExpression { 12 + @EqualsAndHashCode.Exclude 13 + private final Either<Long, String> either; 14 + 15 + private final long value; 16 + 17 + public ConstantLongExpression(Either<Long, String> either, long value) { 18 + this.either = either; 19 + this.value = value; 20 + } 21 + 22 + @Override 23 + public Either<Long, String> toSource() { 24 + return either; 25 + } 26 + 27 + @Override 28 + public long asLong(LootContext context, @Nullable Map<String, ?> parameters) { 29 + return value; 30 + } 31 + 32 + @Override 33 + public String toString() { 34 + return "LongExpression(value=" + value + ')'; 35 + } 36 + }
+36
src/main/java/me/melontini/commander/impl/expression/intermediaries/DynamicArithmetica.java
··· 1 + package me.melontini.commander.impl.expression.intermediaries; 2 + 3 + import com.mojang.datafixers.util.Either; 4 + import java.util.Map; 5 + import lombok.EqualsAndHashCode; 6 + import me.melontini.commander.api.expression.Arithmetica; 7 + import me.melontini.commander.api.expression.Expression; 8 + import net.minecraft.loot.context.LootContext; 9 + import org.jetbrains.annotations.Nullable; 10 + 11 + @EqualsAndHashCode 12 + public final class DynamicArithmetica implements Arithmetica { 13 + @EqualsAndHashCode.Exclude 14 + private final Either<Double, String> either; 15 + 16 + private final Expression expression; 17 + 18 + public DynamicArithmetica(Either<Double, String> either, Expression expression) { 19 + this.either = either; 20 + this.expression = expression; 21 + } 22 + 23 + @Override 24 + public Either<Double, String> toSource() { 25 + return either; 26 + } 27 + 28 + @Override 29 + public double asDouble(LootContext context, @Nullable Map<String, ?> parameters) { 30 + return expression.eval(context, parameters).getAsDecimal().doubleValue(); 31 + } 32 + 33 + public String toString() { 34 + return "Arithmetica(expression=" + this.expression + ')'; 35 + } 36 + }
+36
src/main/java/me/melontini/commander/impl/expression/intermediaries/DynamicBooleanExpression.java
··· 1 + package me.melontini.commander.impl.expression.intermediaries; 2 + 3 + import com.mojang.datafixers.util.Either; 4 + import java.util.Map; 5 + import lombok.EqualsAndHashCode; 6 + import me.melontini.commander.api.expression.BooleanExpression; 7 + import me.melontini.commander.api.expression.Expression; 8 + import net.minecraft.loot.context.LootContext; 9 + import org.jetbrains.annotations.Nullable; 10 + 11 + @EqualsAndHashCode 12 + public final class DynamicBooleanExpression implements BooleanExpression { 13 + @EqualsAndHashCode.Exclude 14 + private final Either<Boolean, String> either; 15 + 16 + private final Expression expression; 17 + 18 + public DynamicBooleanExpression(Either<Boolean, String> either, Expression expression) { 19 + this.either = either; 20 + this.expression = expression; 21 + } 22 + 23 + @Override 24 + public Either<Boolean, String> toSource() { 25 + return either; 26 + } 27 + 28 + @Override 29 + public boolean asBoolean(LootContext context, @Nullable Map<String, ?> parameters) { 30 + return expression.eval(context, parameters).getAsBoolean(); 31 + } 32 + 33 + public String toString() { 34 + return "BooleanExpression(expression=" + this.expression + ')'; 35 + } 36 + }
+37
src/main/java/me/melontini/commander/impl/expression/intermediaries/DynamicLongExpression.java
··· 1 + package me.melontini.commander.impl.expression.intermediaries; 2 + 3 + import com.mojang.datafixers.util.Either; 4 + import java.util.Map; 5 + import lombok.EqualsAndHashCode; 6 + import me.melontini.commander.api.expression.Expression; 7 + import me.melontini.commander.api.expression.LongExpression; 8 + import net.minecraft.loot.context.LootContext; 9 + import org.jetbrains.annotations.Nullable; 10 + 11 + @EqualsAndHashCode 12 + public final class DynamicLongExpression implements LongExpression { 13 + @EqualsAndHashCode.Exclude 14 + private final Either<Long, String> either; 15 + 16 + private final Expression expression; 17 + 18 + public DynamicLongExpression(Either<Long, String> either, Expression expression) { 19 + this.either = either; 20 + this.expression = expression; 21 + } 22 + 23 + @Override 24 + public Either<Long, String> toSource() { 25 + return either; 26 + } 27 + 28 + @Override 29 + public long asLong(LootContext context, @Nullable Map<String, ?> parameters) { 30 + return expression.eval(context, parameters).getAsDecimal().longValue(); 31 + } 32 + 33 + @Override 34 + public String toString() { 35 + return "LongExpression(expression=" + expression + ')'; 36 + } 37 + }
+20
src/main/java/me/melontini/commander/impl/expression/intermediaries/NegatedBooleanExpression.java
··· 1 + package me.melontini.commander.impl.expression.intermediaries; 2 + 3 + import com.mojang.datafixers.util.Either; 4 + import java.util.Map; 5 + import me.melontini.commander.api.expression.BooleanExpression; 6 + import net.minecraft.loot.context.LootContext; 7 + import org.jetbrains.annotations.Nullable; 8 + 9 + public record NegatedBooleanExpression(BooleanExpression delegate) implements BooleanExpression { 10 + 11 + @Override 12 + public boolean asBoolean(LootContext context, @Nullable Map<String, ?> parameters) { 13 + return !delegate().asBoolean(context, parameters); 14 + } 15 + 16 + @Override 17 + public Either<Boolean, String> toSource() { 18 + return delegate.toSource(); 19 + } 20 + }
+87
src/main/java/me/melontini/commander/impl/expression/library/ExpressionLibraryLoader.java
··· 1 + package me.melontini.commander.impl.expression.library; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.data.DataAccessorIfc; 6 + import com.ezylang.evalex.data.EvaluationValue; 7 + import com.ezylang.evalex.parser.Token; 8 + import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 9 + import com.mojang.serialization.Codec; 10 + import com.mojang.serialization.codecs.RecordCodecBuilder; 11 + import java.util.Collections; 12 + import java.util.HashMap; 13 + import java.util.Map; 14 + import me.melontini.commander.api.expression.Expression; 15 + import me.melontini.commander.api.expression.ExpressionLibrary; 16 + import me.melontini.commander.impl.Commander; 17 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 18 + import me.melontini.dark_matter.api.data.codecs.JsonCodecDataLoader; 19 + import me.melontini.dark_matter.api.data.loading.ReloaderType; 20 + import me.melontini.dark_matter.api.minecraft.util.TextUtil; 21 + import net.minecraft.resource.ResourceManager; 22 + import net.minecraft.util.Identifier; 23 + import org.jetbrains.annotations.Nullable; 24 + import org.jetbrains.annotations.UnmodifiableView; 25 + 26 + public class ExpressionLibraryLoader extends JsonCodecDataLoader<ExpressionLibraryLoader.Shelf> 27 + implements ExpressionLibrary, DataAccessorIfc { 28 + 29 + public static final DynamicCommandExceptionType NO_EXPRESSION_EXCEPTION = 30 + new DynamicCommandExceptionType( 31 + object -> TextUtil.literal("No such expression in library %s!".formatted(object))); 32 + 33 + public static final ReloaderType<ExpressionLibraryLoader> RELOADER = 34 + ReloaderType.create(Commander.id("expression_library")); 35 + 36 + private Map<Identifier, Expression> library = new HashMap<>(); 37 + 38 + public ExpressionLibraryLoader() { 39 + super(RELOADER.identifier(), Shelf.CODEC); 40 + } 41 + 42 + public Expression getExpression(Identifier id) { 43 + return this.library.get(id); 44 + } 45 + 46 + @Override 47 + public @UnmodifiableView Map<Identifier, Expression> allExpressions() { 48 + return Collections.unmodifiableMap(library); 49 + } 50 + 51 + @Override 52 + protected void apply(Map<Identifier, Shelf> parsed, ResourceManager manager) { 53 + Map<Identifier, Expression> base = new HashMap<>(); 54 + Map<Identifier, Expression> replace = new HashMap<>(); 55 + parsed.values().forEach(shelf -> { 56 + if (!shelf.replace()) { 57 + base.putAll(shelf.expressions()); 58 + } else { 59 + replace.putAll(shelf.expressions()); 60 + } 61 + }); 62 + base.putAll(replace); 63 + this.library = base; 64 + } 65 + 66 + @Override 67 + public @Nullable EvaluationValue getData(String variable, Token token, EvaluationContext context) 68 + throws EvaluationException { 69 + var expression = ((com.ezylang.evalex.Expression) library.get(Identifier.of(variable))); 70 + if (expression == null) return null; 71 + return expression.evaluate(context); 72 + } 73 + 74 + @Override 75 + public String toString() { 76 + return "ExpressionLibrary()"; 77 + } 78 + 79 + public record Shelf(boolean replace, Map<Identifier, Expression> expressions) { 80 + public static final Codec<Shelf> CODEC = RecordCodecBuilder.create(data -> data.group( 81 + ExtraCodecs.optional("replace", Codec.BOOL, false).forGetter(Shelf::replace), 82 + Codec.unboundedMap(Identifier.CODEC, Expression.CODEC) 83 + .fieldOf("expressions") 84 + .forGetter(Shelf::expressions)) 85 + .apply(data, Shelf::new)); 86 + } 87 + }
+12
src/main/java/me/melontini/commander/impl/expression/macro/ConstantMacro.java
··· 1 + package me.melontini.commander.impl.expression.macro; 2 + 3 + import java.util.Map; 4 + import me.melontini.commander.api.expression.BrigadierMacro; 5 + import net.minecraft.loot.context.LootContext; 6 + 7 + public record ConstantMacro(String original) implements BrigadierMacro { 8 + @Override 9 + public String build(LootContext context, Map<String, Object> params) { 10 + return original(); 11 + } 12 + }
+16
src/main/java/me/melontini/commander/impl/expression/macro/DynamicMacro.java
··· 1 + package me.melontini.commander.impl.expression.macro; 2 + 3 + import java.util.Map; 4 + import lombok.extern.log4j.Log4j2; 5 + import me.melontini.commander.api.expression.BrigadierMacro; 6 + import net.minecraft.loot.context.LootContext; 7 + 8 + @Log4j2 9 + public record DynamicMacro(String original, PatternParser.Appender start) 10 + implements BrigadierMacro { 11 + 12 + @Override 13 + public String build(LootContext context, Map<String, Object> params) { 14 + return start.build(context, params, new StringBuilder()).toString(); 15 + } 16 + }
+192
src/main/java/me/melontini/commander/impl/expression/macro/PatternParser.java
··· 1 + package me.melontini.commander.impl.expression.macro; 2 + 3 + import static me.melontini.commander.impl.expression.EvalUtils.evaluate; 4 + 5 + import com.ezylang.evalex.data.EvaluationValue; 6 + import com.ezylang.evalex.data.types.BooleanValue; 7 + import com.ezylang.evalex.data.types.NumberValue; 8 + import com.google.common.collect.ImmutableMap; 9 + import com.mojang.serialization.DataResult; 10 + import java.math.RoundingMode; 11 + import java.util.ArrayList; 12 + import java.util.List; 13 + import java.util.Map; 14 + import java.util.function.BiFunction; 15 + import java.util.function.Function; 16 + import me.melontini.commander.api.expression.BrigadierMacro; 17 + import me.melontini.commander.impl.expression.EvalUtils; 18 + import me.melontini.dark_matter.api.base.util.Result; 19 + import net.minecraft.loot.context.LootContext; 20 + import org.jetbrains.annotations.Nullable; 21 + 22 + public class PatternParser { 23 + 24 + public static final Map<String, Function<EvaluationValue, EvaluationValue>> CONVERTERS = 25 + ImmutableMap.of( 26 + "bool", v -> BooleanValue.of(v.getBooleanValue()), 27 + "long", v -> NumberValue.of(v.getNumberValue().setScale(0, RoundingMode.DOWN)), 28 + "int", v -> NumberValue.of(v.getNumberValue().setScale(0, RoundingMode.DOWN)), 29 + "double", v -> NumberValue.of(v.getNumberValue())); 30 + 31 + // Parses brigadier macros from strings 32 + public static DataResult<BrigadierMacro> parse(String input) { 33 + StringReader reader = new StringReader(input); 34 + 35 + List<Appender> list = new ArrayList<>(); 36 + StringBuilder start = new StringBuilder(); 37 + while (reader.hasNext()) { 38 + char c = reader.read(); 39 + if (c == '$') { // Possible macro start 40 + var result = processMacroStart(reader); 41 + if (result.error().isPresent()) 42 + return DataResult.error(() -> result.error().orElseThrow()); 43 + if (result.value().isPresent()) { 44 + var expression = result.value().orElseThrow(); 45 + var fin = start.toString(); 46 + list.add((context, params, builder) -> builder 47 + .append(fin) 48 + .append(EvalUtils.toMacroString(expression.apply(context, params)))); 49 + start = new StringBuilder(); 50 + continue; 51 + } 52 + } 53 + start.append(c); 54 + } 55 + if (list.isEmpty()) { // Empty == no macros. 56 + return DataResult.success(new ConstantMacro(input)); 57 + } 58 + 59 + var fin = start.toString(); 60 + if (list.size() == 1) { 61 + var appender = list.get(0); 62 + return DataResult.success(new DynamicMacro( 63 + input, 64 + (context, params, builder) -> appender.build(context, params, builder).append(fin))); 65 + } 66 + list.add((context, params, builder) -> builder.append(fin)); 67 + return DataResult.success(new DynamicMacro( 68 + input, 69 + list.stream() 70 + .reduce((a1, a2) -> (context, params, builder) -> 71 + a2.build(context, params, a1.build(context, params, builder))) 72 + .orElseThrow())); 73 + } 74 + 75 + public interface Appender { 76 + StringBuilder build(LootContext context, Map<String, ?> params, StringBuilder builder); 77 + } 78 + 79 + public static Result<BiFunction<LootContext, Map<String, ?>, EvaluationValue>, String> 80 + processMacroStart(StringReader reader) { 81 + int start = reader.pointer(); 82 + 83 + // This might not be a macro start, but we have to pre-read 84 + // the cast in case of dangling `(`. 85 + StringBuilder cast = null; 86 + if (reader.peek() == '(') 87 + cast: 88 + { 89 + reader.skip(); 90 + cast = new StringBuilder(); 91 + while (reader.hasNext()) { 92 + char c = reader.read(); 93 + if (c == ')') break cast; 94 + cast.append(c); 95 + } 96 + return Result.error("Dangling parentheses '(' at index %s".formatted(start)); 97 + } 98 + 99 + StringBuilder expression = null; 100 + // Check if it *is* a macro start. 101 + if (reader.peek() == '{' && reader.peek(1) == '{') 102 + expression: 103 + { 104 + reader.skip(2); 105 + expression = new StringBuilder(); 106 + while (reader.hasNext()) { 107 + char c = reader.read(); 108 + if (c == '}' && reader.canRead(1) && reader.peek() == '}') { 109 + reader.skip(); 110 + break expression; 111 + } 112 + expression.append(c); 113 + } 114 + return Result.error("Dangling braces '{{' at index %s".formatted(start)); 115 + } 116 + 117 + // Nope, not a macro. 118 + if (expression == null) return Result.empty(); 119 + if (cast != null) { // Now we can verify the cast. 120 + String s = cast.toString(); 121 + if (s.isBlank()) return Result.error("Illegal empty cast at index %s".formatted(start)); 122 + for (char c : s.toCharArray()) { 123 + if (!Character.isLetter(c)) // Only letters are allowed. 124 + return Result.error( 125 + "Illegal cast (%s). Must only contain letters and no whitespace!".formatted(s)); 126 + } 127 + if (!CONVERTERS.containsKey(s)) return Result.error("No such cast (%s)".formatted(s)); 128 + } 129 + 130 + var result = parseExpression(expression.toString(), cast == null ? null : cast.toString()); 131 + if (result.error().isPresent()) return Result.error(result.error().get().message()); 132 + return Result.ok(result.result().orElseThrow()); 133 + } 134 + 135 + public static DataResult<BiFunction<LootContext, Map<String, ?>, EvaluationValue>> 136 + parseExpression(String expression, @Nullable String cast) { 137 + if (cast == null) 138 + return EvalUtils.parseExpression(expression) 139 + .map(exp -> (context, params) -> evaluate(context, exp, params)); 140 + 141 + var c = CONVERTERS.get(cast); 142 + return EvalUtils.parseExpression(expression).map(exp -> (context, params) -> { 143 + var evalResult = evaluate(context, exp, params); 144 + return evalResult.getValue() == null ? null : c.apply(evalResult); 145 + }); 146 + } 147 + 148 + public static class StringReader { 149 + private final String input; 150 + private int pointer = 0; 151 + 152 + public StringReader(String input) { 153 + this.input = input; 154 + } 155 + 156 + public char peek() { 157 + return this.peek(0); 158 + } 159 + 160 + public char peek(int offset) { 161 + return this.input.charAt(pointer + offset); 162 + } 163 + 164 + public char read() { 165 + return this.input.charAt(pointer++); 166 + } 167 + 168 + public void skip(int offset) { 169 + pointer += offset; 170 + } 171 + 172 + public void skip() { 173 + skip(1); 174 + } 175 + 176 + public boolean canRead(int length) { 177 + return pointer + length <= this.input.length(); 178 + } 179 + 180 + public boolean hasNext() { 181 + return canRead(1); 182 + } 183 + 184 + public int pointer() { 185 + return pointer; 186 + } 187 + 188 + public void pointer(int pointer) { 189 + this.pointer = pointer; 190 + } 191 + } 192 + }
+26
src/main/java/me/melontini/commander/impl/expression/operators/SafeOrOperator.java
··· 1 + package me.melontini.commander.impl.expression.operators; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.data.EvaluationValue; 6 + import com.ezylang.evalex.operators.AbstractOperator; 7 + import com.ezylang.evalex.operators.InfixOperator; 8 + import com.ezylang.evalex.operators.OperatorIfc; 9 + import com.ezylang.evalex.parser.Token; 10 + 11 + // The `?` operator. Allows users to easily handle null values. 12 + // 13 + // This is shorter than writing an `if(a == null, a, b)` statement. `a ? b` 14 + @InfixOperator(precedence = OperatorIfc.OPERATOR_PRECEDENCE_OR, operandsLazy = true) 15 + public class SafeOrOperator extends AbstractOperator { 16 + 17 + @Override 18 + public EvaluationValue evaluate( 19 + EvaluationContext context, Token operatorToken, EvaluationValue... operands) 20 + throws EvaluationException { 21 + var result = context.expression().evaluateSubtree(operands[0].getExpressionNode(), context); 22 + return result.isNullValue() 23 + ? context.expression().evaluateSubtree(operands[1].getExpressionNode(), context) 24 + : result; 25 + } 26 + }
+44
src/main/java/me/melontini/commander/impl/expression/operators/SafeStructureOperator.java
··· 1 + package me.melontini.commander.impl.expression.operators; 2 + 3 + import com.ezylang.evalex.EvaluationContext; 4 + import com.ezylang.evalex.EvaluationException; 5 + import com.ezylang.evalex.data.EvaluationValue; 6 + import com.ezylang.evalex.data.types.NullValue; 7 + import com.ezylang.evalex.operators.AbstractOperator; 8 + import com.ezylang.evalex.operators.InfixOperator; 9 + import com.ezylang.evalex.parser.Token; 10 + 11 + // The `?.` operator. Allows users to safely handle structures with changing keys. 12 + // This separation from the default implementation saves users from situations where 13 + // returning null or throwing an exception would derail everything. 14 + // 15 + // This is shorter than `if(structContainsKey(a, 'key'), a.key, null)`. `a?.key`. 16 + @InfixOperator(precedence = Integer.MAX_VALUE, operandsLazy = true) 17 + public class SafeStructureOperator extends AbstractOperator { 18 + 19 + // A copy of the default expression implementation but without exceptions. 20 + @Override 21 + public EvaluationValue evaluate( 22 + EvaluationContext context, Token operatorToken, EvaluationValue... operands) 23 + throws EvaluationException { 24 + EvaluationValue structure = 25 + context.expression().evaluateSubtree(operands[0].getExpressionNode(), context); 26 + if (structure.isNullValue()) return structure; 27 + 28 + Token nameToken = operands[1].getExpressionNode().getToken(); 29 + String name = nameToken.getValue(); 30 + 31 + if (structure.isDataAccessorValue()) { 32 + var result = structure.getDataAccessorValue().getData(name, nameToken, context); 33 + return result == null ? NullValue.of() : result; 34 + } 35 + 36 + if (structure.isStructureValue()) { 37 + if (!structure.getStructureValue().containsKey(name)) { 38 + return NullValue.of(); 39 + } 40 + return structure.getStructureValue().get(name); 41 + } 42 + throw EvaluationException.ofUnsupportedDataTypeInOperation(operatorToken); 43 + } 44 + }
+82
src/main/java/me/melontini/commander/impl/mixin/AdvancementRewardsMixin.java
··· 1 + package me.melontini.commander.impl.mixin; 2 + 3 + import com.mojang.datafixers.kinds.App; 4 + import com.mojang.serialization.Codec; 5 + import com.mojang.serialization.MapCodec; 6 + import com.mojang.serialization.codecs.RecordCodecBuilder; 7 + import java.util.Collections; 8 + import java.util.List; 9 + import java.util.function.Function; 10 + import me.melontini.commander.api.command.Command; 11 + import me.melontini.commander.api.event.EventContext; 12 + import me.melontini.commander.api.event.EventKey; 13 + import me.melontini.commander.api.event.EventType; 14 + import me.melontini.commander.impl.util.loot.LootUtil; 15 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 16 + import net.minecraft.advancement.AdvancementRewards; 17 + import net.minecraft.loot.context.LootContext; 18 + import net.minecraft.loot.context.LootContextParameterSet; 19 + import net.minecraft.loot.context.LootContextParameters; 20 + import net.minecraft.loot.context.LootContextTypes; 21 + import net.minecraft.server.network.ServerPlayerEntity; 22 + import org.spongepowered.asm.mixin.Mixin; 23 + import org.spongepowered.asm.mixin.Unique; 24 + import org.spongepowered.asm.mixin.injection.At; 25 + import org.spongepowered.asm.mixin.injection.Inject; 26 + import org.spongepowered.asm.mixin.injection.ModifyArg; 27 + import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 28 + 29 + @Mixin(AdvancementRewards.class) 30 + public class AdvancementRewardsMixin { 31 + 32 + @Unique private List<Command.Conditioned> commands; 33 + 34 + // https://gist.github.com/kvverti/dec17e824922e1974313b8beadc621c5 35 + @ModifyArg( 36 + at = 37 + @At( 38 + value = "INVOKE", 39 + target = 40 + "Lcom/mojang/serialization/codecs/RecordCodecBuilder;create(Ljava/util/function/Function;)Lcom/mojang/serialization/Codec;"), 41 + index = 0, 42 + method = "<clinit>", 43 + remap = false) 44 + private static Function< 45 + RecordCodecBuilder.Instance<AdvancementRewards>, 46 + ? extends App<RecordCodecBuilder.Mu<AdvancementRewards>, AdvancementRewards>> 47 + commander$modifyCodec( 48 + Function< 49 + RecordCodecBuilder.Instance<AdvancementRewards>, 50 + ? extends App<RecordCodecBuilder.Mu<AdvancementRewards>, AdvancementRewards>> 51 + builder) { 52 + MapCodec<AdvancementRewards> mapCodec = RecordCodecBuilder.mapCodec(builder); 53 + Codec<List<Command.Conditioned>> commanderCodec = ExtraCodecs.list(Command.CODEC.codec()); 54 + 55 + return data -> data.group( 56 + mapCodec.forGetter(Function.identity()), 57 + ExtraCodecs.optional("commander:commands", commanderCodec, Collections.emptyList()) 58 + .forGetter(object -> ((AdvancementRewardsMixin) (Object) object).commands)) 59 + .apply(data, (advancementRewards, commands) -> { 60 + ((AdvancementRewardsMixin) (Object) advancementRewards).commands = commands; 61 + return advancementRewards; 62 + }); 63 + } 64 + 65 + @Inject(at = @At("TAIL"), method = "apply") 66 + private void commander$applyCommands(ServerPlayerEntity player, CallbackInfo ci) { 67 + if (this.commands == null) return; 68 + LootContext context = 69 + LootUtil.build(new LootContextParameterSet.Builder(player.getServerWorld()) 70 + .add(LootContextParameters.THIS_ENTITY, player) 71 + .add(LootContextParameters.ORIGIN, player.getPos()) 72 + .build(LootContextTypes.ADVANCEMENT_REWARD)); 73 + 74 + EventContext context1 = EventContext.builder(EventType.NULL) 75 + .addParameter(EventKey.LOOT_CONTEXT, context) 76 + .build(); 77 + 78 + for (Command.Conditioned command : this.commands) { 79 + command.execute(context1); 80 + } 81 + } 82 + }
+96
src/main/java/me/melontini/commander/impl/mixin/ExecuteCommandMixin.java
··· 1 + package me.melontini.commander.impl.mixin; 2 + 3 + import static me.melontini.commander.impl.expression.library.ExpressionLibraryLoader.NO_EXPRESSION_EXCEPTION; 4 + 5 + import com.mojang.brigadier.arguments.StringArgumentType; 6 + import com.mojang.brigadier.builder.ArgumentBuilder; 7 + import com.mojang.brigadier.builder.LiteralArgumentBuilder; 8 + import com.mojang.brigadier.context.CommandContext; 9 + import com.mojang.brigadier.tree.CommandNode; 10 + import me.melontini.commander.api.expression.Expression; 11 + import me.melontini.commander.api.expression.ExpressionLibrary; 12 + import me.melontini.commander.impl.Commander; 13 + import me.melontini.commander.impl.util.loot.LootUtil; 14 + import net.minecraft.command.CommandRegistryAccess; 15 + import net.minecraft.command.CommandSource; 16 + import net.minecraft.command.argument.IdentifierArgumentType; 17 + import net.minecraft.loot.context.LootContextParameterSet; 18 + import net.minecraft.loot.context.LootContextParameters; 19 + import net.minecraft.loot.context.LootContextTypes; 20 + import net.minecraft.server.command.CommandManager; 21 + import net.minecraft.server.command.ExecuteCommand; 22 + import net.minecraft.server.command.ServerCommandSource; 23 + import org.spongepowered.asm.mixin.Mixin; 24 + import org.spongepowered.asm.mixin.Shadow; 25 + import org.spongepowered.asm.mixin.injection.At; 26 + import org.spongepowered.asm.mixin.injection.Inject; 27 + import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 28 + 29 + @Mixin(ExecuteCommand.class) 30 + public abstract class ExecuteCommandMixin { 31 + 32 + @Shadow 33 + private static ArgumentBuilder<ServerCommandSource, ?> addConditionLogic( 34 + CommandNode<ServerCommandSource> root, 35 + ArgumentBuilder<ServerCommandSource, ?> builder, 36 + boolean positive, 37 + ExecuteCommand.Condition condition) { 38 + throw new AssertionError(); 39 + } 40 + 41 + @Inject( 42 + at = 43 + @At( 44 + value = "FIELD", 45 + target = 46 + "Lnet/minecraft/server/command/DataCommand;SOURCE_OBJECT_TYPES:Ljava/util/List;", 47 + shift = At.Shift.BEFORE), 48 + method = "addConditionArguments") 49 + private static void commander$addExpressionCondition( 50 + CommandNode<ServerCommandSource> root, 51 + LiteralArgumentBuilder<ServerCommandSource> argumentBuilder, 52 + boolean positive, 53 + CommandRegistryAccess commandRegistryAccess, 54 + CallbackInfoReturnable<ArgumentBuilder<ServerCommandSource, ?>> cir) { 55 + argumentBuilder 56 + .then(CommandManager.literal("cmd:expression") 57 + .then(addConditionLogic( 58 + root, 59 + CommandManager.argument("expression", StringArgumentType.string()), 60 + positive, 61 + context -> { 62 + var r = Expression.parse(StringArgumentType.getString(context, "expression")); 63 + if (r.error().isPresent()) 64 + throw Commander.EXPRESSION_EXCEPTION.create(r.error().get().message()); 65 + return evaluateExpression(r.result().orElseThrow(), context); 66 + }))) 67 + .then(CommandManager.literal("cmd:library") 68 + .then(addConditionLogic( 69 + root, 70 + CommandManager.argument("expression", IdentifierArgumentType.identifier()) 71 + .suggests((context, builder) -> CommandSource.suggestIdentifiers( 72 + ExpressionLibrary.get(context.getSource().getServer()) 73 + .allExpressions() 74 + .keySet(), 75 + builder)), 76 + positive, 77 + context -> { 78 + var identifier = IdentifierArgumentType.getIdentifier(context, "expression"); 79 + var expression = ExpressionLibrary.get(context.getSource().getServer()) 80 + .getExpression(identifier); 81 + if (expression == null) throw NO_EXPRESSION_EXCEPTION.create(identifier); 82 + return evaluateExpression(expression, context); 83 + }))); 84 + } 85 + 86 + private static boolean evaluateExpression( 87 + Expression expression, CommandContext<ServerCommandSource> context) { 88 + return expression 89 + .eval(LootUtil.build(new LootContextParameterSet.Builder( 90 + context.getSource().getWorld()) 91 + .add(LootContextParameters.ORIGIN, context.getSource().getPosition()) 92 + .addOptional(LootContextParameters.THIS_ENTITY, context.getSource().getEntity()) 93 + .build(LootContextTypes.COMMAND))) 94 + .getAsBoolean(); 95 + } 96 + }
+14
src/main/java/me/melontini/commander/impl/mixin/NbtCompoundAccessor.java
··· 1 + package me.melontini.commander.impl.mixin; 2 + 3 + import java.util.Map; 4 + import net.minecraft.nbt.NbtCompound; 5 + import net.minecraft.nbt.NbtElement; 6 + import org.spongepowered.asm.mixin.Mixin; 7 + import org.spongepowered.asm.mixin.gen.Accessor; 8 + 9 + @Mixin(NbtCompound.class) 10 + public interface NbtCompoundAccessor { 11 + 12 + @Accessor("entries") 13 + Map<String, NbtElement> commander$toMap(); 14 + }
+134
src/main/java/me/melontini/commander/impl/mixin/ScoreboardCommandMixin.java
··· 1 + package me.melontini.commander.impl.mixin; 2 + 3 + import static me.melontini.commander.impl.expression.library.ExpressionLibraryLoader.NO_EXPRESSION_EXCEPTION; 4 + 5 + import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 6 + import com.mojang.brigadier.arguments.StringArgumentType; 7 + import com.mojang.brigadier.builder.LiteralArgumentBuilder; 8 + import com.mojang.brigadier.context.CommandContext; 9 + import com.mojang.brigadier.exceptions.CommandSyntaxException; 10 + import java.math.BigDecimal; 11 + import java.util.ArrayList; 12 + import java.util.Collections; 13 + import java.util.Optional; 14 + import me.melontini.commander.api.expression.Expression; 15 + import me.melontini.commander.api.expression.ExpressionLibrary; 16 + import me.melontini.commander.impl.Commander; 17 + import me.melontini.commander.impl.util.loot.LootUtil; 18 + import net.minecraft.command.CommandSource; 19 + import net.minecraft.command.argument.IdentifierArgumentType; 20 + import net.minecraft.command.argument.ScoreHolderArgumentType; 21 + import net.minecraft.command.argument.ScoreboardObjectiveArgumentType; 22 + import net.minecraft.loot.context.LootContext; 23 + import net.minecraft.loot.context.LootContextParameterSet; 24 + import net.minecraft.loot.context.LootContextParameters; 25 + import net.minecraft.loot.context.LootContextTypes; 26 + import net.minecraft.scoreboard.ScoreAccess; 27 + import net.minecraft.scoreboard.ScoreHolder; 28 + import net.minecraft.scoreboard.Scoreboard; 29 + import net.minecraft.server.command.CommandManager; 30 + import net.minecraft.server.command.ScoreboardCommand; 31 + import net.minecraft.server.command.ServerCommandSource; 32 + import net.minecraft.text.Text; 33 + import org.spongepowered.asm.mixin.Mixin; 34 + import org.spongepowered.asm.mixin.Unique; 35 + import org.spongepowered.asm.mixin.injection.At; 36 + 37 + @Mixin(ScoreboardCommand.class) 38 + public class ScoreboardCommandMixin { 39 + 40 + @ModifyExpressionValue( 41 + method = "register", 42 + at = 43 + @At( 44 + value = "INVOKE", 45 + target = 46 + "Lnet/minecraft/server/command/CommandManager;literal(Ljava/lang/String;)Lcom/mojang/brigadier/builder/LiteralArgumentBuilder;", 47 + ordinal = 10)) 48 + private static LiteralArgumentBuilder<ServerCommandSource> addCommanderOperator( 49 + LiteralArgumentBuilder<ServerCommandSource> original) { 50 + return original 51 + .then(CommandManager.literal("cmd:operate") 52 + .then(CommandManager.argument("targets", ScoreHolderArgumentType.scoreHolders()) 53 + .suggests(ScoreHolderArgumentType.SUGGESTION_PROVIDER) 54 + .then(CommandManager.argument( 55 + "objective", ScoreboardObjectiveArgumentType.scoreboardObjective()) 56 + .then(CommandManager.argument("expression", StringArgumentType.string()) 57 + .executes(context -> { 58 + var r = 59 + Expression.parse(StringArgumentType.getString(context, "expression")); 60 + if (r.error().isPresent()) 61 + throw Commander.EXPRESSION_EXCEPTION.create( 62 + r.error().get().message()); 63 + return evaluateExpression(r.result().orElseThrow(), context); 64 + }))))) 65 + .then(CommandManager.literal("cmd:library") 66 + .then(CommandManager.argument("targets", ScoreHolderArgumentType.scoreHolders()) 67 + .suggests(ScoreHolderArgumentType.SUGGESTION_PROVIDER) 68 + .then(CommandManager.argument( 69 + "objective", ScoreboardObjectiveArgumentType.scoreboardObjective()) 70 + .then(CommandManager.argument("expression", IdentifierArgumentType.identifier()) 71 + .suggests((context, builder) -> CommandSource.suggestIdentifiers( 72 + ExpressionLibrary.get(context.getSource().getServer()) 73 + .allExpressions() 74 + .keySet(), 75 + builder)) 76 + .executes(context -> { 77 + var identifier = 78 + IdentifierArgumentType.getIdentifier(context, "expression"); 79 + var expression = ExpressionLibrary.get( 80 + context.getSource().getServer()) 81 + .getExpression(identifier); 82 + if (expression == null) throw NO_EXPRESSION_EXCEPTION.create(identifier); 83 + return evaluateExpression(expression, context); 84 + }))))); 85 + } 86 + 87 + @Unique private static int evaluateExpression( 88 + Expression expression, CommandContext<ServerCommandSource> context) 89 + throws CommandSyntaxException { 90 + var targets = 91 + new ArrayList<>(ScoreHolderArgumentType.getScoreboardScoreHolders(context, "targets")); 92 + var objective = ScoreboardObjectiveArgumentType.getWritableObjective(context, "objective"); 93 + 94 + Scoreboard scoreboard = context.getSource().getServer().getScoreboard(); 95 + 96 + LootContext context1 = 97 + LootUtil.build(new LootContextParameterSet.Builder(context.getSource().getWorld()) 98 + .add(LootContextParameters.ORIGIN, context.getSource().getPosition()) 99 + .build(LootContextTypes.COMMAND)); 100 + 101 + for (ScoreHolder target : targets) { 102 + ScoreAccess score = scoreboard.getOrCreateScore(target, objective); 103 + Optional.ofNullable(expression 104 + .eval(context1, Collections.singletonMap("score", score.getScore())) 105 + .getAsDecimal()) 106 + .map(BigDecimal::intValue) 107 + .ifPresent(score::setScore); 108 + } 109 + 110 + if (targets.size() == 1) { 111 + context 112 + .getSource() 113 + .sendFeedback( 114 + () -> Text.translatable( 115 + "commands.scoreboard.players.set.success.single", 116 + objective.toHoverableText(), 117 + targets.get(0).getStyledDisplayName(), 118 + expression.original()), 119 + true); 120 + } else { 121 + context 122 + .getSource() 123 + .sendFeedback( 124 + () -> Text.translatable( 125 + "commands.scoreboard.players.set.success.multiple", 126 + objective.toHoverableText(), 127 + targets.size(), 128 + expression.original()), 129 + true); 130 + } 131 + 132 + return targets.size(); 133 + } 134 + }
+73
src/main/java/me/melontini/commander/impl/mixin/evalex/EvaluationValueMixin.java
··· 1 + package me.melontini.commander.impl.mixin.evalex; 2 + 3 + import com.ezylang.evalex.data.EvaluationValue; 4 + import java.math.BigDecimal; 5 + import java.time.Duration; 6 + import java.time.Instant; 7 + import me.melontini.commander.api.expression.Expression; 8 + import org.jetbrains.annotations.Nullable; 9 + import org.spongepowered.asm.mixin.Mixin; 10 + import org.spongepowered.asm.mixin.Shadow; 11 + 12 + @Mixin(value = EvaluationValue.class, remap = false) 13 + public abstract class EvaluationValueMixin implements Expression.Result { 14 + 15 + @Shadow 16 + public abstract BigDecimal getNumberValue(); 17 + 18 + @Shadow 19 + public abstract Boolean getBooleanValue(); 20 + 21 + @Shadow 22 + public abstract String getStringValue(); 23 + 24 + @Shadow 25 + public abstract Instant getDateTimeValue(); 26 + 27 + @Shadow 28 + public abstract Duration getDurationValue(); 29 + 30 + @Shadow 31 + public abstract boolean isNumberValue(); 32 + 33 + @Shadow 34 + public abstract boolean isDateTimeValue(); 35 + 36 + @Shadow 37 + public abstract Object getValue(); 38 + 39 + @Override 40 + @Nullable public BigDecimal getAsDecimal() { 41 + return getNumberValue(); 42 + } 43 + 44 + @Override 45 + public boolean getAsBoolean() { 46 + return getBooleanValue(); 47 + } 48 + 49 + @Override 50 + @Nullable public String getAsString() { 51 + return getStringValue(); 52 + } 53 + 54 + @Override 55 + @Nullable public Instant getAsInstant() { 56 + return getDateTimeValue(); 57 + } 58 + 59 + @Override 60 + @Nullable public Duration getAsDuration() { 61 + return getDurationValue(); 62 + } 63 + 64 + @Override 65 + public boolean isDecimalValue() { 66 + return isNumberValue(); 67 + } 68 + 69 + @Override 70 + public boolean isInstantValue() { 71 + return isDateTimeValue(); 72 + } 73 + }
+39
src/main/java/me/melontini/commander/impl/mixin/evalex/ExpressionMixin.java
··· 1 + package me.melontini.commander.impl.mixin.evalex; 2 + 3 + import com.ezylang.evalex.Expression; 4 + import java.util.Map; 5 + import java.util.Objects; 6 + import me.melontini.commander.impl.expression.EvalUtils; 7 + import net.minecraft.loot.context.LootContext; 8 + import org.jetbrains.annotations.NotNull; 9 + import org.spongepowered.asm.mixin.Mixin; 10 + import org.spongepowered.asm.mixin.Shadow; 11 + 12 + @Mixin(value = Expression.class, remap = false) 13 + public abstract class ExpressionMixin implements me.melontini.commander.api.expression.Expression { 14 + 15 + @Shadow 16 + public abstract String getExpressionString(); 17 + 18 + @Override 19 + public @NotNull Result eval(LootContext context, Map<String, ?> parameters) { 20 + return (Result) (Object) EvalUtils.evaluate(context, (Expression) (Object) this, parameters); 21 + } 22 + 23 + @Override 24 + public String original() { 25 + return getExpressionString(); 26 + } 27 + 28 + @Override 29 + public int hashCode() { 30 + return Objects.hash(original()); 31 + } 32 + 33 + @Override 34 + public boolean equals(Object obj) { 35 + if (this == obj) return true; 36 + if (!(obj instanceof Expression exp)) return false; 37 + return Objects.equals(original(), exp.getExpressionString()); 38 + } 39 + }
+8
src/main/java/me/melontini/commander/impl/util/DataTarget.java
··· 1 + package me.melontini.commander.impl.util; 2 + 3 + public enum DataTarget { 4 + LEVEL, 5 + CHUNK, 6 + ENTITY, 7 + BLOCK_ENTITY 8 + }
+16
src/main/java/me/melontini/commander/impl/util/Identity.java
··· 1 + package me.melontini.commander.impl.util; 2 + 3 + // Used by registry caches 4 + public record Identity<V>(V value) { 5 + 6 + @Override 7 + public boolean equals(Object object) { 8 + if (!(object instanceof Identity<?> identity)) return false; 9 + return value == identity.value; 10 + } 11 + 12 + @Override 13 + public int hashCode() { 14 + return System.identityHashCode(value); 15 + } 16 + }
+30
src/main/java/me/melontini/commander/impl/util/NbtCodecs.java
··· 1 + package me.melontini.commander.impl.util; 2 + 3 + import com.mojang.serialization.Codec; 4 + import com.mojang.serialization.DataResult; 5 + import com.mojang.serialization.Dynamic; 6 + import java.util.function.Function; 7 + import net.minecraft.nbt.*; 8 + 9 + public class NbtCodecs { 10 + 11 + public static final Codec<NbtElement> ELEMENT_CODEC = Codec.PASSTHROUGH.xmap( 12 + (dynamic) -> dynamic.convert(NbtOps.INSTANCE).getValue(), 13 + (element) -> new Dynamic<>(NbtOps.INSTANCE, element)); 14 + 15 + public static final Codec<NbtElement> PRIMITIVE_CODEC = ELEMENT_CODEC.comapFlatMap( 16 + element -> { 17 + if (element instanceof AbstractNbtNumber || element instanceof NbtString) 18 + return DataResult.success(element); 19 + return DataResult.error(() -> "Not an nbt primitive %s!".formatted(element)); 20 + }, 21 + Function.identity()); 22 + 23 + public static final Codec<NbtCompound> COMPOUND_CODEC = ELEMENT_CODEC.comapFlatMap( 24 + element -> { 25 + if (!(element instanceof NbtCompound compound)) 26 + return DataResult.error(() -> "Not an NbtCompound %s!".formatted(element)); 27 + return DataResult.success(compound); 28 + }, 29 + Function.identity()); 30 + }
+23
src/main/java/me/melontini/commander/impl/util/ServerHelper.java
··· 1 + package me.melontini.commander.impl.util; 2 + 3 + import lombok.experimental.UtilityClass; 4 + import net.minecraft.server.MinecraftServer; 5 + import net.minecraft.text.Text; 6 + 7 + @UtilityClass 8 + public class ServerHelper { 9 + 10 + public static void broadcastToOps(MinecraftServer server, Text text) { 11 + server 12 + .getPlayerManager() 13 + .broadcast( 14 + text, 15 + player -> { 16 + if (server.getPlayerManager().isOperator(player.getGameProfile())) { 17 + return text; 18 + } 19 + return null; 20 + }, 21 + false); 22 + } 23 + }
+146
src/main/java/me/melontini/commander/impl/util/ThrowingOptional.java
··· 1 + package me.melontini.commander.impl.util; 2 + 3 + import java.util.NoSuchElementException; 4 + import java.util.Objects; 5 + import java.util.function.Supplier; 6 + import java.util.stream.Stream; 7 + import lombok.EqualsAndHashCode; 8 + import me.melontini.dark_matter.api.base.util.functions.ThrowingConsumer; 9 + import me.melontini.dark_matter.api.base.util.functions.ThrowingFunction; 10 + import me.melontini.dark_matter.api.base.util.functions.ThrowingRunnable; 11 + import me.melontini.dark_matter.api.base.util.functions.ThrowingSupplier; 12 + import org.jetbrains.annotations.NotNull; 13 + 14 + @EqualsAndHashCode 15 + public class ThrowingOptional<T> { 16 + 17 + private static final ThrowingOptional<?> EMPTY = new ThrowingOptional<>(null); 18 + 19 + private final T value; 20 + 21 + public static <T> ThrowingOptional<T> empty() { 22 + return (ThrowingOptional<T>) EMPTY; 23 + } 24 + 25 + public static <T> ThrowingOptional<T> of(T value) { 26 + return new ThrowingOptional<>(Objects.requireNonNull(value)); 27 + } 28 + 29 + public static <T> ThrowingOptional<T> ofNullable(T value) { 30 + return value == null ? empty() : new ThrowingOptional<>(value); 31 + } 32 + 33 + private ThrowingOptional(T value) { 34 + this.value = value; 35 + } 36 + 37 + public T get() { 38 + if (value == null) { 39 + throw new NoSuchElementException("No value present"); 40 + } 41 + return value; 42 + } 43 + 44 + public boolean isPresent() { 45 + return value != null; 46 + } 47 + 48 + public boolean isEmpty() { 49 + return value == null; 50 + } 51 + 52 + public <E extends Throwable> void ifPresent(ThrowingConsumer<? super T, E> action) throws E { 53 + if (value != null) { 54 + action.accept(value); 55 + } 56 + } 57 + 58 + public <E extends Throwable> void ifPresentOrElse( 59 + ThrowingConsumer<? super T, E> action, ThrowingRunnable<E> emptyAction) throws E { 60 + if (value != null) { 61 + action.accept(value); 62 + } else { 63 + emptyAction.run(); 64 + } 65 + } 66 + 67 + public <E extends Throwable> ThrowingOptional<T> filter( 68 + ThrowingFunction<? super T, @NotNull Boolean, E> predicate) throws E { 69 + Objects.requireNonNull(predicate); 70 + if (isEmpty()) { 71 + return this; 72 + } else { 73 + return predicate.apply(value) ? this : empty(); 74 + } 75 + } 76 + 77 + public <E extends Throwable, U> ThrowingOptional<U> map( 78 + ThrowingFunction<? super T, ? extends U, E> mapper) throws E { 79 + Objects.requireNonNull(mapper); 80 + if (isEmpty()) { 81 + return empty(); 82 + } else { 83 + return ofNullable(mapper.apply(value)); 84 + } 85 + } 86 + 87 + public <E extends Throwable, U> ThrowingOptional<U> flatMap( 88 + ThrowingFunction<? super T, ? extends ThrowingOptional<? extends U>, E> mapper) throws E { 89 + Objects.requireNonNull(mapper); 90 + if (isEmpty()) { 91 + return empty(); 92 + } else { 93 + @SuppressWarnings("unchecked") 94 + ThrowingOptional<U> r = (ThrowingOptional<U>) mapper.apply(value); 95 + return Objects.requireNonNull(r); 96 + } 97 + } 98 + 99 + public <E extends Throwable> ThrowingOptional<T> or( 100 + ThrowingSupplier<? extends ThrowingOptional<? extends T>, E> supplier) throws E { 101 + Objects.requireNonNull(supplier); 102 + if (isPresent()) { 103 + return this; 104 + } else { 105 + @SuppressWarnings("unchecked") 106 + ThrowingOptional<T> r = (ThrowingOptional<T>) supplier.get(); 107 + return Objects.requireNonNull(r); 108 + } 109 + } 110 + 111 + public Stream<T> stream() { 112 + if (isEmpty()) { 113 + return Stream.empty(); 114 + } else { 115 + return Stream.of(value); 116 + } 117 + } 118 + 119 + public T orElse(T other) { 120 + return value != null ? value : other; 121 + } 122 + 123 + public <E extends Throwable> T orElseGet(ThrowingSupplier<? extends T, E> supplier) throws E { 124 + return value != null ? value : supplier.get(); 125 + } 126 + 127 + public T orElseThrow() { 128 + if (value == null) { 129 + throw new NoSuchElementException("No value present"); 130 + } 131 + return value; 132 + } 133 + 134 + public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { 135 + if (value != null) { 136 + return value; 137 + } else { 138 + throw exceptionSupplier.get(); 139 + } 140 + } 141 + 142 + @Override 143 + public String toString() { 144 + return value != null ? ("ThrowingOptional[" + value + "]") : "ThrowingOptional.empty"; 145 + } 146 + }
+28
src/main/java/me/melontini/commander/impl/util/loot/ArithmeticaLootNumberProvider.java
··· 1 + package me.melontini.commander.impl.util.loot; 2 + 3 + import com.mojang.serialization.MapCodec; 4 + import me.melontini.commander.api.expression.Arithmetica; 5 + import me.melontini.commander.impl.Commander; 6 + import net.minecraft.loot.context.LootContext; 7 + import net.minecraft.loot.provider.number.LootNumberProvider; 8 + import net.minecraft.loot.provider.number.LootNumberProviderType; 9 + 10 + public record ArithmeticaLootNumberProvider(Arithmetica value) implements LootNumberProvider { 11 + public static final MapCodec<ArithmeticaLootNumberProvider> CODEC = Arithmetica.CODEC 12 + .xmap(ArithmeticaLootNumberProvider::new, ArithmeticaLootNumberProvider::value) 13 + .fieldOf("value"); 14 + 15 + @Override 16 + public LootNumberProviderType getType() { 17 + return Commander.ARITHMETICA_PROVIDER; 18 + } 19 + 20 + @Override 21 + public float nextFloat(LootContext context) { 22 + return this.value.asFloat(context); 23 + } 24 + 25 + public static ArithmeticaLootNumberProvider create(Arithmetica value) { 26 + return new ArithmeticaLootNumberProvider(value); 27 + } 28 + }
+24
src/main/java/me/melontini/commander/impl/util/loot/ExpressionLootCondition.java
··· 1 + package me.melontini.commander.impl.util.loot; 2 + 3 + import com.mojang.serialization.MapCodec; 4 + import me.melontini.commander.api.expression.Expression; 5 + import me.melontini.commander.impl.Commander; 6 + import net.minecraft.loot.condition.LootCondition; 7 + import net.minecraft.loot.condition.LootConditionType; 8 + import net.minecraft.loot.context.LootContext; 9 + 10 + public record ExpressionLootCondition(Expression expression) implements LootCondition { 11 + public static final MapCodec<ExpressionLootCondition> CODEC = Expression.CODEC 12 + .xmap(ExpressionLootCondition::new, ExpressionLootCondition::expression) 13 + .fieldOf("value"); 14 + 15 + @Override 16 + public LootConditionType getType() { 17 + return Commander.EXPRESSION_CONDITION; 18 + } 19 + 20 + @Override 21 + public boolean test(LootContext context) { 22 + return expression().eval(context).getAsBoolean(); 23 + } 24 + }
+12
src/main/java/me/melontini/commander/impl/util/loot/LootUtil.java
··· 1 + package me.melontini.commander.impl.util.loot; 2 + 3 + import java.util.Optional; 4 + import net.minecraft.loot.context.LootContext; 5 + import net.minecraft.loot.context.LootContextParameterSet; 6 + 7 + public class LootUtil { 8 + 9 + public static LootContext build(LootContextParameterSet parameters) { 10 + return new LootContext.Builder(parameters).build(Optional.empty()); 11 + } 12 + }
+7
src/main/java/me/melontini/commander/impl/util/mappings/AmbiguousRemapper.java
··· 1 + package me.melontini.commander.impl.util.mappings; 2 + 3 + import org.jetbrains.annotations.Nullable; 4 + 5 + public interface AmbiguousRemapper { 6 + @Nullable String getFieldOrMethod(Class<?> cls, String name); 7 + }
+105
src/main/java/me/melontini/commander/impl/util/mappings/MappingKeeper.java
··· 1 + package me.melontini.commander.impl.util.mappings; 2 + 3 + import java.io.IOException; 4 + import java.io.InputStreamReader; 5 + import java.nio.charset.StandardCharsets; 6 + import java.nio.file.Files; 7 + import java.util.List; 8 + import java.util.Objects; 9 + import java.util.zip.InflaterInputStream; 10 + import lombok.extern.log4j.Log4j2; 11 + import me.melontini.commander.impl.Commander; 12 + import me.melontini.dark_matter.api.base.util.MakeSure; 13 + import net.fabricmc.loader.api.FabricLoader; 14 + import net.fabricmc.mappingio.MappingReader; 15 + import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; 16 + import net.fabricmc.mappingio.tree.MappingTree; 17 + import net.fabricmc.mappingio.tree.MemoryMappingTree; 18 + import org.jetbrains.annotations.Nullable; 19 + import org.objectweb.asm.Type; 20 + 21 + @Log4j2 22 + public record MappingKeeper(MemoryMappingTree mojmapTarget) implements AmbiguousRemapper { 23 + 24 + public static final String NAMESPACE = 25 + FabricLoader.getInstance().getMappingResolver().getCurrentRuntimeNamespace(); 26 + 27 + public static MemoryMappingTree loadMojmapTarget( 28 + MemoryMappingTree offMojmap, MemoryMappingTree offTarget) { 29 + log.info("Merging mappings..."); 30 + 31 + var merged = new MemoryMappingTree(); 32 + try { 33 + MemoryMappingTree temp = new MemoryMappingTree(); 34 + offMojmap.accept(temp); 35 + offTarget.accept(temp); 36 + temp.accept(new MappingSourceNsSwitch(merged, "mojang", true)); 37 + } catch (IOException e) { 38 + throw new IllegalStateException("Failed to merge official and mojang mappings!", e); 39 + } 40 + 41 + MakeSure.notNull( 42 + merged.getClass("net/minecraft/server/MinecraftServer"), 43 + "No built-in MinecraftServer mapping?") 44 + .setDstName("net/minecraft/server/MinecraftServer", merged.getNamespaceId(NAMESPACE)); 45 + return merged; 46 + } 47 + 48 + @Nullable public static MemoryMappingTree loadOffMojmap() { 49 + log.info("Loading official->mojmap mappings..."); 50 + 51 + var mappings = new MemoryMappingTree(); 52 + try (var reader = new InputStreamReader( 53 + new InflaterInputStream( 54 + Files.newInputStream(Commander.COMMANDER_PATH.resolve("mappings/server_mappings.bin"))), 55 + StandardCharsets.UTF_8)) { 56 + MappingReader.read(reader, new MappingSourceNsSwitch(mappings, "target")); 57 + } catch (IOException e) { 58 + throw new RuntimeException("Failed to load official-mojmap mappings!", e); 59 + } 60 + 61 + mappings.setSrcNamespace("official"); 62 + mappings.setDstNamespaces(List.of("mojang")); 63 + return mappings; 64 + } 65 + 66 + public static MemoryMappingTree loadOffTarget() { 67 + log.info("Loading official->{} mappings...", NAMESPACE); 68 + 69 + try (var reader = new InputStreamReader( 70 + MakeSure.notNull( 71 + MappingKeeper.class.getClassLoader().getResourceAsStream("mappings/mappings.tiny"), 72 + "mappings/mappings.tiny is not available? Are you running a fork of Fabric?"), 73 + StandardCharsets.UTF_8)) { 74 + var tree = new MemoryMappingTree(); 75 + MappingReader.read(reader, tree); 76 + return tree; 77 + } catch (IOException e) { 78 + throw new IllegalStateException(("Failed to read official->%s mappings! " 79 + + "Those mappings are provided by the loader and must be readable!") 80 + .formatted(NAMESPACE)); 81 + } 82 + } 83 + 84 + // We cannot reliably tell if a member has a mapping without a descriptor. 85 + // So we have to iterate through all members to see if something matches up. 86 + @Override 87 + public @Nullable String getFieldOrMethod(Class<?> cls, String name) { 88 + var clsData = mojmapTarget() 89 + .getClass(Type.getInternalName(cls), mojmapTarget().getNamespaceId(NAMESPACE)); 90 + if (clsData == null) return null; 91 + 92 + for (MappingTree.MethodMapping method : clsData.getMethods()) { 93 + if (Objects.equals(method.getSrcName(), name)) { 94 + var srcDesc = method.getSrcDesc(); 95 + if (srcDesc != null && srcDesc.startsWith("()") && !srcDesc.endsWith("V")) 96 + return method.getName(NAMESPACE); 97 + } 98 + } 99 + 100 + for (MappingTree.FieldMapping field : clsData.getFields()) { 101 + if (Objects.equals(field.getSrcName(), name)) return field.getName(NAMESPACE); 102 + } 103 + return null; 104 + } 105 + }
+100
src/main/java/me/melontini/commander/impl/util/mappings/MinecraftDownloader.java
··· 1 + package me.melontini.commander.impl.util.mappings; 2 + 3 + import static me.melontini.dark_matter.api.base.util.Exceptions.runAsResult; 4 + import static me.melontini.dark_matter.api.base.util.Exceptions.throwNow; 5 + 6 + import com.google.gson.JsonElement; 7 + import com.google.gson.JsonObject; 8 + import com.google.gson.JsonParser; 9 + import java.io.IOException; 10 + import java.io.InputStreamReader; 11 + import java.io.OutputStreamWriter; 12 + import java.net.URL; 13 + import java.nio.charset.StandardCharsets; 14 + import java.nio.file.Files; 15 + import java.nio.file.Path; 16 + import java.util.Objects; 17 + import java.util.zip.DeflaterOutputStream; 18 + import lombok.experimental.UtilityClass; 19 + import lombok.extern.log4j.Log4j2; 20 + import me.melontini.commander.impl.Commander; 21 + import me.melontini.dark_matter.api.base.util.Exceptions; 22 + import net.fabricmc.mappingio.MappingReader; 23 + import net.fabricmc.mappingio.MappingWriter; 24 + import net.fabricmc.mappingio.format.MappingFormat; 25 + import net.fabricmc.mappingio.tree.MemoryMappingTree; 26 + 27 + @Log4j2 28 + @UtilityClass 29 + public class MinecraftDownloader { 30 + 31 + private static final URL MANIFEST = 32 + url("https://launchermeta.mojang.com/mc/game/version_manifest_v2.json"); 33 + private static final String LICENSE = 34 + """ 35 + (c) 2020 Microsoft Corporation. 36 + These mappings are provided "as-is" and you bear the risk of using them. 37 + You may copy and use the mappings for development purposes, but you may not redistribute the mappings complete and unmodified. 38 + Microsoft makes no warranties, express or implied, with respect to the mappings provided here. 39 + Use and modification of this document or the source code (in any form) of Minecraft: Java Edition is governed by the Minecraft End User License Agreement available at https://account.mojang.com/documents/minecraft_eula. 40 + """; 41 + 42 + static URL url(String s) { 43 + return Exceptions.process(s, URL::new); 44 + } 45 + 46 + public static void downloadMappings() { 47 + Path mappings = Commander.COMMANDER_PATH.resolve("mappings/server_mappings.bin"); 48 + if (Files.exists(mappings)) return; 49 + var url = url(getManifest() 50 + .getAsJsonObject("downloads") 51 + .getAsJsonObject("server_mappings") 52 + .get("url") 53 + .getAsString()); 54 + 55 + var parent = Objects.requireNonNull(mappings.getParent()); 56 + runAsResult(IOException.class, () -> Files.createDirectories(parent)) 57 + .ifErrPresent( 58 + e -> throwNow(new RuntimeException("Failed to create mapping directories!", e))); 59 + 60 + log.info("Downloading {}...", Objects.requireNonNull(mappings.getFileName()).toString()); 61 + runAsResult(IOException.class, () -> Files.writeString(parent.resolve("LICENSE.txt"), LICENSE)) 62 + .ifErrPresent(e -> { 63 + log.error("Failed to write license to file! Logging instead...", e); 64 + log.info(LICENSE); 65 + }); 66 + 67 + MemoryMappingTree tree = new MemoryMappingTree(); 68 + try (var reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) { 69 + MappingReader.read(reader, tree); 70 + } catch (IOException e) { 71 + throw new RuntimeException("Failed to read downloaded mojang mappings! Corrupt download?", e); 72 + } 73 + 74 + try (var output = new OutputStreamWriter( 75 + new DeflaterOutputStream(Files.newOutputStream(mappings)), StandardCharsets.UTF_8)) { 76 + tree.accept(MappingWriter.create(output, MappingFormat.TSRG_2_FILE)); 77 + } catch (IOException e) { 78 + throw new RuntimeException("Failed to write compressed mappings!", e); 79 + } 80 + } 81 + 82 + public static JsonObject getManifest() { 83 + var o = downloadObject(MANIFEST); 84 + for (JsonElement versions : o.getAsJsonArray("versions")) { 85 + if (Commander.MINECRAFT_VERSION.equals( 86 + versions.getAsJsonObject().get("id").getAsString())) { 87 + return downloadObject(url(versions.getAsJsonObject().get("url").getAsString())); 88 + } 89 + } 90 + throw new IllegalStateException("Unknown version '%s'".formatted(Commander.MINECRAFT_VERSION)); 91 + } 92 + 93 + private static JsonObject downloadObject(URL url) { 94 + try (var reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) { 95 + return JsonParser.parseReader(reader).getAsJsonObject(); 96 + } catch (IOException e) { 97 + throw new RuntimeException("Failed to download JSON object from %s".formatted(url)); 98 + } 99 + } 100 + }
src/main/resources/assets/commander/icon.png

This is a binary file and will not be displayed.

+3
src/main/resources/commander.accesswidener
··· 1 + accessWidener v2 named 2 + 3 + accessible class net/minecraft/server/command/ExecuteCommand$Condition
+19
src/main/resources/commander.mixins.json
··· 1 + { 2 + "required": true, 3 + "minVersion": "0.8", 4 + "package": "me.melontini.commander.impl.mixin", 5 + "compatibilityLevel": "JAVA_17", 6 + "mixins": [ 7 + "AdvancementRewardsMixin", 8 + "ExecuteCommandMixin", 9 + "NbtCompoundAccessor", 10 + "ScoreboardCommandMixin", 11 + "evalex.EvaluationValueMixin", 12 + "evalex.ExpressionMixin" 13 + ], 14 + "client": [ 15 + ], 16 + "injectors": { 17 + "defaultRequire": 1 18 + } 19 + }
+31
src/main/resources/fabric.mod.json
··· 1 + { 2 + "schemaVersion": 1, 3 + "id": "commander", 4 + "version": "${version}", 5 + "name": "Commander", 6 + "description": "A data pack event system", 7 + "authors": [ 8 + "melontini" 9 + ], 10 + "contact": { 11 + "repo": "https://github.com/constellation-mc/commander", 12 + "issues": "https://github.com/constellation-mc/commander/issues" 13 + }, 14 + "license": "MIT", 15 + "icon": "assets/commander/icon.png", 16 + "environment": "*", 17 + "entrypoints": { 18 + "main": [ 19 + "me.melontini.commander.impl.Commander::init" 20 + ] 21 + }, 22 + "mixins": [ 23 + "commander.mixins.json" 24 + ], 25 + "accessWidener": "commander.accesswidener", 26 + "depends": { 27 + "fabricloader": ">=${loader_version}", 28 + "fabric": "*", 29 + "minecraft": ">=${minecraft_version}" 30 + } 31 + }
+77
src/testmod/java/me/melontini/commander/test/ConvertersTest.java
··· 1 + package me.melontini.commander.test; 2 + 3 + import com.ezylang.evalex.data.EvaluationValue; 4 + import com.ezylang.evalex.data.types.*; 5 + import com.google.gson.JsonArray; 6 + import com.google.gson.JsonObject; 7 + import com.google.gson.JsonPrimitive; 8 + import java.math.BigDecimal; 9 + import java.util.Map; 10 + import java.util.Optional; 11 + import me.melontini.commander.impl.expression.extensions.ReflectiveValueConverter; 12 + import me.melontini.commander.impl.expression.extensions.convert.nbt.NbtCompoundStruct; 13 + import me.melontini.handytests.server.ServerTestContext; 14 + import me.melontini.handytests.server.ServerTestEntrypoint; 15 + import me.melontini.handytests.util.runner.HandyTest; 16 + import net.minecraft.nbt.*; 17 + import net.minecraft.util.Identifier; 18 + import org.assertj.core.api.Assertions; 19 + 20 + public class ConvertersTest implements ServerTestEntrypoint { 21 + 22 + @HandyTest 23 + void testOptionalConvert(ServerTestContext context) { 24 + Assertions.assertThat(ReflectiveValueConverter.convert(Optional.empty())) 25 + .isEqualTo(NullValue.of()); 26 + 27 + Assertions.assertThat(ReflectiveValueConverter.convert(Optional.of(123))) 28 + .isEqualTo(NumberValue.of(BigDecimal.valueOf(123))); 29 + } 30 + 31 + @HandyTest 32 + void testIdentifierConvert(ServerTestContext context) { 33 + Assertions.assertThat(ReflectiveValueConverter.convert(Identifier.of("cmd", "test_id"))) 34 + .isEqualTo(StringValue.of("cmd:test_id")); 35 + 36 + Assertions.assertThat(ReflectiveValueConverter.convert(Identifier.of("test_id"))) 37 + .isEqualTo(StringValue.of("minecraft:test_id")); 38 + } 39 + 40 + @HandyTest 41 + void testNbtConvert(ServerTestContext context) { 42 + Assertions.assertThat(ReflectiveValueConverter.convert(new NbtCompound())) 43 + .isInstanceOf(StructureValue.class) 44 + .isEqualTo(StructureValue.of( 45 + (Map<String, EvaluationValue>) (Object) new NbtCompoundStruct(new NbtCompound()))); 46 + 47 + Assertions.assertThat(ReflectiveValueConverter.convert(new NbtList())) 48 + .isInstanceOf(ArrayValue.class); 49 + 50 + Assertions.assertThat(ReflectiveValueConverter.convert(NbtString.of("Hello World!"))) 51 + .isEqualTo(StringValue.of("Hello World!")); 52 + 53 + Assertions.assertThat(ReflectiveValueConverter.convert(NbtInt.of(123))) 54 + .isEqualTo(NumberValue.of(BigDecimal.valueOf(123))); 55 + 56 + Assertions.assertThat(ReflectiveValueConverter.convert(NbtDouble.of(123.4587))) 57 + .isEqualTo(NumberValue.of(BigDecimal.valueOf(123.4587))); 58 + } 59 + 60 + @HandyTest 61 + void testGsonConvert(ServerTestContext context) { 62 + Assertions.assertThat(ReflectiveValueConverter.convert(new JsonObject())) 63 + .isInstanceOf(StructureValue.class); 64 + 65 + Assertions.assertThat(ReflectiveValueConverter.convert(new JsonArray())) 66 + .isInstanceOf(ArrayValue.class); 67 + 68 + Assertions.assertThat(ReflectiveValueConverter.convert(new JsonPrimitive("Hello World!"))) 69 + .isEqualTo(StringValue.of("Hello World!")); 70 + 71 + Assertions.assertThat(ReflectiveValueConverter.convert(new JsonPrimitive(123))) 72 + .isEqualTo(NumberValue.of(BigDecimal.valueOf(123))); 73 + 74 + Assertions.assertThat(ReflectiveValueConverter.convert(new JsonPrimitive(123.4587))) 75 + .isEqualTo(NumberValue.of(BigDecimal.valueOf(123.4587))); 76 + } 77 + }
+35
src/testmod/java/me/melontini/commander/test/CustomEventsTest.java
··· 1 + package me.melontini.commander.test; 2 + 3 + import static me.melontini.commander.test.ExpressionTest.*; 4 + 5 + import lombok.extern.log4j.Log4j2; 6 + import me.melontini.commander.api.event.EventType; 7 + import me.melontini.commander.api.util.EventExecutors; 8 + import me.melontini.commander.impl.Commander; 9 + import me.melontini.dark_matter.api.data.codecs.ExtraCodecs; 10 + import me.melontini.handytests.server.ServerTestContext; 11 + import me.melontini.handytests.server.ServerTestEntrypoint; 12 + import me.melontini.handytests.util.runner.HandyTest; 13 + import net.fabricmc.api.ModInitializer; 14 + import net.minecraft.util.ActionResult; 15 + import org.assertj.core.api.Assertions; 16 + 17 + @Log4j2 18 + public class CustomEventsTest implements ModInitializer, ServerTestEntrypoint { 19 + 20 + public static final EventType CUSTOM = EventType.builder() 21 + .cancelTerm(ExtraCodecs.enumCodec(ActionResult.class)) 22 + .build(Commander.id("test")); 23 + 24 + @HandyTest // custom_events_test.json 25 + void testCustomEventType(ServerTestContext context) { 26 + Assertions.assertThat(EventExecutors.runActionResult( 27 + CUSTOM, context.context().getOverworld(), () -> emptyContext(context))) 28 + .isEqualTo(ActionResult.FAIL); // Default is pass, the JSON cancels the event with fail. 29 + } 30 + 31 + @Override 32 + public void onInitialize() { 33 + // NOOP 34 + } 35 + }
+20
src/testmod/java/me/melontini/commander/test/ExpressionLibraryTest.java
··· 1 + package me.melontini.commander.test; 2 + 3 + import me.melontini.commander.api.expression.Expression; 4 + import me.melontini.handytests.server.ServerTestContext; 5 + import me.melontini.handytests.server.ServerTestEntrypoint; 6 + import me.melontini.handytests.util.runner.HandyTest; 7 + import org.assertj.core.api.Assertions; 8 + 9 + public class ExpressionLibraryTest implements ServerTestEntrypoint { 10 + 11 + @HandyTest 12 + void testLibraryInExpressions(ServerTestContext context) { 13 + var r = context.submitAndWait(server -> { 14 + server.getCommandManager().executeWithPrefix(server.getCommandSource(), "/time set day"); 15 + return ExpressionTest.parse("library.cmd:is_day").eval(ExpressionTest.emptyContext(context)); 16 + }); 17 + 18 + Assertions.assertThat(r).isEqualTo(Expression.Result.convert(true)); 19 + } 20 + }
+64
src/testmod/java/me/melontini/commander/test/ExpressionTest.java
··· 1 + package me.melontini.commander.test; 2 + 3 + import com.ezylang.evalex.data.EvaluationValue; 4 + import com.ezylang.evalex.data.types.NumberValue; 5 + import com.ezylang.evalex.data.types.StringValue; 6 + import com.ezylang.evalex.data.types.StructureValue; 7 + import java.math.BigDecimal; 8 + import java.util.Map; 9 + import me.melontini.commander.api.expression.Expression; 10 + import me.melontini.commander.impl.util.loot.LootUtil; 11 + import me.melontini.handytests.server.ServerTestContext; 12 + import me.melontini.handytests.server.ServerTestEntrypoint; 13 + import me.melontini.handytests.util.runner.HandyTest; 14 + import net.minecraft.loot.context.LootContext; 15 + import net.minecraft.loot.context.LootContextParameterSet; 16 + import net.minecraft.loot.context.LootContextTypes; 17 + import org.assertj.core.api.Assertions; 18 + 19 + public class ExpressionTest implements ServerTestEntrypoint { 20 + 21 + @HandyTest 22 + void testNewOperators(ServerTestContext context) { 23 + var lootContext = emptyContext(context); 24 + 25 + Map<String, EvaluationValue> map = 26 + Map.of("x", NumberValue.of(BigDecimal.valueOf(23)), "y", StringValue.of("Hello")); 27 + 28 + Assertions.assertThat( 29 + parse("struct?.x ? 42").eval(lootContext, Map.of("struct", StructureValue.of(map)))) 30 + .isEqualTo(Expression.Result.convert(BigDecimal.valueOf(23))); 31 + 32 + Assertions.assertThat( 33 + parse("struct?.u").eval(lootContext, Map.of("struct", StructureValue.of(map)))) 34 + .matches(Expression.Result::isNullValue, "result is null"); 35 + 36 + Assertions.assertThat( 37 + parse("null ? 42").eval(lootContext, Map.of("struct", StructureValue.of(map)))) 38 + .isEqualTo(Expression.Result.convert(BigDecimal.valueOf(42))); 39 + 40 + Assertions.assertThat( 41 + parse("struct?.u ? 42").eval(lootContext, Map.of("struct", StructureValue.of(map)))) 42 + .isEqualTo(Expression.Result.convert(BigDecimal.valueOf(42))); 43 + } 44 + 45 + @HandyTest 46 + void testLootContextAccess(ServerTestContext context) { 47 + var lootContext = emptyContext(context); 48 + 49 + Assertions.assertThat(parse("level.dimension.location").eval(lootContext)) 50 + .isEqualTo(Expression.Result.convert("minecraft:overworld")); 51 + } 52 + 53 + public static LootContext emptyContext(ServerTestContext context) { 54 + return LootUtil.build(new LootContextParameterSet.Builder(context.server().getOverworld()) 55 + .build(LootContextTypes.EMPTY)); 56 + } 57 + 58 + public static Expression parse(String expression) { 59 + var result = Expression.parse(expression); 60 + if (result.error().isPresent()) 61 + throw new RuntimeException(result.error().get().message()); 62 + return result.result().orElseThrow(); 63 + } 64 + }
+77
src/testmod/java/me/melontini/commander/test/VirtualFieldsTest.java
··· 1 + package me.melontini.commander.test; 2 + 3 + import static me.melontini.commander.test.ExpressionTest.emptyContext; 4 + import static me.melontini.commander.test.ExpressionTest.parse; 5 + 6 + import com.ezylang.evalex.data.EvaluationValue; 7 + import com.ezylang.evalex.data.types.BooleanValue; 8 + import com.ezylang.evalex.data.types.DataAccessorValue; 9 + import com.ezylang.evalex.data.types.StructureValue; 10 + import java.util.Map; 11 + import me.melontini.commander.impl.expression.extensions.ReflectiveValueConverter; 12 + import me.melontini.commander.impl.expression.extensions.convert.components.ComponentStruct; 13 + import me.melontini.commander.impl.expression.extensions.convert.nbt.NbtCompoundStruct; 14 + import me.melontini.handytests.server.ServerTestContext; 15 + import me.melontini.handytests.server.ServerTestEntrypoint; 16 + import me.melontini.handytests.util.runner.HandyTest; 17 + import net.minecraft.block.Blocks; 18 + import net.minecraft.block.entity.BlockEntity; 19 + import net.minecraft.block.entity.BlockEntityType; 20 + import net.minecraft.entity.Entity; 21 + import net.minecraft.entity.EntityType; 22 + import net.minecraft.item.ItemStack; 23 + import net.minecraft.item.Items; 24 + import net.minecraft.nbt.NbtCompound; 25 + import net.minecraft.predicate.NbtPredicate; 26 + import net.minecraft.util.math.BlockPos; 27 + import org.assertj.core.api.Assertions; 28 + 29 + public class VirtualFieldsTest implements ServerTestEntrypoint { 30 + 31 + @HandyTest 32 + void testStackFields(ServerTestContext context) { 33 + ItemStack stack = Items.ACACIA_LOG.getDefaultStack(); 34 + 35 + var lootContext = emptyContext(context); 36 + Assertions.assertThat(parse("stack.nbt").eval(lootContext, Map.of("stack", stack))) 37 + .isInstanceOf(StructureValue.class) 38 + .isEqualTo(StructureValue.of((Map<String, EvaluationValue>) (Object) new NbtCompoundStruct( 39 + (NbtCompound) stack.encodeAllowEmpty(context.server().getRegistryManager())))); 40 + 41 + Assertions.assertThat(parse("stack.components").eval(lootContext, Map.of("stack", stack))) 42 + .isInstanceOf(DataAccessorValue.class) 43 + .isEqualTo(ReflectiveValueConverter.convert(new ComponentStruct(stack.getComponents()))); 44 + } 45 + 46 + @HandyTest 47 + void testEntityFields(ServerTestContext context) { 48 + Entity entity = EntityType.SLIME.create(context.context().getOverworld()); 49 + 50 + var lootContext = emptyContext(context); 51 + Assertions.assertThat(parse("entity.nbt").eval(lootContext, Map.of("entity", entity))) 52 + .isInstanceOf(StructureValue.class) 53 + .isEqualTo(StructureValue.of((Map<String, EvaluationValue>) 54 + (Object) new NbtCompoundStruct(NbtPredicate.entityToNbt(entity)))); 55 + } 56 + 57 + @HandyTest 58 + void testBlockEntityFields(ServerTestContext context) { 59 + BlockEntity entity = BlockEntityType.CAMPFIRE.instantiate( 60 + new BlockPos(100, 60, 100), Blocks.CAMPFIRE.getDefaultState()); 61 + 62 + var lootContext = emptyContext(context); 63 + Assertions.assertThat(parse("be.nbt").eval(lootContext, Map.of("be", entity))) 64 + .isInstanceOf(StructureValue.class) 65 + .isEqualTo(StructureValue.of((Map<String, EvaluationValue>) (Object) new NbtCompoundStruct( 66 + entity.createNbtWithIdentifyingData(context.server().getRegistryManager())))); 67 + } 68 + 69 + @HandyTest 70 + void testBlockStateFields(ServerTestContext context) { 71 + var lootContext = emptyContext(context); 72 + 73 + Assertions.assertThat(parse("state.properties.lit") 74 + .eval(lootContext, Map.of("state", Blocks.CAMPFIRE.getDefaultState()))) 75 + .isEqualTo(BooleanValue.of(true)); 76 + } 77 + }
+13
src/testmod/resources/data/commander/commander/events/test/custom_events_test.json
··· 1 + { 2 + "event": "commander:test", 3 + "commands": [ 4 + { 5 + "type": "commander:print", 6 + "text": "Hello from JSON!" 7 + }, 8 + { 9 + "type": "commander:cancel", 10 + "value": "fail" 11 + } 12 + ] 13 + }
+7
src/testmod/resources/data/commander/commander/expression_library/test_shelf.json
··· 1 + { 2 + "replace": false, 3 + "expressions": { 4 + "cmd:is_day": "level.isDay", 5 + "cmd:day_time": "strFormat('%02.0f:%02.0f', floor((level.getDayTime / 1000 + 8) % 24), floor(60 * (level.getDayTime % 1000) / 1000))" 6 + } 7 + }
+18
src/testmod/resources/fabric.mod.json
··· 1 + { 2 + "schemaVersion": 1, 3 + "id": "commander-testmod", 4 + "version": "1.0.0", 5 + "license": "MIT", 6 + "entrypoints": { 7 + "main": [ 8 + "me.melontini.commander.test.CustomEventsTest" 9 + ], 10 + "handy:server_test": [ 11 + "me.melontini.commander.test.ExpressionLibraryTest", 12 + "me.melontini.commander.test.ConvertersTest", 13 + "me.melontini.commander.test.ExpressionTest", 14 + "me.melontini.commander.test.VirtualFieldsTest", 15 + "me.melontini.commander.test.CustomEventsTest" 16 + ] 17 + } 18 + }