dfpyre 0.4.6__py3-none-any.whl → 0.6.0__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.

Potentially problematic release.


This version of dfpyre might be problematic. Click here for more details.

dfpyre/pyre.py CHANGED
@@ -2,22 +2,20 @@
2
2
  A package for externally creating code templates for the DiamondFire Minecraft server.
3
3
 
4
4
  By Amp
5
- 2/24/2024
6
5
  """
7
6
 
8
7
  import base64
9
8
  import gzip
10
- import socket
11
- import websocket
12
- import time
13
9
  import json
14
10
  import os
15
11
  from difflib import get_close_matches
16
12
  import datetime
17
13
  from typing import Tuple, List, Dict
18
14
  from enum import Enum
15
+ import socket
19
16
  from mcitemlib.itemlib import Item as NbtItem
20
17
  from dfpyre.items import *
18
+ from dfpyre.scriptgen import generate_script, GeneratorFlags
21
19
 
22
20
  COL_WARN = '\x1b[33m'
23
21
  COL_RESET = '\x1b[0m'
@@ -28,6 +26,7 @@ CODEBLOCK_DATA_PATH = os.path.join(os.path.dirname(__file__), 'data/data.json')
28
26
 
29
27
  VARIABLE_TYPES = {'txt', 'comp', 'num', 'item', 'loc', 'var', 'snd', 'part', 'pot', 'g_val', 'vec', 'pn_el'}
30
28
  TEMPLATE_STARTERS = {'event', 'entity_event', 'func', 'process'}
29
+ SINGLE_NAME_CODEBLOCKS = {'func', 'process', 'call_func', 'start_process', 'else'}
31
30
 
32
31
  TARGETS = ['Selection', 'Default', 'Killer', 'Damager', 'Shooter', 'Victim', 'AllPlayers', 'Projectile', 'AllEntities', 'AllMobs', 'LastEntity']
33
32
  TARGET_CODEBLOCKS = {'player_action', 'entity_action', 'if_player', 'if_entity'}
@@ -51,7 +50,7 @@ class Target(Enum):
51
50
  ALL_MOBS = 9
52
51
  LAST_ENTITY = 10
53
52
 
54
- def getStringValue(self):
53
+ def get_string_value(self):
55
54
  return TARGETS[self.value]
56
55
 
57
56
  DEFAULT_TARGET = Target.SELECTION
@@ -63,41 +62,50 @@ class CodeBlock:
63
62
  self.args = args
64
63
  self.target = target
65
64
  self.data = data
65
+
66
+ def __repr__(self) -> str:
67
+ if self.name in SINGLE_NAME_CODEBLOCKS:
68
+ if self.name == 'else':
69
+ return 'CodeBlock(else)'
70
+ return f'CodeBlock({self.name}, {self.data["data"]})'
71
+ elif 'block' in self.data:
72
+ return f'CodeBlock({self.data["block"]}, {self.name})'
73
+ return f'CodeBlock(bracket, {self.data["type"]}, {self.data["direct"]})'
66
74
 
67
75
 
68
76
  def _warn(message):
69
77
  print(f'{COL_WARN}! WARNING ! {message}{COL_RESET}')
70
78
 
71
79
 
72
- def _warnUnrecognizedName(codeblockType: str, codeblockName: str):
73
- close = get_close_matches(codeblockName, TAGDATA[codeblockType].keys())
80
+ def _warn_unrecognized_name(codeblock_type: str, codeblock_name: str):
81
+ close = get_close_matches(codeblock_name, TAGDATA[codeblock_type].keys())
74
82
  if close:
75
- _warn(f'Code block name "{codeblockName}" not recognized. Did you mean "{close[0]}"?')
83
+ _warn(f'Code block name "{codeblock_name}" not recognized. Did you mean "{close[0]}"?')
76
84
  else:
77
- _warn(f'Code block name "{codeblockName}" not recognized. Try spell checking or retyping without spaces.')
85
+ _warn(f'Code block name "{codeblock_name}" not recognized. Try spell checking or retyping without spaces.')
78
86
 
79
87
 
80
- def _loadCodeblockData() -> Tuple:
81
- tagData = {}
88
+ def _load_codeblock_data() -> Tuple:
89
+ tag_data = {}
82
90
  if os.path.exists(CODEBLOCK_DATA_PATH):
83
91
  with open(CODEBLOCK_DATA_PATH, 'r') as f:
84
- tagData = json.load(f)
92
+ tag_data = json.load(f)
85
93
  else:
86
94
  _warn('data.json not found -- Item tags and error checking will not work.')
87
95
  return ({}, set(), set())
88
96
 
89
- del tagData['meta']
97
+ del tag_data['meta']
90
98
 
91
- allNames = [x for l in [d.keys() for d in tagData.values()] for x in l] # flatten list
99
+ all_names = [x for l in [d.keys() for d in tag_data.values()] for x in l] # flatten list
92
100
  return (
93
- tagData,
94
- set(tagData['extras'].keys()),
95
- set(allNames)
101
+ tag_data,
102
+ set(tag_data['extras'].keys()),
103
+ set(all_names)
96
104
  )
97
105
 
98
- TAGDATA, TAGDATA_EXTRAS_KEYS, ALL_CODEBLOCK_NAMES = _loadCodeblockData()
106
+ TAGDATA, TAGDATA_EXTRAS_KEYS, ALL_CODEBLOCK_NAMES = _load_codeblock_data()
99
107
 
100
- def _addInverted(data, inverted):
108
+ def _add_inverted(data, inverted):
101
109
  """
