+24
.github/dependabot.yml
+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
+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: 17
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: 17
18
+
server_task: runTestServer
-63
.github/workflows/deploy.yml
-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
+11
.github/workflows/project.yml
+11
.github/workflows/project.yml
+23
.github/workflows/publish.yml
+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: 17
22
+
version_type: ${{ inputs.version_type }}
23
+
secrets: inherit
+121
-3
.gitignore
+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
+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
+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
-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
+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
+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
+21
README.md
···
1
+
## Commander
2
+
3
+
[](https://modrinth.com/mod/cmd)
4
+
[](https://www.curseforge.com/minecraft/mc-mods/cmd-project)
5
+

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
+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
bun.lockb
This is a binary file and will not be displayed.
-57
docs/.vitepress/config.mts
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-29
docs/index.md
···
1
-
---
2
-
comment: false
3
-
---
4
-
# Commander <Badge type="warning" text="beta" />
5
-
6
-
<div class="badge-holder">
7
-
8
-
[](https://modrinth.com/mod/cmd)
9
-
[](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
-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
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
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
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
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
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
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
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
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
docs/public/favicon.png
This is a binary file and will not be displayed.
docs/public/logo.png
docs/public/logo.png
This is a binary file and will not be displayed.
-20
docs/public/robots.txt
-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
-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
-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
-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
-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
-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
-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
-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
-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
-
[](https://modrinth.com/mod/cmd)
9
-
[](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
+24
gradle/libs.versions.toml
···
1
+
[versions]
2
+
dark-matter = "4.1.1-1.20.1-build.90"
3
+
4
+
[libraries]
5
+
fabric-loader = { group = "net.fabricmc", name = "fabric-loader", version = "0.16.9" }
6
+
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version = "0.92.1+1.20.1" }
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.20.1-build.16" }
15
+
assertj-core = { module = "org.assertj:assertj-core", version = "3.26.3" }
16
+
mockito-core = { module = "org.mockito:mockito-core", version = "5.14.2" }
17
+
18
+
[plugins]
19
+
fabric-loom = { id = "fabric-loom", version = "1.7.3" }
20
+
lombok = { id = "io.freefair.lombok", version = "8.11"}
21
+
shadow = { id = "com.github.johnrengelman.shadow", version = "8.1.1" }
22
+
spotbugs-base = { id = "com.github.spotbugs-base", version = "6.0.27" }
23
+
spotless = { id = "com.diffplug.spotless", version = "6.25.0" }
24
+
modmuss50-publish = { id = "me.modmuss50.mod-publish-plugin", version = "0.7.4" }
gradle/wrapper/gradle-wrapper.jar
gradle/wrapper/gradle-wrapper.jar
This is a binary file and will not be displayed.
+7
gradle/wrapper/gradle-wrapper.properties
+7
gradle/wrapper/gradle-wrapper.properties
+12
gradle.properties
+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.20.1
8
+
yarn_mappings=1.20.1+build.10
9
+
# Mod Properties
10
+
mod_version=0.9.0
11
+
maven_group=me.melontini
12
+
archives_base_name=commander
+249
gradlew
+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
+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
+1
lombok.config
+1
lombok.config
···
1
+
lombok.accessors.fluent=true
-8
package.json
-8
package.json
+9
settings.gradle
+9
settings.gradle
+17
spotbugs.xml
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+
}
+176
src/main/java/me/melontini/commander/impl/Commander.java
+176
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.codecs.ExtraCodecs;
34
+
import me.melontini.dark_matter.api.data.loading.ServerReloadersEvent;
35
+
import me.melontini.dark_matter.api.minecraft.util.TextUtil;
36
+
import net.fabricmc.fabric.api.attachment.v1.AttachmentRegistry;
37
+
import net.fabricmc.fabric.api.attachment.v1.AttachmentType;
38
+
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
39
+
import net.minecraft.loot.condition.LootConditionType;
40
+
import net.minecraft.loot.provider.number.LootNumberProviderType;
41
+
import net.minecraft.loot.provider.number.LootNumberProviderTypes;
42
+
import net.minecraft.nbt.NbtCompound;
43
+
import net.minecraft.registry.Registries;
44
+
import net.minecraft.registry.Registry;
45
+
import net.minecraft.util.Identifier;
46
+
import net.minecraft.util.Util;
47
+
48
+
@SuppressWarnings("UnstableApiUsage")
49
+
@Accessors(fluent = true)
50
+
@Log4j2
51
+
public class Commander {
52
+
53
+
public static final LootNumberProviderType ARITHMETICA_PROVIDER =
54
+
LootNumberProviderTypes.register(
55
+
"commander:arithmetica",
56
+
ExtraCodecs.toJsonSerializer(ArithmeticaLootNumberProvider.CODEC.codec()));
57
+
public static final LootConditionType EXPRESSION_CONDITION = Registry.register(
58
+
Registries.LOOT_CONDITION_TYPE,
59
+
id("expression"),
60
+
new LootConditionType(ExtraCodecs.toJsonSerializer(ExpressionLootCondition.CODEC.codec())));
61
+
62
+
private static final Path BASE_PATH =
63
+
Path.of(System.getProperty("user.home")).resolve(".commander");
64
+
public static final String MINECRAFT_VERSION = getVersion();
65
+
public static final Path COMMANDER_PATH = BASE_PATH.resolve(MINECRAFT_VERSION);
66
+
67
+
public static final AttachmentType<NbtCompound> DATA_ATTACHMENT =
68
+
AttachmentRegistry.<NbtCompound>builder()
69
+
.initializer(NbtCompound::new)
70
+
.persistent(NbtCodecs.COMPOUND_CODEC)
71
+
.buildAndRegister(id("persistent"));
72
+
73
+
public static final DynamicCommandExceptionType EXPRESSION_EXCEPTION =
74
+
new DynamicCommandExceptionType(object -> TextUtil.literal("Failed to evaluate: " + object));
75
+
76
+
@Getter
77
+
private AmbiguousRemapper mappingKeeper;
78
+
79
+
public static Identifier id(String path) {
80
+
return new Identifier("commander", path);
81
+
}
82
+
83
+
private static Supplier<Commander> instance = () -> {
84
+
throw new NullPointerException("Commander instance requested too early!");
85
+
};
86
+
87
+
public static void init() {
88
+
var cmd = new Commander();
89
+
cmd.onInitialize();
90
+
instance = () -> cmd;
91
+
}
92
+
93
+
public static Commander get() {
94
+
return instance.get();
95
+
}
96
+
97
+
public void onInitialize() {
98
+
if (!Files.exists(COMMANDER_PATH)) {
99
+
Exceptions.run(() -> Files.createDirectories(COMMANDER_PATH));
100
+
try {
101
+
// Some users don't like junk in their home folder and windows is very special.
102
+
if (BASE_PATH.getFileSystem().supportedFileAttributeViews().contains("dos"))
103
+
Files.setAttribute(BASE_PATH, "dos:hidden", Boolean.TRUE, LinkOption.NOFOLLOW_LINKS);
104
+
} catch (IOException ignored) {
105
+
log.warn("Failed to hide the .commander folder");
106
+
}
107
+
}
108
+
109
+
ServerReloadersEvent.EVENT.register(context -> {
110
+
this.resetCaches();
111
+
context.register(new ExpressionLibraryLoader());
112
+
context.register(new DynamicEventManager());
113
+
});
114
+
115
+
ServerLifecycleEvents.SERVER_STOPPING.register(server -> this.resetCaches());
116
+
117
+
EvalUtils.init();
118
+
this.loadMappings();
119
+
120
+
// Init built-ins
121
+
BuiltInEvents.init();
122
+
BuiltInCommands.init();
123
+
BuiltInSelectors.init();
124
+
125
+
// Register vanilla parameter types
126
+
LootContextParameterRegistry.register(
127
+
ORIGIN, TOOL,
128
+
THIS_ENTITY, LAST_DAMAGE_PLAYER,
129
+
KILLER_ENTITY, DIRECT_KILLER_ENTITY,
130
+
DAMAGE_SOURCE, EXPLOSION_RADIUS,
131
+
BLOCK_STATE, BLOCK_ENTITY);
132
+
}
133
+
134
+
private void resetCaches() {
135
+
EvalUtils.resetCache();
136
+
RegistryAccessStruct.resetCache();
137
+
}
138
+
139
+
private void loadMappings() {
140
+
if (MappingKeeper.NAMESPACE.equals("mojang")) {
141
+
mappingKeeper = (cls, name) -> name; // Nothing to remap.
142
+
return;
143
+
}
144
+
145
+
var offTarget = supplyAsync(MappingKeeper::loadOffTarget, Util.getMainWorkerExecutor());
146
+
var offMojmap = runAsync(MinecraftDownloader::downloadMappings, Util.getMainWorkerExecutor())
147
+
.thenApplyAsync(unused -> MappingKeeper.loadOffMojmap(), Util.getMainWorkerExecutor());
148
+
149
+
mappingKeeper = Exceptions.<AmbiguousRemapper>supplyAsResult(() ->
150
+
new MappingKeeper(MappingKeeper.loadMojmapTarget(offMojmap.join(), offTarget.join())))
151
+
.ifErrPresent(t -> log.error(
152
+
"Failed to download and prepare mappings! Data access remapping will not work!!!",
153
+
Exceptions.unwrap(t)))
154
+
.flatmapErr(t -> Result.ok((cls, name) -> name))
155
+
.value()
156
+
.orElseThrow();
157
+
}
158
+
159
+
// Returns the current MC version parsed from included version.json
160
+
private static String getVersion() {
161
+
return Exceptions.supplyAsResult(() -> {
162
+
try (var stream = new InputStreamReader(
163
+
MinecraftDownloader.class.getResourceAsStream("/version.json"),
164
+
StandardCharsets.UTF_8)) {
165
+
JsonObject o = JsonParser.parseReader(stream).getAsJsonObject();
166
+
return o.getAsJsonPrimitive("id").getAsString();
167
+
}
168
+
})
169
+
.ifErrPresent(e -> {
170
+
throw new IllegalStateException(
171
+
"Failed to read 'version.json' included in the Minecraft jar!");
172
+
})
173
+
.value()
174
+
.orElseThrow();
175
+
}
176
+
}
+19
src/main/java/me/melontini/commander/impl/builtin/BrigadierCommands.java
+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
+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
+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
+
}
+99
src/main/java/me/melontini/commander/impl/builtin/BuiltInSelectors.java
+99
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("killer_entity"),
43
+
context -> forEntity(context.requireParameter(LootContextParameters.KILLER_ENTITY)));
44
+
public static final Selector DIRECT_KILLER_ENTITY = Selector.register(
45
+
mc("direct_killer_entity"),
46
+
context -> forEntity(context.requireParameter(LootContextParameters.DIRECT_KILLER_ENTITY)));
47
+
public static final Selector LAST_DAMAGE_PLAYER = Selector.register(
48
+
mc("last_damage_player"),
49
+
context -> forEntity(context.requireParameter(LootContextParameters.LAST_DAMAGE_PLAYER)));
50
+
public static final Selector BLOCK_ENTITY = Selector.register(mc("block_entity"), context -> {
51
+
var be = context.requireParameter(LootContextParameters.BLOCK_ENTITY);
52
+
return new ServerCommandSource(
53
+
context.getWorld().getServer(),
54
+
Vec3d.ofCenter(be.getPos()),
55
+
Vec2f.ZERO,
56
+
(ServerWorld) be.getWorld(),
57
+
4,
58
+
"BlockEntity",
59
+
TextUtil.literal("BlockEntity"),
60
+
context.getWorld().getServer(),
61
+
null);
62
+
});
63
+
64
+
public static final Selector DAMAGE_SOURCE_SOURCE =
65
+
Selector.register(id("damage_source/source"), context -> {
66
+
var s = context.requireParameter(LootContextParameters.DAMAGE_SOURCE).getSource();
67
+
return s != null ? forEntity(s) : null;
68
+
});
69
+
public static final Selector DAMAGE_SOURCE_ATTACKER =
70
+
Selector.register(id("damage_source/attacker"), context -> {
71
+
var s = context.requireParameter(LootContextParameters.DAMAGE_SOURCE).getAttacker();
72
+
return s != null ? forEntity(s) : null;
73
+
});
74
+
75
+
public static final Selector RANDOM_PLAYER = Selector.register(id("random_player"), context -> {
76
+
var l = context.getWorld().getServer().getPlayerManager().getPlayerList();
77
+
if (l == null || l.isEmpty()) return null;
78
+
return forEntity(Utilities.pickAtRandom(l));
79
+
});
80
+
81
+
public static void init() {}
82
+
83
+
public static Identifier mc(String string) {
84
+
return new Identifier("minecraft", string);
85
+
}
86
+
87
+
public static ServerCommandSource forEntity(Entity entity) {
88
+
return new ServerCommandSource(
89
+
entity.getWorld().getServer(),
90
+
entity.getPos(),
91
+
new Vec2f(entity.getPitch(), entity.getYaw()),
92
+
(ServerWorld) entity.getWorld(),
93
+
4,
94
+
entity.getEntityName(),
95
+
entity.getName(),
96
+
entity.getWorld().getServer(),
97
+
entity);
98
+
}
99
+
}
+63
src/main/java/me/melontini/commander/impl/builtin/brigadier/ArithmeticaCommand.java
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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(KILLER_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_KILLER_ENTITY, source.getAttacker())
124
+
.addOptional(KILLER_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
+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
+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
+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
+
}
+62
src/main/java/me/melontini/commander/impl/command/ConditionedCommand.java
+62
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 me.melontini.commander.impl.util.MagicCodecs;
12
+
import net.minecraft.loot.condition.LootCondition;
13
+
14
+
public record ConditionedCommand(Optional<LootCondition> condition, Command other)
15
+
implements Command.Conditioned {
16
+
17
+
public static final MapCodec<? extends Command.Conditioned> CODEC =
18
+
new MapCodec<ConditionedCommand>() {
19
+
@Override
20
+
public <T> RecordBuilder<T> encode(
21
+
ConditionedCommand input, DynamicOps<T> ops, RecordBuilder<T> prefix) {
22
+
var r = ((MapCodecCodec<Command>) CommandTypes.CODEC)
23
+
.codec()
24
+
.encode(input.other(), ops, prefix);
25
+
input
26
+
.condition()
27
+
.map(condition1 -> MagicCodecs.LOOT_CONDITION.encodeStart(ops, condition1))
28
+
.ifPresent(tDataResult -> r.add("condition", tDataResult));
29
+
return r;
30
+
}
31
+
32
+
@Override
33
+
public <T> DataResult<ConditionedCommand> decode(DynamicOps<T> ops, MapLike<T> input) {
34
+
var r = ((MapCodecCodec<Command>) CommandTypes.CODEC).codec().decode(ops, input);
35
+
T condition = input.get("condition");
36
+
if (condition == null)
37
+
return r.map(command -> new ConditionedCommand(Optional.empty(), command));
38
+
return r.map(command -> MagicCodecs.LOOT_CONDITION
39
+
.parse(ops, condition)
40
+
.map(condition1 -> new ConditionedCommand(Optional.of(condition1), command)))
41
+
.flatMap(Function.identity());
42
+
}
43
+
44
+
@Override
45
+
public <T> Stream<T> keys(DynamicOps<T> ops) {
46
+
return Stream.of("condition").map(ops::createString);
47
+
}
48
+
};
49
+
50
+
@Override
51
+
public boolean execute(EventContext context) {
52
+
if (!this.condition
53
+
.map(condition1 -> condition1.test(context.lootContext()))
54
+
.orElse(true)) return false;
55
+
return other().execute(context);
56
+
}
57
+
58
+
@Override
59
+
public DataResult<Void> validate(EventType type) {
60
+
return this.other().validate(type);
61
+
}
62
+
}
+48
src/main/java/me/melontini/commander/impl/command/ConditionedSelector.java
+48
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.MagicCodecs;
12
+
import me.melontini.commander.impl.util.loot.LootUtil;
13
+
import me.melontini.dark_matter.api.data.codecs.ExtraCodecs;
14
+
import net.minecraft.loot.condition.LootCondition;
15
+
import net.minecraft.loot.context.LootContextParameterSet;
16
+
import net.minecraft.loot.context.LootContextParameters;
17
+
import net.minecraft.loot.context.LootContextTypes;
18
+
import net.minecraft.server.command.ServerCommandSource;
19
+
20
+
public record ConditionedSelector(Optional<LootCondition> condition, Selector other)
21
+
implements Selector.Conditioned {
22
+
23
+
public static final Codec<? extends Selector.Conditioned> CODEC = ExtraCodecs.either(
24
+
RecordCodecBuilder.<ConditionedSelector>create(data -> data.group(
25
+
ExtraCodecs.optional("condition", MagicCodecs.LOOT_CONDITION)
26
+
.forGetter(ConditionedSelector::condition),
27
+
SelectorTypes.CODEC.fieldOf("value").forGetter(ConditionedSelector::other))
28
+
.apply(data, ConditionedSelector::new)),
29
+
SelectorTypes.CODEC)
30
+
.xmap(
31
+
e -> e.map(
32
+
Function.identity(), selector -> new ConditionedSelector(Optional.empty(), selector)),
33
+
Either::left);
34
+
35
+
@Override
36
+
public Optional<ServerCommandSource> select(EventContext context) {
37
+
var source = other.select(context.lootContext());
38
+
if (source == null) return Optional.empty();
39
+
return condition
40
+
.filter(lootCondition -> !lootCondition.test(LootUtil.build(
41
+
new LootContextParameterSet.Builder(context.lootContext().getWorld())
42
+
.add(LootContextParameters.ORIGIN, source.getPosition())
43
+
.addOptional(LootContextParameters.THIS_ENTITY, source.getEntity())
44
+
.build(LootContextTypes.COMMAND))))
45
+
.<Optional<ServerCommandSource>>map(lootCondition -> Optional.empty())
46
+
.orElseGet(() -> Optional.of(source));
47
+
}
48
+
}
+82
src/main/java/me/melontini/commander/impl/event/EventContextImpl.java
+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
+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
+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(new Identifier("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
+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
+
}
+41
src/main/java/me/melontini/commander/impl/event/data/types/CommandTypes.java
+41
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.IdentityHashMap;
7
+
import java.util.Objects;
8
+
import lombok.experimental.UtilityClass;
9
+
import me.melontini.commander.api.command.Command;
10
+
import me.melontini.commander.api.command.CommandType;
11
+
import me.melontini.dark_matter.api.data.codecs.ExtraCodecs;
12
+
import net.minecraft.util.Identifier;
13
+
14
+
@UtilityClass
15
+
public final class CommandTypes {
16
+
17
+
private static final BiMap<Identifier, CommandType> COMMANDS = HashBiMap.create();
18
+
private static final Codec<CommandType> TYPE_CODEC =
19
+
ExtraCodecs.mapLookup(Identifier.CODEC, COMMANDS);
20
+
private static final IdentityHashMap<CommandType, Codec<? extends Command>> CACHE =
21
+
new IdentityHashMap<>();
22
+
public static final Codec<Command> CODEC = TYPE_CODEC.dispatch(
23
+
"type", Command::type, type -> CACHE.computeIfAbsent(type, t -> t.codec().codec()));
24
+
25
+
public static Identifier getId(CommandType type) {
26
+
return Objects.requireNonNull(
27
+
COMMANDS.inverse().get(type), () -> "Unregistered CommandType %s!".formatted(type));
28
+
}
29
+
30
+
public static CommandType getType(Identifier identifier) {
31
+
return Objects.requireNonNull(
32
+
COMMANDS.get(identifier), () -> "Unknown CommandType %s!".formatted(identifier));
33
+
}
34
+
35
+
public static CommandType register(Identifier identifier, CommandType type) {
36
+
var old = COMMANDS.put(identifier, type);
37
+
if (old != null)
38
+
throw new IllegalStateException("Already registered command %s".formatted(identifier));
39
+
return type;
40
+
}
41
+
}
+41
src/main/java/me/melontini/commander/impl/event/data/types/EventTypes.java
+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
+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
+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
+6
src/main/java/me/melontini/commander/impl/expression/CmdEvalException.java
+262
src/main/java/me/melontini/commander/impl/expression/EvalUtils.java
+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
+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
+
new Identifier("level"), LootContext::getWorld,
23
+
new Identifier("luck"), LootContext::getLuck,
24
+
new Identifier("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
+
}
+41
src/main/java/me/melontini/commander/impl/expression/extensions/DefaultCustomFields.java
+41
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.states.StateStruct;
8
+
import net.fabricmc.fabric.api.attachment.v1.AttachmentTarget;
9
+
import net.minecraft.block.entity.BlockEntity;
10
+
import net.minecraft.entity.Entity;
11
+
import net.minecraft.entity.LivingEntity;
12
+
import net.minecraft.item.ItemStack;
13
+
import net.minecraft.nbt.NbtCompound;
14
+
import net.minecraft.predicate.NbtPredicate;
15
+
import net.minecraft.registry.Registry;
16
+
import net.minecraft.state.State;
17
+
18
+
class DefaultCustomFields {
19
+
20
+
private static final NbtCompound EMPTY = new NbtCompound();
21
+
22
+
static void init() {
23
+
CustomFields.addVirtualField(ItemStack.class, "nbt", object -> {
24
+
if (object.getNbt() == null) return EMPTY;
25
+
return object.getNbt();
26
+
});
27
+
CustomFields.addVirtualField(Entity.class, "nbt", NbtPredicate::entityToNbt);
28
+
CustomFields.addVirtualField(
29
+
BlockEntity.class, "nbt", BlockEntity::createNbtWithIdentifyingData);
30
+
31
+
CustomFields.addVirtualField(State.class, "properties", StateStruct::new);
32
+
CustomFields.addVirtualField(
33
+
LivingEntity.class, "attributes", e -> new EntityAttributesStruct(e.getAttributes()));
34
+
35
+
CustomFields.addVirtualField(
36
+
AttachmentTarget.class,
37
+
"storage",
38
+
target -> target.getAttachedOrCreate(Commander.DATA_ATTACHMENT));
39
+
CustomFields.addVirtualField(Registry.class, "access", RegistryAccessStruct::forRegistry);
40
+
}
41
+
}
+227
src/main/java/me/melontini/commander/impl/expression/extensions/ReflectiveMapStructure.java
+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
+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
+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
+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(new Identifier(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
+
}
+40
src/main/java/me/melontini/commander/impl/expression/extensions/convert/attributes/EntityAttributesStruct.java
+40
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 me.melontini.commander.impl.expression.extensions.convert.RegistryAccessStruct;
12
+
import net.minecraft.entity.attribute.AttributeContainer;
13
+
import net.minecraft.entity.attribute.EntityAttribute;
14
+
import net.minecraft.registry.Registries;
15
+
import org.jetbrains.annotations.Nullable;
16
+
17
+
@EqualsAndHashCode(callSuper = false)
18
+
public final class EntityAttributesStruct implements DataAccessorIfc {
19
+
20
+
private final AttributeContainer container;
21
+
private final RegistryAccessStruct registryAccess =
22
+
RegistryAccessStruct.forRegistry(Registries.ATTRIBUTE);
23
+
24
+
public EntityAttributesStruct(AttributeContainer container) {
25
+
this.container = container;
26
+
}
27
+
28
+
@Override
29
+
public @Nullable EvaluationValue getData(String variable, Token token, EvaluationContext context)
30
+
throws EvaluationException {
31
+
var attr = registryAccess.cache().apply(variable);
32
+
if (attr == null) return null;
33
+
return NumberValue.of(BigDecimal.valueOf(container.getValue((EntityAttribute) attr)));
34
+
}
35
+
36
+
@Override
37
+
public String toString() {
38
+
return String.valueOf(container.toNbt());
39
+
}
40
+
}
+48
src/main/java/me/melontini/commander/impl/expression/extensions/convert/gson/GsonConverter.java
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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 = new Identifier(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
+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 = new Identifier(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
+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 = new Identifier(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
+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
+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
+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
+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
+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
+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
+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
+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(new Identifier(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
+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
+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
+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
+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
+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
+
}
+81
src/main/java/me/melontini/commander/impl/mixin/AdvancementRewardsMixin.java
+81
src/main/java/me/melontini/commander/impl/mixin/AdvancementRewardsMixin.java
···
1
+
package me.melontini.commander.impl.mixin;
2
+
3
+
import com.google.gson.JsonElement;
4
+
import com.google.gson.JsonObject;
5
+
import com.google.gson.JsonParseException;
6
+
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
7
+
import com.llamalad7.mixinextras.sugar.Local;
8
+
import com.mojang.serialization.Codec;
9
+
import com.mojang.serialization.JsonOps;
10
+
import java.util.List;
11
+
import me.melontini.commander.api.command.Command;
12
+
import me.melontini.commander.api.event.EventContext;
13
+
import me.melontini.commander.api.event.EventKey;
14
+
import me.melontini.commander.api.event.EventType;
15
+
import me.melontini.commander.impl.util.loot.LootUtil;
16
+
import me.melontini.dark_matter.api.data.codecs.ExtraCodecs;
17
+
import net.minecraft.advancement.AdvancementRewards;
18
+
import net.minecraft.loot.context.LootContext;
19
+
import net.minecraft.loot.context.LootContextParameterSet;
20
+
import net.minecraft.loot.context.LootContextParameters;
21
+
import net.minecraft.loot.context.LootContextTypes;
22
+
import net.minecraft.server.network.ServerPlayerEntity;
23
+
import org.spongepowered.asm.mixin.Mixin;
24
+
import org.spongepowered.asm.mixin.Unique;
25
+
import org.spongepowered.asm.mixin.injection.At;
26
+
import org.spongepowered.asm.mixin.injection.Inject;
27
+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
28
+
29
+
@Mixin(AdvancementRewards.class)
30
+
public class AdvancementRewardsMixin {
31
+
32
+
@Unique private static final Codec<List<Command.Conditioned>> COMMANDER_CODEC =
33
+
ExtraCodecs.list(Command.CODEC.codec());
34
+
35
+
@Unique private List<Command.Conditioned> commands;
36
+
37
+
@ModifyReturnValue(at = @At("TAIL"), method = "toJson")
38
+
private JsonElement commander$encodeCommands(JsonElement original) {
39
+
if (!original.isJsonNull() && this.commands != null) {
40
+
var result = COMMANDER_CODEC.encodeStart(JsonOps.INSTANCE, this.commands);
41
+
if (result.error().isPresent())
42
+
throw new IllegalStateException(result.error().get().message());
43
+
original.getAsJsonObject().add("commander:commands", result.get().orThrow());
44
+
}
45
+
return original;
46
+
}
47
+
48
+
@ModifyReturnValue(at = @At("TAIL"), method = "fromJson")
49
+
private static AdvancementRewards commander$parseCommands(
50
+
AdvancementRewards original, @Local(argsOnly = true) JsonObject object) {
51
+
if (object.has("commander:commands")) {
52
+
var result = COMMANDER_CODEC.parse(JsonOps.INSTANCE, object.get("commander:commands"));
53
+
if (result.error().isPresent())
54
+
throw new JsonParseException(result.error().get().message());
55
+
((AdvancementRewardsMixin) (Object) original).commands = result.get().orThrow();
56
+
for (Command.Conditioned command : ((AdvancementRewardsMixin) (Object) original).commands) {
57
+
var r = command.validate(EventType.NULL);
58
+
if (r.error().isPresent()) throw new JsonParseException(r.error().get().message());
59
+
}
60
+
}
61
+
return original;
62
+
}
63
+
64
+
@Inject(at = @At("TAIL"), method = "apply")
65
+
private void commander$applyCommands(ServerPlayerEntity player, CallbackInfo ci) {
66
+
if (this.commands == null) return;
67
+
LootContext context =
68
+
LootUtil.build(new LootContextParameterSet.Builder(player.getServerWorld())
69
+
.add(LootContextParameters.THIS_ENTITY, player)
70
+
.add(LootContextParameters.ORIGIN, player.getPos())
71
+
.build(LootContextTypes.ADVANCEMENT_REWARD));
72
+
73
+
EventContext context1 = EventContext.builder(EventType.NULL)
74
+
.addParameter(EventKey.LOOT_CONTEXT, context)
75
+
.build();
76
+
77
+
for (Command.Conditioned command : this.commands) {
78
+
command.execute(context1);
79
+
}
80
+
}
81
+
}
+96
src/main/java/me/melontini/commander/impl/mixin/ExecuteCommandMixin.java
+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
+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.Invoker;
8
+
9
+
@Mixin(NbtCompound.class)
10
+
public interface NbtCompoundAccessor {
11
+
12
+
@Invoker("toMap")
13
+
Map<String, NbtElement> commander$toMap();
14
+
}
+133
src/main/java/me/melontini/commander/impl/mixin/ScoreboardCommandMixin.java
+133
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.Scoreboard;
27
+
import net.minecraft.scoreboard.ScoreboardPlayerScore;
28
+
import net.minecraft.server.command.CommandManager;
29
+
import net.minecraft.server.command.ScoreboardCommand;
30
+
import net.minecraft.server.command.ServerCommandSource;
31
+
import net.minecraft.text.Text;
32
+
import org.spongepowered.asm.mixin.Mixin;
33
+
import org.spongepowered.asm.mixin.Unique;
34
+
import org.spongepowered.asm.mixin.injection.At;
35
+
36
+
@Mixin(ScoreboardCommand.class)
37
+
public class ScoreboardCommandMixin {
38
+
39
+
@ModifyExpressionValue(
40
+
method = "register",
41
+
at =
42
+
@At(
43
+
value = "INVOKE",
44
+
target =
45
+
"Lnet/minecraft/server/command/CommandManager;literal(Ljava/lang/String;)Lcom/mojang/brigadier/builder/LiteralArgumentBuilder;",
46
+
ordinal = 8))
47
+
private static LiteralArgumentBuilder<ServerCommandSource> addCommanderOperator(
48
+
LiteralArgumentBuilder<ServerCommandSource> original) {
49
+
return original
50
+
.then(CommandManager.literal("cmd:operate")
51
+
.then(CommandManager.argument("targets", ScoreHolderArgumentType.scoreHolders())
52
+
.suggests(ScoreHolderArgumentType.SUGGESTION_PROVIDER)
53
+
.then(CommandManager.argument(
54
+
"objective", ScoreboardObjectiveArgumentType.scoreboardObjective())
55
+
.then(CommandManager.argument("expression", StringArgumentType.string())
56
+
.executes(context -> {
57
+
var r =
58
+
Expression.parse(StringArgumentType.getString(context, "expression"));
59
+
if (r.error().isPresent())
60
+
throw Commander.EXPRESSION_EXCEPTION.create(
61
+
r.error().get().message());
62
+
return evaluateExpression(r.result().orElseThrow(), context);
63
+
})))))
64
+
.then(CommandManager.literal("cmd:library")
65
+
.then(CommandManager.argument("targets", ScoreHolderArgumentType.scoreHolders())
66
+
.suggests(ScoreHolderArgumentType.SUGGESTION_PROVIDER)
67
+
.then(CommandManager.argument(
68
+
"objective", ScoreboardObjectiveArgumentType.scoreboardObjective())
69
+
.then(CommandManager.argument("expression", IdentifierArgumentType.identifier())
70
+
.suggests((context, builder) -> CommandSource.suggestIdentifiers(
71
+
ExpressionLibrary.get(context.getSource().getServer())
72
+
.allExpressions()
73
+
.keySet(),
74
+
builder))
75
+
.executes(context -> {
76
+
var identifier =
77
+
IdentifierArgumentType.getIdentifier(context, "expression");
78
+
var expression = ExpressionLibrary.get(
79
+
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
+
@Unique private static int evaluateExpression(
87
+
Expression expression, CommandContext<ServerCommandSource> context)
88
+
throws CommandSyntaxException {
89
+
var targets =
90
+
new ArrayList<>(ScoreHolderArgumentType.getScoreboardScoreHolders(context, "targets"));
91
+
var objective = ScoreboardObjectiveArgumentType.getWritableObjective(context, "objective");
92
+
93
+
Scoreboard scoreboard = context.getSource().getServer().getScoreboard();
94
+
95
+
LootContext context1 =
96
+
LootUtil.build(new LootContextParameterSet.Builder(context.getSource().getWorld())
97
+
.add(LootContextParameters.ORIGIN, context.getSource().getPosition())
98
+
.build(LootContextTypes.COMMAND));
99
+
100
+
for (String target : targets) {
101
+
ScoreboardPlayerScore score = scoreboard.getPlayerScore(target, objective);
102
+
Optional.ofNullable(expression
103
+
.eval(context1, Collections.singletonMap("score", score.getScore()))
104
+
.getAsDecimal())
105
+
.map(BigDecimal::intValue)
106
+
.ifPresent(score::setScore);
107
+
}
108
+
109
+
if (targets.size() == 1) {
110
+
context
111
+
.getSource()
112
+
.sendFeedback(
113
+
() -> Text.translatable(
114
+
"commands.scoreboard.players.set.success.single",
115
+
objective.toHoverableText(),
116
+
targets.get(0),
117
+
expression.original()),
118
+
true);
119
+
} else {
120
+
context
121
+
.getSource()
122
+
.sendFeedback(
123
+
() -> Text.translatable(
124
+
"commands.scoreboard.players.set.success.multiple",
125
+
objective.toHoverableText(),
126
+
targets.size(),
127
+
expression.original()),
128
+
true);
129
+
}
130
+
131
+
return targets.size();
132
+
}
133
+
}
+73
src/main/java/me/melontini/commander/impl/mixin/evalex/EvaluationValueMixin.java
+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
+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
+8
src/main/java/me/melontini/commander/impl/util/DataTarget.java
+16
src/main/java/me/melontini/commander/impl/util/Identity.java
+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
+
}
+50
src/main/java/me/melontini/commander/impl/util/MagicCodecs.java
+50
src/main/java/me/melontini/commander/impl/util/MagicCodecs.java
···
1
+
package me.melontini.commander.impl.util;
2
+
3
+
import com.google.gson.*;
4
+
import com.mojang.serialization.Codec;
5
+
import java.lang.reflect.Type;
6
+
import lombok.experimental.UtilityClass;
7
+
import me.melontini.dark_matter.api.data.codecs.ExtraCodecs;
8
+
import net.minecraft.loot.LootGsons;
9
+
import net.minecraft.loot.condition.LootCondition;
10
+
import net.minecraft.registry.Registries;
11
+
import net.minecraft.util.JsonSerializableType;
12
+
13
+
@UtilityClass
14
+
public class MagicCodecs {
15
+
16
+
public static final GsonContextImpl lootContext =
17
+
new GsonContextImpl(LootGsons.getConditionGsonBuilder().create());
18
+
19
+
public static final Codec<LootCondition> LOOT_CONDITION = ExtraCodecs.jsonSerializerDispatch(
20
+
"condition",
21
+
Registries.LOOT_CONDITION_TYPE.getCodec(),
22
+
LootCondition::getType,
23
+
JsonSerializableType::getJsonSerializer,
24
+
lootContext);
25
+
26
+
public static final class GsonContextImpl
27
+
implements JsonSerializationContext, JsonDeserializationContext {
28
+
29
+
private final Gson gson;
30
+
31
+
public GsonContextImpl(Gson gson) {
32
+
this.gson = gson;
33
+
}
34
+
35
+
@Override
36
+
public JsonElement serialize(Object src) {
37
+
return gson.toJsonTree(src);
38
+
}
39
+
40
+
@Override
41
+
public JsonElement serialize(Object src, Type typeOfSrc) {
42
+
return gson.toJsonTree(src, typeOfSrc);
43
+
}
44
+
45
+
@Override
46
+
public <R> R deserialize(JsonElement json, Type typeOfT) throws JsonParseException {
47
+
return gson.fromJson(json, typeOfT);
48
+
}
49
+
}
50
+
}
+30
src/main/java/me/melontini/commander/impl/util/NbtCodecs.java
+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
+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
+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
+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
+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
+
}
+11
src/main/java/me/melontini/commander/impl/util/loot/LootUtil.java
+11
src/main/java/me/melontini/commander/impl/util/loot/LootUtil.java
···
1
+
package me.melontini.commander.impl.util.loot;
2
+
3
+
import net.minecraft.loot.context.LootContext;
4
+
import net.minecraft.loot.context.LootContextParameterSet;
5
+
6
+
public class LootUtil {
7
+
8
+
public static LootContext build(LootContextParameterSet parameters) {
9
+
return new LootContext.Builder(parameters).build(null);
10
+
}
11
+
}
+7
src/main/java/me/melontini/commander/impl/util/mappings/AmbiguousRemapper.java
+7
src/main/java/me/melontini/commander/impl/util/mappings/AmbiguousRemapper.java
+105
src/main/java/me/melontini/commander/impl/util/mappings/MappingKeeper.java
+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
+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
src/main/resources/assets/commander/icon.png
This is a binary file and will not be displayed.
+5
src/main/resources/commander.accesswidener
+5
src/main/resources/commander.accesswidener
···
1
+
accessWidener v2 named
2
+
3
+
accessible class net/minecraft/server/command/ExecuteCommand$Condition
4
+
5
+
accessible method net/minecraft/loot/provider/number/LootNumberProviderTypes register (Ljava/lang/String;Lnet/minecraft/util/JsonSerializer;)Lnet/minecraft/loot/provider/number/LootNumberProviderType;
+19
src/main/resources/commander.mixins.json
+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
+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
+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(new Identifier("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
+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
+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
+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.dimensionTypeId.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
+
}
+69
src/testmod/java/me/melontini/commander/test/VirtualFieldsTest.java
+69
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.StructureValue;
9
+
import java.util.Map;
10
+
import me.melontini.commander.impl.expression.extensions.convert.nbt.NbtCompoundStruct;
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.block.Blocks;
15
+
import net.minecraft.block.entity.BlockEntity;
16
+
import net.minecraft.block.entity.BlockEntityType;
17
+
import net.minecraft.entity.Entity;
18
+
import net.minecraft.entity.EntityType;
19
+
import net.minecraft.item.ItemStack;
20
+
import net.minecraft.item.Items;
21
+
import net.minecraft.predicate.NbtPredicate;
22
+
import net.minecraft.util.math.BlockPos;
23
+
import org.assertj.core.api.Assertions;
24
+
25
+
public class VirtualFieldsTest implements ServerTestEntrypoint {
26
+
27
+
@HandyTest
28
+
void testStackFields(ServerTestContext context) {
29
+
ItemStack stack = Items.ACACIA_LOG.getDefaultStack();
30
+
31
+
var lootContext = emptyContext(context);
32
+
Assertions.assertThat(parse("stack.nbt").eval(lootContext, Map.of("stack", stack)))
33
+
.isInstanceOf(StructureValue.class)
34
+
.isEqualTo(StructureValue.of(
35
+
(Map<String, EvaluationValue>) (Object) new NbtCompoundStruct(stack.getOrCreateNbt())));
36
+
}
37
+
38
+
@HandyTest
39
+
void testEntityFields(ServerTestContext context) {
40
+
Entity entity = EntityType.SLIME.create(context.context().getOverworld());
41
+
42
+
var lootContext = emptyContext(context);
43
+
Assertions.assertThat(parse("entity.nbt").eval(lootContext, Map.of("entity", entity)))
44
+
.isInstanceOf(StructureValue.class)
45
+
.isEqualTo(StructureValue.of((Map<String, EvaluationValue>)
46
+
(Object) new NbtCompoundStruct(NbtPredicate.entityToNbt(entity))));
47
+
}
48
+
49
+
@HandyTest
50
+
void testBlockEntityFields(ServerTestContext context) {
51
+
BlockEntity entity = BlockEntityType.CAMPFIRE.instantiate(
52
+
new BlockPos(100, 60, 100), Blocks.CAMPFIRE.getDefaultState());
53
+
54
+
var lootContext = emptyContext(context);
55
+
Assertions.assertThat(parse("be.nbt").eval(lootContext, Map.of("be", entity)))
56
+
.isInstanceOf(StructureValue.class)
57
+
.isEqualTo(StructureValue.of((Map<String, EvaluationValue>)
58
+
(Object) new NbtCompoundStruct(entity.createNbtWithIdentifyingData())));
59
+
}
60
+
61
+
@HandyTest
62
+
void testBlockStateFields(ServerTestContext context) {
63
+
var lootContext = emptyContext(context);
64
+
65
+
Assertions.assertThat(parse("state.properties.lit")
66
+
.eval(lootContext, Map.of("state", Blocks.CAMPFIRE.getDefaultState())))
67
+
.isEqualTo(BooleanValue.of(true));
68
+
}
69
+
}
+13
src/testmod/resources/data/commander/commander/events/test/custom_events_test.json
+13
src/testmod/resources/data/commander/commander/events/test/custom_events_test.json
+7
src/testmod/resources/data/commander/commander/expression_library/test_shelf.json
+7
src/testmod/resources/data/commander/commander/expression_library/test_shelf.json
+18
src/testmod/resources/fabric.mod.json
+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
+
}