minecraft-datapack-language 15.4.39__py3-none-any.whl → 15.4.41__py3-none-any.whl
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.
- minecraft_datapack_language/_version.py +2 -2
- minecraft_datapack_language/cli.py +10 -0
- minecraft_datapack_language/mdl_compiler.py +10 -6
- minecraft_datapack_language/python_api.py +100 -25
- {minecraft_datapack_language-15.4.39.dist-info → minecraft_datapack_language-15.4.41.dist-info}/METADATA +5 -5
- {minecraft_datapack_language-15.4.39.dist-info → minecraft_datapack_language-15.4.41.dist-info}/RECORD +10 -10
- {minecraft_datapack_language-15.4.39.dist-info → minecraft_datapack_language-15.4.41.dist-info}/WHEEL +0 -0
- {minecraft_datapack_language-15.4.39.dist-info → minecraft_datapack_language-15.4.41.dist-info}/entry_points.txt +0 -0
- {minecraft_datapack_language-15.4.39.dist-info → minecraft_datapack_language-15.4.41.dist-info}/licenses/LICENSE +0 -0
- {minecraft_datapack_language-15.4.39.dist-info → minecraft_datapack_language-15.4.41.dist-info}/top_level.txt +0 -0
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
28
28
|
commit_id: COMMIT_ID
|
29
29
|
__commit_id__: COMMIT_ID
|
30
30
|
|
31
|
-
__version__ = version = '15.4.
|
32
|
-
__version_tuple__ = version_tuple = (15, 4,
|
31
|
+
__version__ = version = '15.4.41'
|
32
|
+
__version_tuple__ = version_tuple = (15, 4, 41)
|
33
33
|
|
34
34
|
__commit_id__ = commit_id = None
|
@@ -7,6 +7,7 @@ import argparse
|
|
7
7
|
import sys
|
8
8
|
import os
|
9
9
|
from pathlib import Path
|
10
|
+
import shutil
|
10
11
|
from .mdl_lexer import MDLLexer
|
11
12
|
from .mdl_parser import MDLParser
|
12
13
|
from .mdl_compiler import MDLCompiler
|
@@ -37,6 +38,7 @@ Examples:
|
|
37
38
|
build_parser.add_argument('-o', '--output', required=True, help='Output directory for the datapack')
|
38
39
|
build_parser.add_argument('--verbose', action='store_true', help='Show detailed output')
|
39
40
|
build_parser.add_argument('--wrapper', help='Optional wrapper directory name for the datapack output')
|
41
|
+
build_parser.add_argument('--no-zip', action='store_true', help='Do not create a zip archive (zip is created by default)')
|
40
42
|
|
41
43
|
# Check command
|
42
44
|
check_parser = subparsers.add_parser('check', help='Check MDL files for syntax errors')
|
@@ -150,6 +152,14 @@ def build_command(args):
|
|
150
152
|
output_dir = output_dir / args.wrapper
|
151
153
|
compiler = MDLCompiler()
|
152
154
|
output_path = compiler.compile(final_ast, str(output_dir))
|
155
|
+
|
156
|
+
# Zip the datapack by default unless disabled
|
157
|
+
if not getattr(args, 'no_zip', False):
|
158
|
+
base_name = str(Path(output_path))
|
159
|
+
# Create archive next to the output directory (base_name.zip)
|
160
|
+
archive_path = shutil.make_archive(base_name, 'zip', root_dir=str(Path(output_path)))
|
161
|
+
if args.verbose:
|
162
|
+
print(f"Created archive: {archive_path}")
|
153
163
|
|
154
164
|
print(f"Successfully built datapack: {output_path}")
|
155
165
|
return 0
|
@@ -225,10 +225,11 @@ class MDLCompiler:
|
|
225
225
|
print(f"Tag {tag.tag_type}: {tag.name} -> {tag_file} (placeholder)")
|
226
226
|
else:
|
227
227
|
# Write simple values list
|
228
|
-
|
229
|
-
#
|
230
|
-
if ":" in tag.name
|
231
|
-
|
228
|
+
# For item tags, the TagDeclaration.name may include namespace:name
|
229
|
+
# The output filename should be the local name (after ':') if present
|
230
|
+
name_for_file = tag.name.split(":", 1)[1] if ":" in tag.name else tag.name
|
231
|
+
tag_file = tag_dir / f"{name_for_file}.json"
|
232
|
+
values = [tag.name if ":" in tag.name else f"{self.current_namespace}:{tag.name}"]
|
232
233
|
tag_data = {"values": values}
|
233
234
|
with open(tag_file, 'w') as f:
|
234
235
|
json.dump(tag_data, f, indent=2)
|
@@ -249,11 +250,14 @@ class MDLCompiler:
|
|
249
250
|
load_file = functions_dir / "load.mcfunction"
|
250
251
|
with open(load_file, 'w') as f:
|
251
252
|
f.write(load_content)
|
252
|
-
# Ensure minecraft load tag points to namespace:load
|
253
|
+
# Ensure minecraft load tag points to namespace:load
|
253
254
|
tags_fn_dir = self.output_dir / "data" / "minecraft" / self.dir_map.tags_function
|
254
255
|
tags_fn_dir.mkdir(parents=True, exist_ok=True)
|
255
256
|
load_tag_file = tags_fn_dir / "load.json"
|
256
|
-
|
257
|
+
# If there are explicit on_load hooks, include them; otherwise reference namespace:load
|
258
|
+
values = [f"{self.current_namespace}:load"]
|
259
|
+
if has_on_load:
|
260
|
+
values = [f"{hook.namespace}:{hook.name}" for hook in hooks if hook.hook_type == "on_load"]
|
257
261
|
with open(load_tag_file, 'w') as f:
|
258
262
|
json.dump({"values": values}, f, indent=2)
|
259
263
|
|
@@ -129,33 +129,108 @@ class Namespace:
|
|
129
129
|
self._functions: List[FunctionDeclaration] = []
|
130
130
|
self._hooks: List = []
|
131
131
|
|
132
|
-
def function(self, name: str, *commands_or_builder: Union[str, Callable[
|
133
|
-
builder
|
134
|
-
|
135
|
-
# If a single callable is given, treat it as a builder lambda
|
132
|
+
def function(self, name: str, *commands_or_builder: Union[str, Callable["FunctionBuilder"], None]):
|
133
|
+
# Case 1: builder callable - use FunctionBuilder API directly
|
136
134
|
if len(commands_or_builder) == 1 and callable(commands_or_builder[0]):
|
135
|
+
builder = FunctionBuilder(self._pack, self, name)
|
137
136
|
commands_or_builder[0](builder)
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
137
|
+
func_node = FunctionDeclaration(namespace=self.name, name=name, scope=None, body=builder._body)
|
138
|
+
self._functions.append(func_node)
|
139
|
+
return func_node
|
140
|
+
|
141
|
+
# Case 2: string-based function body - try parsing as MDL source to build real AST
|
142
|
+
body_lines: List[str] = []
|
143
|
+
for cmd in commands_or_builder:
|
144
|
+
if isinstance(cmd, str):
|
145
|
+
body_lines.append(cmd)
|
146
|
+
|
147
|
+
if body_lines:
|
148
|
+
try:
|
149
|
+
from .mdl_parser import MDLParser
|
150
|
+
pack_decl = self._pack._pack
|
151
|
+
|
152
|
+
# Heuristic: attempt MDL parse only if there are control structures or var declarations
|
153
|
+
looks_like_mdl = any(
|
154
|
+
l.strip().startswith(("var ", "if ", "while ", "on_load", "on_tick")) or
|
155
|
+
l.strip().endswith("{") or l.strip() == "}" or "$" in l
|
156
|
+
for l in body_lines
|
157
|
+
)
|
158
|
+
if looks_like_mdl:
|
159
|
+
normalized: List[str] = []
|
160
|
+
for line in body_lines:
|
161
|
+
s = line.strip()
|
162
|
+
if not s:
|
163
|
+
continue
|
164
|
+
if s.startswith("say "):
|
165
|
+
msg = s[len("say ") :].strip()
|
166
|
+
if not (msg.startswith('"') and msg.endswith('"')):
|
167
|
+
msg = f'"{msg}"'
|
168
|
+
normalized.append(f"say {msg};")
|
169
|
+
elif s.startswith("exec "):
|
170
|
+
if not s.endswith(";"):
|
171
|
+
s = s + ";"
|
172
|
+
normalized.append(s)
|
173
|
+
elif s.startswith("var "):
|
174
|
+
if not s.endswith(";"):
|
175
|
+
s = s + ";"
|
176
|
+
normalized.append(s)
|
177
|
+
elif s.endswith("{") or s == "}" or s.endswith("}"):
|
178
|
+
normalized.append(s)
|
179
|
+
else:
|
180
|
+
# Heuristic: detect assignment like name<scope> = expr
|
181
|
+
try:
|
182
|
+
import re as _re
|
183
|
+
if _re.match(r'^[A-Za-z_][A-Za-z0-9_]*(<[^>]+>)?\s*=\s*', s):
|
184
|
+
if not s.endswith(";"):
|
185
|
+
s = s + ";"
|
186
|
+
normalized.append(s)
|
187
|
+
else:
|
188
|
+
# Unknown command; wrap as single-line raw so parser accepts it
|
189
|
+
normalized.append(f"$!raw {s} raw!$")
|
190
|
+
except Exception:
|
191
|
+
normalized.append(f"$!raw {s} raw!$")
|
192
|
+
|
193
|
+
src_lines = [
|
194
|
+
f'pack "{pack_decl.name}" "{pack_decl.description}" {pack_decl.pack_format};',
|
195
|
+
f'namespace "{self.name}";',
|
196
|
+
f'function {self.name}:{name} {{'
|
197
|
+
]
|
198
|
+
src_lines.extend(normalized)
|
199
|
+
src_lines.append('}')
|
200
|
+
source = "\n".join(src_lines)
|
201
|
+
parser = MDLParser("<python_api>")
|
202
|
+
ast = parser.parse(source)
|
203
|
+
if ast.variables:
|
204
|
+
self._pack._variables.extend(ast.variables)
|
205
|
+
if ast.tags:
|
206
|
+
self._pack._tags.extend(ast.tags)
|
207
|
+
if ast.hooks:
|
208
|
+
self._pack._hooks.extend([(h.hook_type, f"{h.namespace}:{h.name}", h.scope) for h in ast.hooks])
|
209
|
+
if ast.functions:
|
210
|
+
fn = ast.functions[0]
|
211
|
+
self._functions.append(fn)
|
212
|
+
return fn
|
213
|
+
except Exception:
|
214
|
+
pass
|
215
|
+
|
216
|
+
# Fallback: interpret simple strings
|
217
|
+
builder = FunctionBuilder(self._pack, self, name)
|
218
|
+
for cmd in body_lines:
|
219
|
+
stripped = cmd.strip().rstrip(";")
|
220
|
+
if stripped.startswith("say "):
|
221
|
+
msg = stripped[len("say ") :].strip()
|
222
|
+
if msg.startswith('"') and msg.endswith('"'):
|
223
|
+
msg = msg[1:-1]
|
224
|
+
builder.say(msg)
|
225
|
+
elif stripped.startswith("exec "):
|
226
|
+
target = stripped[len("exec ") :].strip()
|
227
|
+
scope = None
|
228
|
+
if "<" in target and target.endswith(">"):
|
229
|
+
scope = target[target.index("<") :]
|
230
|
+
target = target[: target.index("<")]
|
231
|
+
builder.exec(target, scope)
|
232
|
+
else:
|
233
|
+
builder.raw(cmd)
|
159
234
|
|
160
235
|
func_node = FunctionDeclaration(namespace=self.name, name=name, scope=None, body=builder._body)
|
161
236
|
self._functions.append(func_node)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: minecraft-datapack-language
|
3
|
-
Version: 15.4.
|
3
|
+
Version: 15.4.41
|
4
4
|
Summary: Compile MDL language with explicit scoping into a Minecraft datapack (1.21+ ready). Features variables, control flow, error handling, and VS Code extension.
|
5
5
|
Project-URL: Homepage, https://www.mcmdl.com
|
6
6
|
Project-URL: Documentation, https://www.mcmdl.com/docs
|
@@ -146,17 +146,17 @@ namespace "game";
|
|
146
146
|
var num player_score<@s> = 0;
|
147
147
|
var num team_score<@a[team=red]> = 0;
|
148
148
|
|
149
|
-
function game:start
|
149
|
+
function game:start {
|
150
150
|
player_score<@s> = 100;
|
151
151
|
say "Welcome! Your score is $player_score<@s>$";
|
152
152
|
}
|
153
153
|
|
154
|
-
on_load game:start
|
154
|
+
on_load game:start;
|
155
155
|
```
|
156
156
|
|
157
157
|
### Control Structures
|
158
158
|
```mdl
|
159
|
-
function game:check_score
|
159
|
+
function game:check_score {
|
160
160
|
if $player_score<@s>$ > 10 {
|
161
161
|
say "Great score!";
|
162
162
|
player_score<@s> = $player_score<@s>$ + 5;
|
@@ -185,7 +185,7 @@ tag structure "wizard_tower" "structures/wizard_tower.json";
|
|
185
185
|
|
186
186
|
### Raw Blocks and Say Commands
|
187
187
|
```mdl
|
188
|
-
function game:special_effect
|
188
|
+
function game:special_effect {
|
189
189
|
$!raw
|
190
190
|
execute as @s run particle minecraft:explosion ~ ~ ~ 1 1 1 0 10
|
191
191
|
execute as @s run playsound minecraft:entity.player.levelup player @s ~ ~ ~ 1 1
|
@@ -1,18 +1,18 @@
|
|
1
1
|
minecraft_datapack_language/__init__.py,sha256=0KVXBE4ScRaRUrf83aA2tVB-y8A_jplyaxVvtHH6Uw0,1199
|
2
|
-
minecraft_datapack_language/_version.py,sha256=
|
2
|
+
minecraft_datapack_language/_version.py,sha256=7O183XaGv-j8PcITk4W9KCJ0Sx7gIXLD8SvmgNMUBnU,708
|
3
3
|
minecraft_datapack_language/ast_nodes.py,sha256=nbWrRz137MGMRpmnq8QkXNzrtlaCgyPEknytbkrS_M8,3899
|
4
|
-
minecraft_datapack_language/cli.py,sha256=
|
4
|
+
minecraft_datapack_language/cli.py,sha256=R4QZYtox-Da9B8pr_kCg_9qc9aI-ORTah7kMkhsI5tw,10373
|
5
5
|
minecraft_datapack_language/dir_map.py,sha256=HmxFkuvWGkzHF8o_GFb4BpuMCRc6QMw8UbmcAI8JVdY,1788
|
6
|
-
minecraft_datapack_language/mdl_compiler.py,sha256=
|
6
|
+
minecraft_datapack_language/mdl_compiler.py,sha256=CaIHmsv4cjZa70PpderdgPlH6yNaB86WQhGyIaB0040,41396
|
7
7
|
minecraft_datapack_language/mdl_errors.py,sha256=r0Gu3KhoX1YLPAVW_iO7Q_fPgaf_Dv9tOGSOdKNSzmw,16114
|
8
8
|
minecraft_datapack_language/mdl_lexer.py,sha256=CjbEUpuuF4eU_ucA_WIhw6wSMcHGk2BchtQ0bLAGvwg,22033
|
9
9
|
minecraft_datapack_language/mdl_linter.py,sha256=z85xoAglENurCh30bR7kEHZ_JeMxcYaLDcGNRAl-RAI,17253
|
10
10
|
minecraft_datapack_language/mdl_parser.py,sha256=aQPKcmATM9BOMzO7vCXmMdxU1qjOJNLCSAKJopu5h3g,23429
|
11
|
-
minecraft_datapack_language/python_api.py,sha256=
|
11
|
+
minecraft_datapack_language/python_api.py,sha256=Iao1jbdeW6ekeA80BZG6gNqHVjxQJEheB3DbpVsuTZQ,12304
|
12
12
|
minecraft_datapack_language/utils.py,sha256=Aq0HAGlXqj9BUTEjaEilpvzEW0EtZYYMMwOqG9db6dE,684
|
13
|
-
minecraft_datapack_language-15.4.
|
14
|
-
minecraft_datapack_language-15.4.
|
15
|
-
minecraft_datapack_language-15.4.
|
16
|
-
minecraft_datapack_language-15.4.
|
17
|
-
minecraft_datapack_language-15.4.
|
18
|
-
minecraft_datapack_language-15.4.
|
13
|
+
minecraft_datapack_language-15.4.41.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
14
|
+
minecraft_datapack_language-15.4.41.dist-info/METADATA,sha256=8VRf590kEqLu2Te-AHafQxQjpT0pFhz-VEay36-olQ0,8344
|
15
|
+
minecraft_datapack_language-15.4.41.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
16
|
+
minecraft_datapack_language-15.4.41.dist-info/entry_points.txt,sha256=c6vjBeCiyQflvPHBRyBk2nJCSfYt3Oc7Sc9V87ySi_U,108
|
17
|
+
minecraft_datapack_language-15.4.41.dist-info/top_level.txt,sha256=ADtFI476tbKLLxEAA-aJQAfg53MA3k_DOb0KTFiggfw,28
|
18
|
+
minecraft_datapack_language-15.4.41.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|