102
110
  If inverted is true, add 'inverted': 'NOT' to data.
103
111
  """
@@ -105,99 +113,103 @@ def _addInverted(data, inverted):
105
113
  data['inverted'] = 'NOT'
106
114
 
107
115
 
108
- def _convertDataTypes(args):
109
- convertedArgs = []
116
+ def _convert_data_types(args):
117
+ converted_args = []
110
118
  for value in args:
111
119
  if type(value) in {int, float}:
112
- convertedArgs.append(num(value))
120
+ converted_args.append(num(value))
113
121
  elif type(value) is str:
114
- if value[0] == VAR_SHORTHAND_CHAR and value[1] in VAR_SCOPES:
115
- varObject = var(value[2:], VAR_SCOPES[value[1]])
116
- convertedArgs.append(varObject)
122
+ if len(value) > 2 and value[0] == VAR_SHORTHAND_CHAR and value[1] in VAR_SCOPES:
123
+ var_object = var(value[2:], VAR_SCOPES[value[1]])
124
+ converted_args.append(var_object)
117
125
  else:
118
- convertedArgs.append(text(value))
126
+ converted_args.append(text(value))
119
127
  else:
120
- convertedArgs.append(value)
121
- return tuple(convertedArgs)
128
+ converted_args.append(value)
129
+ return tuple(converted_args)
122
130
 
123
131
 
124
- def _reformatCodeblockTags(tags, codeblockType: str, codeblockName: str):
132
+ def _reformat_codeblock_tags(tags, codeblock_type: str, codeblock_name: str):
125
133
  """
126
134
  Turns data.json tag items into DiamondFire formatted tag items
127
135
  """
128
- reformattedTags = []
129
- for tagItem in tags:
130
- actionValue = codeblockName if 'action' not in tagItem else tagItem['action']
131
- newTagItem = {
136
+ reformatted_tags = []
137
+ for tag_item in tags:
138
+ action_value = codeblock_name if 'action' not in tag_item else tag_item['action']
139
+ new_tag_item = {
132
140
  'item': {
133
141
  'id': 'bl_tag',
134
142
  'data': {
135
- 'option': tagItem['option'],
136
- 'tag': tagItem['tag'],
137
- 'action': actionValue,
138
- 'block': codeblockType
143
+ 'option': tag_item['option'],
144
+ 'tag': tag_item['tag'],
145
+ 'action': action_value,
146
+ 'block': codeblock_type
139
147
  }
140
148
  },
141
- 'slot': tagItem['slot']
149
+ 'slot': tag_item['slot']
142
150
  }
143
- reformattedTags.append(newTagItem)
144
- return reformattedTags
151
+ reformatted_tags.append(new_tag_item)
152
+ return reformatted_tags
145
153
 
146
154
 
147
- def _getCodeblockTags(codeblockType: str, codeblockName: str):
155
+ def _get_codeblock_tags(codeblock_type: str, codeblock_name: str):
148
156
  """
