sandstone 1.0.0-beta.4 → 1.0.0

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.
Files changed (150) hide show
  1. package/README.md +3 -4
  2. package/dist/_internal/index.d.ts +992 -335
  3. package/dist/_internal/index.js +516 -518
  4. package/dist/_internal/index.js.map +1 -1
  5. package/dist/_internal/types/arguments/coords.d.ts +13 -14
  6. package/dist/_internal/types/arguments/generated/_registry/activities.d.ts +1 -1
  7. package/dist/_internal/types/arguments/generated/_registry/atlases.d.ts +1 -1
  8. package/dist/_internal/types/arguments/generated/_registry/attribute_types.d.ts +1 -1
  9. package/dist/_internal/types/arguments/generated/_registry/attributes.d.ts +1 -1
  10. package/dist/_internal/types/arguments/generated/_registry/banner_patterns.d.ts +1 -1
  11. package/dist/_internal/types/arguments/generated/_registry/block_definitions.d.ts +1 -1
  12. package/dist/_internal/types/arguments/generated/_registry/block_entity_types.d.ts +1 -1
  13. package/dist/_internal/types/arguments/generated/_registry/block_types.d.ts +1 -1
  14. package/dist/_internal/types/arguments/generated/_registry/blocks.d.ts +1 -1
  15. package/dist/_internal/types/arguments/generated/_registry/chunk_statuses.d.ts +1 -1
  16. package/dist/_internal/types/arguments/generated/_registry/command_argument_types.d.ts +1 -1
  17. package/dist/_internal/types/arguments/generated/_registry/creative_mode_tabs.d.ts +1 -1
  18. package/dist/_internal/types/arguments/generated/_registry/custom_stats.d.ts +1 -1
  19. package/dist/_internal/types/arguments/generated/_registry/damage_types.d.ts +1 -1
  20. package/dist/_internal/types/arguments/generated/_registry/data_component_predicate_types.d.ts +1 -1
  21. package/dist/_internal/types/arguments/generated/_registry/data_component_types.d.ts +1 -1
  22. package/dist/_internal/types/arguments/generated/_registry/debug_subscriptions.d.ts +1 -1
  23. package/dist/_internal/types/arguments/generated/_registry/decorated_pot_patterns.d.ts +1 -1
  24. package/dist/_internal/types/arguments/generated/_registry/dimension_types.d.ts +1 -1
  25. package/dist/_internal/types/arguments/generated/_registry/enchantment_effect_component_types.d.ts +1 -1
  26. package/dist/_internal/types/arguments/generated/_registry/enchantment_entity_effect_types.d.ts +1 -1
  27. package/dist/_internal/types/arguments/generated/_registry/enchantment_level_based_value_types.d.ts +1 -1
  28. package/dist/_internal/types/arguments/generated/_registry/enchantment_location_based_effect_types.d.ts +1 -1
  29. package/dist/_internal/types/arguments/generated/_registry/enchantment_provider_types.d.ts +1 -1
  30. package/dist/_internal/types/arguments/generated/_registry/enchantment_value_effect_types.d.ts +1 -1
  31. package/dist/_internal/types/arguments/generated/_registry/enchantments.d.ts +1 -1
  32. package/dist/_internal/types/arguments/generated/_registry/equipments.d.ts +1 -1
  33. package/dist/_internal/types/arguments/generated/_registry/float_provider_types.d.ts +1 -1
  34. package/dist/_internal/types/arguments/generated/_registry/fluids.d.ts +1 -1
  35. package/dist/_internal/types/arguments/generated/_registry/game_events.d.ts +1 -1
  36. package/dist/_internal/types/arguments/generated/_registry/game_rules.d.ts +1 -1
  37. package/dist/_internal/types/arguments/generated/_registry/height_provider_types.d.ts +1 -1
  38. package/dist/_internal/types/arguments/generated/_registry/int_provider_types.d.ts +1 -1
  39. package/dist/_internal/types/arguments/generated/_registry/item_definitions.d.ts +1 -1
  40. package/dist/_internal/types/arguments/generated/_registry/items.d.ts +1 -1
  41. package/dist/_internal/types/arguments/generated/_registry/jukebox_songs.d.ts +1 -1
  42. package/dist/_internal/types/arguments/generated/_registry/loot_condition_types.d.ts +1 -1
  43. package/dist/_internal/types/arguments/generated/_registry/loot_function_types.d.ts +1 -1
  44. package/dist/_internal/types/arguments/generated/_registry/loot_nbt_provider_types.d.ts +1 -1
  45. package/dist/_internal/types/arguments/generated/_registry/loot_number_provider_types.d.ts +1 -1
  46. package/dist/_internal/types/arguments/generated/_registry/memory_module_types.d.ts +1 -1
  47. package/dist/_internal/types/arguments/generated/_registry/menus.d.ts +1 -1
  48. package/dist/_internal/types/arguments/generated/_registry/mob_effects.d.ts +1 -1
  49. package/dist/_internal/types/arguments/generated/_registry/models.d.ts +1 -1
  50. package/dist/_internal/types/arguments/generated/_registry/painting_variants.d.ts +1 -1
  51. package/dist/_internal/types/arguments/generated/_registry/particle_types.d.ts +1 -1
  52. package/dist/_internal/types/arguments/generated/_registry/point_of_interest_types.d.ts +1 -1
  53. package/dist/_internal/types/arguments/generated/_registry/post_effects.d.ts +1 -1
  54. package/dist/_internal/types/arguments/generated/_registry/potions.d.ts +1 -1
  55. package/dist/_internal/types/arguments/generated/_registry/recipe_displays.d.ts +1 -1
  56. package/dist/_internal/types/arguments/generated/_registry/recipe_serializers.d.ts +1 -1
  57. package/dist/_internal/types/arguments/generated/_registry/recipe_types.d.ts +1 -1
  58. package/dist/_internal/types/arguments/generated/_registry/recipes.d.ts +1 -1
  59. package/dist/_internal/types/arguments/generated/_registry/slot_displays.d.ts +1 -1
  60. package/dist/_internal/types/arguments/generated/_registry/slot_source_types.d.ts +1 -1
  61. package/dist/_internal/types/arguments/generated/_registry/structures.d.ts +1 -1
  62. package/dist/_internal/types/arguments/generated/_registry/tag_blocks.d.ts +1 -1
  63. package/dist/_internal/types/arguments/generated/_registry/tag_entity_types.d.ts +1 -1
  64. package/dist/_internal/types/arguments/generated/_registry/tag_fluids.d.ts +1 -1
  65. package/dist/_internal/types/arguments/generated/_registry/tag_items.d.ts +1 -1
  66. package/dist/_internal/types/arguments/generated/_registry/tag_point_of_interest_types.d.ts +1 -1
  67. package/dist/_internal/types/arguments/generated/_registry/tag_timelines.d.ts +1 -1
  68. package/dist/_internal/types/arguments/generated/_registry/tag_villager_trades.d.ts +1 -1
  69. package/dist/_internal/types/arguments/generated/_registry/tag_worldgen_structures.d.ts +1 -1
  70. package/dist/_internal/types/arguments/generated/_registry/textures.d.ts +1 -1
  71. package/dist/_internal/types/arguments/generated/_registry/ticket_types.d.ts +1 -1
  72. package/dist/_internal/types/arguments/generated/_registry/trigger_types.d.ts +1 -1
  73. package/dist/_internal/types/arguments/generated/_registry/trim_patterns.d.ts +1 -1
  74. package/dist/_internal/types/arguments/generated/_registry/villager_professions.d.ts +1 -1
  75. package/dist/_internal/types/arguments/generated/_registry/wolf_variants.d.ts +1 -1
  76. package/dist/_internal/types/arguments/generated/_registry/worldgen_biomes.d.ts +1 -1
  77. package/dist/_internal/types/arguments/generated/_registry/worldgen_density_function_types.d.ts +1 -1
  78. package/dist/_internal/types/arguments/generated/_registry/worldgen_features.d.ts +1 -1
  79. package/dist/_internal/types/arguments/generated/_registry/worldgen_material_conditions.d.ts +1 -1
  80. package/dist/_internal/types/arguments/generated/_registry/worldgen_noise_settings.d.ts +1 -1
  81. package/dist/_internal/types/arguments/generated/_registry/worldgen_noises.d.ts +1 -1
  82. package/dist/_internal/types/arguments/generated/_registry/worldgen_placed_features.d.ts +1 -1
  83. package/dist/_internal/types/arguments/generated/_registry/worldgen_placement_modifier_types.d.ts +1 -1
  84. package/dist/_internal/types/arguments/generated/_registry/worldgen_processor_lists.d.ts +1 -1
  85. package/dist/_internal/types/arguments/generated/_registry/worldgen_structure_sets.d.ts +1 -1
  86. package/dist/_internal/types/arguments/generated/_registry/worldgen_structure_types.d.ts +1 -1
  87. package/dist/_internal/types/arguments/generated/_registry/worldgen_structures.d.ts +1 -1
  88. package/dist/_internal/types/arguments/generated/_registry/worldgen_template_pools.d.ts +1 -1
  89. package/dist/_internal/types/arguments/generated/_registry/worldgen_tree_decorator_types.d.ts +1 -1
  90. package/dist/_internal/types/arguments/objective.d.ts +1 -1
  91. package/dist/_internal/types/arguments/range.d.ts +7 -7
  92. package/dist/_internal/types/commands/commands.d.ts +1 -4
  93. package/dist/_internal/types/commands/implementations/entity/execute.d.ts +9 -3
  94. package/dist/_internal/types/commands/implementations/server/gamerule.d.ts +38 -3
  95. package/dist/_internal/types/commands/implementations/server/random.d.ts +3 -3
  96. package/dist/_internal/types/commands/implementations/world/time.d.ts +6 -6
  97. package/dist/_internal/types/core/Macro.d.ts +0 -1
  98. package/dist/_internal/types/core/sandstoneCore.d.ts +8 -0
  99. package/dist/_internal/types/pack/pack.d.ts +2 -3
  100. package/dist/_internal/types/utils.d.ts +79 -0
  101. package/dist/_internal/types/variables/Score.d.ts +20 -4
  102. package/dist/_internal/types/variables/Sleep.d.ts +0 -3
  103. package/dist/_internal/types/variables/nbt/NBTs.d.ts +28 -1
  104. package/dist/browser/package.json +3 -0
  105. package/dist/browser/sandstone.esm.js +61581 -0
  106. package/dist/browser/sandstone.esm.js.map +1 -0
  107. package/dist/exports/commands/index.d.ts +1 -1
  108. package/dist/exports/core/index.d.ts +1 -1
  109. package/dist/exports/index.d.ts +1 -1
  110. package/dist/exports/index.js +1 -1
  111. package/package.json +22 -18
  112. package/src/arguments/coords.ts +11 -14
  113. package/src/arguments/objective.ts +1 -1
  114. package/src/arguments/range.ts +9 -9
  115. package/src/commands/commands.ts +3 -3
  116. package/src/commands/implementations/block/fill.ts +3 -3
  117. package/src/commands/implementations/block/setblock.ts +2 -2
  118. package/src/commands/implementations/entity/clear.ts +5 -1
  119. package/src/commands/implementations/entity/execute.ts +27 -6
  120. package/src/commands/implementations/entity/summon.ts +2 -2
  121. package/src/commands/implementations/player/dialog.ts +2 -2
  122. package/src/commands/implementations/player/give.ts +2 -2
  123. package/src/commands/implementations/player/particle.ts +2 -2
  124. package/src/commands/implementations/server/debug.ts +6 -1
  125. package/src/commands/implementations/server/function.ts +8 -3
  126. package/src/commands/implementations/server/gamerule.ts +75 -3
  127. package/src/commands/implementations/server/random.ts +3 -3
  128. package/src/commands/implementations/server/schedule.ts +10 -2
  129. package/src/commands/implementations/world/data.ts +5 -5
  130. package/src/commands/implementations/world/item.ts +2 -2
  131. package/src/commands/implementations/world/time.ts +5 -5
  132. package/src/core/Macro.ts +0 -2
  133. package/src/core/nodes.ts +17 -1
  134. package/src/core/resources/datapack/mcfunction.ts +12 -5
  135. package/src/core/resources/datapack/structure.ts +1 -0
  136. package/src/core/resources/resourcepack/sound.ts +2 -4
  137. package/src/core/sandstoneCore.ts +17 -0
  138. package/src/flow/conditions/variables/block.ts +2 -2
  139. package/src/flow/loops/for_i.ts +17 -15
  140. package/src/flow/loops/for_of.ts +0 -1
  141. package/src/pack/pack.ts +13 -6
  142. package/src/sandstone.ts +2 -2
  143. package/src/utils.ts +159 -0
  144. package/src/variables/Data.ts +3 -3
  145. package/src/variables/ItemPredicate.ts +2 -2
  146. package/src/variables/Score.ts +18 -10
  147. package/src/variables/Selector.ts +21 -3
  148. package/src/variables/Sleep.ts +2 -10
  149. package/src/variables/Trigger.ts +3 -5
  150. package/src/variables/nbt/NBTs.ts +70 -19
