dfpyre 0.5.0__py3-none-any.whl → 0.6.1__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/items.py CHANGED
@@ -3,15 +3,24 @@ Contains class definitions for code items.
3
3
  """
4
4
 
5
5
  from enum import Enum
6
- from typing import Literal, Dict
6
+ import re
7
+ from typing import Literal, Dict, Any
7
8
  from dfpyre.style import is_ampersand_coded, ampersand_to_minimessage
8
9
  from mcitemlib.itemlib import Item as NbtItem
9
10
 
10
11
 
12
+ NUMBER_REGEX = r'-?\d*\.?\d+'
13
+
14
+
15
+ class PyreException(Exception):
16
+ pass
17
+
18
+
11
19
  def _add_slot(d: Dict, slot: int|None):
12
20
  if slot is not None:
13
21
  d['slot'] = slot
14
22
 
23
+
15
24
  class item(NbtItem):
16
25
  """
17
26
  Represents a Minecraft item.
@@ -62,7 +71,7 @@ class num:
62
71
  """
63
72
  type = 'num'
64
73
 
65
- def __init__(self, num: int|float):
74
+ def __init__(self, num: int|float|str):
66
75
  self.value = num
67
76
 
68
77
  def format(self, slot: int|None):
@@ -140,34 +149,11 @@ class particle:
140
149
  Represents a DiamondFire particle object.
141
150
  """
142
151
  type = 'part'
143
- def __init__(self, name: str='Cloud', amount: int=1, horizontal: float=0.0, vertical: float=0.0,
144
- x: float=1.0, y: float=0.0, z: float=0.0, motionVariation: float=100):
145
- self.name = name
146
- self.amount = amount
147
- self.horizontal = horizontal
148
- self.vertical = vertical
149
- self.x = x
150
- self.y = y
151
- self.z = z
152
- self.motionVariation = motionVariation
152
+ def __init__(self, particle_data: dict):
153
+ self.particle_data = particle_data
153
154
 
154
155
  def format(self, slot: int|None):
155
- formatted_dict = {"item": {"id": self.type,
156
- "data": {
157
- "particle": self.name,
158
- "cluster": {
159
- "amount": self.amount,
160
- "horizontal": self.horizontal,
161
- "vertical": self.vertical
162
- },
163
- "data": {
164
- "x": self.x,
165
- "y": self.y,
166
- "z": self.z,
167
- "motionVariation": self.motionVariation
168
- }
169
- }
170
- }}
156
+ formatted_dict = {"item": {"id": self.type, "data": self.particle_data}}
171
157
  _add_slot(formatted_dict, slot)
172
158
  return formatted_dict
173
159
 
@@ -276,4 +262,61 @@ class parameter:
276
262
  if self.default_value is not None and not self.plural and self.optional:
277
263
  formatted_dict['item']['data']['default_value'] = self.default_value.format(None)['item']
278
264
 
279
- return formatted_dict
265
+ return formatted_dict
266
+
267
+
268
+ def _some_or(value: Any, none_value: Any):
269
+ """
270
+ Returns `none_value` if `value` is None, otherwise returns `value`.
271
+ """
272
+ if value is None:
273
+ return none_value
274
+ return value
275
+
276
+
277
+ def item_from_dict(item_dict: Dict) -> object:
278
+ item_id = item_dict['id']
279
+ item_data = item_dict['data']
280
+
281
+ if item_id == 'item':
282
+ return item.from_nbt(item_data['item'])
283
+ elif item_id == 'txt':
284
+ return string(item_data['name'])
285
+ elif item_id == 'comp':
286
+ return text(item_data['name'])
287
+ elif item_id == 'num':
288
+ num_value = item_data['name']
289
+ if re.match(NUMBER_REGEX, num_value):
290
+ num_value = float(item_data['name'])
291
+ if num_value % 1 == 0:
292
+ num_value = int(num_value)
293
+ return num(num_value)
294
+ return num(num_value)
295
+ elif item_id == 'loc':
296
+ item_loc = item_data['loc']
297
+ return loc(item_loc['x'], item_loc['y'], item_loc['z'], item_loc['pitch'], item_loc['yaw'])
298
+ elif item_id == 'var':
299
+ return var(item_data['name'], item_data['scope'])
300
+ elif item_id == 'snd':
301
+ return sound(item_data['sound'], item_data['pitch'], item_data['vol'])
302
+ elif item_id == 'part':
303
+ return particle(item_data)
304
+ elif item_id == 'pot':
305
+ return potion(item_data['pot'], item_data['dur'], item_data['amp'])
306
+ elif item_id == 'g_val':
307
+ return gamevalue(item_data['type'], item_data['target'])
308
+ elif item_id == 'vec':
309
+ return vector(item_data['x'], item_data['y'], item_data['z'])
310
+ elif item_id == 'pn_el':
311
+ description = _some_or(item_data.get('description'), '')
312
+ note = _some_or(item_data.get('note'), '')
313
+ param_type = ParameterType(PARAMETER_TYPE_LOOKUP.index(item_data['type']))
314
+ if item_data['optional']:
315
+ param = parameter(item_data['name'], param_type, item_data['plural'], True, description, note, item_from_dict(item_data['default_value']))
316
+ else:
317
+ param = parameter(item_data['name'], param_type, item_data['plural'], False, description, note)
318
+ return param
319
+ elif item_id == 'bl_tag':
320
+ return
321
+ else:
322
+ raise PyreException(f'Unrecognized item id `{item_id}`')
dfpyre/pyre.py CHANGED
@@ -2,7 +2,6 @@
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
@@ -13,8 +12,10 @@ from difflib import get_close_matches
13
12
  import datetime