149
157
  Get tags for the specified codeblock type and name
150
158
  """
151
159
  tags = None
152
- if codeblockType in TAGDATA_EXTRAS_KEYS:
153
- tags = TAGDATA['extras'][codeblockType]
160
+ if codeblock_type in TAGDATA_EXTRAS_KEYS:
161
+ tags = TAGDATA['extras'][codeblock_type]
154
162
  else:
155
- tags = TAGDATA[codeblockType].get(codeblockName)
156
- return _reformatCodeblockTags(tags, codeblockType, codeblockName)
163
+ tags = TAGDATA[codeblock_type].get(codeblock_name)
164
+ return _reformat_codeblock_tags(tags, codeblock_type, codeblock_name)
157
165
 
158
166
 
159
- def _buildBlock(codeblock: CodeBlock, includeTags: bool):
167
+ def _build_block(codeblock: CodeBlock, include_tags: bool):
160
168
  """
161
169
  Builds a properly formatted block from a CodeBlock object.
162
170
  """
163
- finalBlock = codeblock.data.copy()
164
- codeblockType = codeblock.data.get('block')
171
+ built_block = codeblock.data.copy()
172
+ codeblock_type = codeblock.data.get('block')
165
173
 
166
174
  # add target if necessary ('Selection' is the default when 'target' is blank)
167
- if codeblockType in TARGET_CODEBLOCKS and codeblock.target != DEFAULT_TARGET:
168
- finalBlock['target'] = codeblock.target.getStringValue()
175
+ if codeblock_type in TARGET_CODEBLOCKS and codeblock.target != DEFAULT_TARGET:
176
+ built_block['target'] = codeblock.target.get_string_value()
169
177
 
170
178
  # add items into args
171
- finalArgs = [arg.format(slot) for slot, arg in enumerate(codeblock.args) if arg.type in VARIABLE_TYPES]
179
+ final_args = [arg.format(slot) for slot, arg in enumerate(codeblock.args) if arg.type in VARIABLE_TYPES]
172
180
 
173
181
  # check for unrecognized name, add tags
174
- if codeblockType is not None: # for brackets
175
- if codeblockType not in TAGDATA_EXTRAS_KEYS and codeblock.name not in ALL_CODEBLOCK_NAMES:
176
- _warnUnrecognizedName(codeblockType, codeblock.name)
177
- elif includeTags:
178
- tags = _getCodeblockTags(codeblockType, codeblock.name)
179
- if len(finalArgs) + len(tags) > 27:
180
- finalArgs = finalArgs[:(27-len(tags))] # trim list if over 27 elements
181
- finalArgs.extend(tags) # add tags to end
182
+ if codeblock_type is not None: # for brackets
183
+ if codeblock_type not in TAGDATA_EXTRAS_KEYS and codeblock.name not in ALL_CODEBLOCK_NAMES:
184
+ _warn_unrecognized_name(codeblock_type, codeblock.name)
185
+ elif include_tags:
186
+ tags = _get_codeblock_tags(codeblock_type, codeblock.name)
187
+ if len(final_args) + len(tags) > 27:
188
+ final_args = final_args[:(27-len(tags))] # trim list if over 27 elements
189
+ final_args.extend(tags) # add tags to end
182
190
 
183
- finalBlock['args'] = {'items': finalArgs}
184
- return finalBlock
191
+ built_block['args'] = {'items': final_args}
192
+ return built_block
185
193
 
186
194
 
187
- def _dfEncode(jsonString: str) -> str:
195
+ def _df_encode(json_string: str) -> str:
188
196
  """
189
197
  Encodes a stringified json.
