minecraft-datapack-language 15.4.38__py3-none-any.whl → 15.4.40__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 +15 -6
- minecraft_datapack_language/python_api.py +100 -25
- {minecraft_datapack_language-15.4.38.dist-info → minecraft_datapack_language-15.4.40.dist-info}/METADATA +1 -1
- {minecraft_datapack_language-15.4.38.dist-info → minecraft_datapack_language-15.4.40.dist-info}/RECORD +10 -10
- {minecraft_datapack_language-15.4.38.dist-info → minecraft_datapack_language-15.4.40.dist-info}/WHEEL +0 -0
- {minecraft_datapack_language-15.4.38.dist-info → minecraft_datapack_language-15.4.40.dist-info}/entry_points.txt +0 -0
- {minecraft_datapack_language-15.4.38.dist-info → minecraft_datapack_language-15.4.40.dist-info}/licenses/LICENSE +0 -0
- {minecraft_datapack_language-15.4.38.dist-info → minecraft_datapack_language-15.4.40.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.40'
|
32
|
+
__version_tuple__ = version_tuple = (15, 4, 40)
|
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)
|
@@ -240,6 +241,8 @@ class MDLCompiler:
|
|
240
241
|
functions_dir = namespace_dir / self.dir_map.function
|
241
242
|
else:
|
242
243
|
functions_dir = namespace_dir / "functions"
|
244
|
+
# Ensure functions dir exists
|
245
|
+
functions_dir.mkdir(parents=True, exist_ok=True)
|
243
246
|
|
244
247
|
# Always create load function to initialize objectives; add tag only if on_load hooks exist
|
245
248
|
has_on_load = any(h.hook_type == "on_load" for h in hooks)
|
@@ -247,11 +250,14 @@ class MDLCompiler:
|
|
247
250
|
load_file = functions_dir / "load.mcfunction"
|
248
251
|
with open(load_file, 'w') as f:
|
249
252
|
f.write(load_content)
|
250
|
-
# Ensure minecraft load tag points to namespace:load
|
253
|
+
# Ensure minecraft load tag points to namespace:load
|
251
254
|
tags_fn_dir = self.output_dir / "data" / "minecraft" / self.dir_map.tags_function
|
252
255
|
tags_fn_dir.mkdir(parents=True, exist_ok=True)
|
253
256
|
load_tag_file = tags_fn_dir / "load.json"
|
254
|
-
|
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"]
|
255
261
|
with open(load_tag_file, 'w') as f:
|
256
262
|
json.dump({"values": values}, f, indent=2)
|
257
263
|
|
@@ -330,6 +336,9 @@ class MDLCompiler:
|
|
330
336
|
|
331
337
|
def _variable_assignment_to_command(self, assignment: VariableAssignment) -> str:
|
332
338
|
"""Convert variable assignment to scoreboard command."""
|
339
|
+
# Auto-declare objective on first use
|
340
|
+
if assignment.name not in self.variables:
|
341
|
+
self.variables[assignment.name] = assignment.name
|
333
342
|
objective = self.variables.get(assignment.name, assignment.name)
|
334
343
|
scope = assignment.scope.strip("<>")
|
335
344
|
|
@@ -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.40
|
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
|
@@ -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=P2VVYhMLipXcQKyw7Y1OEvR4mKshcYtZAmzVwV3FZfs,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.40.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
14
|
+
minecraft_datapack_language-15.4.40.dist-info/METADATA,sha256=KIF4J5dORym2e-a7jCryY_SRCkqq5tyPxtCgC5vVFBA,8360
|
15
|
+
minecraft_datapack_language-15.4.40.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
16
|
+
minecraft_datapack_language-15.4.40.dist-info/entry_points.txt,sha256=c6vjBeCiyQflvPHBRyBk2nJCSfYt3Oc7Sc9V87ySi_U,108
|
17
|
+
minecraft_datapack_language-15.4.40.dist-info/top_level.txt,sha256=ADtFI476tbKLLxEAA-aJQAfg53MA3k_DOb0KTFiggfw,28
|
18
|
+
minecraft_datapack_language-15.4.40.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|