14
13
  from typing import Tuple, List, Dict
15
14
  from enum import Enum
15
+ import socket
16
16
  from mcitemlib.itemlib import Item as NbtItem
17
17
  from dfpyre.items import *
18
+ from dfpyre.scriptgen import generate_script, GeneratorFlags
18
19
 
19
20
  COL_WARN = '\x1b[33m'
20
21
  COL_RESET = '\x1b[0m'
@@ -25,6 +26,7 @@ CODEBLOCK_DATA_PATH = os.path.join(os.path.dirname(__file__), 'data/data.json')
25
26
 
26
27
  VARIABLE_TYPES = {'txt', 'comp', 'num', 'item', 'loc', 'var', 'snd', 'part', 'pot', 'g_val', 'vec', 'pn_el'}
27
28
  TEMPLATE_STARTERS = {'event', 'entity_event', 'func', 'process'}
29
+ SINGLE_NAME_CODEBLOCKS = {'func', 'process', 'call_func', 'start_process', 'else'}
28
30
 
29
31
  TARGETS = ['Selection', 'Default', 'Killer', 'Damager', 'Shooter', 'Victim', 'AllPlayers', 'Projectile', 'AllEntities', 'AllMobs', 'LastEntity']
30
32
  TARGET_CODEBLOCKS = {'player_action', 'entity_action', 'if_player', 'if_entity'}
@@ -60,6 +62,15 @@ class CodeBlock:
60
62
  self.args = args
61
63
  self.target = target
62
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"]})'
63
74
 
64
75
 
65
76
  def _warn(message):
@@ -108,7 +119,7 @@ def _convert_data_types(args):
108
119
  if type(value) in {int, float}:
109
120
  converted_args.append(num(value))
110
121
  elif type(value) is str:
111
- if value[0] == VAR_SHORTHAND_CHAR and value[1] in VAR_SCOPES:
122
+ if len(value) > 2 and value[0] == VAR_SHORTHAND_CHAR and value[1] in VAR_SCOPES:
112
123
  var_object = var(value[2:], VAR_SCOPES[value[1]])
113
124
  converted_args.append(var_object)
114
125
  else:
@@ -157,12 +168,12 @@ def _build_block(codeblock: CodeBlock, include_tags: bool):
157
168
  """
158
169
  Builds a properly formatted block from a CodeBlock object.
159
170
  """
160
- final_block = codeblock.data.copy()
171
+ built_block = codeblock.data.copy()
161
172
  codeblock_type = codeblock.data.get('block')
162
173
 
163
174
  # add target if necessary ('Selection' is the default when 'target' is blank)
164
175
  if codeblock_type in TARGET_CODEBLOCKS and codeblock.target != DEFAULT_TARGET:
165
- final_block['target'] = codeblock.target.get_string_value()
176
+ built_block['target'] = codeblock.target.get_string_value()
166
177
 
167
178
  # add items into args
168
179
  final_args = [arg.format(slot) for slot, arg in enumerate(codeblock.args) if arg.type in VARIABLE_TYPES]
@@ -177,16 +188,20 @@ def _build_block(codeblock: CodeBlock, include_tags: bool):
177
188
  final_args = final_args[:(27-len(tags))] # trim list if over 27 elements
178
189
  final_args.extend(tags) # add tags to end
179
190
 
180
- final_block['args'] = {'items': final_args}
181
- return final_block
191
+ built_block['args'] = {'items': final_args}
192
+ return built_block
182
193
 
183
194
 
184
- def _df_encode(jsonString: str) -> str:
195
+ def _df_encode(json_string: str) -> str:
185
196
  """
186
197
  Encodes a stringified json.
187
198
  """
188
- encodedString = gzip.compress(jsonString.encode('utf-8'))
189
- 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')
190
205
 
191
206
 