@@ -1,5 +1,6 @@
1
1
  import type { TimeArgument } from 'sandstone/arguments'
2
2
  import type { Macroable, Node } from 'sandstone/core'
3
+ import { isMacroArgument } from 'sandstone/core/Macro'
3
4
  import { ContainerCommandNode } from 'sandstone/core/nodes'
4
5
  import type { MCFunctionClass, MCFunctionNode } from 'sandstone/core/resources/datapack'
5
6
  import type { TagClass } from 'sandstone/core/resources/datapack/tag'
@@ -61,7 +62,10 @@ export class ScheduleCommand<MACRO extends boolean> extends CommandArguments<typ
61
62
  * ```
62
63
  */
63
64
  clear = (func: Macroable<MCFunctionClass<any, any> | string | TagClass<'function'>, MACRO>) => {
64
- const result = this.finalCommand(['clear', `${func}`])
65
+ const funcArg = isMacroArgument(this.sandstoneCore, func)
66
+ ? func
67
+ : `${func}`
68
+ const result = this.finalCommand(['clear', funcArg])
65
69
  return result
66
70
  }
67
71
 
@@ -101,6 +105,10 @@ export class ScheduleCommand<MACRO extends boolean> extends CommandArguments<typ
101
105
  return this.finalCommand(['function', undefined, delay, type], node)
102
106
  }
103
107
 
104
- return this.finalCommand(['function', `${func}`, delay, type], node)
108
+ const funcArg = isMacroArgument(this.sandstoneCore, func)
109
+ ? func
110
+ : `${func}`
111
+
112
+ return this.finalCommand(['function', funcArg, delay, type], node)
105
113
  }
106
114
  }
@@ -3,7 +3,7 @@ import { type MacroArgument, type Macroable } from 'sandstone/core'
3
3
  import { CommandNode } from 'sandstone/core/nodes'
4
4
  import { makeCallable } from 'sandstone/utils'
5
5
  import { DataPointClass, type VectorClass } from 'sandstone/variables'
6
- import { nbtStringifier } from 'sandstone/variables/nbt/NBTs'
6
+ import { nbtResolver } from 'sandstone/variables/nbt/NBTs'
7
7
  import { coordinatesParser, targetParser } from 'sandstone/variables/parsers'
8
8
  import { CommandArguments, type FinalCommandOutput } from '../../helpers'
9
9
 
@@ -156,7 +156,7 @@ export class DataMergeCommand<MACRO extends boolean> extends CommandArguments {
156
156
  * @param nbt The NBT to merge with.
157
157
  */
158
158
  block = (targetPos: Macroable<Coordinates<MACRO>, MACRO>, nbt: Macroable<NBTObject, MACRO>) =>
159
- this.finalCommand(['block', coordinatesParser(targetPos), nbtStringifier(nbt)])
159
+ this.finalCommand(['block', coordinatesParser(targetPos), nbtResolver(nbt)])
160
160
 
161
161
  /**
162
162
  * Merge the NBT of the given entity, with the given NBT.
@@ -165,7 +165,7 @@ export class DataMergeCommand<MACRO extends boolean> extends CommandArguments {
165
165
  * @param nbt The NBT to merge with.
166
166
  */
167
167
  entity = <T extends string>(target: Macroable<SingleEntityArgumentOf<MACRO, T>, MACRO>, nbt: Macroable<NBTObject, MACRO>) =>
168
- this.finalCommand(['entity', targetParser(target), nbtStringifier(nbt)])
168
+ this.finalCommand(['entity', targetParser(target), nbtResolver(nbt)])
169
169
 
170
170
  /**
171
171
  * Merge the NBT of the given storage path, with the given NBT.
@@ -174,7 +174,7 @@ export class DataMergeCommand<MACRO extends boolean> extends CommandArguments {
174
174
  * @param nbt The NBT to merge with.
175
175
  */
176
176
  storage = (target: Macroable<string, MACRO>, nbt: Macroable<NBTObject, MACRO>) =>
177
- this.finalCommand(['storage', target, nbtStringifier(nbt)])
177
+ this.finalCommand(['storage', target, nbtResolver(nbt)])
178
178
  }
179
179
 
180
180
  export class DataModifyFromCommand<MACRO extends boolean> extends CommandArguments {
@@ -302,7 +302,7 @@ export class DataModifyValuesCommand<MACRO extends boolean> extends CommandArgum
302
302
  /**
303
303
  * Modify the NBT with the given value.
304
304
  */
305
- value = (value: Macroable<NBTObject, MACRO>) => this.finalCommand(['value', nbtStringifier(value)])
305
+ value = (value: Macroable<NBTObject, MACRO>) => this.finalCommand(['value', nbtResolver(value)])
306
306
  }
307
307
 
308
308
  export class DataModifyTypeCommand<MACRO extends boolean> extends CommandArguments {
@@ -9,7 +9,7 @@ import type {
9
9
  import type { ItemModifierClass, Macroable } from 'sandstone/core'
10
10
  import { CommandNode } from 'sandstone/core/nodes'
11
11
  import type { LiteralUnion } from 'sandstone/utils'
12
- import { nbtStringifier } from 'sandstone/variables'
12
+ import { nbtResolver } from 'sandstone/variables'
13
13
  import { coordinatesParser, targetParser } from 'sandstone/variables/parsers'
14
14
  import type { FinalCommandOutput } from '../../helpers'
15
15
  import { CommandArguments } from '../../helpers'
@@ -44,7 +44,7 @@ export class ItemSourceCommand<MACRO extends boolean> extends CommandArguments {
44
44
  count?: Macroable<number, MACRO>,
45
45
  ) {
46
46
  if (typeof countOrNBT === 'object') {
47
- return this.finalCommand(['with', `${item}${nbtStringifier(countOrNBT)}`, count])
47
+ return this.finalCommand(['with', `${item}${nbtResolver(countOrNBT)}`, count])
48
48
  }
49
49
  return this.finalCommand(['with', item, countOrNBT])
50
50
  }
@@ -1,4 +1,4 @@
1
- import type { TimeArgument } from 'sandstone/arguments'
1
+ import type { Registry, TimeArgument } from 'sandstone/arguments'
2
2
  import type { Macroable, WorldClockClass } from 'sandstone/core'
3
3
  import { CommandNode } from 'sandstone/core/nodes'
4
4
  import { CommandArguments } from '../../helpers'
@@ -32,11 +32,11 @@ export class TimeArguments<MACRO extends boolean> extends CommandArguments {
32
32
  * Queries current time.
33
33
  *
34
34
  * @param type Specifies the time to query. Can be one of the following:
35
- * - `daytime`: the number of game ticks since dawn. (the in-game daytime modulo 24000)
36
35
  * - `gametime`: the age of the world in game ticks. (the game time modulo 2147483647)
37
- * - `day`: the number of in-game days passed. (the in-game daytime divided by 24000, then modulo 2147483647)
36
+ * - `time`: an alias for the `minecraft:overworld` timeline
37
+ * - Any data-driven timeline (eg. `minecraft:villager_schedule`)
38
38
  */
39
- query = (type: Macroable<LiteralUnion<'daytime' | 'gametime' | 'day'>, MACRO>) => this.finalCommand(['query', type])
39
+ query = (type: Macroable<'gametime' | 'time' | Registry['minecraft:timeline'], MACRO>) => this.finalCommand(['query', type])
40
40
 
41
41
  /**
42
42
  * Sets the in-game daytime.
@@ -46,7 +46,7 @@ export class TimeArguments<MACRO extends boolean> extends CommandArguments {
46
46
  * - `s`: a second, 20 gameticks;
47
47
  * - `t` (default and omitable): a single gametick; the default unit.
48
48
  */
49
- set = (time: Macroable<TimeArgument | LiteralUnion<'day' | 'night' | 'noon' | 'midnight'>, MACRO>) =>
49
+ set = (time: Macroable<TimeArgument | NamespacedLiteralUnion<'day' | 'night' | 'noon' | 'midnight'>, MACRO>) =>
50
50
  this.finalCommand(['set', time])
51
51
  }
52
52
 
package/src/core/Macro.ts CHANGED
@@ -39,8 +39,6 @@ export class MacroArgument {
39
39
 
40
40
  export type Macroable<T, MACRO extends boolean> = MACRO extends true ? T | MacroArgument : T
41
41
 
42
- export type MacroString<T, MACRO extends boolean> = MACRO extends true ? T | string : T
43
-
44
42
  export function isMacroArgument(core: SandstoneCore, arg: any) {
45
43
  if (typeof arg === 'object' && Object.hasOwn(arg, 'toMacro')) {
46
44
  // eslint-disable-next-line prefer-destructuring
package/src/core/nodes.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { SandstonePack } from 'sandstone/pack'
2
2
  import type { LoopArgument } from 'sandstone/variables'
3
+ import { ResolvedNBT } from 'sandstone/variables/nbt/NBTs'
3
4
  import * as util from 'util'
4
5
  import { formatDebugString } from '../utils'
5
6
  import { isMacroArgument, type MacroArgument } from './Macro'
@@ -110,10 +111,25 @@ export abstract class CommandNode<ARGS extends unknown[] = unknown[]> extends No
110
111
  if (arg !== undefined && arg !== null) {
111
112
  // Yes these are cursed, unfortunately, there's not really a better way to do this as visitors only visit the root nodes.
112
113
  if (typeof arg === 'object') {
113
- if (isMacroArgument(this.sandstoneCore, arg)) {
114
+ if (arg instanceof ResolvedNBT) {
115
+ // ResolvedNBT carries macro info forward from nbtResolver recursion,
116
+ // so we can detect $(...) substitutions that originated inside nested
117
+ // NBT values rather than from a top-level MacroArgument object.
118
+ if (arg.containsMacro) {
119
+ this.isMacro = true
120
+ }
121
+ filteredArgs.push(arg)
122
+ } else if (isMacroArgument(this.sandstoneCore, arg)) {
114
123
  this.isMacro = true
115
124
 
116
125
  filteredArgs.push((arg as MacroArgument).toMacro())
126
+ } else if (Object.hasOwn(arg, '_hasMacro') && (arg as { _hasMacro: boolean })._hasMacro) {
127
+ // Selector (and any similar complex arg that stringifies `$(...)`
128
+ // via its own toString) exposes macro info via _hasMacro so the
129
+ // command can still be flagged as a macro command.
130
+ this.isMacro = true
131
+
132
+ filteredArgs.push(arg)
117
133
  } else if (Object.hasOwn(arg, 'toLoop')) {
118
134
  filteredArgs.push((arg as LoopArgument).toLoop())
119
135
  } else {
@@ -19,8 +19,6 @@ import { CallableResourceClass } from '../resource'
19
19
  import { TagClass } from './tag'
20
20
  import type { DataPointClass } from 'sandstone/variables'
21
21
 
22
- const tags: Record<string, TagClass<'function'>> = {}
23
-
24
22
  // interface AttributeWrapper {
25
23
  // attributes: string[]
26
24
  // }
@@ -342,15 +340,20 @@ export class _RawMCFunctionClass<
342
340
  func = this as unknown as MCFunctionClass<undefined, undefined>
343
341
  }
344
342
  if (typeof tag === 'string') {
345
- if (tags[tag]) {
346
- tags[tag].push(func)
343
+ // Cache lives on SandstoneCore so its lifetime matches the core (cleared on reset).
344
+ // This prevents stale TagClass references from a previous pack surviving across
345
+ // watch-mode rebuilds and silently dropping the tag from the new build's output.
346
+ const existing = this.core.functionTags.get(tag)
347
+ if (existing) {
348
+ existing.push(func)
347
349
  } else {
348
- tags[tag] = new TagClass(this.core, 'function', tag, {
350
+ const newTag = new TagClass(this.core, 'function', tag, {
349
351
  values: [func],
350
352
  addToSandstoneCore: true,
351
353
  creator: 'sandstone',
352
354
  onConflict: 'append',
353
355
  })
356
+ this.core.functionTags.set(tag, newTag)
354
357
  }
355
358
  } else {
356
359
  tag.push(func)
@@ -391,6 +394,10 @@ export class _RawMCFunctionClass<
391
394
  false,
392
395
  )
393
396
  this.core.exitMCFunction()
397
+ // Mark as generated so the later generateResources iteration doesn't re-run
398
+ // the callback (which would re-execute with undefined params and append a
399
+ // second broken command to the body).
400
+ this.generated = true
394
401
  } else if (this.callback.length > 1) {
395
402
  throw new Error(`[MCFunctionClass] Function ${this.name} expects ${this.callback.length - 1} params at the call-site`)
396
403
  }
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable no-plusplus */
2
2
  /* eslint-disable max-len */
3
+ // TODO: prismarine-nbt uses Node.js zlib, needs browser-compatible alternative for playground
3
4
  import lodash from 'lodash'
4
5
  import prismarine, { type NBT } from 'prismarine-nbt'
5
6
  import {
@@ -5,8 +5,6 @@ import type { SandstoneCore } from '../../sandstoneCore'
5
5
  import type { ListResource, ResourceClassArguments, ResourceNode } from '../resource'
6
6
  import { ResourceClass, jsonStringify } from '../resource'
7
7
 
8
- const sounds: Map<string, SoundsClass> = new Map()
9
-
10
8
  /**
11
9
  * A node representing a Minecraft sound.
12
10
  */
@@ -63,10 +61,10 @@ export class SoundEventClass<Type extends SOUND_TYPES = SOUND_TYPES> extends Res
63
61
  }
64
62
 
65
63
  if (args.addToSounds) {
66
- let def = sounds.get(this.path[0])
64
+ let def = this.core.sounds.get(this.path[0])
67
65
 
68
66
  if (!def) {
69
- def = sounds
67
+ def = this.core.sounds
70
68
  .set(
71
69
  this.path[0],
72
70
  new SoundsClass(this.core, this.path[0], {
@@ -9,7 +9,9 @@ import type { MCMetaBranches } from './mcmeta'
9
9
  import { MCMetaCache } from './mcmeta'
10
10
  import type { AwaitNode } from './nodes'
11
11
  import type { _RawMCFunctionClass, MCFunctionClass, MCFunctionNode } from './resources/datapack/mcfunction'
12
+ import type { TagClass } from './resources/datapack/tag'
12
13
  import { SmithedDependencyClass } from './resources/dependency'
14
+ import type { SoundsClass } from './resources/resourcepack/sound'
13
15
  import { type ResourceClass, ResourceNodesMap } from './resources/resource'
14
16
  import { SmithedDependencyCache } from './smithed'
15
17
  import type { GenericCoreVisitor } from './visitors'
@@ -30,10 +32,22 @@ export class SandstoneCore {
30
32
 
31
33
  dependencies: ((() => Promise<true | false>) | true | false)[] = []
32
34
 
35
+ /** Cache of auto-generated function tags, keyed by tag name. Cleared on reset. */
36
+ functionTags: Map<string, TagClass<'function'>> = new Map()
37
+
38
+ /** Cache of auto-generated sounds.json definitions, keyed by namespace. Cleared on reset. */
39
+ sounds: Map<string, SoundsClass> = new Map()
40
+
41
+ /** Cache of auto-generated polling trigger check functions, keyed by polling interval. Cleared on reset. */
42
+ checkTriggers: Record<number, MCFunctionClass<undefined, undefined>> = {}
43
+
33
44
  constructor(public pack: SandstonePack) {
34
45
  this.resourceNodes = new ResourceNodesMap()
35
46
  this.mcfunctionStack = []
36
47
  this.awaitNodes = new Set()
48
+ this.functionTags = new Map()
49
+ this.sounds = new Map()
50
+ this.checkTriggers = {}
37
51
 
38
52
  // ESM is funny
39
53
 
@@ -54,6 +68,9 @@ export class SandstoneCore {
54
68
  this._mcMetaCache = undefined
55
69
  this._smithed = undefined
56
70
  this.dependencies = []
71
+ this.functionTags.clear()
72
+ this.sounds.clear()
73
+ this.checkTriggers = {}
57
74
  }
58
75
 
59
76
  /**
@@ -1,6 +1,6 @@
1
1
  import type { Coordinates, NBTObject } from 'sandstone/arguments'
2
2
  import type { SandstoneCore } from 'sandstone/core/sandstoneCore'
3
- import { coordinatesParser, nbtStringifier } from 'sandstone/variables'
3
+ import { coordinatesParser, nbtResolver } from 'sandstone/variables'
4
4
  import { SingleConditionNode } from '../condition'
5
5
  import type { Registry } from 'sandstone/arguments/generated/registry'
6
6
  import { blockStateStringifier } from 'sandstone/commands/implementations/block/setblock'
@@ -20,7 +20,7 @@ export class BlockConditionNode extends SingleConditionNode {
20
20
  const stateStr = this.state && Object.keys(this.state).length > 0
21
21
  ? blockStateStringifier(this.state)
22
22
  : ''
23
- const nbtStr = this.nbt ? nbtStringifier(this.nbt) : ''
23
+ const nbtStr = this.nbt ? nbtResolver(this.nbt).toString() : ''
24
24
  return ['block', coordinatesParser(this.position), `${this.block}${stateStr}${nbtStr}`]
25
25
  }
26
26
  }
@@ -4,7 +4,6 @@ import { LoopArgument } from 'sandstone/variables'
4
4
  import { conditionToNode, type Condition } from '..'
5
5
  import { IfStatement } from '../if_else'
6
6
  import { LoopNode } from '../loop'
7
- import { analyzeCondition, warnStaticallyFalseCondition } from './staticAnalysis'
8
7
 
9
8
  export class ForINode extends LoopNode {
10
9
  // eslint-disable-next-line max-len
@@ -24,21 +23,24 @@ export class ForINode extends LoopNode {
24
23
  () => new IfStatement(sandstoneCore, condition, () => new LoopArgument(sandstoneCore.pack)),
25
24
  )
26
25
 
27
- // Check if we can inline the first iteration via static analysis
28
- const analysisResult = typeof initialValue === 'number'
29
- ? analyzeCondition(condition, iterator, initialValue)
30
- : { canAnalyze: false as const }
26
+ // TODO: Move this to a visitor, it is too unsafe to have this because of `_continue`, `returnCmd` and nested loops
27
+ // // Check if we can inline the first iteration via static analysis
28
+ // const analysisResult = typeof initialValue === 'number'
29
+ // ? analyzeCondition(condition, iterator, initialValue)
30
+ // : { canAnalyze: false as const }
31
31
 
32
- if (analysisResult.canAnalyze) {
33
- if (analysisResult.result) {
34
- // Condition is statically true - inline the first iteration
35
- callback(iterator, _continue)
36
- iterate(iterator)
37
- } else {
38
- // Condition is statically false - loop will never execute
39
- warnStaticallyFalseCondition('for', `initial value ${initialValue} does not satisfy condition`)
40
- }
41
- }
32
+ // if (analysisResult.canAnalyze) {
33
+ // if (analysisResult.result) {
34
+ // // Condition is statically true - inline the first iteration
35
+ // console.log('#### Statically inlining first ForINode iteration')
36
+ // console.log(`##### ${sandstoneCore.getCurrentMCFunctionOrThrow().resource.name}`)
37
+ // callback(iterator, _continue)
38
+ // iterate(iterator)
39
+ // } else {
40
+ // // Condition is statically false - loop will never execute
41
+ // warnStaticallyFalseCondition('for', `initial value ${initialValue} does not satisfy condition`)
42
+ // }
43
+ // }
42
44
 