190
198
  """
191
- encodedString = gzip.compress(jsonString.encode('utf-8'))
192
- return base64.b64encode(encodedString).decode('utf-8')
199
+ encoded_string = gzip.compress(json_string.encode('utf-8'))
200
+ return base64.b64encode(encoded_string).decode('utf-8')
201
+
202
+
203
+ def _df_decode(encoded_string: str) -> str:
204
+ return gzip.decompress(base64.b64decode(encoded_string)).decode('utf-8')
193
205
 
194
206
 
195
- def getTemplateItem(templateCode: str, name: str, author: str) -> NbtItem:
207
+ def _get_template_item(template_code: str, name: str, author: str) -> NbtItem:
196
208
  now = datetime.datetime.now()
197
209
 
198
- templateItem = NbtItem('yellow_shulker_box')
199
- templateItem.set_name(f'&x&f&f&5&c&0&0>> &x&f&f&c&7&0&0{name}')
200
- templateItem.set_lore([
210
+ template_item = NbtItem('yellow_shulker_box')
211
+ template_item.set_name(f'&x&f&f&5&c&0&0>> &x&f&f&c&7&0&0{name}')
212
+ template_item.set_lore([
201
213
  f'&8Author: {author}',
202
214
  f'&8Date: {now.strftime("%Y-%m-%d")}',
203
215
  '',
@@ -205,138 +217,110 @@ def getTemplateItem(templateCode: str, name: str, author: str) -> NbtItem:
205
217
  '&7https://github.com/Amp63/pyre'
206
218
  ])
207
219
 
208
- pbvTag = {
209
- 'hypercube:codetemplatedata': f'{{"author":"{author}","name":"{name}","version": 1,"code":"{templateCode}"}}',
220
+ pbv_tag = {
221
+ 'hypercube:codetemplatedata': f'{{"author":"{author}","name":"{name}","version": 1,"code":"{template_code}"}}',
210
222
  'hypercube:pyre_creation_timestamp': now.timestamp()
211
223
  }
212
- templateItem.set_tag('PublicBukkitValues', pbvTag, raw=True)
224
+ template_item.set_tag('PublicBukkitValues', pbv_tag, raw=True)
213
225
 
214
- return templateItem
226
+ return template_item
215
227
 
216
228
 
217
- def sendRecode(templateCode: str, name: str='Unnamed Template', author: str='pyre') -> int:
229
+ # TODO:
230
+ # - add inserting codeblocks at any index
231
+ class DFTemplate:
218
232
  """
