sandstone 1.1.3 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/index.d.ts +7 -15
- package/dist/_internal/index.d.ts.map +1 -1
- package/dist/_internal/index.js +227 -75
- package/dist/_internal/index.js.map +1 -1
- package/dist/_internal/types/arguments/generated/resources.d.ts +2 -2
- package/dist/_internal/types/arguments/generated/resources.d.ts.map +1 -1
- package/dist/_internal/types/commands/implementations/block/place.d.ts +1 -1
- package/dist/_internal/types/commands/implementations/block/place.d.ts.map +1 -1
- package/dist/_internal/types/commands/implementations/server/schedule.d.ts +1 -1
- package/dist/_internal/types/core/resources/index.d.ts +1 -1
- package/dist/_internal/types/core/resources/resourcepack/blockstate.d.ts +13 -13
- package/dist/_internal/types/core/resources/resourcepack/blockstate.d.ts.map +1 -1
- package/dist/_internal/types/flow/Flow.d.ts +27 -13
- package/dist/_internal/types/flow/Flow.d.ts.map +1 -1
- package/dist/_internal/types/flow/conditions/command.d.ts.map +1 -1
- package/dist/_internal/types/flow/conditions/condition.d.ts +8 -0
- package/dist/_internal/types/flow/conditions/condition.d.ts.map +1 -1
- package/dist/_internal/types/flow/conditions/variables/dataPoint.d.ts.map +1 -1
- package/dist/_internal/types/flow/loop.d.ts +1 -1
- package/dist/_internal/types/pack/index.d.ts +1 -1
- package/dist/_internal/types/pack/pack.d.ts +7 -7
- package/dist/_internal/types/pack/pack.d.ts.map +1 -1
- package/dist/_internal/types/pack/visitors/orTransformationVisitor.d.ts +41 -6
- package/dist/_internal/types/pack/visitors/orTransformationVisitor.d.ts.map +1 -1
- package/dist/_internal/types/variables/Data.d.ts +1 -1
- package/dist/_internal/types/variables/DataSets.d.ts +4 -4
- package/dist/_internal/types/variables/DataSets.d.ts.map +1 -1
- package/dist/_internal/types/variables/ResolveNBT.d.ts +2 -2
- package/dist/_internal/types/variables/abstractClasses.d.ts +9 -0
- package/dist/_internal/types/variables/abstractClasses.d.ts.map +1 -1
- package/dist/browser/sandstone.esm.js +227 -75
- package/dist/browser/sandstone.esm.js.map +1 -1
- package/dist/exports/core/index.d.ts +1 -1
- package/dist/exports/core/index.js +1 -1
- package/dist/exports/index.d.ts +1 -1
- package/dist/exports/index.js +1 -1
- package/dist/exports/variables/index.d.ts +1 -1
- package/dist/exports/variables/index.js +1 -1
- package/package.json +3 -3
- package/src/arguments/generated/resources.ts +2 -2
- package/src/commands/implementations/block/place.ts +1 -1
- package/src/core/resources/datapack/itemModifier.ts +2 -2
- package/src/core/resources/resourcepack/blockstate.ts +32 -32
- package/src/flow/Flow.ts +24 -4
- package/src/flow/conditions/command.ts +7 -0
- package/src/flow/conditions/condition.ts +9 -0
- package/src/flow/conditions/variables/dataPoint.ts +11 -3
- package/src/pack/pack.ts +19 -38
- package/src/pack/visitors/orTransformationVisitor.ts +311 -37
- package/src/sandstone.ts +4 -20
- package/src/variables/DataSets.ts +4 -4
- package/src/variables/abstractClasses.ts +10 -0
- package/src/core/resources/datapack/decoratedPotPattern.ts.disable +0 -47
- package/src/core/resources/datapack/slotSource.ts.disable +0 -47
|
@@ -1,68 +1,342 @@
|
|
|
1
|
-
import { ScoreboardCommandNode } from '../../commands'
|
|
2
|
-
import type
|
|
3
|
-
import
|
|
1
|
+
import { ExecuteCommandNode, type SubCommand, ReturnCommandNode, ScoreboardCommandNode } from '../../commands'
|
|
2
|
+
import { MCFunctionClass, type MCFunctionNode, type Node, type SandstoneCore } from '../../core'
|
|
3
|
+
import type { Score } from '../../variables'
|
|
4
|
+
import { AndNode, ConditionNode, IfNode, NotNode, OrNode } from '../../flow'
|
|
4
5
|
import { GenericSandstoneVisitor } from './visitor'
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Condition node whose `getValue()` returns `"if function <name>"`.
|
|
9
|
+
* Used to make an IfNode's existing `IfElseTransformationVisitor` emit
|
|
10
|
+
* `execute if function <child> run <body>`, which succeeds exactly when the
|
|
11
|
+
* child MCFunction returns a positive value (e.g. via an early `return 1`).
|
|
12
|
+
*/
|
|
13
|
+
class FunctionReturnConditionNode extends ConditionNode {
|
|
14
|
+
constructor(
|
|
15
|
+
public sandstoneCore: SandstoneCore,
|
|
16
|
+
public functionName: string,
|
|
17
|
+
) {
|
|
18
|
+
super(sandstoneCore)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getValue = () => `if function ${this.functionName}`
|
|
22
|
+
}
|
|
23
|
+
|
|
6
24
|
export class OrTransformationVisitor extends GenericSandstoneVisitor {
|
|
7
25
|
visitIfNode = (node_: IfNode) => {
|
|
8
|
-
const { preNodes, conditionNode } = this.parseConditionNode(node_.condition)
|
|
26
|
+
const { preNodes, conditionNode } = this.parseConditionNode(node_.condition, node_.parentMCFunction)
|
|
9
27
|
node_.condition = conditionNode
|
|
10
28
|
return [...preNodes, this.genericVisit(node_)]
|
|
11
29
|
}
|
|
12
30
|
|
|
13
|
-
parseConditionNode = (node: ConditionNode): { preNodes: Node[]; conditionNode: ConditionNode } => {
|
|
31
|
+
parseConditionNode = (node: ConditionNode, parentMCFunction?: MCFunctionNode): { preNodes: Node[]; conditionNode: ConditionNode } => {
|
|
14
32
|
if (node instanceof OrNode) {
|
|
15
|
-
return this.parseOrNode(node)
|
|
33
|
+
return this.parseOrNode(node, parentMCFunction)
|
|
16
34
|
}
|
|
17
35
|
if (node instanceof AndNode) {
|
|
18
|
-
return this.parseAndNode(node)
|
|
36
|
+
return this.parseAndNode(node, parentMCFunction)
|
|
19
37
|
}
|
|
20
38
|
if (node instanceof NotNode) {
|
|
21
|
-
return this.parseNotNode(node)
|
|
39
|
+
return this.parseNotNode(node, parentMCFunction)
|
|
22
40
|
}
|
|
23
41
|
|
|
24
|
-
//
|
|
25
|
-
|
|
42
|
+
// `preNodes` is optional on ConditionNode. Side-effecting conditions (those that
|
|
43
|
+
// commit commands at construction, e.g. `data.equals`, `Label.equals`, any
|
|
44
|
+
// `CommandCondition`) populate it; pure predicates leave it unset.
|
|
45
|
+
return { preNodes: [...(node.preNodes ?? [])], conditionNode: node }
|
|
26
46
|
}
|
|
27
|
-
parseOrNode = (node: OrNode): { preNodes: Node[]; conditionNode: ConditionNode } => {
|
|
28
|
-
const variable = this.core.pack.Variable(undefined, 'condition')
|
|
29
47
|
|
|
30
|
-
|
|
48
|
+
parseOrNode = (node: OrNode, parentMCFunction?: MCFunctionNode): { preNodes: Node[]; conditionNode: ConditionNode } => {
|
|
49
|
+
// Single branch: collapse through so the IfNode behaves as if it had the inner condition.
|
|
50
|
+
if (node.conditions.length === 1) {
|
|
51
|
+
return this.parseConditionNode(node.conditions[0], parentMCFunction)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Multiple branches: extract to a child MCFunction with execute-if-return-1 short-circuit.
|
|
55
|
+
// Inside the child: `execute if <c1> run return 1`, `execute if <c2> run return 1`, ..., `return 0`.
|
|
56
|
+
// The first matching branch returns 1 and exits the function immediately; the rest are skipped.
|
|
57
|
+
// The caller uses `execute if function <child>` to test whether any branch matched.
|
|
58
|
+
const parentName = parentMCFunction?.resource.name ?? '__sandstone'
|
|
59
|
+
const orMCFunction = new MCFunctionClass(
|
|
60
|
+
this.core,
|
|
61
|
+
`${parentName}/or_check`,
|
|
62
|
+
{
|
|
63
|
+
addToSandstoneCore: true,
|
|
64
|
+
creator: 'sandstone',
|
|
65
|
+
onConflict: 'rename',
|
|
66
|
+
},
|
|
67
|
+
)
|
|
31
68
|
|
|
32
|
-
|
|
33
|
-
const orExecuteNodes = node.conditions.map((condition) => {
|
|
34
|
-
const { preNodes, conditionNode: parsedConditionNode } = this.parseConditionNode(condition)
|
|
35
|
-
realPreNodes.push(...preNodes)
|
|
69
|
+
this.core.enterMCFunction(orMCFunction)
|
|
36
70
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
71
|
+
try {
|
|
72
|
+
for (const condition of node.conditions) {
|
|
73
|
+
this.pushCheckBranch(orMCFunction.node, this.parseConditionNode(condition, orMCFunction.node), false, new ReturnCommandNode(this.pack, [1]))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Default return: no branch matched.
|
|
77
|
+
orMCFunction.node.body.push(new ReturnCommandNode(this.pack, [0]))
|
|
78
|
+
} finally {
|
|
79
|
+
this.core.exitMCFunction()
|
|
80
|
+
}
|
|
41
81
|
|
|
42
82
|
return {
|
|
43
|
-
preNodes: [
|
|
44
|
-
conditionNode,
|
|
83
|
+
preNodes: [],
|
|
84
|
+
conditionNode: new FunctionReturnConditionNode(this.core, orMCFunction.name),
|
|
45
85
|
}
|
|
46
86
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
node.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
87
|
+
|
|
88
|
+
parseAndNode = (node: AndNode, parentMCFunction?: MCFunctionNode): { preNodes: Node[]; conditionNode: ConditionNode } => {
|
|
89
|
+
// Single branch: collapse through so the IfNode behaves as if it had the inner condition.
|
|
90
|
+
if (node.conditions.length === 1) {
|
|
91
|
+
return this.parseConditionNode(node.conditions[0], parentMCFunction)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Parse every condition to learn whether it carries commands (preNodes) that must run
|
|
95
|
+
// before the predicate is evaluated. Pure-predicate conditions chain cleanly inside a
|
|
96
|
+
// single execute — those are batched. Command-needing conditions force the AND into a
|
|
97
|
+
// child MCFunction so commands can run inline before their check.
|
|
98
|
+
const parsed = node.conditions.map((condition) => this.parseConditionNode(condition, parentMCFunction))
|
|
99
|
+
const anyHasCommands = parsed.some((p) => (p.preNodes ?? []).length > 0)
|
|
100
|
+
|
|
101
|
+
if (!anyHasCommands) {
|
|
102
|
+
// Inline: pure conditions chain via the existing AndNode behavior. Order is preserved
|
|
103
|
+
// by `Array.prototype.map`. No child function needed.
|
|
104
|
+
const finalPreNodes: Node[] = []
|
|
105
|
+
const conditionNode = new AndNode(
|
|
106
|
+
node.sandstoneCore,
|
|
107
|
+
parsed.map((p) => {
|
|
108
|
+
finalPreNodes.push(...p.preNodes)
|
|
109
|
+
return p.conditionNode
|
|
110
|
+
}),
|
|
111
|
+
)
|
|
112
|
+
return { preNodes: finalPreNodes, conditionNode }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// At least one condition needs to run commands. Extract to a child MCFunction:
|
|
116
|
+
// - Pure-predicate runs are batched into `execute <pos-c1> <pos-c2> ... run return 1`
|
|
117
|
+
// (positive form, NOT flipped — chain fires `return 1` only when the whole batch
|
|
118
|
+
// holds; otherwise we fall through to the next check).
|
|
119
|
+
// - Command-needing conditions emit their stashed side-effects inline, then a flipped
|
|
120
|
+
// `execute if <failure-form> run return 0` so the function returns 0 as soon as a
|
|
121
|
+
// side-effecting check fails to hold.
|
|
122
|
+
// - Default `return 0` reaches the end only when no command-needing check failed and
|
|
123
|
+
// no pure batch all-held; the function returns 1 only via a pure-batch success path,
|
|
124
|
+
// signalled to the caller by `execute if function <and>`.
|
|
125
|
+
const parentName = parentMCFunction?.resource.name ?? '__sandstone'
|
|
126
|
+
const andMCFunction = new MCFunctionClass(
|
|
127
|
+
this.core,
|
|
128
|
+
`${parentName}/and_check`,
|
|
129
|
+
{
|
|
130
|
+
addToSandstoneCore: true,
|
|
131
|
+
creator: 'sandstone',
|
|
132
|
+
onConflict: 'rename',
|
|
133
|
+
},
|
|
57
134
|
)
|
|
58
135
|
|
|
136
|
+
this.core.enterMCFunction(andMCFunction)
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
let batch: { preNodes: Node[][]; conditionNodes: ConditionNode[] } = { preNodes: [], conditionNodes: [] }
|
|
140
|
+
|
|
141
|
+
const flushBatch = () => {
|
|
142
|
+
if (batch.conditionNodes.length === 0 && batch.preNodes.every((pn) => pn.length === 0)) {
|
|
143
|
+
batch = { preNodes: [], conditionNodes: [] }
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
// Per-batch tracker-var pattern: reset the tracker, run the chained execute to set
|
|
147
|
+
// the tracker to 1 only when the entire batch holds, then check the tracker and
|
|
148
|
+
// `return 0` if any condition failed (var != 1). Each batch uses its own tracker var
|
|
149
|
+
// so concurrent/sequential batches stay independent.
|
|
150
|
+
const tracker = this.core.pack.Variable(undefined, 'and_batch')
|
|
151
|
+
this.pushBatchedCheckBranch(
|
|
152
|
+
andMCFunction.node,
|
|
153
|
+
batch.preNodes.flat(),
|
|
154
|
+
batch.conditionNodes,
|
|
155
|
+
tracker,
|
|
156
|
+
)
|
|
157
|
+
batch = { preNodes: [], conditionNodes: [] }
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
for (const p of parsed) {
|
|
161
|
+
if (p.preNodes.length === 0) {
|
|
162
|
+
// Pure predicate: extend the current batch (still in declared order).
|
|
163
|
+
batch.preNodes.push(p.preNodes)
|
|
164
|
+
batch.conditionNodes.push(p.conditionNode)
|
|
165
|
+
} else {
|
|
166
|
+
// Command-needing condition: flush any pending batch first (preserving order),
|
|
167
|
+
// then emit this condition's commands followed by its own fail-fast check.
|
|
168
|
+
flushBatch()
|
|
169
|
+
this.pushFailFastBranch(
|
|
170
|
+
andMCFunction.node,
|
|
171
|
+
p,
|
|
172
|
+
new ReturnCommandNode(this.pack, [0]),
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
flushBatch()
|
|
177
|
+
|
|
178
|
+
// Default return: every check ran without success.
|
|
179
|
+
andMCFunction.node.body.push(new ReturnCommandNode(this.pack, [1]))
|
|
180
|
+
} finally {
|
|
181
|
+
this.core.exitMCFunction()
|
|
182
|
+
}
|
|
183
|
+
|
|
59
184
|
return {
|
|
60
|
-
preNodes:
|
|
61
|
-
conditionNode,
|
|
185
|
+
preNodes: [],
|
|
186
|
+
conditionNode: new FunctionReturnConditionNode(this.core, andMCFunction.name),
|
|
62
187
|
}
|
|
63
188
|
}
|
|
64
|
-
|
|
65
|
-
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Pushes a batched check into `targetMCFunction.body` using the tracker-var pattern:
|
|
192
|
+
* <preNodes...>
|
|
193
|
+
* scoreboard players reset <tracker>
|
|
194
|
+
* execute <if|unless> <c1> <if|unless> <c2> ... run scoreboard players set <tracker> 1
|
|
195
|
+
* execute unless score <tracker> matches 1 run return 0
|
|
196
|
+
*
|
|
197
|
+
* Consecutive pure-predicate conditions from the AND list coalesce into a single chained
|
|
198
|
+
* execute that sets the tracker to 1 only when the entire batch holds. The trailing
|
|
199
|
+
* fail-check returns 0 from the child MCFunction when any condition failed (var != 1).
|
|
200
|
+
* Using a tracker var avoids the "execute-chain only fires on ALL-pass" limitation of
|
|
201
|
+
* pure MC chains while keeping the body tight.
|
|
202
|
+
*/
|
|
203
|
+
private pushBatchedCheckBranch = (
|
|
204
|
+
targetMCFunction: MCFunctionNode,
|
|
205
|
+
preNodes: Node[],
|
|
206
|
+
conditionNodes: ConditionNode[],
|
|
207
|
+
tracker: Score,
|
|
208
|
+
) => {
|
|
209
|
+
for (const n of preNodes) {
|
|
210
|
+
targetMCFunction.body.push(n)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
targetMCFunction.body.push(new ScoreboardCommandNode(this.pack, 'players', 'reset', tracker))
|
|
214
|
+
|
|
215
|
+
const args: SubCommand[] = []
|
|
216
|
+
for (const c of conditionNodes) {
|
|
217
|
+
const condStr = c.getValue(false)
|
|
218
|
+
const match = condStr.match(/^(if|unless)\s+([\s\S]+)$/)
|
|
219
|
+
if (!match) {
|
|
220
|
+
throw new Error(`OrTransformationVisitor: unexpected condition getValue() shape: ${condStr}`)
|
|
221
|
+
}
|
|
222
|
+
const [, keyword, condBody] = match
|
|
223
|
+
args.push([keyword, condBody])
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (args.length === 0) {
|
|
227
|
+
// No conditions but preNodes ran — push an unconditional set so the trailing check
|
|
228
|
+
// doesn't fire on a stale tracker.
|
|
229
|
+
targetMCFunction.body.push(new ScoreboardCommandNode(this.pack, 'players', 'set', tracker, 1))
|
|
230
|
+
} else {
|
|
231
|
+
const executeNode = new ExecuteCommandNode(this.pack, args, {
|
|
232
|
+
isSingleExecute: true,
|
|
233
|
+
body: [],
|
|
234
|
+
})
|
|
235
|
+
targetMCFunction.enterContext(executeNode, false)
|
|
236
|
+
executeNode.body = [new ScoreboardCommandNode(this.pack, 'players', 'set', tracker, 1)]
|
|
237
|
+
targetMCFunction.body.push(executeNode)
|
|
238
|
+
targetMCFunction.exitContext()
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const failCheckNode = new ExecuteCommandNode(
|
|
242
|
+
this.pack,
|
|
243
|
+
[['unless', `score ${tracker} matches 1`]],
|
|
244
|
+
{
|
|
245
|
+
isSingleExecute: true,
|
|
246
|
+
body: [],
|
|
247
|
+
},
|
|
248
|
+
)
|
|
249
|
+
targetMCFunction.enterContext(failCheckNode, false)
|
|
250
|
+
failCheckNode.body = [new ReturnCommandNode(this.pack, [0])]
|
|
251
|
+
targetMCFunction.body.push(failCheckNode)
|
|
252
|
+
targetMCFunction.exitContext()
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Pushes a single check branch into `targetMCFunction.body`:
|
|
257
|
+
* <preNodes...> (side-effect commands that must run before checking)
|
|
258
|
+
* execute <if|unless> <condBody> run <bodyNode>
|
|
259
|
+
*
|
|
260
|
+
* When `negated` is false, the helper uses the condition's positive form
|
|
261
|
+
* (`getValue(false)`); when true, it uses the failure form (`getValue(true)`).
|
|
262
|
+
* For OR semantics: pass `negated=false` with `bodyNode = return 1` (exit on first match).
|
|
263
|
+
*/
|
|
264
|
+
private pushCheckBranch = (
|
|
265
|
+
targetMCFunction: MCFunctionNode,
|
|
266
|
+
parsed: { preNodes: Node[]; conditionNode: ConditionNode },
|
|
267
|
+
negated: boolean,
|
|
268
|
+
bodyNode: Node,
|
|
269
|
+
) => {
|
|
270
|
+
// Any nested preNodes (e.g. nested or/and reset+score commands, or the inner
|
|
271
|
+
// condition's side-effect commands) run inside the child function body in order.
|
|
272
|
+
for (const n of parsed.preNodes) {
|
|
273
|
+
targetMCFunction.body.push(n)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Extract the chain keyword ("if" or "unless") and the condition body from `getValue()`.
|
|
277
|
+
// SingleConditionNode returns "if <body>"; NotNode-wrapped conditions return "unless <body>".
|
|
278
|
+
// AndNode already produces execute-chain strings; a nested FunctionReturnConditionNode
|
|
279
|
+
// returns "if function <name>" which fits the same shape.
|
|
280
|
+
const condStr = parsed.conditionNode.getValue(negated)
|
|
281
|
+
const match = condStr.match(/^(if|unless)\s+([\s\S]+)$/)
|
|
282
|
+
if (!match) {
|
|
283
|
+
throw new Error(`OrTransformationVisitor: unexpected condition getValue() shape: ${condStr}`)
|
|
284
|
+
}
|
|
285
|
+
const [, keyword, condBody] = match
|
|
286
|
+
|
|
287
|
+
// Construct ExecuteCommandNode for `execute <keyword> <condBody> run <bodyNode>`.
|
|
288
|
+
// The constructor's append() calls exitContext, so we first push an empty executeNode
|
|
289
|
+
// into the MCFunction context stack (addNode=false) so the exit succeeds.
|
|
290
|
+
const executeNode = new ExecuteCommandNode(this.pack, [[keyword, condBody]], {
|
|
291
|
+
isSingleExecute: true,
|
|
292
|
+
body: [],
|
|
293
|
+
})
|
|
294
|
+
targetMCFunction.enterContext(executeNode, false)
|
|
295
|
+
executeNode.body = [bodyNode]
|
|
296
|
+
targetMCFunction.body.push(executeNode)
|
|
297
|
+
targetMCFunction.exitContext()
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Pushes a fail-fast check for an AND command-needing condition into `targetMCFunction.body`:
|
|
302
|
+
* <preNodes...>
|
|
303
|
+
* execute <if|unless> <condBody> run <bodyNode>
|
|
304
|
+
*
|
|
305
|
+
* Uses the condition's FAILURE form (`getValue(true)`) and flips the leading
|
|
306
|
+
* `if`/`unless` keyword so the execute fires when the condition fails to hold. The
|
|
307
|
+
* `bodyNode` (typically `return 0`) exits the child MCFunction immediately. When the
|
|
308
|
+
* condition holds, the chain doesn't fire and we fall through to the next check.
|
|
309
|
+
*/
|
|
310
|
+
private pushFailFastBranch = (
|
|
311
|
+
targetMCFunction: MCFunctionNode,
|
|
312
|
+
parsed: { preNodes: Node[]; conditionNode: ConditionNode },
|
|
313
|
+
bodyNode: Node,
|
|
314
|
+
) => {
|
|
315
|
+
for (const n of parsed.preNodes) {
|
|
316
|
+
targetMCFunction.body.push(n)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const condStr = parsed.conditionNode.getValue(true)
|
|
320
|
+
const match = condStr.match(/^(if|unless)\s+([\s\S]+)$/)
|
|
321
|
+
if (!match) {
|
|
322
|
+
throw new Error(`OrTransformationVisitor: unexpected condition getValue() shape: ${condStr}`)
|
|
323
|
+
}
|
|
324
|
+
// Flip the leading keyword so the execute fires on failure rather than success.
|
|
325
|
+
const flippedKeyword = match[1] === 'if' ? 'unless' : 'if'
|
|
326
|
+
const condBody = match[2]
|
|
327
|
+
|
|
328
|
+
const executeNode = new ExecuteCommandNode(this.pack, [[flippedKeyword, condBody]], {
|
|
329
|
+
isSingleExecute: true,
|
|
330
|
+
body: [],
|
|
331
|
+
})
|
|
332
|
+
targetMCFunction.enterContext(executeNode, false)
|
|
333
|
+
executeNode.body = [bodyNode]
|
|
334
|
+
targetMCFunction.body.push(executeNode)
|
|
335
|
+
targetMCFunction.exitContext()
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
parseNotNode = (node: NotNode, parentMCFunction?: MCFunctionNode): { preNodes: Node[]; conditionNode: ConditionNode } => {
|
|
339
|
+
const { preNodes, conditionNode } = this.parseConditionNode(node.condition, parentMCFunction)
|
|
66
340
|
return {
|
|
67
341
|
preNodes,
|
|
68
342
|
conditionNode: new NotNode(node.sandstoneCore, conditionNode),
|
package/src/sandstone.ts
CHANGED
|
@@ -5,10 +5,9 @@ import type {
|
|
|
5
5
|
AdvancementClassArguments,
|
|
6
6
|
AtlasClassArguments,
|
|
7
7
|
BannerPatternClassArguments,
|
|
8
|
-
|
|
8
|
+
BlockStateDefinitionArguments,
|
|
9
9
|
ChatTypeClassArguments,
|
|
10
10
|
DamageTypeClassArguments,
|
|
11
|
-
// DecoratedPotPatternClassArguments,
|
|
12
11
|
DialogClassArguments,
|
|
13
12
|
EnchantmentClassArguments,
|
|
14
13
|
EnchantmentProviderClassArguments,
|
|
@@ -28,7 +27,6 @@ import type {
|
|
|
28
27
|
PredicateClassArguments,
|
|
29
28
|
RecipeClassArguments,
|
|
30
29
|
ShaderClassArguments,
|
|
31
|
-
// SlotSourceClassArguments,
|
|
32
30
|
SoundEventArguments,
|
|
33
31
|
SoundsIndexArguments,
|
|
34
32
|
SulfurCubeArchetypeClassArguments,
|
|
@@ -295,7 +293,6 @@ export const {
|
|
|
295
293
|
BannerPattern,
|
|
296
294
|
ChatType,
|
|
297
295
|
DamageType,
|
|
298
|
-
// DecoratedPotPattern,
|
|
299
296
|
Dialog,
|
|
300
297
|
Enchantment,
|
|
301
298
|
EnchantmentProvider,
|
|
@@ -305,7 +302,6 @@ export const {
|
|
|
305
302
|
LootTable,
|
|
306
303
|
Predicate,
|
|
307
304
|
Recipe,
|
|
308
|
-
// SlotSource,
|
|
309
305
|
SulfurCubeArchetype,
|
|
310
306
|
Tag,
|
|
311
307
|
TestEnvironment,
|
|
@@ -320,7 +316,7 @@ export const {
|
|
|
320
316
|
WorldClock,
|
|
321
317
|
|
|
322
318
|
Atlas,
|
|
323
|
-
|
|
319
|
+
BlockStateDefinition,
|
|
324
320
|
Equipment,
|
|
325
321
|
Font,
|
|
326
322
|
ItemModelDefinition,
|
|
@@ -380,7 +376,6 @@ export {
|
|
|
380
376
|
BannerPatternClass,
|
|
381
377
|
ChatTypeClass,
|
|
382
378
|
DamageTypeClass,
|
|
383
|
-
// DecoratedPotPatternClass,
|
|
384
379
|
DialogClass,
|
|
385
380
|
EnchantmentClass,
|
|
386
381
|
EnchantmentProviderClass,
|
|
@@ -390,7 +385,6 @@ export {
|
|
|
390
385
|
LootTableClass,
|
|
391
386
|
PredicateClass,
|
|
392
387
|
RecipeClass,
|
|
393
|
-
// SlotSourceClass,
|
|
394
388
|
StructureClass,
|
|
395
389
|
SulfurCubeArchetypeClass,
|
|
396
390
|
TagClass,
|
|
@@ -407,7 +401,7 @@ export {
|
|
|
407
401
|
|
|
408
402
|
// Resourcepack resources
|
|
409
403
|
AtlasClass,
|
|
410
|
-
|
|
404
|
+
BlockStateDefinitionClass,
|
|
411
405
|
EquipmentClass,
|
|
412
406
|
FontClass,
|
|
413
407
|
ItemModelDefinitionClass,
|
|
@@ -673,11 +667,6 @@ type ContentStrategy =
|
|
|
673
667
|
* Will override the defined `default` strategy.
|
|
674
668
|
*/
|
|
675
669
|
| ContentStrategyKind<'recipe', NonNullable<RecipeClassArguments['onConflict']>>
|
|
676
|
-
/**
|
|
677
|
-
* The conflict strategy to use for Slot sources.
|
|
678
|
-
* Will override the defined `default` strategy.
|
|
679
|
-
*/
|
|
680
|
-
// | ContentStrategyKind<'slot_source', NonNullable<SlotSourceClassArguments['onConflict']>>
|
|
681
670
|
/**
|
|
682
671
|
* The conflict strategy to use for Sulfur cube archetypes.
|
|
683
672
|
* Will override the defined `default` strategy.
|
|
@@ -713,11 +702,6 @@ type ContentStrategy =
|
|
|
713
702
|
* Will override the defined `default` strategy.
|
|
714
703
|
*/
|
|
715
704
|
| ContentStrategyKind<'chat_type', NonNullable<ChatTypeClassArguments['onConflict']>>
|
|
716
|
-
/**
|
|
717
|
-
* The conflict strategy to use for Decorated pot patterns.
|
|
718
|
-
* Will override the defined `default` strategy.
|
|
719
|
-
*/
|
|
720
|
-
// | ContentStrategyKind<'decorated_pot_pattern', NonNullable<DecoratedPotPatternClassArguments['onConflict']>>
|
|
721
705
|
/**
|
|
722
706
|
* The conflict strategy to use for Dialogs.
|
|
723
707
|
* Will override the defined `default` strategy.
|
|
@@ -793,7 +777,7 @@ type ContentStrategy =
|
|
|
793
777
|
* The conflict strategy to use for Block states.
|
|
794
778
|
* Will override the defined `default` strategy.
|
|
795
779
|
*/
|
|
796
|
-
| ContentStrategyKind<'
|
|
780
|
+
| ContentStrategyKind<'block_definition', NonNullable<BlockStateDefinitionArguments<any>['onConflict']>>
|
|
797
781
|
/**
|
|
798
782
|
* The conflict strategy to use for Fonts.
|
|
799
783
|
* Will override the defined `default` strategy.
|
|
@@ -456,7 +456,7 @@ export class DataArrayClass<INITIAL extends DataArrayInitial> extends IterableDa
|
|
|
456
456
|
}
|
|
457
457
|
}
|
|
458
458
|
|
|
459
|
-
export function
|
|
459
|
+
export function DataIndexMapInternal<INITIAL extends DataIndexMapInitial>(
|
|
460
460
|
pack: SandstonePack,
|
|
461
461
|
initialize: INITIAL,
|
|
462
462
|
dataPoint?: DataPointClass<'storage'>,
|
|
@@ -486,11 +486,11 @@ export function DataIndexMap<INITIAL extends DataIndexMapInitial>(
|
|
|
486
486
|
}
|
|
487
487
|
}
|
|
488
488
|
|
|
489
|
-
export type
|
|
489
|
+
export type DataIndexMapType<INITIAL extends DataIndexMapInitial> = DataIndexMapClass<INITIAL> & {
|
|
490
490
|
[K in keyof INITIAL]: DataIndexMapInitial[`${any}${string}`]
|
|
491
491
|
} & { [K in string]: DataIndexMapInitial[`${any}${string}`] }
|
|
492
492
|
|
|
493
|
-
export function
|
|
493
|
+
export function DataArrayInternal<INITIAL extends DataArrayInitial>(
|
|
494
494
|
pack: SandstonePack,
|
|
495
495
|
initialize: INITIAL,
|
|
496
496
|
dataPoint?: DataPointClass<'storage'>,
|
|
@@ -520,6 +520,6 @@ export function DataArray<INITIAL extends DataArrayInitial>(
|
|
|
520
520
|
}
|
|
521
521
|
}
|
|
522
522
|
|
|
523
|
-
export type
|
|
523
|
+
export type DataArrayType<INITIAL extends DataArrayInitial> = DataArrayClass<INITIAL> & {
|
|
524
524
|
[K in keyof INITIAL]: DataArrayInitial[number]
|
|
525
525
|
} & { [K in number]: DataArrayInitial[number] }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { JSONTextComponent } from 'sandstone/arguments/jsonTextComponent'
|
|
2
|
+
import type { Node } from 'sandstone/core'
|
|
2
3
|
import type { ConditionNode } from '../flow'
|
|
3
4
|
import type { SelectorClass } from './Selector'
|
|
4
5
|
import type { NBTSerializable } from 'sandstone/arguments'
|
|
@@ -24,6 +25,15 @@ export class ConditionClass {
|
|
|
24
25
|
*/
|
|
25
26
|
declare readonly __conditionClassBrand: true
|
|
26
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Optional commands that must run before this condition's predicate is evaluated.
|
|
30
|
+
* Conditions that emit side-effect commands at construction (e.g. `data.equals`
|
|
31
|
+
* building `execute store result ...`) capture them here so the visitor pass can
|
|
32
|
+
* re-emit them at the right location — inline before the IfNode, or inside the
|
|
33
|
+
* child MCFunction when AND/OR extracts. Optional; pure predicates leave it unset.
|
|
34
|
+
*/
|
|
35
|
+
preNodes?: Node[]
|
|
36
|
+
|
|
27
37
|
/**
|
|
28
38
|
* @internal
|
|
29
39
|
*/
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { RESOURCE_PATHS, type SymbolResource } from 'sandstone/arguments'
|
|
2
|
-
import { ContainerNode } from '../../nodes'
|
|
3
|
-
import type { SandstoneCore } from '../../sandstoneCore'
|
|
4
|
-
import type { ResourceClassArguments, ResourceNode } from '../resource'
|
|
5
|
-
import { ResourceClass, jsonStringify } from '../resource'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* A node representing a Minecraft decorated pot pattern.
|
|
9
|
-
*/
|
|
10
|
-
export class DecoratedPotPatternNode extends ContainerNode implements ResourceNode<DecoratedPotPatternClass> {
|
|
11
|
-
constructor(
|
|
12
|
-
sandstoneCore: SandstoneCore,
|
|
13
|
-
public resource: DecoratedPotPatternClass,
|
|
14
|
-
) {
|
|
15
|
-
super(sandstoneCore)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
getValue = () => jsonStringify(this.resource.decoratedPotPatternJSON, this.resource._resourceType as keyof typeof RESOURCE_PATHS)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export type DecoratedPotPatternClassArguments = {
|
|
22
|
-
/**
|
|
23
|
-
* The decorated pot pattern's JSON.
|
|
24
|
-
*/
|
|
25
|
-
json: SymbolResource[(typeof DecoratedPotPatternClass)['resourceType']]
|
|
26
|
-
} & ResourceClassArguments<'default'>
|
|
27
|
-
|
|
28
|
-
export class DecoratedPotPatternClass extends ResourceClass<DecoratedPotPatternNode> {
|
|
29
|
-
static readonly resourceType = 'decorated_pot_pattern' as const
|
|
30
|
-
|
|
31
|
-
public decoratedPotPatternJSON: NonNullable<DecoratedPotPatternClassArguments['json']>
|
|
32
|
-
|
|
33
|
-
constructor(sandstoneCore: SandstoneCore, name: string, args: DecoratedPotPatternClassArguments) {
|
|
34
|
-
super(
|
|
35
|
-
sandstoneCore,
|
|
36
|
-
{ packType: sandstoneCore.pack.dataPack(), extension: 'json' },
|
|
37
|
-
DecoratedPotPatternNode,
|
|
38
|
-
DecoratedPotPatternClass.resourceType,
|
|
39
|
-
sandstoneCore.pack.resourceToPath(name, RESOURCE_PATHS[DecoratedPotPatternClass.resourceType].path),
|
|
40
|
-
args,
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
this.decoratedPotPatternJSON = args.json
|
|
44
|
-
|
|
45
|
-
this.handleConflicts()
|
|
46
|
-
}
|
|
47
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { RESOURCE_PATHS, type SymbolResource } from 'sandstone/arguments'
|
|
2
|
-
import { ContainerNode } from '../../nodes'
|
|
3
|
-
import type { SandstoneCore } from '../../sandstoneCore'
|
|
4
|
-
import type { ResourceClassArguments, ResourceNode } from '../resource'
|
|
5
|
-
import { ResourceClass, jsonStringify } from '../resource'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* A node representing a Minecraft slot source.
|
|
9
|
-
*/
|
|
10
|
-
export class SlotSourceNode extends ContainerNode implements ResourceNode<SlotSourceClass> {
|
|
11
|
-
constructor(
|
|
12
|
-
sandstoneCore: SandstoneCore,
|
|
13
|
-
public resource: SlotSourceClass,
|
|
14
|
-
) {
|
|
15
|
-
super(sandstoneCore)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
getValue = () => jsonStringify(this.resource.slotSourceJSON, this.resource._resourceType as keyof typeof RESOURCE_PATHS)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export type SlotSourceClassArguments = {
|
|
22
|
-
/**
|
|
23
|
-
* The slot source's JSON.
|
|
24
|
-
*/
|
|
25
|
-
json: NonNullable<SymbolResource[(typeof SlotSourceClass)['resourceType']]>
|
|
26
|
-
} & ResourceClassArguments<'default'>
|
|
27
|
-
|
|
28
|
-
export class SlotSourceClass extends ResourceClass<SlotSourceNode> {
|
|
29
|
-
static readonly resourceType = 'slot_source' as const
|
|
30
|
-
|
|
31
|
-
public slotSourceJSON: NonNullable<SlotSourceClassArguments['json']>
|
|
32
|
-
|
|
33
|
-
constructor(sandstoneCore: SandstoneCore, name: string, args: SlotSourceClassArguments) {
|
|
34
|
-
super(
|
|
35
|
-
sandstoneCore,
|
|
36
|
-
{ packType: sandstoneCore.pack.dataPack(), extension: 'json' },
|
|
37
|
-
SlotSourceNode,
|
|
38
|
-
SlotSourceClass.resourceType,
|
|
39
|
-
sandstoneCore.pack.resourceToPath(name, RESOURCE_PATHS[SlotSourceClass.resourceType].path),
|
|
40
|
-
args,
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
this.slotSourceJSON = args.json
|
|
44
|
-
|
|
45
|
-
this.handleConflicts()
|
|
46
|
-
}
|
|
47
|
-
}
|