192
207
  def _get_template_item(template_code: str, name: str, author: str) -> NbtItem:
@@ -217,9 +232,50 @@ class DFTemplate:
217
232
  """
218
233
  def __init__(self, name: str=None, author: str='pyre'):
219
234
  self.codeblocks: List[CodeBlock] = []
220
- self.closebracket = None
235
+ self.bracket_stack: list[str] = []
221
236
  self.name = name
222
237
  self.author = author
238
+
239
+
240
+ def __repr__(self) -> str:
241
+ return f'DFTemplate(name: {self.name}, author: {self.author}, codeblocks: {len(self.codeblocks)})'
242
+
243
+
244
+ @staticmethod
245
+ def from_code(template_code: str):
246
+ """
247
+ Create a template object from an existing template code.
248
+ """
249
+ template_dict = json.loads(_df_decode(template_code))
250
+ template = DFTemplate()
251
+ for block_dict in template_dict['blocks']:
252
+ if 'args' in block_dict:
253
+ args = []
254
+ for item_dict in block_dict['args']['items']:
255
+ parsed_item = item_from_dict(item_dict['item'])
256
+ if parsed_item is not None:
257
+ args.append(parsed_item)
258
+ target = Target(TARGETS.index(block_dict['target'])) if 'target' in block_dict else DEFAULT_TARGET
259
+
260
+ codeblock_name = 'bracket'
261
+ if 'block' in block_dict and block_dict['block'] in SINGLE_NAME_CODEBLOCKS:
262
+ codeblock_name = block_dict['block']
263
+ elif 'action' in block_dict:
264
+ codeblock_name = block_dict['action']
265
+ codeblock = CodeBlock(codeblock_name, args, target, block_dict)
266
+ template.codeblocks.append(codeblock)
267
+
268
+ return template
269
+
270
+
271
+ @staticmethod
272
+ def receive_from_recode():
273
+ print('Waiting for item to be sent...')
274
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
275
+ s.connect(('localhost', 31372))
276
+ received = s.recv(8192)
277
+ print(received)
278
+ s.close()
223
279
 
224
280
 
225
281
  def _set_template_name(self, first_block):
@@ -237,7 +293,7 @@ class DFTemplate:
237
293
  """
238
294
  Build this template.
239
295
 
240
- :param bool includeTags: If True, include item tags in code blocks. Otherwise omit them.
296
+ :param bool include_tags: If True, include item tags in code blocks. Otherwise omit them.
241
297
  :return: String containing encoded template data.
242
298
  """
243
299
  template_dict_blocks = [_build_block(codeblock, include_tags) for codeblock in self.codeblocks]
@@ -248,21 +304,19 @@ class DFTemplate:
248
304
 
249
305
  self._set_template_name(first_block)
250
306
 
251
- print(f'{COL_SUCCESS}Template built successfully.{COL_RESET}')
252
-
253
307
  json_string = json.dumps(template_dict, separators=(',', ':'))
254
308
  return _df_encode(json_string)
255
309
 
256
310
 
257
- def build_and_send(self, method: Literal['recode', 'codeclient'], includeTags: bool=True) -> int:
311
+ def build_and_send(self, method: Literal['recode', 'codeclient'], include_tags: bool=True) -> int:
258
312
  """
259
313
  Builds this template and sends it to DiamondFire automatically.
260
314
 