219
- Sends a template to DiamondFire via recode item api.
220
-
221
- :param str templateCode: The code for the template as a base64 string.
222
- :param str name: The name of the template.
223
- :param str author: The author of the template.
224
-
225
- :return: status code
226
- - `0` = Success
227
- - `1` = Connection refused
228
- - `2` = Other socket error
233
+ Represents a DiamondFire code template.
229
234
  """
235
+ def __init__(self, name: str=None, author: str='pyre'):
236
+ self.codeblocks: List[CodeBlock] = []
237
+ self.bracket_stack: list[str] = []
238
+ self.name = name
239
+ self.author = author
230
240
 
231
- templateItem = getTemplateItem(templateCode, name, author)
232
- data = {'type': 'nbt', 'source': f'pyre Template - {name}', 'data': templateItem.get_nbt()}
233
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
234
- try:
235
- s.connect(('127.0.0.1', 31372))
236
- except ConnectionRefusedError:
237
- print(f"""{COL_ERROR}Could not connect to recode item API. Possible problems:
238
- - Minecraft is not open
239
- - Recode is not installed (get it here: https://modrinth.com/mod/recode or join the discord here: https://discord.gg/GWxWtcwA2C){COL_RESET}""")
240
- s.close()
241
- return 1
242
-
243
- s.send((str(data) + '\n').encode('utf-8'))
244
- received = json.loads(s.recv(1024).decode())
245
- status = received['status']
246
- s.close()
247
- time.sleep(0.5)
248
-
249
- if status == 'success':
250
- print(f'{COL_SUCCESS}Template sent to client successfully.{COL_RESET}')
251
- return 0
252
- error = received['error']
253
- print(f'{COL_ERROR}Error sending template: {error}{COL_RESET}')
254
- return 2
255
-
256
-
257
- def sendCodeClient(templateCode: str, name: str='Unnamed Template', author: str='pyre') -> int:
258
- try:
259
- ws = websocket.WebSocket()
260
- ws.connect(CODECLIENT_URL)
261
- print(f'{COL_SUCCESS}Connected. {COL_WARN}Please run /auth in game.{COL_RESET}')
262
-
263
- ws.recv() # auth response
264
241
 
265
- templateItem = getTemplateItem(templateCode, name, author)
266
- command = f'give {templateItem.get_nbt()}'
267
- ws.send(command)
268
- ws.close()
242
+ def __repr__(self) -> str:
243
+ return f'DFTemplate(name: {self.name}, author: {self.author}, codeblocks: {len(self.codeblocks)})'
269
244
 
270
- print(f'{COL_SUCCESS}Template sent to client successfully.{COL_RESET}')
271
- return 0
272
-
273
- except Exception as e:
274
- if isinstance(e, ConnectionRefusedError):
275
- print(f'{COL_ERROR}Could not connect to CodeClient API. Possible problems:')
276
- print(f' - Minecraft is not open')
277
- print(f' - CodeClient is not installed (get it here: https://modrinth.com/mod/codeclient)')
278
- print(f' - CodeClient API is not enabled (enable it in CodeClient general settings)')
279
- return 1
245
+
246
+ @staticmethod
247
+ def from_code(template_code: str):
248
+ """
249
+ Create a template object from an existing template code.
250
+ """
251
+ template_dict = json.loads(_df_decode(template_code))
252
+ template = DFTemplate()
253
+ for block_dict in template_dict['blocks']:
254
+ if 'args' in block_dict:
255
+ args = []
256
+ for item_dict in block_dict['args']['items']:
257
+ parsed_item = item_from_dict(item_dict['item'])
258
+ if parsed_item is not None:
259
+ args.append(parsed_item)
260
+ target = Target(TARGETS.index(block_dict['target'])) if 'target' in block_dict else DEFAULT_TARGET
261
+
262
+ codeblock_name = 'bracket'
263
+ if 'block' in block_dict and block_dict['block'] in SINGLE_NAME_CODEBLOCKS:
264
+ codeblock_name = block_dict['block']
265
+ elif 'action' in block_dict:
266
+ codeblock_name = block_dict['action']
267
+ codeblock = CodeBlock(codeblock_name, args, target, block_dict)
268
+ template.codeblocks.append(codeblock)
280
269
 
281
- print(f'Connection failed: {e}')
282
- return 2
270
+ return template
283
271
 
284
272
 
285
- class DFTemplate:
286
- """
287
- Represents a DiamondFire code template.
288
- """
289
- def __init__(self, name: str=None, author: str='pyre'):
290
- self.codeBlocks: List[CodeBlock] = []
291
- self.closebracket = None
292
- self.name = name
293
- self.author = author
273
+ @staticmethod
274
+ def receive_from_recode():
275
+ print('Waiting for item to be sent...')
276
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
277
+ s.connect(('localhost', 31372))
278
+ received = s.recv(8192)
279
+ print(received)
280
+ s.close()
294
281
 
295
282
 
296
- def _setTemplateName(self, firstBlock):
283
+ def _set_template_name(self, first_block):
297
284
  if self.name is not None:
298
285
  return
299
- if 'data' in firstBlock:
300
- self.name = firstBlock['data']
286
+ if 'data' in first_block:
287
+ self.name = first_block['data']
301
288
  if not self.name:
302
289
  self.name = 'Unnamed Template'
303
290
  else:
304
- self.name = firstBlock['block'] + '_' + firstBlock['action']
291
+ self.name = first_block['block'] + '_' + first_block['action']
305
292
 
306
293
 
307
- def build(self, includeTags: bool=True) -> str:
294
+ def build(self, include_tags: bool=True) -> str:
308
295
  """
309
296
  Build this template.
310
297
 
311
- :param bool includeTags: If True, include item tags in code blocks. Otherwise omit them.
298
+ :param bool include_tags: If True, include item tags in code blocks. Otherwise omit them.
312
299
  :return: String containing encoded template data.
313
300
  """
314
- templateDictBlocks = [_buildBlock(codeblock, includeTags) for codeblock in self.codeBlocks]
315
- templateDict = {'blocks': templateDictBlocks}
316
- firstBlock = templateDictBlocks[0]
317
- if firstBlock['block'] not in TEMPLATE_STARTERS:
301
+ template_dict_blocks = [_build_block(codeblock, include_tags) for codeblock in self.codeblocks]
302
+ template_dict = {'blocks': template_dict_blocks}
303
+ first_block = template_dict_blocks[0]
304
+ if first_block['block'] not in TEMPLATE_STARTERS:
318
305
  _warn('Template does not start with an event, function, or process.')
319
306
 
320
- self._setTemplateName(firstBlock)
307
+ self._set_template_name(first_block)
321
308
 
322
309
  print(f'{COL_SUCCESS}Template built successfully.{COL_RESET}')
323
310
 
324
- jsonString = json.dumps(templateDict, separators=(',', ':'))
325
- return _dfEncode(jsonString)
311
+ json_string = json.dumps(template_dict, separators=(',', ':'))
312
+ return _df_encode(json_string)
326
313
 
327
314
 
328
- def buildAndSend(self, sendMethod: Literal['recode', 'codeclient']='recode', includeTags: bool=True) -> int:
315
+ def build_and_send(self, method: Literal['recode', 'codeclient'], includeTags: bool=True) -> int:
329
316
  """
330
317
  Builds this template and sends it to DiamondFire automatically.
331
318
 
332
319
  :param bool includeTags: If True, include item tags in code blocks. Otherwise omit them.
333
320
  """
334
321
  templateCode = self.build(includeTags)
335
- if sendMethod == 'recode':
336
- return sendRecode(templateCode, name=self.name, author=self.author)
337
- if sendMethod == 'codeclient':
338
- return sendCodeClient(templateCode, name=self.name, author=self.author)
339
- return -1
322
+ templateItem = _get_template_item(templateCode, self.name, self.author)
323
+ return templateItem.send_to_minecraft(method, 'pyre')
340
324
 
341
325
 
342
326
  def clear(self):
@@ -347,133 +331,149 @@ class DFTemplate:
347
331
 
348
332
 
349
333
  def _openbracket(self, btype: Literal['norm', 'repeat']='norm'):
350
- bracket = CodeBlock('Bracket', data={'id': 'bracket', 'direct': 'open', 'type': btype})
351
- self.codeBlocks.append(bracket)
352
- self.closebracket = btype
334
+ bracket = CodeBlock('bracket', data={'id': 'bracket', 'direct': 'open', 'type': btype})
335
+ self.codeblocks.append(bracket)
336
+ self.bracket_stack.append(btype)
353
337
 
354
338
 
355
339
  # command methods
356
- def playerEvent(self, name: str):
340
+ def player_event(self, name: str):
357
341
  cmd = CodeBlock(name, data={'id': 'block', 'block': 'event', 'action': name})
358
- self.codeBlocks.append(cmd)
342
+ self.codeblocks.append(cmd)
359
343
 
360
344
 
361
- def entityEvent(self, name: str):
345
+ def entity_event(self, name: str):
362
346
  cmd = CodeBlock(name, data={'id': 'block', 'block': 'entity_event', 'action': name})
363
- self.codeBlocks.append(cmd)
347
+ self.codeblocks.append(cmd)
364
348
 
365
349
 
366
350
  def function(self, name: str, *args):
367
- args = _convertDataTypes(args)
368
- cmd = CodeBlock('function', args, data={'id': 'block', 'block': 'func', 'data': name})
369
- self.codeBlocks.append(cmd)
351
+ args = _convert_data_types(args)
352
+ cmd = CodeBlock('func', args, data={'id': 'block', 'block': 'func', 'data': name})
353
+ self.codeblocks.append(cmd)
370
354
 
371
355
 
372
- def process(self, name: str):
373
- cmd = CodeBlock('process', data={'id': 'block', 'block': 'process', 'data': name})
374
- self.codeBlocks.append(cmd)
356
+ def process(self, name: str, *args):
357
+ args = _convert_data_types(args)
358
+ cmd = CodeBlock('process', args, data={'id': 'block', 'block': 'process', 'data': name})
359
+ self.codeblocks.append(cmd)
375
360
 
376
361
 
377
- def callFunction(self, name: str, *args):
378
- args = _convertDataTypes(args)
362
+ def call_function(self, name: str, *args):
363
+ args = _convert_data_types(args)
379
364
  cmd = CodeBlock('call_func', args, data={'id': 'block', 'block': 'call_func', 'data': name})
380
- self.codeBlocks.append(cmd)
365
+ self.codeblocks.append(cmd)
381
366
 
382
367
 
383
- def startProcess(self, name: str):
368
+ def start_process(self, name: str):
384
369
  cmd = CodeBlock('start_process', data={'id': 'block', 'block': 'start_process', 'data': name})
385
- self.codeBlocks.append(cmd)
370
+ self.codeblocks.append(cmd)
386
371
 
387
372
 
388
- def playerAction(self, name: str, *args, target: Target=DEFAULT_TARGET):
389
- args = _convertDataTypes(args)
373
+ def player_action(self, name: str, *args, target: Target=DEFAULT_TARGET):
374
+ args = _convert_data_types(args)
390
375
  cmd = CodeBlock(name, args, target=target, data={'id': 'block', 'block': 'player_action', 'action': name})
391
- self.codeBlocks.append(cmd)
376
+ self.codeblocks.append(cmd)
392
377
 
393
378
 
394
- def gameAction(self, name: str, *args):
395
- args = _convertDataTypes(args)
379
+ def game_action(self, name: str, *args):
380
+ args = _convert_data_types(args)
396
381
  cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'game_action', 'action': name})
