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