261
- :param bool includeTags: If True, include item tags in code blocks. Otherwise omit them.
315
+ :param bool include_tags: If True, include item tags in code blocks. Otherwise omit them.
262
316
  """
263
- templateCode = self.build(includeTags)
264
- templateItem = _get_template_item(templateCode, self.name, self.author)
265
- return templateItem.send_to_minecraft(method)
317
+ template_code = self.build(include_tags)
318
+ template_item = _get_template_item(template_code, self.name, self.author)
319
+ return template_item.send_to_minecraft(method, 'pyre')
266
320
 
267
321
 
268
322
  def clear(self):
@@ -272,134 +326,156 @@ class DFTemplate:
272
326
  self.__init__()
273
327
 
274
328
 
275
- def _openbracket(self, btype: Literal['norm', 'repeat']='norm'):
276
- bracket = CodeBlock('Bracket', data={'id': 'bracket', 'direct': 'open', 'type': btype})
277
- self.codeblocks.append(bracket)
278
- self.closebracket = btype
329
+ def _add_codeblock(self, codeblock: CodeBlock, index: int|None):
330
+ if index is None:
331
+ self.codeblocks.append(codeblock)
332
+ else:
333
+ self.codeblocks.insert(index, codeblock)
334
+
335
+
336
+ def _openbracket(self, index: int|None, btype: Literal['norm', 'repeat']='norm'):
337
+ bracket = CodeBlock('bracket', data={'id': 'bracket', 'direct': 'open', 'type': btype})
338
+ self._add_codeblock(bracket, index+1)
339
+ self.bracket_stack.append(btype)
279
340
 
280
341
 
281
342
  # command methods
282
- def player_event(self, name: str):
343
+ def player_event(self, name: str, index: int|None=None):
283
344
  cmd = CodeBlock(name, data={'id': 'block', 'block': 'event', 'action': name})
284
- self.codeblocks.append(cmd)
345
+ self._add_codeblock(cmd, index)
285
346
 
286
347
 
287
- def entity_event(self, name: str):
348
+ def entity_event(self, name: str, index: int|None=None):
288
349
  cmd = CodeBlock(name, data={'id': 'block', 'block': 'entity_event', 'action': name})
289
- self.codeblocks.append(cmd)
350
+ self._add_codeblock(cmd, index)
290
351
 
291
352
 
292
- def function(self, name: str, *args):
353
+ def function(self, name: str, *args, index: int|None=None):
293
354
  args = _convert_data_types(args)
294
- cmd = CodeBlock('function', args, data={'id': 'block', 'block': 'func', 'data': name})
295
- self.codeblocks.append(cmd)
355
+ cmd = CodeBlock('func', args, data={'id': 'block', 'block': 'func', 'data': name})
356
+ self._add_codeblock(cmd, index)
296
357
 
297
358
 
298
- def process(self, name: str):
299
- cmd = CodeBlock('process', data={'id': 'block', 'block': 'process', 'data': name})
300
- self.codeblocks.append(cmd)
359
+ def process(self, name: str, *args, index: int|None=None):
360
+ args = _convert_data_types(args)
361
+ cmd = CodeBlock('process', args, data={'id': 'block', 'block': 'process', 'data': name})
362
+ self._add_codeblock(cmd, index)
301
363
 
302
364
 
303
- def call_function(self, name: str, *args):
365
+ def call_function(self, name: str, *args, index: int|None=None):
304
366
  args = _convert_data_types(args)
305
367
  cmd = CodeBlock('call_func', args, data={'id': 'block', 'block': 'call_func', 'data': name})
306
- self.codeblocks.append(cmd)
307
-
368
+ self._add_codeblock(cmd, index)
308
369
 
309
- def start_process(self, name: str):
370
+ def start_process(self, name: str, index: int|None=None):
310
371
  cmd = CodeBlock('start_process', data={'id': 'block', 'block': 'start_process', 'data': name})
311
- self.codeblocks.append(cmd)
372
+ self._add_codeblock(cmd, index)
312
373
 
313
374
 
314
- def player_action(self, name: str, *args, target: Target=DEFAULT_TARGET):
375
+ def player_action(self, name: str, *args, target: Target=DEFAULT_TARGET, index: int|None=None):
315
376
  args = _convert_data_types(args)
316
377
  cmd = CodeBlock(name, args, target=target, data={'id': 'block', 'block': 'player_action', 'action': name})
317
- self.codeblocks.append(cmd)
378
+ self._add_codeblock(cmd, index)
318
379
 
319
380
 
320
- def game_action(self, name: str, *args):
381
+ def game_action(self, name: str, *args, index: int|None=None):
321
382
  args = _convert_data_types(args)
322
383
  cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'game_action', 'action': name})
323
- self.codeblocks.append(cmd)
384
+ self._add_codeblock(cmd, index)
324
385
 
325
386
 
326
- def entity_action(self, name: str, *args, target: Target=DEFAULT_TARGET):
387
+ def entity_action(self, name: str, *args, target: Target=DEFAULT_TARGET, index: int|None=None):
327
388
  args = _convert_data_types(args)
328
389
  cmd = CodeBlock(name, args, target=target, data={'id': 'block', 'block': 'entity_action', 'action': name})
329
- self.codeblocks.append(cmd)
390
+ self._add_codeblock(cmd, index)
330
391
 
331
392
 
332
- def if_player(self, name: str, *args, target: Target=DEFAULT_TARGET, inverted: bool=False):
393
+ def if_player(self, name: str, *args, target: Target=DEFAULT_TARGET, inverted: bool=False, index: int|None=None):
333
394
  args = _convert_data_types(args)
334
395
  data = {'id': 'block', 'block': 'if_player', 'action': name}
335
396
  _add_inverted(data, inverted)
336
397
  cmd = CodeBlock(name, args, target=target, data=data)
337
- self.codeblocks.append(cmd)
338
- self._openbracket()
398
+ self._add_codeblock(cmd, index)
399
+ self._openbracket(index)
339
400
 
340
401
 
341
- def if_variable(self, name: str, *args, inverted: bool=False):
402
+ def if_variable(self, name: str, *args, inverted: bool=False, index: int|None=None):
342
403
  args = _convert_data_types(args)
343
404
  data = {'id': 'block', 'block': 'if_var', 'action': name}
344
405
  _add_inverted(data, inverted)
345
406
  cmd = CodeBlock(name, args, data=data)
346
- self.codeblocks.append(cmd)
347
- self._openbracket()
407
+ self._add_codeblock(cmd, index)
408
+ self._openbracket(index)
348
409
 
349
410
 
350
- def if_game(self, name: str, *args, inverted: bool=False):
411
+ def if_game(self, name: str, *args, inverted: bool=False, index: int|None=None):
351
412
  args = _convert_data_types(args)
352
413
  data = {'id': 'block', 'block': 'if_game', 'action': name}
353
414
  _add_inverted(data, inverted)
354
415
  cmd = CodeBlock(name, args, data=data)
355
- self.codeblocks.append(cmd)
356
- self._openbracket()
416
+ self._add_codeblock(cmd, index)
417
+ self._openbracket(index)
357
418
 
358
419
 
359
- def if_entity(self, name: str, *args, target: Target=DEFAULT_TARGET, inverted: bool=False):
420
+ def if_entity(self, name: str, *args, target: Target=DEFAULT_TARGET, inverted: bool=False, index: int|None=None):
360
421
  args = _convert_data_types(args)
361
422
  data = {'id': 'block', 'block': 'if_entity', 'action': name}
362
423
  _add_inverted(data, inverted)
363
424
  cmd = CodeBlock(name, args, target=target, data=data)
364
- self.codeblocks.append(cmd)
365
- self._openbracket()
425
+ self._add_codeblock(cmd, index)
426
+ self._openbracket(index)
366
427
 
367
428
 
368
- def else_(self):
429
+ def else_(self, index: int|None=None):
369
430
  cmd = CodeBlock('else', data={'id': 'block', 'block': 'else'})
370
- self.codeblocks.append(cmd)
371
- self._openbracket()
431
+ self._add_codeblock(cmd, index)
432
+ self._openbracket(index)
372
433
 
373
434
 
374
- def repeat(self, name: str, *args, sub_action: str=None):
435
+ def repeat(self, name: str, *args, sub_action: str=None, index: int|None=None):
375
436
  args = _convert_data_types(args)
376
437
  data = {'id': 'block', 'block': 'repeat', 'action': name}
377
438
  if sub_action is not None:
378
439
  data['subAction'] = sub_action
379
440
  cmd = CodeBlock(name, args, data=data)
380
- self.codeblocks.append(cmd)
381
- self._openbracket('repeat')
441
+ self._add_codeblock(cmd, index)
442
+ self._openbracket(index, 'repeat')
382
443
 
383
444
 
384
- def bracket(self, *args):
445
+ def bracket(self, *args, index: int|None=None):
385
446
  args = _convert_data_types(args)
386
- cmd = CodeBlock('Bracket', data={'id': 'bracket', 'direct': 'close', 'type': self.closebracket})
387
- self.codeblocks.append(cmd)
447
+ cmd = CodeBlock('bracket', data={'id': 'bracket', 'direct': 'close', 'type': self.bracket_stack.pop()})
448
+ self._add_codeblock(cmd, index)
388
449
 
389
450
 
390
- def control(self, name: str, *args):
451
+ def control(self, name: str, *args, index: int|None=None):
391
452
  args = _convert_data_types(args)
392
453
  cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'control', 'action': name})
393
- self.codeblocks.append(cmd)
454
+ self._add_codeblock(cmd, index)
394
455
 
395
456
 
396
- def select_object(self, name: str, *args):
457
+ def select_object(self, name: str, *args, index: int|None=None):
397
458
  args = _convert_data_types(args)
398
459
  cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'select_obj', 'action': name})
399
- self.codeblocks.append(cmd)
460
+ self._add_codeblock(cmd, index)
400
461
 
401
462
 
402
- def set_variable(self, name: str, *args):
463
+ def set_variable(self, name: str, *args, index: int|None=None):
403
464
  args = _convert_data_types(args)
404
465
  cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'set_var', 'action': name})
405
- self.codeblocks.append(cmd)
466
+ self._add_codeblock(cmd, index)
467
+
468
+
469
+ def generate_script(self, output_path: str, indent_size: int=4, literal_shorthand: bool=True, var_shorthand: bool=False):
470
+ """
471
+ Generate an equivalent python script for this template.
472
+
473
+ :param str output_path: The file path to write the script to.
474
+ :param int indent_size: The multiple of spaces to add when indenting lines.
475
+ :param bool literal_shorthand: If True, `text` and `num` items will be written as strings and ints respectively.
476
+ :param bool var_shorthand: If True, all variables will be written using variable shorthand.
477
+ """
478
+ flags = GeneratorFlags(indent_size, literal_shorthand, var_shorthand)
479
+ with open(output_path, 'w') as f:
480
+ f.write(generate_script(self, flags))
481
+
dfpyre/scriptgen.py ADDED
@@ -0,0 +1,146 @@
1
+ import dataclasses
2
+ import re
3
+ from dfpyre.items import *
4
+
5
+ SCRIPT_START = '''from dfpyre import *
6
+ t = DFTemplate()
7
+ '''
8
+
9
+ TEMPLATE_METHOD_LOOKUP = {
10
+ 'event': 'player_event',
11
+ 'entity_event': 'entity_event',
12
+ 'func': 'function',
13
+ 'process': 'process',
14
+ 'call_func': 'call_function',
15
+ 'start_process': 'start_process',
16
+ 'player_action': 'player_action',
17
+ 'game_action': 'game_action',
18
+ 'entity_action': 'entity_action',
19
+ 'if_player': 'if_player',
20
+ 'if_var': 'if_variable',
21
+ 'if_game': 'if_game',
22
+ 'if_entity': 'if_entity',
23
+ 'else': 'else_',
24
+ 'repeat': 'repeat',
25
+ 'control': 'control',
26
+ 'select_obj': 'select_object',
27
+ 'set_var': 'set_variable'
28
+ }
29
+
30
+ TARGET_CODEBLOCKS = {'player_action', 'entity_action', 'if_player', 'if_entity'}
31
+ SINGLE_NAME_CODEBLOCKS = {'func', 'process', 'call_func', 'start_process'}
32
+ VAR_SCOPES = {'unsaved': 'g', 'saved': 's', 'local': 'l', 'line': 'i'}
33
+
34
+
35
+ @dataclasses.dataclass
36
+ class GeneratorFlags:
37
+ indent_size: int
38
+ literal_shorthand: bool
39
+ var_shorthand: bool
40
+
41
+
42
+ def argument_item_to_string(flags: GeneratorFlags, arg_item: object) -> str:
43
+ class_name = arg_item.__class__.__name__
44
+ if isinstance(arg_item, item):
45
+ return f'{class_name}.from_nbt("""{arg_item.get_nbt()}""")'
46
+
47
+ if isinstance(arg_item, string):
48
+ value = arg_item.value.replace('\n', '\\n')
49
+ return f'{class_name}("{value}")'
50
+
51
+ if isinstance(arg_item, text):
52
+ value = arg_item.value.replace('\n', '\\n')
53
+ if flags.literal_shorthand:
54
+ return f'"{value}"'
55
+ return f'{class_name}("{value}")'
56
+
57
+ if isinstance(arg_item, num):
58
+ if not re.match(NUMBER_REGEX, str(arg_item.value)):
59
+ return f'{class_name}("{arg_item.value}")'
60
+ if flags.literal_shorthand:
61
+ return str(arg_item.value)
62
+ return f'{class_name}({arg_item.value})'
63
+
64
+ if isinstance(arg_item, loc):
65
+ loc_components = [arg_item.x, arg_item.y, arg_item.z]
66
+ if arg_item.pitch != 0:
67
+ loc_components.append(arg_item.pitch)
68
+ if arg_item.yaw != 0:
69
+ loc_components.append(arg_item.yaw)
70
+ return f'{class_name}({", ".join(str(c) for c in loc_components)})'
71
+
72
+ if isinstance(arg_item, var):
73
+ if flags.var_shorthand:
74
+ return f'"${VAR_SCOPES[arg_item.scope]}{arg_item.name}"'
75
+ if arg_item.scope == 'unsaved':
76
+ return f'{class_name}("{arg_item.name}")'
77
+ return f'{class_name}("{arg_item.name}", "{arg_item.scope}")'
78
+
79
+ if isinstance(arg_item, sound):
80
+ return f'{class_name}("{arg_item.name}", {arg_item.pitch}, {arg_item.vol})'
81
+
82
+ if isinstance(arg_item, particle):
83
+ return f'{class_name}({arg_item.particle_data})'
84
+
85
+ if isinstance(arg_item, potion):
86
+ return f'{class_name}("{arg_item.name}", {arg_item.dur}, {arg_item.amp})'
87
+
88
+ if isinstance(arg_item, gamevalue):
89
+ return f'{class_name}("{arg_item.name}", "{arg_item.target}")'
90
+
91
+ if isinstance(arg_item, parameter):
92
+ param_type_class_name = arg_item.param_type.__class__.__name__
93
+ param_args = [f'"{arg_item.name}"', f'{param_type_class_name}.{arg_item.param_type.name}']
94
+ if arg_item.plural:
95
+ param_args.append('plural=True')
96
+ if arg_item.optional:
97
+ param_args.append('optional=True')
98
+ if arg_item.default_value is not None:
99
+ param_args.append(f'default_value={argument_item_to_string(arg_item.default_value)}')
100
+ if arg_item.description:
101
+ param_args.append(f'description="{arg_item.description}"')
102
+ if arg_item.note:
103
+ param_args.append(f'note="{arg_item.note}"')
104
+ return f'{class_name}({", ".join(param_args)})'
105
+
106
+ if isinstance(arg_item, vector):
107
+ return f'{class_name}({arg_item.x}, {arg_item.y}, {arg_item.z})'
108
+
109
+
110
+ def add_script_line(flags: GeneratorFlags, script_lines: list[str], indent_level: int, line: str, add_comma: bool=True):
111
+ added_line = ' '*flags.indent_size*indent_level + line
112
+ if add_comma and indent_level > 0:
113
+ added_line += ','
114
+ script_lines.append(added_line)
115
+
116
+
117
+ def generate_script(template, flags: GeneratorFlags) -> str:
118
+ indent_level = 0
119
+ script_lines = []
120
+ for codeblock in template.codeblocks:
121
+ if codeblock.name == 'bracket':
122
+ if codeblock.data['direct'] == 'open':
123
+ add_script_line(flags, script_lines, indent_level, 't.bracket(', False)
124
+ indent_level += 1
125
+ elif codeblock.data['direct'] == 'close':
126
+ indent_level -= 1
127
+ add_script_line(flags, script_lines, indent_level, ')')
128
+ continue
129
+ if codeblock.name == 'else':
130
+ add_script_line(flags, script_lines, indent_level, 't.else_()')
131
+ continue
132
+
133
+ method_name = TEMPLATE_METHOD_LOOKUP[codeblock.data['block']]
134
+ method_args = [f'"{codeblock.name}"']
135
+ if codeblock.name in SINGLE_NAME_CODEBLOCKS:
136
+ method_args[0] = f'"{codeblock.data["data"]}"'
137
+
138
+ codeblock_args = [argument_item_to_string(flags, i) for i in codeblock.args]
139
+ if codeblock_args:
140
+ method_args.extend(codeblock_args)
141
+ if method_name in TARGET_CODEBLOCKS and codeblock.target.name != 'SELECTION':
142
+ method_args.append(f'target=Target.{codeblock.target.name}')
143
+
144
+ line = f't.{method_name}({", ".join(method_args)})'
145
+ add_script_line(flags, script_lines, indent_level, line)
146
+ return SCRIPT_START + '\n'.join(script_lines)
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 Amp
3
+ Copyright (c) 2024 Amp
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dfpyre
3
- Version: 0.5.0
3
+ Version: 0.6.1
4
4
  Summary: A package for external creation of code templates for the DiamondFire Minecraft server.
5
5
  Home-page: https://github.com/Amp63/pyre
6
6
  License: MIT
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Programming Language :: Python :: 3.12
15
- Requires-Dist: mcitemlib (>=0.1.2,<0.2.0)
15
+ Requires-Dist: mcitemlib (>=0.2.4,<0.3.0)
16
16
  Project-URL: Repository, https://github.com/Amp63/pyre
17
17
  Description-Content-Type: text/markdown
18
18
 
@@ -70,6 +70,8 @@ pip install dfpyre
70
70
 
71
71
  ## Extras
72
72
 
73
+ - [Importing from Code](#importing-from-code)
74
+ - [Script Generation](#script-generation)
73
75
  - [Method List](#method-list)
74
76
 
75
77
  ___
@@ -279,7 +281,7 @@ t.player_action('PlaySound', sound('Grass Place'))
279
281
  Represents a diamondfire particle item:
280
282
 
281
283
  ```py
282
- particle(name='Cloud', amount=10, horizontal=1.0, vertical=0.5, x=1.0, y=0.0, z=0.0, motionVariation=100)
284
+ particle({'particle':'Cloud','cluster':{'amount':1,'horizontal':0.0,'vertical':0.0},'data':{'x':1.0,'y':0.0,'z':0.0,'motionVariation':100}})
283
285
  ```
284
286
 
285
287
  Example:
@@ -289,7 +291,8 @@ Example:
289
291
  from dfpyre import *
290
292
  t = DFTemplate()
291
293
  t.player_event('Join')
292
- t.player_action('Particle', particle('Cloud'), loc(5, 50, 5))
294
+ part = particle({'particle':'Cloud','cluster':{'amount':1,'horizontal':0.0,'vertical':0.0},'data':{'x':1.0,'y':0.0,'z':0.0,'motionVariation':100}})
295
+ t.player_action('Particle', part, loc(5, 50, 5))
293
296
  ```
294
297
 
295
298
  Currently, the particle object does not support colors.
@@ -441,6 +444,31 @@ t = DFTemplate()
441
444
  t.player_event('Join')
442
445
  t.call_function('doStuff')
443
446
  ```
447
+
448
+ ### Importing from Code
449
+
450
+ You can import existing templates from their built code using the `from_code` method:
451
+
452
+ ```py
453
+ from dfpyre import *
454
+ template_code = 'H4sIAGVyIGYC/3WOMQ7CMAxFz4LnDsw5AhITI6qQSaw2IrGrxkJCVe5eh3boAJP9n/Kfs8AziX8VcPcFYgC3Zej26YDexGoZvUZhAxeJ3PI8WMtKSrnV+1q7P4op4Yfmx244qG7E4Uql4EA/jNv2Jc3qJU/2KqBiY4yZjI6UkpzAjkNJouDO1X7S1xUDaGUl2QAAAA=='
455
+
456
+ t = DFTemplate.from_code(template_code)
457
+ # add onto the template from here
458
+ ```
459
+
460
+
461
+ ### Script Generation
462
+
463
+ You can also generate an equivalent python script for a template from a template object:
464
+
465
+ ```py
466
+ from dfpyre import *
467
+ template_code = 'H4sIAGVyIGYC/3WOMQ7CMAxFz4LnDsw5AhITI6qQSaw2IrGrxkJCVe5eh3boAJP9n/Kfs8AziX8VcPcFYgC3Zej26YDexGoZvUZhAxeJ3PI8WMtKSrnV+1q7P4op4Yfmx244qG7E4Uql4EA/jNv2Jc3qJU/2KqBiY4yZjI6UkpzAjkNJouDO1X7S1xUDaGUl2QAAAA=='
468
+
469
+ t = DFTemplate.from_code(template_code)
470
+ t.generate_script('my_template.py') # generated python script will be written to my_template.py
471
+ ```
444
472
 
445
473
  ### Method List
446
474
 
@@ -0,0 +1,10 @@
1
+ dfpyre/__init__.py,sha256=apPsSxJ1Tztfl71MoORoSmDfX7LyKLYlLwOGeLQUitw,25
2
+ dfpyre/data/data.json,sha256=M4EHXKkh7Cx7y3BQ6c3csvmNmSqP2oE4txLI9MZcDDA,30347
3
+ dfpyre/items.py,sha256=t9KsvhPOEe36_E7CNdgxNmEKtOF6nMvNZURyYFiXJb4,9215
4
+ dfpyre/pyre.py,sha256=OHsdTP9kTzuLXakhqS2ncl5frw75akl3zQ9oQ45NqHs,17616
5
+ dfpyre/scriptgen.py,sha256=TG_qYp2GChkwCD0f9WVCJOORDDL4RueuJeRkddB4YJw,5568
6
+ dfpyre/style.py,sha256=mLW1CFvvi8_9fk8JaH10I5S4WI0YBdQIXHtI3G_4sR8,980
7
+ dfpyre-0.6.1.dist-info/LICENSE,sha256=_vuDskB0ja2c-Fgm7Gt8Q8cO9NsLNpZAVyvmZwX7E6o,1060
8
+ dfpyre-0.6.1.dist-info/METADATA,sha256=tvnWndGOuItw_G-dzsxRPGXkvPiTn7zLuADsHWGJ87w,11344
9
+ dfpyre-0.6.1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
10
+ dfpyre-0.6.1.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- dfpyre/__init__.py,sha256=apPsSxJ1Tztfl71MoORoSmDfX7LyKLYlLwOGeLQUitw,25
2
- dfpyre/data/data.json,sha256=M4EHXKkh7Cx7y3BQ6c3csvmNmSqP2oE4txLI9MZcDDA,30347
3
- dfpyre/items.py,sha256=L2e9b9BZQrI7AIBw46puENReDQRlWgHnw7T9NBG1Vcs,7688
4
- dfpyre/pyre.py,sha256=5UryjtwsNRxl6mGknsKm8iM2DP49-B-tqwkr9LdoRJ8,13805
5
- dfpyre/style.py,sha256=mLW1CFvvi8_9fk8JaH10I5S4WI0YBdQIXHtI3G_4sR8,980
6
- dfpyre-0.5.0.dist-info/LICENSE,sha256=atkly29RNUY2evLoPyurzCJeQnhP-VCAD-JZEbq3mno,1060
7
- dfpyre-0.5.0.dist-info/METADATA,sha256=RpuPps-V4FruPh1p0L7wroEDwZfC5lP6ITgl14334Cs,10142
8
- dfpyre-0.5.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
9
- dfpyre-0.5.0.dist-info/RECORD,,
File without changes