397
- self.codeBlocks.append(cmd)
382
+ self.codeblocks.append(cmd)
398
383
 
399
384
 
400
- def entityAction(self, name: str, *args, target: Target=DEFAULT_TARGET):
401
- args = _convertDataTypes(args)
385
+ def entity_action(self, name: str, *args, target: Target=DEFAULT_TARGET):
386
+ args = _convert_data_types(args)
402
387
  cmd = CodeBlock(name, args, target=target, data={'id': 'block', 'block': 'entity_action', 'action': name})
403
- self.codeBlocks.append(cmd)
388
+ self.codeblocks.append(cmd)
404
389
 
405
390
 
406
- def ifPlayer(self, name: str, *args, target: Target=DEFAULT_TARGET, inverted: bool=False):
407
- args = _convertDataTypes(args)
391
+ def if_player(self, name: str, *args, target: Target=DEFAULT_TARGET, inverted: bool=False):
392
+ args = _convert_data_types(args)
408
393
  data = {'id': 'block', 'block': 'if_player', 'action': name}
409
- _addInverted(data, inverted)
394
+ _add_inverted(data, inverted)
410
395
  cmd = CodeBlock(name, args, target=target, data=data)
411
- self.codeBlocks.append(cmd)
396
+ self.codeblocks.append(cmd)
412
397
  self._openbracket()
