minecraft-datapack-language 15.4.28__py3-none-any.whl → 15.4.29__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/__init__.py +17 -2
- minecraft_datapack_language/_version.py +2 -2
- minecraft_datapack_language/ast_nodes.py +87 -59
- minecraft_datapack_language/mdl_compiler.py +470 -0
- minecraft_datapack_language/mdl_errors.py +14 -0
- minecraft_datapack_language/mdl_lexer.py +624 -0
- minecraft_datapack_language/mdl_parser.py +573 -0
- minecraft_datapack_language-15.4.29.dist-info/METADATA +266 -0
- minecraft_datapack_language-15.4.29.dist-info/RECORD +16 -0
- minecraft_datapack_language/cli.py +0 -159
- minecraft_datapack_language/cli_build.py +0 -1292
- minecraft_datapack_language/cli_check.py +0 -155
- minecraft_datapack_language/cli_colors.py +0 -264
- minecraft_datapack_language/cli_help.py +0 -508
- minecraft_datapack_language/cli_new.py +0 -300
- minecraft_datapack_language/cli_utils.py +0 -276
- minecraft_datapack_language/expression_processor.py +0 -352
- minecraft_datapack_language/linter.py +0 -409
- minecraft_datapack_language/mdl_lexer_js.py +0 -754
- minecraft_datapack_language/mdl_parser_js.py +0 -1049
- minecraft_datapack_language/pack.py +0 -758
- minecraft_datapack_language-15.4.28.dist-info/METADATA +0 -1274
- minecraft_datapack_language-15.4.28.dist-info/RECORD +0 -25
- {minecraft_datapack_language-15.4.28.dist-info → minecraft_datapack_language-15.4.29.dist-info}/WHEEL +0 -0
- {minecraft_datapack_language-15.4.28.dist-info → minecraft_datapack_language-15.4.29.dist-info}/entry_points.txt +0 -0
- {minecraft_datapack_language-15.4.28.dist-info → minecraft_datapack_language-15.4.29.dist-info}/licenses/LICENSE +0 -0
- {minecraft_datapack_language-15.4.28.dist-info → minecraft_datapack_language-15.4.29.dist-info}/top_level.txt +0 -0
@@ -1,300 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
CLI New Project - Create new MDL projects with templates
|
3
|
-
"""
|
4
|
-
|
5
|
-
import os
|
6
|
-
import shutil
|
7
|
-
from pathlib import Path
|
8
|
-
from typing import Optional
|
9
|
-
|
10
|
-
from .cli_utils import _slugify
|
11
|
-
|
12
|
-
|
13
|
-
def create_new_project(project_name: str, pack_name: str = None, pack_format: int = 82) -> None:
|
14
|
-
"""Create a new MDL project with template files."""
|
15
|
-
# Validate project name
|
16
|
-
if not project_name or not project_name.strip():
|
17
|
-
raise ValueError("Project name cannot be empty")
|
18
|
-
|
19
|
-
# Clean and validate project name
|
20
|
-
clean_name = _slugify(project_name.strip())
|
21
|
-
if not clean_name:
|
22
|
-
raise ValueError("Project name must contain valid characters")
|
23
|
-
|
24
|
-
# Use pack_name if provided, otherwise use project name
|
25
|
-
if pack_name is None:
|
26
|
-
pack_name = project_name
|
27
|
-
|
28
|
-
# Validate pack format
|
29
|
-
if not isinstance(pack_format, int) or pack_format < 1:
|
30
|
-
raise ValueError("Pack format must be a positive integer")
|
31
|
-
|
32
|
-
# Create project directory
|
33
|
-
project_dir = Path(clean_name)
|
34
|
-
if project_dir.exists():
|
35
|
-
raise ValueError(f"Project directory '{clean_name}' already exists")
|
36
|
-
|
37
|
-
try:
|
38
|
-
# Create the project directory
|
39
|
-
project_dir.mkdir(parents=True, exist_ok=False)
|
40
|
-
|
41
|
-
# Create the main MDL file
|
42
|
-
mdl_file = project_dir / "main.mdl"
|
43
|
-
|
44
|
-
# Generate template content
|
45
|
-
template_content = _generate_mdl_template(clean_name, pack_name, pack_format)
|
46
|
-
|
47
|
-
with open(mdl_file, 'w', encoding='utf-8') as f:
|
48
|
-
f.write(template_content)
|
49
|
-
|
50
|
-
# Create README.md
|
51
|
-
readme_content = _generate_readme_template(clean_name, pack_name)
|
52
|
-
readme_file = project_dir / "README.md"
|
53
|
-
|
54
|
-
with open(readme_file, 'w', encoding='utf-8') as f:
|
55
|
-
f.write(readme_content)
|
56
|
-
|
57
|
-
try:
|
58
|
-
from .cli_colors import color
|
59
|
-
print(f"{color.success('[OK]')} Successfully created new MDL project: {color.highlight(clean_name)}")
|
60
|
-
print(f"{color.info('[DIR]')} Project directory: {color.file_path(str(project_dir.absolute()))}")
|
61
|
-
print(f"{color.info('[FILE]')} Main file: {color.file_path(str(mdl_file))}")
|
62
|
-
print(f"{color.info('[DOC]')} Documentation: {color.file_path(str(readme_file))}")
|
63
|
-
print()
|
64
|
-
print(f"{color.section('[NEXT]')} Next steps:")
|
65
|
-
print(f" 1. cd {color.highlight(clean_name)}")
|
66
|
-
print(f" 2. Edit main.mdl with your code")
|
67
|
-
print(f" 3. mdl build --mdl main.mdl -o dist")
|
68
|
-
print(f" 4. mdl check main.mdl")
|
69
|
-
print()
|
70
|
-
print(f"{color.section('[INFO]')} Learn more:")
|
71
|
-
print(" • Language Reference: https://www.mcmdl.com/docs/language-reference")
|
72
|
-
print(" • Examples: https://www.mcmdl.com/docs/examples")
|
73
|
-
print(" • CLI Reference: https://www.mcmdl.com/docs/cli-reference")
|
74
|
-
except ImportError:
|
75
|
-
# Fallback if colors aren't available
|
76
|
-
print(f"[OK] Successfully created new MDL project: {clean_name}")
|
77
|
-
print(f"[DIR] Project directory: {project_dir.absolute()}")
|
78
|
-
print(f"[FILE] Main file: {mdl_file}")
|
79
|
-
print(f"[DOC] Documentation: {readme_file}")
|
80
|
-
print()
|
81
|
-
print("[NEXT] Next steps:")
|
82
|
-
print(f" 1. cd {clean_name}")
|
83
|
-
print(f" 2. Edit main.mdl with your code")
|
84
|
-
print(f" 3. mdl build --mdl main.mdl -o dist")
|
85
|
-
print(f" 4. mdl check main.mdl")
|
86
|
-
print()
|
87
|
-
print("[INFO] Learn more:")
|
88
|
-
print(" • Language Reference: https://www.mcmdl.com/docs/language-reference")
|
89
|
-
print(" • Examples: https://www.mcmdl.com/docs/examples")
|
90
|
-
print(" • CLI Reference: https://www.mcmdl.com/docs/cli-reference")
|
91
|
-
|
92
|
-
except Exception as e:
|
93
|
-
# Clean up on error
|
94
|
-
if project_dir.exists():
|
95
|
-
shutil.rmtree(project_dir)
|
96
|
-
raise ValueError(f"Failed to create project: {str(e)}")
|
97
|
-
|
98
|
-
|
99
|
-
def _generate_mdl_template(project_name: str, pack_name: str, pack_format: int) -> str:
|
100
|
-
"""Generate the template MDL content."""
|
101
|
-
return f'''pack "{pack_name}" "Generated by MDL CLI" {pack_format};
|
102
|
-
namespace "{pack_name}";
|
103
|
-
|
104
|
-
// Example variables
|
105
|
-
var num score = 0;
|
106
|
-
var num lives = 3;
|
107
|
-
|
108
|
-
// Main function - this runs when called
|
109
|
-
function "main" {{
|
110
|
-
say Hello from {project_name}!;
|
111
|
-
|
112
|
-
// Set initial values
|
113
|
-
score = 10;
|
114
|
-
lives = 3;
|
115
|
-
|
116
|
-
// Display current values
|
117
|
-
say Score: $score$;
|
118
|
-
say Lives: $lives$;
|
119
|
-
|
120
|
-
// Example conditional statement
|
121
|
-
if "$score$ > 5" {{
|
122
|
-
say Great score!;
|
123
|
-
}} else {{
|
124
|
-
say Keep trying!;
|
125
|
-
}};
|
126
|
-
|
127
|
-
// Example while loop
|
128
|
-
while "$lives$ > 0" {{
|
129
|
-
say You have $lives$ lives remaining;
|
130
|
-
lives = lives - 1;
|
131
|
-
}};
|
132
|
-
|
133
|
-
say Game over!;
|
134
|
-
}}
|
135
|
-
|
136
|
-
// Init function - this runs when the datapack loads
|
137
|
-
function "init" {{
|
138
|
-
say [GAME] {pack_name} loaded successfully!;
|
139
|
-
say Type: /function {project_name}:main;
|
140
|
-
}}
|
141
|
-
|
142
|
-
// Tick function - this runs every tick (20 times per second)
|
143
|
-
function "tick" {{
|
144
|
-
// Add your tick logic here
|
145
|
-
// Be careful with performance!
|
146
|
-
}}
|
147
|
-
|
148
|
-
// Example function with parameters
|
149
|
-
function "greet_player" {{
|
150
|
-
say Welcome to {pack_name}!;
|
151
|
-
say Have fun playing!;
|
152
|
-
}}
|
153
|
-
|
154
|
-
// Raw Minecraft commands example
|
155
|
-
$!raw
|
156
|
-
# You can use raw Minecraft commands here
|
157
|
-
# These are passed through directly to the output
|
158
|
-
# Useful for complex commands or commands not yet supported by MDL
|
159
|
-
tellraw @a {{"text":"Raw command example","color":"gold"}}
|
160
|
-
raw!$
|
161
|
-
|
162
|
-
// Example recipe (optional)
|
163
|
-
// recipe "diamond_sword" "diamond_sword.json";
|
164
|
-
|
165
|
-
// Example loot table (optional)
|
166
|
-
// loot_table "chest_loot" "chest_loot.json";
|
167
|
-
|
168
|
-
// Example advancement (optional)
|
169
|
-
// advancement "first_diamond" "first_diamond.json";
|
170
|
-
|
171
|
-
// Hook the init function to run when the datapack loads
|
172
|
-
on_load "{project_name}:init";
|
173
|
-
'''
|
174
|
-
|
175
|
-
|
176
|
-
def _generate_readme_template(project_name: str, pack_name: str) -> str:
|
177
|
-
"""Generate the README template content."""
|
178
|
-
return f'''# {pack_name}
|
179
|
-
|
180
|
-
A Minecraft datapack created with MDL (Minecraft Datapack Language).
|
181
|
-
|
182
|
-
## [GAME] About
|
183
|
-
|
184
|
-
This datapack was generated using the MDL CLI tool. MDL is a simplified language for creating Minecraft datapacks with variables, control structures, and easy syntax.
|
185
|
-
|
186
|
-
## [DIR] Project Structure
|
187
|
-
|
188
|
-
```
|
189
|
-
{project_name}/
|
190
|
-
├── README.md # This file
|
191
|
-
└── main.mdl # Main MDL source file
|
192
|
-
```
|
193
|
-
|
194
|
-
## [NEXT] Getting Started
|
195
|
-
|
196
|
-
### Prerequisites
|
197
|
-
|
198
|
-
- Minecraft Java Edition (1.20+ recommended)
|
199
|
-
- MDL CLI tool installed (`pipx install minecraft-datapack-language`)
|
200
|
-
|
201
|
-
### Building the Datapack
|
202
|
-
|
203
|
-
1. **Build the project:**
|
204
|
-
```bash
|
205
|
-
mdl build --mdl {project_name}.mdl -o dist
|
206
|
-
```
|
207
|
-
|
208
|
-
2. **Check for errors:**
|
209
|
-
```bash
|
210
|
-
mdl check {project_name}.mdl
|
211
|
-
```
|
212
|
-
|
213
|
-
3. **Install in Minecraft:**
|
214
|
-
- Copy the `dist` folder to your world's `datapacks` directory
|
215
|
-
- Or use the `--wrapper` option to create a zip file:
|
216
|
-
```bash
|
217
|
-
mdl build --mdl {project_name}.mdl -o dist --wrapper {project_name}
|
218
|
-
```
|
219
|
-
|
220
|
-
### Using the Datapack
|
221
|
-
|
222
|
-
1. Load your world in Minecraft
|
223
|
-
2. The datapack will automatically load
|
224
|
-
3. Run the main function: `/function {project_name}:main`
|
225
|
-
|
226
|
-
## [OPT] Development
|
227
|
-
|
228
|
-
### Editing the Code
|
229
|
-
|
230
|
-
Open `{project_name}.mdl` in your favorite text editor. The file contains:
|
231
|
-
|
232
|
-
- **Pack declaration** - Datapack metadata
|
233
|
-
- **Variables** - Scoreboard variables for storing data
|
234
|
-
- **Functions** - The main logic of your datapack
|
235
|
-
- **Control structures** - If/else statements and loops
|
236
|
-
- **Raw commands** - Direct Minecraft commands when needed
|
237
|
-
|
238
|
-
### Key Features
|
239
|
-
|
240
|
-
- **Variables**: Use `variable name = value` to create scoreboard objectives
|
241
|
-
- **Functions**: Define reusable code blocks with `function name { ... }`
|
242
|
-
- **Conditionals**: Use `if (condition) { ... } else { ... }`
|
243
|
-
- **Loops**: Use `while (condition) { ... }` for repeating actions
|
244
|
-
- **Variable substitution**: Use `$variable$` in commands to insert values
|
245
|
-
|
246
|
-
### Example Code
|
247
|
-
|
248
|
-
```mdl
|
249
|
-
// Set a variable
|
250
|
-
score = 10
|
251
|
-
|
252
|
-
// Use it in a command
|
253
|
-
say "Your score is: $score$"
|
254
|
-
|
255
|
-
// Conditional logic
|
256
|
-
if (score > 5) {{
|
257
|
-
say "Great job!"
|
258
|
-
}} else {{
|
259
|
-
say "Keep trying!"
|
260
|
-
}}
|
261
|
-
```
|
262
|
-
|
263
|
-
## [INFO] Resources
|
264
|
-
|
265
|
-
- **Language Reference**: https://www.mcmdl.com/docs/language-reference
|
266
|
-
- **CLI Reference**: https://www.mcmdl.com/docs/cli-reference
|
267
|
-
- **Examples**: https://www.mcmdl.com/docs/examples
|
268
|
-
- **Website**: https://www.mcmdl.com
|
269
|
-
- **GitHub**: https://github.com/aaron777collins/MinecraftDatapackLanguage
|
270
|
-
|
271
|
-
## 🐛 Troubleshooting
|
272
|
-
|
273
|
-
### Common Issues
|
274
|
-
|
275
|
-
1. **"No .mdl files found"**
|
276
|
-
- Make sure you're in the correct directory
|
277
|
-
- Check that the file has a `.mdl` extension
|
278
|
-
|
279
|
-
2. **Syntax errors**
|
280
|
-
- Use `mdl check {project_name}.mdl` to find and fix errors
|
281
|
-
- Check the error messages for line numbers and suggestions
|
282
|
-
|
283
|
-
3. **Datapack not loading**
|
284
|
-
- Verify the pack format is correct for your Minecraft version
|
285
|
-
- Check that the `dist` folder is in the right location
|
286
|
-
|
287
|
-
### Getting Help
|
288
|
-
|
289
|
-
- Check the error messages - they include helpful suggestions
|
290
|
-
- Visit the documentation: https://www.mcmdl.com/docs
|
291
|
-
- Report bugs: https://github.com/aaron777collins/MinecraftDatapackLanguage/issues
|
292
|
-
|
293
|
-
## [FILE] License
|
294
|
-
|
295
|
-
This project is licensed under the MIT License - see the LICENSE file for details.
|
296
|
-
|
297
|
-
---
|
298
|
-
|
299
|
-
Happy coding! [GAME]
|
300
|
-
'''
|
@@ -1,276 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
CLI Utilities - Helper functions for the MDL CLI
|
3
|
-
"""
|
4
|
-
|
5
|
-
import os
|
6
|
-
import json
|
7
|
-
import re
|
8
|
-
from pathlib import Path
|
9
|
-
from typing import Any, List
|
10
|
-
|
11
|
-
|
12
|
-
def ensure_dir(path: str) -> None:
|
13
|
-
"""Ensure a directory exists, creating it if necessary."""
|
14
|
-
os.makedirs(path, exist_ok=True)
|
15
|
-
|
16
|
-
|
17
|
-
def write_json(path: str, data: Any) -> None:
|
18
|
-
"""Write JSON data to a file."""
|
19
|
-
with open(path, 'w', encoding='utf-8') as f:
|
20
|
-
json.dump(data, f, indent=2)
|
21
|
-
|
22
|
-
|
23
|
-
def _process_variable_substitutions(command: str, selector: str = "@s") -> str:
|
24
|
-
"""Process $variable$ and $variable<selector>$ substitutions in commands."""
|
25
|
-
|
26
|
-
# Skip say commands - they have their own variable substitution logic
|
27
|
-
if command.strip().startswith('say '):
|
28
|
-
return command
|
29
|
-
|
30
|
-
# Check if this is a tellraw command with JSON
|
31
|
-
if command.strip().startswith('tellraw'):
|
32
|
-
# Special handling for tellraw commands with variable substitutions
|
33
|
-
# Find the JSON part of the tellraw command
|
34
|
-
json_start = command.find('[')
|
35
|
-
if json_start == -1:
|
36
|
-
json_start = command.find('{')
|
37
|
-
json_end = command.rfind(']') + 1
|
38
|
-
if json_end == 0:
|
39
|
-
json_end = command.rfind('}') + 1
|
40
|
-
|
41
|
-
if json_start != -1 and json_end != -1:
|
42
|
-
prefix = command[:json_start]
|
43
|
-
json_part = command[json_start:json_end]
|
44
|
-
suffix = command[json_end:]
|
45
|
-
|
46
|
-
# Parse the JSON to handle variable substitutions properly
|
47
|
-
try:
|
48
|
-
data = json.loads(json_part)
|
49
|
-
|
50
|
-
# Handle both single object and array formats
|
51
|
-
if isinstance(data, list):
|
52
|
-
# JSON array format - process each element
|
53
|
-
for item in data:
|
54
|
-
if isinstance(item, dict) and 'score' in item and 'name' in item['score']:
|
55
|
-
# Resolve scope selector in score components
|
56
|
-
name = item['score']['name']
|
57
|
-
if name == "global":
|
58
|
-
item['score']['name'] = _resolve_selector("global")
|
59
|
-
|
60
|
-
# Return the processed JSON
|
61
|
-
new_json = json.dumps(data)
|
62
|
-
return f"{prefix}{new_json}{suffix}"
|
63
|
-
|
64
|
-
elif isinstance(data, dict) and 'text' in data and '$' in data['text']:
|
65
|
-
# Single object format with variable substitutions
|
66
|
-
# Split the text into parts before and after variables
|
67
|
-
text = data['text']
|
68
|
-
parts = []
|
69
|
-
current_pos = 0
|
70
|
-
|
71
|
-
# Find all variable substitutions (including scoped ones)
|
72
|
-
var_pattern = r'\$([^$]+)\$'
|
73
|
-
for match in re.finditer(var_pattern, text):
|
74
|
-
start, end = match.span()
|
75
|
-
var_name = match.group(1)
|
76
|
-
|
77
|
-
# Add text before the variable
|
78
|
-
if start > current_pos:
|
79
|
-
parts.append(text[current_pos:start])
|
80
|
-
|
81
|
-
# Process the variable
|
82
|
-
if '<' in var_name and '>' in var_name:
|
83
|
-
# Scoped variable: $var<selector>$
|
84
|
-
base_name, scope = var_name.split('<', 1)
|
85
|
-
scope = scope.rstrip('>')
|
86
|
-
resolved_scope = _resolve_selector(scope)
|
87
|
-
parts.append(f"${{{base_name}}}")
|
88
|
-
# Replace the scope in the command
|
89
|
-
command = command.replace(f"${{{base_name}}}", f"${{{base_name}}}")
|
90
|
-
else:
|
91
|
-
# Simple variable: $var$
|
92
|
-
parts.append(f"${{{var_name}}}")
|
93
|
-
|
94
|
-
current_pos = end
|
95
|
-
|
96
|
-
# Add remaining text
|
97
|
-
if current_pos < len(text):
|
98
|
-
parts.append(text[current_pos:])
|
99
|
-
|
100
|
-
# Update the text in the JSON
|
101
|
-
data['text'] = ''.join(parts)
|
102
|
-
new_json = json.dumps(data)
|
103
|
-
return f"{prefix}{new_json}{suffix}"
|
104
|
-
|
105
|
-
except json.JSONDecodeError:
|
106
|
-
# If JSON parsing fails, fall back to simple substitution
|
107
|
-
pass
|
108
|
-
|
109
|
-
# Simple variable substitution for non-JSON commands
|
110
|
-
# Handle scoped variables: $variable<selector>$
|
111
|
-
scoped_pattern = r'\$([^$<]+)<([^>]+)>\$'
|
112
|
-
command = re.sub(scoped_pattern, lambda m: f"${{{m.group(1)}}}", command)
|
113
|
-
|
114
|
-
# Handle simple variables: $variable$
|
115
|
-
simple_pattern = r'\$([^$]+)\$'
|
116
|
-
command = re.sub(simple_pattern, lambda m: f"${{{m.group(1)}}}", command)
|
117
|
-
|
118
|
-
return command
|
119
|
-
|
120
|
-
|
121
|
-
def _convert_condition_to_minecraft_syntax(condition: str, selector: str = "@s", variable_scopes: dict = None) -> str:
|
122
|
-
"""Convert MDL condition syntax to Minecraft scoreboard syntax."""
|
123
|
-
# Remove extra whitespace and normalize
|
124
|
-
condition = condition.strip()
|
125
|
-
|
126
|
-
# Handle variable substitution in conditions (e.g., "$value$ > 3")
|
127
|
-
import re
|
128
|
-
var_pattern = r'\$([^$]+)\$'
|
129
|
-
|
130
|
-
# Replace variable references with proper scoreboard syntax
|
131
|
-
def replace_var(match):
|
132
|
-
var_name = match.group(1)
|
133
|
-
# Check if variable has explicit scope selector
|
134
|
-
if '<' in var_name and var_name.endswith('>'):
|
135
|
-
var_parts = var_name.split('<', 1)
|
136
|
-
base_var = var_parts[0]
|
137
|
-
scope_selector = var_parts[1][:-1] # Remove trailing >
|
138
|
-
# Resolve special selectors
|
139
|
-
if scope_selector == "global":
|
140
|
-
scope_selector = "@e[type=armor_stand,tag=mdl_server,limit=1]"
|
141
|
-
return f"{scope_selector} {base_var}"
|
142
|
-
else:
|
143
|
-
# Use declared scope if available, otherwise default to current selector
|
144
|
-
if variable_scopes and var_name in variable_scopes:
|
145
|
-
declared_scope = variable_scopes[var_name]
|
146
|
-
if declared_scope == 'global':
|
147
|
-
return f"@e[type=armor_stand,tag=mdl_server,limit=1] {var_name}"
|
148
|
-
else:
|
149
|
-
return f"{declared_scope} {var_name}"
|
150
|
-
else:
|
151
|
-
# Default to current selector
|
152
|
-
return f"{selector} {var_name}"
|
153
|
-
|
154
|
-
# Apply variable substitution
|
155
|
-
condition = re.sub(var_pattern, replace_var, condition)
|
156
|
-
|
157
|
-
# Handle common patterns
|
158
|
-
if '==' in condition:
|
159
|
-
var1, var2 = condition.split('==', 1)
|
160
|
-
var1 = var1.strip()
|
161
|
-
var2 = var2.strip()
|
162
|
-
|
163
|
-
# Check if var2 is a number
|
164
|
-
try:
|
165
|
-
value = int(var2)
|
166
|
-
return f"score {var1} matches {value}"
|
167
|
-
except ValueError:
|
168
|
-
# Both are variables, compare them
|
169
|
-
return f"score {var1} = {var2}"
|
170
|
-
|
171
|
-
elif '!=' in condition:
|
172
|
-
var1, var2 = condition.split('!=', 1)
|
173
|
-
var1 = var1.strip()
|
174
|
-
var2 = var2.strip()
|
175
|
-
|
176
|
-
try:
|
177
|
-
value = int(var2)
|
178
|
-
return f"score {var1} matches ..{value-1} {value+1}.."
|
179
|
-
except ValueError:
|
180
|
-
return f"score {var1} != {var2}"
|
181
|
-
|
182
|
-
elif '>' in condition:
|
183
|
-
var1, var2 = condition.split('>', 1)
|
184
|
-
var1 = var1.strip()
|
185
|
-
var2 = var2.strip()
|
186
|
-
|
187
|
-
try:
|
188
|
-
value = int(var2)
|
189
|
-
return f"score {var1} matches {value+1}.."
|
190
|
-
except ValueError:
|
191
|
-
return f"score {var1} > {var2}"
|
192
|
-
|
193
|
-
elif '<' in condition:
|
194
|
-
var1, var2 = condition.split('<', 1)
|
195
|
-
var1 = var1.strip()
|
196
|
-
var2 = var2.strip()
|
197
|
-
|
198
|
-
try:
|
199
|
-
value = int(var2)
|
200
|
-
return f"score {var1} matches ..{value-1}"
|
201
|
-
except ValueError:
|
202
|
-
return f"score {var1} < {var2}"
|
203
|
-
|
204
|
-
elif '>=' in condition:
|
205
|
-
var1, var2 = condition.split('>=', 1)
|
206
|
-
var1 = var1.strip()
|
207
|
-
var2 = var2.strip()
|
208
|
-
|
209
|
-
try:
|
210
|
-
value = int(var2)
|
211
|
-
return f"score {var1} matches {value}.."
|
212
|
-
except ValueError:
|
213
|
-
return f"score {var1} >= {var2}"
|
214
|
-
|
215
|
-
elif '<=' in condition:
|
216
|
-
var1, var2 = condition.split('<=', 1)
|
217
|
-
var1 = var1.strip()
|
218
|
-
var2 = var2.strip()
|
219
|
-
|
220
|
-
try:
|
221
|
-
value = int(var2)
|
222
|
-
return f"score {var1} matches ..{value}"
|
223
|
-
except ValueError:
|
224
|
-
return f"score {var1} <= {var2}"
|
225
|
-
|
226
|
-
# Default: treat as a variable that should be non-zero
|
227
|
-
return f"score {condition} matches 1.."
|
228
|
-
|
229
|
-
|
230
|
-
def _find_mdl_files(directory: Path) -> List[Path]:
|
231
|
-
"""Find all .mdl files in a directory recursively."""
|
232
|
-
mdl_files = []
|
233
|
-
if directory.is_file():
|
234
|
-
if directory.suffix == '.mdl':
|
235
|
-
mdl_files.append(directory)
|
236
|
-
else:
|
237
|
-
for file_path in directory.rglob('*.mdl'):
|
238
|
-
mdl_files.append(file_path)
|
239
|
-
|
240
|
-
return sorted(mdl_files)
|
241
|
-
|
242
|
-
|
243
|
-
def _validate_selector(selector: str, variable_name: str) -> None:
|
244
|
-
"""Validate a selector string."""
|
245
|
-
valid_selectors = ['@s', '@p', '@a', '@r', '@e', 'global']
|
246
|
-
if selector not in valid_selectors:
|
247
|
-
raise ValueError(f"Invalid selector '{selector}' for variable '{variable_name}'. Valid selectors: {', '.join(valid_selectors)}")
|
248
|
-
|
249
|
-
|
250
|
-
def _resolve_selector(selector: str) -> str:
|
251
|
-
"""Resolve a selector to its Minecraft equivalent."""
|
252
|
-
if selector == "global":
|
253
|
-
return "global"
|
254
|
-
return selector
|
255
|
-
|
256
|
-
|
257
|
-
def _extract_base_variable_name(var_name: str) -> str:
|
258
|
-
"""Extract the base variable name from a scoped variable."""
|
259
|
-
if '<' in var_name and '>' in var_name:
|
260
|
-
return var_name.split('<')[0]
|
261
|
-
return var_name
|
262
|
-
|
263
|
-
|
264
|
-
def _slugify(name: str) -> str:
|
265
|
-
"""Convert a string to a valid namespace/identifier."""
|
266
|
-
# Remove special characters and replace spaces with underscores
|
267
|
-
import re
|
268
|
-
slug = re.sub(r'[^a-zA-Z0-9_]', '_', name)
|
269
|
-
# Remove multiple consecutive underscores
|
270
|
-
slug = re.sub(r'_+', '_', slug)
|
271
|
-
# Remove leading/trailing underscores
|
272
|
-
slug = slug.strip('_')
|
273
|
-
# Ensure it starts with a letter or underscore
|
274
|
-
if slug and not slug[0].isalpha() and slug[0] != '_':
|
275
|
-
slug = '_' + slug
|
276
|
-
return slug.lower()
|