43
45
  super(
44
46
  sandstoneCore,
@@ -1,4 +1,3 @@
1
- import type { SubCommand } from 'sandstone/commands'
2
1
  import type { SandstoneCore } from 'sandstone/core/sandstoneCore'
3
2
  import type { DataPointClass, IterableDataClass, Score, StringDataPointClass } from 'sandstone/variables'
4
3
  import { LoopArgument } from 'sandstone/variables'
package/src/pack/pack.ts CHANGED
@@ -481,6 +481,9 @@ export class SandstonePack {
481
481
  }
482
482
 
483
483
  resourceToPath = (name: string, resourceFolders?: readonly string[]): ResourcePath => {
484
+ if (name === '') {
485
+ throw new Error('A resource name must not be empty!')
486
+ }
484
487
  let namespace = this.defaultNamespace
485
488
  let fullName = name
486
489
 
@@ -503,6 +506,11 @@ export class SandstonePack {
503
506
  }
504
507
 
505
508
  registerNewObjective = (objective: ObjectiveClass) => {
509
+ for (const obj of this.objectives) {
510
+ if (obj.name === objective.name) {
511
+ return
512
+ }
513
+ }
506
514
  this.objectives.add(objective)
507
515
  }
508
516
 
@@ -523,8 +531,7 @@ export class SandstonePack {
523
531
  * @param options.alreadyExists If true, the objective will not be registered as a new one. Defaults to false.
524
532
  * @param options.useDefaultNamespace If true (the default), the objective will be created in the default namespace. \
525
533
  * For example, if the default namespace is `sandstone`, then the objective will be created as `sandstone.objective_name`. \
526
- * If false, the objective will be created without a namespace. \
527
- * This argument is ignored if the `name` already contains a namespace (determined by the presence of a dot `"."` or a double underscore `"__"`).
534
+ * If false, the objective will be created without a namespace.
528
535
  */
529
536
  create: (
530
537
  name: string,
@@ -573,7 +580,7 @@ export class SandstonePack {
573
580
  } else {
574
581
  this.namedVariableIds[name] ??= 0
575
582
 
576
- return `${name}_${this.namedVariableIds[name]}_${this.packUid}`
583
+ return `${name}_${this.namedVariableIds[name]++}_${this.packUid}`
577
584
  }
578
585
  }
579
586
 
@@ -971,7 +978,7 @@ export class SandstonePack {
971
978
 
972
979
  this.ticked[runEvery] = this.Tag('function', name, [])
973
980
 
974
- this.MCFunction(name, (self) => {
981
+ const looped = this.MCFunction(name, (self) => {
975
982
  this.commands.functionCmd(this.ticked[runEvery])
976
983
 
977
984
  self.schedule.function(runEvery, 'replace')
@@ -981,7 +988,7 @@ export class SandstonePack {
981
988
  this.startAllTicked = this.MCFunction(`__sandstone:ticked/start/${this.packUid.toLowerCase()}`)
982
989
  this.loadTags.load.push(this.startAllTicked)
983
990
  }
984
- this.startAllTicked.push(() => this.commands.schedule.function(this.ticked[runEvery], runEvery, 'replace'))
991
+ this.startAllTicked.push(() => looped())
985
992
 
986
993
  return this.ticked[runEvery]
987
994
  })()
@@ -1009,7 +1016,7 @@ export class SandstonePack {
1009
1016
  this.tickedCommands[runEvery].push(callback)
1010
1017
  }
1011
1018
 
1012
- sleep = (delay: TimeArgument): PromiseLike<SleepClass> => new SleepClass(this.core, delay).promise()
1019
+ sleep = (delay: TimeArgument) => new SleepClass(this.core, delay)
1013
1020
 
1014
1021
  Loop = () => new LoopArgument(this)
1015
1022
 
package/src/sandstone.ts CHANGED
@@ -45,7 +45,7 @@ import type {
45
45
  import { SandstonePack } from './pack'
46
46
  import type { Flow } from './flow'
47
47
  import type { BASIC_CONFLICT_STRATEGIES, LiteralUnion, NamespacedLiteralUnion, SetType } from './utils'
48
- import { Set } from './utils'
48
+ import { randomUUID, Set } from './utils'
49
49
  import * as coordinates from './variables/Coordinates'
50
50
  import { ResolveNBTPart } from './variables/ResolveNBT'
51
51
  import { setSandstoneContext, hasContext, getSandstoneContext } from './context'
@@ -98,7 +98,7 @@ export function resetSandstonePack(): void {
98
98
  }
99
99
  }
100
100
 
101
- export { LiteralUnion, NamespacedLiteralUnion, Set, SetType }
101
+ export { LiteralUnion, NamespacedLiteralUnion, randomUUID, Set, SetType }
102
102
 
103
103
  export { ResolveNBTPart }
104
104
 
package/src/utils.ts CHANGED
@@ -573,3 +573,162 @@ type McdocNegatableIDMapBase<Dispatcher, UnknownValue> = MemberModifiers<Dispatc
573
573
  export type McdocNegatableIDMap<Dispatcher, UnknownValue> = Partial<McdocNegatableIDMapBase<Dispatcher, UnknownValue>> & {
574
574
  [K in keyof MemberModifiers<Dispatcher>]-?: Record<K, McdocNegatableIDMapBase<Dispatcher, UnknownValue>[K]>
575
575
  }[keyof MemberModifiers<Dispatcher>]
576
+
577
+ export type Enumerate<
578
+ N extends number,
579
+ Acc extends number[] = [],
580
+ > = ( number extends N
581
+ ? number // Guard against wide 'number'
582
+ : Acc["length"] extends N
583
+ ? Acc[number]
584
+ : Enumerate<N, [...Acc, Acc["length"]]>
585
+ )
586
+
587
+ export type BuildTuple<N extends number, T extends never[] = []> = (
588
+ number extends N
589
+ ? never[] // Guard against wide 'number'
590
+ : T['length'] extends N
591
+ ? T
592
+ : BuildTuple<N, [...T, never]>
593
+ )
594
+
595
+ export type Increment<N extends number> = (
596
+ number extends N
597
+ ? number // Guard against wide 'number'
598
+ : [...BuildTuple<N>, never]['length'] & number
599
+ )
600
+
601
+ /**
602
+ * `0..`
603
+ *
604
+ * Can be made exclusive of zero via `Exclude<PositiveNumber<N>, 0>`
605
+ */
606
+ export type PositiveNumber<T extends number> = (
607
+ number extends T
608
+ ? never
609
+ : `${T}` extends `-${string}`
610
+ ? never
611
+ : T
612
+ )
613
+
614
+ /**
615
+ * `..<0`; Cannot be zero nor above zero.
616
+ *
617
+ * Can be made inclusive via `NegativeNumber<N> | 0`
618
+ */
619
+ export type NegativeNumber<T extends number> = (
620
+ number extends T
621
+ ? never
622
+ : `${T}` extends `-${string}`
623
+ ? T
624
+ : never
625
+ )
626
+
627
+ /**
628
+ * No decimal allowed.
629
+ */
630
+ export type WholeNumber<T extends number> = (
631
+ number extends T
632
+ ? never
633
+ : `${T}` extends `${string}.${string}`
634
+ ? never
635
+ : T
636
+ )
637
+
638
+ /**
639
+ * `0..` & no decimal allowed.
640
+ *
641
+ * Can be made exclusive of zero via `Exclude<PositiveWholeNumber<N>, 0>`
642
+ */
643
+ export type PositiveWholeNumber<N extends number> = (
644
+ PositiveNumber<N> extends never
645
+ ? never
646
+ : WholeNumber<N> extends never
647
+ ? never
648
+ : N
649
+ )
650
+
651
+ /**
652
+ * `..-1` & no decimal allowed.
653
+ *
654
+ * Can include zero via `NegativeWholeNumber<N> | 0`
655
+ */
656
+ export type NegativeWholeNumber<T extends number> = (
657
+ number extends T
658
+ ? never
659
+ : `${T}` extends `${string}.${string}`
660
+ ? never
661
+ : `${T}` extends `-${string}`
662
+ ? T
663
+ : never
664
+ )
665
+
666
+ /**
667
+ * `0<..<1`; Cannot be zero nor one, below zero, nor above one.
668
+ *
669
+ * Can be made inclusive by adding unioned `0`/`1` literals. (eg. `PositiveFraction<N> | 0 | 1`)
670
+ */
671
+ export type PositiveFraction<T extends number> = (
672
+ number extends T
673
+ ? never
674
+ : `${T}` extends `0.${string}`
675
+ ? T
676
+ : never
677
+ )
678
+
679
+ /**
680
+ * `-1<..<0`; Cannot be negative one nor zero, below negative one, nor above zero.
681
+ *
682
+ * Can be made inclusive by adding unioned `-1`/`0` literals. (eg. `NegativeFraction<N> | -1 | 0`)
683
+ */
684
+ export type NegativeFraction<T extends number> = (
685
+ number extends T
686
+ ? never
687
+ : `${T}` extends `-0.${string}`
688
+ ? T
689
+ : never
690
+ )
691
+
692
+ /**
693
+ * Converts a positive numeric literal to its negative counterpart.
694
+ * Example: ToNegative<5> -> -5
695
+ */
696
+ export type ToNegative<T extends number> = (
697
+ T extends 0
698
+ ? 0
699
+ : `-${T}` extends `${infer N extends number}`
700
+ ? N
701
+ : never
702
+ )
703
+
704
+ /**
705
+ * Checks if a number literal is negative by checking its string representation.
706
+ */
707
+ export type IsNegative<N extends number> = `${N}` extends `-${string}` ? true : false
708
+
709
+ /**
710
+ * Gets the absolute value of a number.
711
+ */
712
+ export type Abs<N extends number> = `${N}` extends `-${infer U extends number}` ? U : N
713
+
714
+ /**
715
+ * Creates a union of numbers from 0 to N-1.
716
+ */
717
+ export type RangeTo<N extends number> = Enumerate<N>
718
+
719
+ /**
720
+ * Generates a union of numbers between two positive integers (inclusive).
721
+ */
722
+ export type PositiveRange<Start extends number, End extends number> = Exclude<Enumerate<Increment<End>>, Enumerate<Start>>
723
+
724
+ /**
725
+ * The core logic for NBTInt<{ min: -5, max: 2 }>
726
+ */
727
+ export type SmartRange<Min extends number, Max extends number> = (
728
+ IsNegative<Min> extends true
729
+ ? IsNegative<Max> extends true
730
+ ? ToNegative<PositiveRange<Abs<Max>, Abs<Min>>>
731
+ : ToNegative<PositiveRange<1, Abs<Min>>> | 0 | PositiveRange<1, Max>
732
+ :
733
+ PositiveRange<Min, Max>
734
+ )
@@ -4,7 +4,7 @@ import type { DataModifyTypeCommand, DataModifyValuesCommand, StoreType } from '
4
4
  import type { SandstonePack } from 'sandstone/pack'
5
5
  import { DataPointPickClass, isMacroArgument, MacroArgument, MacroLiteral } from '../core/Macro'
6
6
  import type { ConditionTextComponentClass } from './abstractClasses'
7
- import { nbtStringifier } from './nbt/NBTs'
7
+ import { nbtResolver } from './nbt/NBTs'
8
8
  import { Score } from './Score'
9
9
 
10
10
  export type DATA_TYPES = 'entity' | 'block' | 'storage'
@@ -50,7 +50,7 @@ export function NBTpathToString(pack: SandstonePack, paths: DATA_PATH[]) {
50
50
  // Special case: array index like [0] - don't add 'd' suffix
51
51
  parsedPaths.push(`[${path[0]}]`)
52
52
  } else {
53
- parsedPaths.push(nbtStringifier(path))
53
+ parsedPaths.push(nbtResolver(path).toString())
54
54
  }
55
55
  } else if (path !== '') {
56
56
  parsedPaths.push(`${path}${i === paths.length - 1 ? '' : '.'}`)
@@ -307,7 +307,7 @@ export class DataPointClass<TYPE extends DATA_TYPES = any>
307
307
  /**
308
308
  * @internal
309
309
  */
310
- toNBT = () => nbtStringifier(this._toChatComponent() as NBTObject)
310
+ toNBT = () => nbtResolver(this._toChatComponent() as NBTObject).toString()
311
311
  }
312
312
 
313
313
  export class StringDataPointClass<TYPE extends DATA_TYPES = any> extends DataPointClass {
@@ -2,7 +2,7 @@ import type { SymbolDataComponent, SymbolDataComponentPredicate } from 'sandston
2
2
  import type { Registry } from 'sandstone/arguments/generated/registry'
3
3
  import type { MinMaxBounds } from 'sandstone/arguments/generated/data/util'
4
4
  import type { SandstonePack } from 'sandstone/pack'
5
- import { nbtStringifier } from 'sandstone/variables/nbt/NBTs'
5
+ import { nbtResolver } from 'sandstone/variables/nbt/NBTs'
6
6
  import * as util from 'util'
7
7
  import { formatDebugString } from '../utils'
8
8
  import type { MacroArgument } from 'sandstone/core/Macro'
@@ -196,7 +196,7 @@ export class ItemPredicateClass {
196
196
  return `${prefix}${test.component}`
197
197
  }
198
198
 
199
- const valueStr = nbtStringifier(test.value)
199
+ const valueStr = nbtResolver(test.value).toString()
200
200
  return `${prefix}${test.component}${test.operator}${valueStr}`
201
201
  }
202
202