413
398
 
414
399
 
415
- def ifVariable(self, name: str, *args, inverted: bool=False):
416
- args = _convertDataTypes(args)
400
+ def if_variable(self, name: str, *args, inverted: bool=False):
401
+ args = _convert_data_types(args)
417
402
  data = {'id': 'block', 'block': 'if_var', 'action': name}
418
- _addInverted(data, inverted)
403
+ _add_inverted(data, inverted)
419
404
  cmd = CodeBlock(name, args, data=data)
420
- self.codeBlocks.append(cmd)
405
+ self.codeblocks.append(cmd)
421
406
  self._openbracket()
422
407
 
423
408
 
424
- def ifGame(self, name: str, *args, inverted: bool=False):
425
- args = _convertDataTypes(args)
409
+ def if_game(self, name: str, *args, inverted: bool=False):
410
+ args = _convert_data_types(args)
426
411
  data = {'id': 'block', 'block': 'if_game', 'action': name}
427
- _addInverted(data, inverted)
412
+ _add_inverted(data, inverted)
428
413
  cmd = CodeBlock(name, args, data=data)
429
- self.codeBlocks.append(cmd)
414
+ self.codeblocks.append(cmd)
430
415
  self._openbracket()
431
416
 
432
417
 
433
- def ifEntity(self, name: str, *args, target: Target=DEFAULT_TARGET, inverted: bool=False):
434
- args = _convertDataTypes(args)
418
+ def if_entity(self, name: str, *args, target: Target=DEFAULT_TARGET, inverted: bool=False):
419
+ args = _convert_data_types(args)
435
420
  data = {'id': 'block', 'block': 'if_entity', 'action': name}
