-
Notifications
You must be signed in to change notification settings - Fork 27
Expand file tree
/
Copy pathmcfunction.py
More file actions
906 lines (708 loc) · 26.3 KB
/
mcfunction.py
File metadata and controls
906 lines (708 loc) · 26.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
from selector_definition import selector_definition
from environment import isNumber
from source_file import source_file
from variable_types.scoreboard_var import scoreboard_var
from block_types.push_block import push_block
from block_types.pop_block import pop_block
from CompileError import CompileError
import math
import traceback
import json
def get_undecorated_selector_name(selector):
if selector.startswith('@'):
selector = selector[1:]
selector = selector.split('[')[0]
return selector
def compile_section(section, environment):
type, name, template_params, params, lines = section
if type == 'function':
f = mcfunction(environment.clone(new_function_name = name), True, params)
elif type == 'reset':
f = environment.get_reset_function()
if f == None:
f = mcfunction(environment.clone(new_function_name = name))
else:
f = mcfunction(environment.clone(new_function_name = name))
environment.register_function(name, f)
if type == 'clock':
environment.register_clock(name)
f.compile_blocks(lines)
def real_command(cmd):
return not cmd.startswith('#') and len(cmd) > 0
class mcfunction(object):
def __init__(self, environment, callable = False, params = []):
self.commands = []
self.environment = environment
self.params = params
self.callable = callable
self.environment_stack = []
self.has_macros = False
self.filename = None
for param in params:
self.register_local(param)
def set_filename(self, filename):
self.filename = filename
# Returns the command to call this function
def get_call(self):
if self.filename == None:
raise CompileError('Tried to call function with no registered filename.')
if self.has_macros:
return f'function {self.namespace}:{self.filename} with storage {self.namespace}:global args'
else:
return f'function {self.namespace}:{self.filename}'
def evaluate_params(self, params):
results = []
for p in range(len(params)):
param_name = f'Param{p}'
param_var = scoreboard_var('Global', param_name)
try:
var = params[p].compile(self, None)
except Exception as e:
print(e)
print(f'Unable to compile parameter {p}.')
return False
param_var.copy_from(self, var)
return True
def get_arrayconst_var(self, name, idxval):
return self.environment.get_arrayconst_var(name, idxval)
def get_if_chain(self, conditions, iftype='if'):
test = ''
for type, val in conditions:
if type == 'selector':
test += f'{iftype} entity {val} '
elif type == 'predicate':
if ':' in val:
test += f'{iftype} predicate {val} '
elif val in self.predicates:
test += f'{iftype} predicate {self.namespace}:{val} '
else:
raise CompileError(f'Predicate "{val}" does not exist')
elif type == 'score':
lexpr, op, rexpr = val
lvar = lexpr.compile(self)
rvar = rexpr.compile(self)
lconst = lvar.get_const_value(self)
rconst = rvar.get_const_value(self)
if lconst != None and rconst != None:
result = False
# Perform comparison, terminate if-chain if false
if op == '=' and lconst == rconst:
result = True
elif op == '>' and lconst > rconst:
result = True
elif op == '<' and lconst < rconst:
result = True
elif op == '>=' and lconst >= rconst:
result = True
elif op == '<=' and lconst <= rconst:
result = True
if iftype == 'if' and not result or iftype == 'unless' and result:
# Clobber entire if chain
return 'if score Global unique matches -1 '
else:
# No modification to the test string is necessary
continue
elif lconst != None or rconst != None:
# Continue if chain comparing the scoreboard value with numeric range
if lconst != None:
sbvar = rvar.get_scoreboard_var(self)
const = lconst
elif rconst != None:
sbvar = lvar.get_scoreboard_var(self)
const = rconst
if op == '>':
test += f'{iftype} score {sbvar.selector} {sbvar.objective} matches {int(const)+1}.. '
if op == '>=':
test += f'{iftype} score {sbvar.selector} {sbvar.objective} matches {const}.. '
if op == '<':
test += f'{iftype} score {sbvar.selector} {sbvar.objective} matches ..{int(const)-1} '
if op == '<=':
test += f'{iftype} score {sbvar.selector} {sbvar.objective} matches ..{const} '
if op == '=':
test += f'{iftype} score {sbvar.selector} {sbvar.objective} matches {const} '
else:
# Continue if chain comparing two score values
lsbvar = lvar.get_scoreboard_var(self)
rsbvar = rvar.get_scoreboard_var(self)
test += f'{iftype} score {lsbvar.selector} {lsbvar.objective} {op} {rsbvar.selector} {rsbvar.objective} '
elif type == 'vector_equality':
if iftype == 'unless':
raise CompileError('Vector equality may not be used with "unless"')
(type1, var1), (type2, var2) = val
if type1 == 'VAR_CONST' and type2 == 'VAR_CONST':
val1 = var1.get_value(self)
val2 = var2.get_value(self)
if val1 != val2:
# Test fails, clobber entire chain
return 'if score Global unique matches -1 '
else:
# Test succeeds, continue with the chain
continue
else:
if type1 == 'VAR_CONST':
# Swap vars so that the constant var is always second
temp_type = type1
temp_var = var1
type1 = type2
var1 = var2
type2 = temp_type
var2 = temp_var
const_vals = []
if type2 == 'VAR_CONST':
components = var2.get_value(self)
try:
const_vals = [int(components[i]) for i in range(3)]
except Exception as e:
print(e)
raise CompileError('Unable to get three components for constant vector.')
for i in range(3):
if type1 == 'VAR_ID':
lvar = scoreboard_var('Global', f'_{var1}_{i}')
elif type1 == 'SEL_VAR_ID':
sel1, selvar1 = var1
lvar = scoreboard_var(sel1, f'_{selvar1}_{i}')
elif type1 == 'VAR_COMPONENTS':
lvar = var1[i].get_scoreboard_var(self)
if type2 == 'VAR_CONST':
test += f'if score {sel1} {sco1} matches {const_vals[i]} '
else:
if type2 == 'VAR_ID':
rvar = scoreboard_var('Global', f'_{var2}_{i}')
elif type2 == 'SEL_VAR_ID':
sel2, selvar2 = var2
rvar = scoreboard_var(sel2, f'_{selvar2}_{i}')
elif type2 == 'VAR_COMPONENTS':
rvar = var2[i].get_scoreboard_var(self)
test += f'if score {lvar.selvar} = {rvar.selvar} '
elif type == 'block':
relcoords, block = val
block = self.apply_environment(block)
if block in self.block_tags:
block = f'#{self.namespace}:{block}'
else:
block = f'minecraft:{block}'
test += f'{iftype} block {relcoords.get_value(self)} {block} '
elif type == 'nbt_path':
test += f'{iftype} data {val.get_dest_path(self)} '
else:
raise ValueError(f'Unknown "if" type: {type}')
return test
def get_execute_items(self, exec_items, exec_func):
cmd = ''
as_count = 0
for type, _ in exec_items:
if type[:2] == 'As':
as_count += 1
if as_count >= 2:
print('Execute chain may only contain a single "as" clause.')
return None
at_vector_count = 0
for type, val in exec_items:
if type == 'If':
cmd += self.get_if_chain(val)
if type == 'Unless':
cmd += self.get_if_chain(val, 'unless')
elif type == 'As':
cmd += f'as {val} '
exec_func.update_self_selector(val)
elif type == 'On':
cmd += f'on {val} '
exec_func.update_self_selector("@s")
elif type == 'AsId':
var, attype = val
sbvar = var.get_scoreboard_var(self)
selector, id = sbvar.selector, sbvar.objective
if attype == None:
psel = '@e'
else:
psel = f'@{attype}'
if selector[0] == '@':
seldef = selector_definition(selector, self.environment)
if seldef.base_name == 's' and self.environment.self_selector != None and id in self.environment.self_selector.pointers:
psel = self.environment.self_selector.pointers[id]
elif id in seldef.pointers:
psel = seldef.pointers[id]
elif selector == 'Global':
if id in self.environment.pointers:
psel = self.environment.pointers[id]
self.register_objective('_id')
self.register_objective(id)
self.add_command(f'scoreboard players operation Global _id = {selector} {id}')
cmd += f'as {psel} if score @s _id = Global _id '
if attype != None:
exec_func.update_self_selector('@' + attype)
elif psel != '@e':
exec_func.update_self_selector('@' + get_undecorated_selector_name(psel))
else:
exec_func.update_self_selector('@s')
elif type == 'AsCreate':
if len(exec_items) > 1:
print('"as create" may not be paired with other execute commands.')
return None
create_operation = val
self.register_objective('_age')
self.add_command(f'scoreboard players set @{create_operation.atid} _age 1')
create_operation.compile(self)
self.add_command(f'scoreboard players add @{create_operation.atid} _age 1')
cmd += f'as @{create_operation.atid}[_age==1,limit=1] '
exec_func.update_self_selector('@'+create_operation.atid)
elif type == 'Rotated':
cmd += f'rotated as {val} '
elif type == 'FacingCoords':
cmd += f'facing {val.get_value(self)} '
elif type == 'FacingEntity':
cmd += f'facing entity {val} feet '
elif type == 'Align':
cmd += f'align {val} '
elif type == 'At':
selector, relcoords, anchor = val
if selector != None:
cmd += f'at {selector} '
if anchor != None:
cmd += f'anchored {anchor} '
if relcoords != None:
cmd += f'positioned {relcoords.get_value(self)} '
elif type == 'AtVector':
at_vector_count += 1
if at_vector_count >= 2:
print('Tried to execute at multiple vector locations.')
return None
scale, expr = val
if scale == None:
scale = self.scale
else:
scale = scale.get_value(self)
vec_vals = expr.compile(self, None)
self.add_command('scoreboard players add @e _age 1')
self.add_command('summon area_effect_cloud')
self.add_command('scoreboard players add @e _age 1')
for i in range(3):
var = vec_vals[i].get_scoreboard_var(self)
self.add_command(f'execute store result entity @e[_age==1,limit=1] Pos[{i}] double {1/float(scale)} run scoreboard players get {var.selector} {var.objective}')
cmd += 'at @e[_age == 1] '
exec_func.add_command('/kill @e[_age == 1]')
elif type == 'In':
dimension = val
cmd += f'in {dimension} '
return cmd
def switch_cases(self, var, cases, switch_func_name = 'switch', case_func_name = 'case'):
for q in range(4):
imin = q * len(cases) // 4
imax = (q+1) * len(cases) // 4
if imin == imax:
continue
vmin = cases[imin][0]
vmax = cases[imax-1][1]
line = cases[imin][3]
sub_cases = cases[imin:imax]
case_func = self.create_child_function()
if len(sub_cases) == 1:
vmin, vmax, sub, line, dollarid = sub_cases[0]
if dollarid != None:
case_func.set_dollarid(dollarid, vmin)
try:
case_func.compile_blocks(sub)
except CompileError as e:
print(e)
raise CompileError(f'Unable to compile case at line {line}')
except Exception as e:
print(traceback.format_exc())
raise CompileError(f'Unable to compile case at line {line}')
single_command = case_func.single_command()
if single_command != None:
if vmin == vmax:
vrange = str(vmin)
else:
vrange = f'{vmin}..{vmax}'
if len(single_command) >= 1 and single_command[0] == '$':
self.add_command(f'$execute if score {var.selector} {var.objective} matches {vrange} run {single_command[1:]}')
else:
self.add_command(f'execute if score {var.selector} {var.objective} matches {vrange} run {single_command}')
else:
unique = self.get_unique_id()
if vmin == vmax:
case_name = f'line{line}/{case_func_name}{vmin}_{unique}'
else:
case_name = f'line{line}/{case_func_name}{vmin}-{vmax}_{unique}'
self.register_function(case_name, case_func)
self.add_command(f'execute if score {var.selector} {var.objective} matches {vmin}..{vmax} run {case_func.get_call()}')
else:
unique = self.get_unique_id()
case_name = f'line{line}/{switch_func_name}{vmin}-{vmax}_{unique}'
self.register_function(case_name, case_func)
self.add_command(f'execute if score {var.selector} {var.objective} matches {vmin}..{vmax} run {case_func.get_call()}')
if not case_func.switch_cases(var, sub_cases):
return False
return True
def add_operation(self, selector, id1, operation, id2):
selector = self.environment.apply(selector)
self.add_command(f"scoreboard players operation {selector} {id1} {operation} {selector} {id2}")
if self.is_scratch(id2):
self.free_scratch(id2)
def add_command(self, command):
self.insert_command(command, len(self.commands))
def insert_command(self, command, index):
if len(command) == 0:
return
if command[0] != '#':
if command[0] == '/':
command = command[1:]
command = self.environment.apply(command)
if '$(' in command:
if command[0] != '$':
command = '$' + command
self.has_macros = True
self.commands.insert(index, command)
def get_utf8_text(self):
return "\n".join([(cmd if cmd[0] != '/' else cmd[1:]) for cmd in self.commands]).encode('utf-8')
def defined_objectives(self):
existing = {}
defineStr = "scoreboard objectives add "
for cmd in self.commands:
if cmd[0] == '/':
cmd = cmd[1:]
if cmd[:len(defineStr)] == defineStr:
existing[cmd[len(defineStr):].split(' ')[0]] = True
return existing
def register_local(self, id):
self.environment.register_local(id)
def finalize(self):
comments = []
while len(self.commands) > 0 and len(self.commands[0]) >= 2 and self.commands[0][0:2] == '##':
comments.append(self.commands[0])
del self.commands[0]
if self.callable:
for v in self.environment.scratch.get_allocated_variables():
self.register_local(v)
for p in range(len(self.params)):
self.insert_command(f'scoreboard players operation Global {self.params[p]} = Global Param{p}', 0)
self.register_objective(f"Param{p}")
self.commands = comments + self.commands
def single_command(self):
ret = None
count = 0
for cmd in self.commands:
if real_command(cmd):
ret = cmd
count += 1
if count >= 2:
return None
return ret
def is_empty(self):
for cmd in self.commands:
if real_command(cmd):
return False
return True
def check_single_entity(self, selector):
if selector[0] != '@':
return True
parsed = self.environment.get_selector_definition(selector)
return parsed.single_entity()
def get_path(self, selector, var):
if selector[0] != '@':
return
id = selector[1:]
if '[' in id:
id = id.split('[',1)[0]
if id in self.environment.selectors:
sel_def = self.environment.selectors[id]
elif id == 's' and self.environment.self_selector != None:
sel_def = self.environment.self_selector
else:
return
if var in sel_def.paths:
path, data_type, scale = sel_def.paths[var]
if scale == None:
scale = self.scale
if not self.check_single_entity(selector):
raise CompileError(f'Tried to get data "{var}" from selector "{selector}" which is not limited to a single entity.')
self.add_command(f'execute store result score {selector} {var} run data get entity {selector} {path} {scale}')
def set_path(self, selector, var):
if selector[0] != '@':
return
id = selector[1:]
if '[' in id:
id = id.split('[',1)[0]
if id in self.environment.selectors:
sel_def = self.environment.selectors[id]
elif id == 's' and self.environment.self_selector != None:
sel_def = self.environment.self_selector
else:
return
if var in sel_def.paths:
path, data_type, scale = sel_def.paths[var]
if scale == None:
scale = self.scale
if not self.check_single_entity(selector):
raise CompileError(f'Tried to set data "{var}" for selector "{selector}" which is not limited to a single entity.')
self.add_command(f'execute store result entity {selector} {path} {data_type} {1/float(scale)} run scoreboard players get {selector} {var}')
def get_vector_path(self, selector, var):
if selector[0] != '@':
return False
id = selector[1:]
if '[' in id:
id = id.split('[',1)[0]
if id in self.environment.selectors:
sel_def = self.environment.selectors[id]
elif id == 's' and self.environment.self_selector != None:
sel_def = self.environment.self_selector
else:
return False
if var in sel_def.vector_paths:
path, data_type, scale = sel_def.vector_paths[var]
if scale == None:
scale = self.scale
if not self.check_single_entity(selector):
raise CompileError(f'Tried to get vector data "{var}" from selector "{selector}" which is not limited to a single entity.')
for i in range(3):
self.add_command(f'execute store result score {selector} _{var}_{i} run data get entity {selector} {path}[{i}] {scale}')
return True
else:
return False
def set_vector_path(self, selector, var, values):
if selector[0] != '@':
return False
id = selector[1:]
if '[' in id:
id = id.split('[',1)[0]
if id in self.environment.selectors:
sel_def = self.environment.selectors[id]
elif id == 's' and self.environment.self_selector != None:
sel_def = self.environment.self_selector
else:
return False
if var in sel_def.vector_paths:
path, data_type, scale = sel_def.vector_paths[var]
if scale == None:
scale = self.scale
if not self.check_single_entity(selector):
raise CompileError(f'Tried to set vector data "{var}" for selector "{selector}" which is not limited to a single entity.')
for i in range(3):
val_var = values[i].get_scoreboard_var(self)
self.add_command(f'execute store result entity {selector} {path}[{i}] {data_type} {1/float(scale)} run scoreboard players get {val_var.selector} {val_var.objective}')
return True
else:
return False
def register_objective(self, objective):
self.environment.register_objective(objective)
def register_array(self, name, from_val, to_val, selector_based):
self.environment.register_array(name, from_val, to_val, selector_based)
def apply_replacements(self, text, overrides = {}):
return self.environment.apply_replacements(text, overrides)
def register_block_tag(self, name, blocks):
self.environment.register_block_tag(name, blocks)
def register_entity_tag(self, name, entities):
self.environment.register_entity_tag(name, entities)
def register_item_tag(self, name, items):
self.environment.register_item_tag(name, items)
def get_scale(self):
return self.environment.scale
def set_scale(self, scale):
self.environment.scale = scale
scale = property(get_scale, set_scale)
@property
def arrays(self):
return self.environment.arrays
@property
def block_tags(self):
return self.environment.block_tags
@property
def item_tags(self):
return self.environment.item_tags
@property
def namespace(self):
return self.environment.namespace
@property
def macros(self):
return self.environment.macros
@property
def template_functions(self):
return self.environment.template_functions
@property
def functions(self):
return self.environment.functions
@property
def selectors(self):
return self.environment.selectors
def get_scratch(self):
return self.environment.get_scratch()
def get_scratch_vector(self):
return self.environment.get_scratch_vector()
def is_scratch(self, var):
return self.environment.is_scratch(var)
def free_scratch(self, id):
self.environment.free_scratch(id)
def get_temp_var(self):
return self.environment.get_temp_var()
def free_temp_var(self):
self.environment.free_temp_var()
def apply_environment(self, text):
return self.environment.apply(text)
def add_constant(self, val):
return self.environment.add_constant(val)
def get_friendly_name(self):
return self.environment.get_friendly_name()
def get_random_objective(self):
return self.environment.get_random_objective()
def register_function(self, name, func):
self.environment.register_function(name, func)
def get_unique_id(self):
return self.environment.get_unique_id()
def update_self_selector(self, selector):
self.environment.update_self_selector(selector)
def get_self_selector_definition(self):
return self.environment.self_selector
def get_python_env(self):
return self.environment.get_python_env()
def clone_environment(self, new_function_name = None):
return self.environment.clone(new_function_name = new_function_name)
# Combines a selector with an existing selector definition in the environment
def get_combined_selector(self, selector):
return selector_definition(selector, self.environment)
def set_dollarid(self, id, val):
self.environment.set_dollarid(id, val)
def get_dollarid(self, id):
return self.environment.get_dollarid(id)
def set_atid(self, id, fullselector):
return self.environment.set_atid(id, fullselector)
def push_environment(self, new_env):
self.environment_stack.append(self.environment)
self.environment = new_env
def pop_environment(self):
self.environment = self.environment_stack.pop()
def run_create(self, atid, relcoords, idx=None):
if atid not in self.selectors:
print(f'Unable to create unknown entity: @{atid}')
return False
selector = self.selectors[atid]
entity_type = selector.get_type()
if entity_type == None:
print(f'Unable to create @{atid}, no entity type is defined.')
return False
if selector.tag == None:
if idx:
self.add_command(f'summon {entity_type} {relcoords.get_value(self)} {idx.get_value(self) + hash(atid) % (2 ** 32)}}}')
else:
self.add_command(f'summon {entity_type} {relcoords.get_value(self)}')
else:
if idx:
parsed = json.loads(selector.tag)
parsed['UUIDMost'] = 0
parsed['UUIDLeast'] = idx.get_value(self) + hash(atid) % (2 ** 32)
tag = json.dumps(parsed)
else:
tag = selector.tag
self.add_command(f'summon {entity_type} {relcoords.get_value(self)} {tag}')
return True
def register_name_definition(self, id, str):
self.environment.register_name_definition(id, str)
def get_name_definition(self, id):
return self.environment.get_name_definition(id)
# Creates an empty function with a copy of the current environment
def create_child_function(self, new_function_name = None, callable = False, params = []):
return mcfunction(self.clone_environment(new_function_name = new_function_name),
callable = callable,
params = params
)
def compile_blocks(self, lines):
for block in lines:
try:
block.compile(self)
except CompileError as e:
print(e)
raise CompileError(f'Error compiling block at line {block.line}')
except:
print(traceback.format_exc())
raise CompileError(f'Error compiling block at line {block.line}')
@property
def parser(self):
return self.environment.parser
def import_file(self, filename):
self.environment.register_dependency(filename)
file = source_file(filename)
result = self.parser('import ' + file.get_text() + '\n')
if result == None:
raise CompileError(f'Unable to parse file "{filename}"')
type, parsed = result
if type != 'lib':
raise CompileError(f'Unable to import non-lib-file "{filename}"')
for line in parsed['lines']:
line.register(self.global_context)
self.compile_blocks(parsed['lines'])
def import_python_file(self, filename):
self.environment.register_dependency(filename)
try:
with open(filename) as file:
text = file.read()
except Exception as e:
print(e)
raise CompileError(f'Unable to open "{filename}"')
try:
exec(text, globals(), self.get_python_env())
except Exception as e:
print(e)
raise CompileError(f'Unable to execute "{filename}"')
def eval(self, expr, line):
try:
return eval(expr, globals(), self.get_python_env())
except Exception as e:
print(e)
raise CompileError(f'Could not evaluate python expression "{expr}" at line {line}')
def add_pointer(self, id, selector):
self.environment.add_pointer(id, selector)
def add_block_definition(self, id, definition):
self.environment.add_block_definition(id, definition)
def get_block_definition(self, block_id):
return self.environment.get_block_definition(block_id)
def get_selector_definition(self, selector):
return self.environment.get_selector_definition(selector)
def add_recipe(self, recipe):
self.environment.add_recipe(recipe)
def add_advancement(self, name, advancement):
self.environment.add_advancement(name, advancement)
def add_loot_table(self, name, loot_table):
self.environment.add_loot_table(name, loot_table)
def add_predicate(self, name, predicate):
self.environment.add_predicate(name, predicate)
def add_item_modifier(self, name, item_modifier):
self.environment.add_item_modifier(name, item_modifier)
def get_block_state_list(self, include_block_states):
return self.environment.get_block_state_list(include_block_states)
def call_function(self, sub_func, sub_name, prefix = ''):
if sub_func.is_empty():
return
single_command = sub_func.single_command()
if single_command:
if single_command.startswith('$'):
self.add_command(f'${prefix}{single_command[1:]}')
else:
self.add_command(f'{prefix}{single_command}')
else:
unique = self.get_unique_id()
sub_name = f'{sub_name}_{unique}'
self.register_function(sub_name, sub_func)
cmd = prefix + sub_func.get_call()
self.add_command(cmd)
def get_reset_function(self):
return self.environment.get_reset_function()
def register_clock(self, id):
self.environment.register_clock(id)
@property
def global_context(self):
return self.environment.global_context
def copy_environment_from(self, func):
self.environment = func.environment.clone()
@property
def name(self):
return self.environment.function_name
def get_local_variables(self):
return [scoreboard_var('Global', l) for l in self.environment.get_all_locals()]
def push_locals(self, locals):
block = push_block(0, locals)
block.compile(self)
def pop_locals(self, locals):
block = pop_block(0, locals)
block.compile(self)
@property
def predicates(self):
return self.environment.predicates