436
- _addInverted(data, inverted)
421
+ _add_inverted(data, inverted)
437
422
  cmd = CodeBlock(name, args, target=target, data=data)
438
- self.codeBlocks.append(cmd)
423
+ self.codeblocks.append(cmd)
439
424
  self._openbracket()
440
425
 
441
426
 
442
427
  def else_(self):
443
428
  cmd = CodeBlock('else', data={'id': 'block', 'block': 'else'})
444
- self.codeBlocks.append(cmd)
429
+ self.codeblocks.append(cmd)
445
430
  self._openbracket()
446
431
 
447
432
 
448
- def repeat(self, name: str, *args, subAction: str=None):
449
- args = _convertDataTypes(args)
433
+ def repeat(self, name: str, *args, sub_action: str=None):
434
+ args = _convert_data_types(args)
450
435
  data = {'id': 'block', 'block': 'repeat', 'action': name}
451
- if subAction is not None:
452
- data['subAction'] = subAction
436
+ if sub_action is not None:
437
+ data['subAction'] = sub_action
453
438
  cmd = CodeBlock(name, args, data=data)
454
- self.codeBlocks.append(cmd)
439
+ self.codeblocks.append(cmd)
455
440
  self._openbracket('repeat')
456
441
 
457
442
 
458
443
  def bracket(self, *args):
459
- args = _convertDataTypes(args)
460
- cmd = CodeBlock('Bracket', data={'id': 'bracket', 'direct': 'close', 'type': self.closebracket})
461
- self.codeBlocks.append(cmd)
444
+ args = _convert_data_types(args)
445
+ cmd = CodeBlock('bracket', data={'id': 'bracket', 'direct': 'close', 'type': self.bracket_stack.pop()})
446
+ self.codeblocks.append(cmd)
462
447
 
463
448
 
464
449
  def control(self, name: str, *args):
465
- args = _convertDataTypes(args)
450
+ args = _convert_data_types(args)
466
451
  cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'control', 'action': name})
467
- self.codeBlocks.append(cmd)
452
+ self.codeblocks.append(cmd)
468
453
 
469
454
 
470
- def selectObject(self, name: str, *args):
471
- args = _convertDataTypes(args)
455
+ def select_object(self, name: str, *args):
456
+ args = _convert_data_types(args)
472
457
  cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'select_obj', 'action': name})
473
- self.codeBlocks.append(cmd)
458
+ self.codeblocks.append(cmd)
474
459
 
475
460
 
476
- def setVariable(self, name: str, *args):
477
- args = _convertDataTypes(args)
461
+ def set_variable(self, name: str, *args):
462
+ args = _convert_data_types(args)
478
463
  cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'set_var', 'action': name})
479
- self.codeBlocks.append(cmd)
464
+ self.codeblocks.append(cmd)
465
+
466
+
467
+ def generate_script(self, output_path: str, indent_size: int=4, literal_shorthand: bool=True, var_shorthand: bool=False):
468
+ """
469
+ Generate an equivalent python script for this template.
470
+
471
+ :param str output_path: The file path to write the script to.
472
+ :param int indent_size: The multiple of spaces to add when indenting lines.
473
+ :param bool literal_shorthand: If True, `text` and `num` items will be written as strings and ints respectively.
474
+ :param bool var_shorthand: If True, all variables will be written using variable shorthand.
475
+ """
476
+ flags = GeneratorFlags(indent_size, literal_shorthand, var_shorthand)
477
+ with open(output_path, 'w') as f:
478
+ f.write(generate_script(self, flags))
479
+