pyrecli 0.2.0__tar.gz → 0.3.0__tar.gz
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.
- pyrecli-0.3.0/PKG-INFO +180 -0
- pyrecli-0.3.0/README.md +157 -0
- pyrecli-0.3.0/pyproject.toml +34 -0
- pyrecli-0.3.0/pyrecli/command/cctoken.py +10 -0
- {pyrecli-0.2.0 → pyrecli-0.3.0}/pyrecli/command/docs.py +5 -13
- pyrecli-0.3.0/pyrecli/command/grabinv.py +44 -0
- {pyrecli-0.2.0 → pyrecli-0.3.0}/pyrecli/command/rename.py +13 -13
- pyrecli-0.3.0/pyrecli/command/scan.py +14 -0
- {pyrecli-0.2.0 → pyrecli-0.3.0}/pyrecli/command/script.py +8 -20
- pyrecli-0.3.0/pyrecli/command/send.py +16 -0
- pyrecli-0.3.0/pyrecli/command/slice.py +15 -0
- {pyrecli-0.2.0 → pyrecli-0.3.0}/pyrecli/pyrecli.py +67 -47
- pyrecli-0.3.0/pyrecli/util.py +132 -0
- pyrecli-0.2.0/PKG-INFO +0 -66
- pyrecli-0.2.0/README.md +0 -48
- pyrecli-0.2.0/pyproject.toml +0 -22
- pyrecli-0.2.0/pyrecli/command/grabinv.py +0 -49
- pyrecli-0.2.0/pyrecli/command/scan.py +0 -19
- pyrecli-0.2.0/pyrecli/command/send.py +0 -26
- pyrecli-0.2.0/pyrecli/command/slice.py +0 -21
- pyrecli-0.2.0/pyrecli/util.py +0 -76
- {pyrecli-0.2.0 → pyrecli-0.3.0}/LICENSE +0 -0
- {pyrecli-0.2.0 → pyrecli-0.3.0}/pyrecli/__init__.py +0 -0
pyrecli-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyrecli
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Command line utilities for DiamondFire templates
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: diamondfire,minecraft,template,cli,tools
|
|
8
|
+
Author: Amp
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: dfpyre (>=0.9.5)
|
|
18
|
+
Requires-Dist: pytest (>=9.0.2) ; extra == "dev"
|
|
19
|
+
Requires-Dist: rapidnbt (>=1.3.3)
|
|
20
|
+
Requires-Dist: twine (>=6.2.0) ; extra == "dev"
|
|
21
|
+
Project-URL: Repository, https://github.com/Amp63/pyrecli
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# pyrecli
|
|
25
|
+
|
|
26
|
+
Command line utilities for DiamondFire templates
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
Run the following command in a terminal:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
pip install pyrecli
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Commands
|
|
37
|
+
|
|
38
|
+
- `scan`: Scan all templates on the plot and dump them to a text file (requires [CodeClient](github.com/DFOnline/CodeClient))
|
|
39
|
+
- `send`: Send template items to DiamondFire (requires [CodeClient](github.com/DFOnline/CodeClient))
|
|
40
|
+
- `rename`: Rename all occurences of a variable (including text codes)
|
|
41
|
+
- `script`: Generate python scripts from template data
|
|
42
|
+
- `grabinv`: Save all templates in your Minecraft inventory to a file (requires [CodeClient](github.com/DFOnline/CodeClient))
|
|
43
|
+
- `docs`: Generate markdown documentation from template data
|
|
44
|
+
- `slice`: Slice a template into multiple smaller templates
|
|
45
|
+
- `cctoken`: Get a reusable CodeClient authentication token
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## What is this useful for?
|
|
49
|
+
|
|
50
|
+
- Backing up a plot
|
|
51
|
+
- Getting an accurate text representation of DF code
|
|
52
|
+
- Open sourcing
|
|
53
|
+
- Version control
|
|
54
|
+
- Large scale refactoring
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## Example Command Usages
|
|
58
|
+
|
|
59
|
+
### Scan
|
|
60
|
+
|
|
61
|
+
**[Requires CodeClient]**
|
|
62
|
+
|
|
63
|
+
Grabs all of the templates on your current plot and saves them to a file.
|
|
64
|
+
You will need to run `/auth` in-game to authorize this action.
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
```sh
|
|
68
|
+
# Dumps all template data into templates.dfts
|
|
69
|
+
pyrecli scan templates.dfts
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Send
|
|
73
|
+
|
|
74
|
+
**[Requires CodeClient]**
|
|
75
|
+
|
|
76
|
+
Sends all templates in a file back to your inventory.
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
```sh
|
|
80
|
+
pyrecli send templates.dfts
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Rename
|
|
84
|
+
|
|
85
|
+
Renames all occurences of a variable in a list of templates.
|
|
86
|
+
You can run this command on a single template, or on an entire plot if a variable is used in many places.
|
|
87
|
+
|
|
88
|
+
This command still requires thorough testing, so make sure you have a backup of your plot before using this command on a large scale.
|
|
89
|
+
|
|
90
|
+
Example:
|
|
91
|
+
```sh
|
|
92
|
+
# Changes all variables named `foo` to `bar`.
|
|
93
|
+
pyrecli rename templates.dfts foo bar
|
|
94
|
+
|
|
95
|
+
# You can also target a specific scope.
|
|
96
|
+
# This changes all occurences of the game variable `plotData` to `gameData`.
|
|
97
|
+
pyrecli rename templates.dfts plotData gameData -s game
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
By default, this modifies the input file in-place, but you can use the `--output_path` flag to output the renamed templates to a new file:
|
|
101
|
+
```sh
|
|
102
|
+
pyrecli rename templates.dfts foo bar --output_path renamed_templates.dfts
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Script
|
|
106
|
+
|
|
107
|
+
Generates Python scripts from template data.
|
|
108
|
+
|
|
109
|
+
Example:
|
|
110
|
+
```sh
|
|
111
|
+
# Convert templates into individual scripts and store them in directory `plot_templates`
|
|
112
|
+
pyrecli script templates.dfts plot_templates
|
|
113
|
+
|
|
114
|
+
# Convert templates into scripts and put them into a single file `plot_templates.py`
|
|
115
|
+
pyrecli script templates.dfts plot_templates.py --onefile
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
### Grabinv
|
|
120
|
+
|
|
121
|
+
**[Requires CodeClient]**
|
|
122
|
+
|
|
123
|
+
Scans your inventory for templates and saves them to a file.
|
|
124
|
+
|
|
125
|
+
Example:
|
|
126
|
+
```sh
|
|
127
|
+
# Save inventory templates to `templates.dfts`
|
|
128
|
+
pyrecli grabinv templates.dfts
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
### Docs
|
|
133
|
+
|
|
134
|
+
Generates a Markdown documentation file for a list of templates.
|
|
135
|
+
|
|
136
|
+
Example:
|
|
137
|
+
```sh
|
|
138
|
+
# Generate documentation and save it to `plot_docs.md`
|
|
139
|
+
pyrecli docs templates.dfts plot_docs.md "My Plot Docs"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
### Slice
|
|
144
|
+
|
|
145
|
+
Slices a single template into multiple smaller templates.
|
|
146
|
+
This is useful for resizing templates to fit on a smaller plot.
|
|
147
|
+
|
|
148
|
+
If multiple templates are passed, only the first one will be used.
|
|
149
|
+
|
|
150
|
+
**NOTE: This feature is not fully implemented yet. Any templates with Control::Return blocks may not work properly if sliced.**
|
|
151
|
+
|
|
152
|
+
Example:
|
|
153
|
+
```sh
|
|
154
|
+
# Slices the first template in `templates.dfts` with a target length of 50 and stores them in `sliced_templates.dfts`
|
|
155
|
+
pyrecli slice templates.dfts sliced_templates.dfts 50
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
### CCToken
|
|
160
|
+
|
|
161
|
+
Returns a CodeClient authentication token that can be used in commands that require CodeClient authorization.
|
|
162
|
+
This is useful for reducing the amount of times you need to run `/auth`.
|
|
163
|
+
|
|
164
|
+
Example:
|
|
165
|
+
```sh
|
|
166
|
+
# Get a token with the read_plot and inventory scopes
|
|
167
|
+
pyrecli cctoken mytoken.txt "read_plot inventory"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
### Command Chaining
|
|
172
|
+
|
|
173
|
+
You can combine the pipe operator (`|`) with hyphen (`-`) file paths to chain multiple commands together.
|
|
174
|
+
|
|
175
|
+
Example:
|
|
176
|
+
```sh
|
|
177
|
+
# Scans the plot, renames a variable, then sends renamed templates back to DiamondFire
|
|
178
|
+
pyrecli scan - | pyrecli rename - foo bar | pyrecli send -
|
|
179
|
+
|
|
180
|
+
```
|
pyrecli-0.3.0/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# pyrecli
|
|
2
|
+
|
|
3
|
+
Command line utilities for DiamondFire templates
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Run the following command in a terminal:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pip install pyrecli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Commands
|
|
14
|
+
|
|
15
|
+
- `scan`: Scan all templates on the plot and dump them to a text file (requires [CodeClient](github.com/DFOnline/CodeClient))
|
|
16
|
+
- `send`: Send template items to DiamondFire (requires [CodeClient](github.com/DFOnline/CodeClient))
|
|
17
|
+
- `rename`: Rename all occurences of a variable (including text codes)
|
|
18
|
+
- `script`: Generate python scripts from template data
|
|
19
|
+
- `grabinv`: Save all templates in your Minecraft inventory to a file (requires [CodeClient](github.com/DFOnline/CodeClient))
|
|
20
|
+
- `docs`: Generate markdown documentation from template data
|
|
21
|
+
- `slice`: Slice a template into multiple smaller templates
|
|
22
|
+
- `cctoken`: Get a reusable CodeClient authentication token
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## What is this useful for?
|
|
26
|
+
|
|
27
|
+
- Backing up a plot
|
|
28
|
+
- Getting an accurate text representation of DF code
|
|
29
|
+
- Open sourcing
|
|
30
|
+
- Version control
|
|
31
|
+
- Large scale refactoring
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## Example Command Usages
|
|
35
|
+
|
|
36
|
+
### Scan
|
|
37
|
+
|
|
38
|
+
**[Requires CodeClient]**
|
|
39
|
+
|
|
40
|
+
Grabs all of the templates on your current plot and saves them to a file.
|
|
41
|
+
You will need to run `/auth` in-game to authorize this action.
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
```sh
|
|
45
|
+
# Dumps all template data into templates.dfts
|
|
46
|
+
pyrecli scan templates.dfts
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Send
|
|
50
|
+
|
|
51
|
+
**[Requires CodeClient]**
|
|
52
|
+
|
|
53
|
+
Sends all templates in a file back to your inventory.
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
```sh
|
|
57
|
+
pyrecli send templates.dfts
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Rename
|
|
61
|
+
|
|
62
|
+
Renames all occurences of a variable in a list of templates.
|
|
63
|
+
You can run this command on a single template, or on an entire plot if a variable is used in many places.
|
|
64
|
+
|
|
65
|
+
This command still requires thorough testing, so make sure you have a backup of your plot before using this command on a large scale.
|
|
66
|
+
|
|
67
|
+
Example:
|
|
68
|
+
```sh
|
|
69
|
+
# Changes all variables named `foo` to `bar`.
|
|
70
|
+
pyrecli rename templates.dfts foo bar
|
|
71
|
+
|
|
72
|
+
# You can also target a specific scope.
|
|
73
|
+
# This changes all occurences of the game variable `plotData` to `gameData`.
|
|
74
|
+
pyrecli rename templates.dfts plotData gameData -s game
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
By default, this modifies the input file in-place, but you can use the `--output_path` flag to output the renamed templates to a new file:
|
|
78
|
+
```sh
|
|
79
|
+
pyrecli rename templates.dfts foo bar --output_path renamed_templates.dfts
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Script
|
|
83
|
+
|
|
84
|
+
Generates Python scripts from template data.
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
```sh
|
|
88
|
+
# Convert templates into individual scripts and store them in directory `plot_templates`
|
|
89
|
+
pyrecli script templates.dfts plot_templates
|
|
90
|
+
|
|
91
|
+
# Convert templates into scripts and put them into a single file `plot_templates.py`
|
|
92
|
+
pyrecli script templates.dfts plot_templates.py --onefile
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
### Grabinv
|
|
97
|
+
|
|
98
|
+
**[Requires CodeClient]**
|
|
99
|
+
|
|
100
|
+
Scans your inventory for templates and saves them to a file.
|
|
101
|
+
|
|
102
|
+
Example:
|
|
103
|
+
```sh
|
|
104
|
+
# Save inventory templates to `templates.dfts`
|
|
105
|
+
pyrecli grabinv templates.dfts
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
### Docs
|
|
110
|
+
|
|
111
|
+
Generates a Markdown documentation file for a list of templates.
|
|
112
|
+
|
|
113
|
+
Example:
|
|
114
|
+
```sh
|
|
115
|
+
# Generate documentation and save it to `plot_docs.md`
|
|
116
|
+
pyrecli docs templates.dfts plot_docs.md "My Plot Docs"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
### Slice
|
|
121
|
+
|
|
122
|
+
Slices a single template into multiple smaller templates.
|
|
123
|
+
This is useful for resizing templates to fit on a smaller plot.
|
|
124
|
+
|
|
125
|
+
If multiple templates are passed, only the first one will be used.
|
|
126
|
+
|
|
127
|
+
**NOTE: This feature is not fully implemented yet. Any templates with Control::Return blocks may not work properly if sliced.**
|
|
128
|
+
|
|
129
|
+
Example:
|
|
130
|
+
```sh
|
|
131
|
+
# Slices the first template in `templates.dfts` with a target length of 50 and stores them in `sliced_templates.dfts`
|
|
132
|
+
pyrecli slice templates.dfts sliced_templates.dfts 50
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
### CCToken
|
|
137
|
+
|
|
138
|
+
Returns a CodeClient authentication token that can be used in commands that require CodeClient authorization.
|
|
139
|
+
This is useful for reducing the amount of times you need to run `/auth`.
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
```sh
|
|
143
|
+
# Get a token with the read_plot and inventory scopes
|
|
144
|
+
pyrecli cctoken mytoken.txt "read_plot inventory"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
### Command Chaining
|
|
149
|
+
|
|
150
|
+
You can combine the pipe operator (`|`) with hyphen (`-`) file paths to chain multiple commands together.
|
|
151
|
+
|
|
152
|
+
Example:
|
|
153
|
+
```sh
|
|
154
|
+
# Scans the plot, renames a variable, then sends renamed templates back to DiamondFire
|
|
155
|
+
pyrecli scan - | pyrecli rename - foo bar | pyrecli send -
|
|
156
|
+
|
|
157
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pyrecli"
|
|
3
|
+
version = "0.3.0"
|
|
4
|
+
description = "Command line utilities for DiamondFire templates"
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Amp"}
|
|
7
|
+
]
|
|
8
|
+
license = "MIT"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
keywords = ["diamondfire", "minecraft", "template", "cli", "tools"]
|
|
11
|
+
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
dependencies = [
|
|
14
|
+
"dfpyre>=0.9.5",
|
|
15
|
+
"rapidnbt>=1.3.3"
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.optional-dependencies]
|
|
19
|
+
dev = [
|
|
20
|
+
"pytest>=9.0.2",
|
|
21
|
+
"twine>=6.2.0"
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Repository = "https://github.com/Amp63/pyrecli"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
[tool.poetry.scripts]
|
|
29
|
+
pyrecli = "pyrecli.pyrecli:main"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
[build-system]
|
|
33
|
+
requires = ["poetry-core"]
|
|
34
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from pyrecli.util import connect_to_codeclient, write_output_file
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def cctoken_command(output_path: str, scopes: str):
|
|
5
|
+
ws = connect_to_codeclient(scopes)
|
|
6
|
+
|
|
7
|
+
ws.send('token')
|
|
8
|
+
token = ws.recv().replace('token ', '')
|
|
9
|
+
|
|
10
|
+
write_output_file(output_path, token)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from typing import Literal, TypedDict
|
|
2
|
-
from result import Result, Err
|
|
3
2
|
from dfpyre import DFTemplate, Item, Parameter
|
|
4
3
|
from mcitemlib.itemlib import MCItemlibException
|
|
5
4
|
from pyrecli.util import read_input_file, write_output_file, parse_templates_from_string
|
|
@@ -24,15 +23,9 @@ class TemplateDocData(TypedDict):
|
|
|
24
23
|
doc_lines: list[str]
|
|
25
24
|
|
|
26
25
|
|
|
27
|
-
def docs_command(input_path: str, output_path: str, title: str, include_hidden: bool, omit_toc: bool)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return Err(input_result.err_value)
|
|
31
|
-
|
|
32
|
-
templates_result = parse_templates_from_string(input_result.ok_value)
|
|
33
|
-
if templates_result.is_err():
|
|
34
|
-
return Err(templates_result.err_value)
|
|
35
|
-
templates = templates_result.ok_value
|
|
26
|
+
def docs_command(input_path: str, output_path: str, title: str, include_hidden: bool, omit_toc: bool):
|
|
27
|
+
templates_string = read_input_file(input_path)
|
|
28
|
+
templates = parse_templates_from_string(templates_string)
|
|
36
29
|
|
|
37
30
|
def get_function_name(template: DFTemplate) -> str:
|
|
38
31
|
first_block = template.codeblocks[0]
|
|
@@ -72,7 +65,7 @@ def docs_command(input_path: str, output_path: str, title: str, include_hidden:
|
|
|
72
65
|
lore_text = [escape_md(l.to_string()) for l in first_arg.get_lore()]
|
|
73
66
|
if lore_text:
|
|
74
67
|
template_doc_lines.extend(lore_text)
|
|
75
|
-
except
|
|
68
|
+
except:
|
|
76
69
|
# There are so many things that can go wrong here due to various legacy
|
|
77
70
|
# item formats and weird MC string edge cases, so we can just skip
|
|
78
71
|
# if there's a problem.
|
|
@@ -120,5 +113,4 @@ def docs_command(input_path: str, output_path: str, title: str, include_hidden:
|
|
|
120
113
|
output_lines.extend(doc_data['doc_lines'])
|
|
121
114
|
output_lines.append('')
|
|
122
115
|
|
|
123
|
-
|
|
124
|
-
return write_result
|
|
116
|
+
write_output_file(output_path, '\n'.join(output_lines))
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from rapidnbt import nbtio, CompoundTagVariant
|
|
3
|
+
from pyrecli.util import write_output_file, connect_to_codeclient, print_status
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def grabinv_command(output_path: str, token: str|None=None):
|
|
7
|
+
ws = connect_to_codeclient('inventory', token)
|
|
8
|
+
|
|
9
|
+
ws.send('inv')
|
|
10
|
+
inventory = ws.recv()
|
|
11
|
+
ws.close()
|
|
12
|
+
|
|
13
|
+
inventory = f'{{inventory:{inventory}}}'
|
|
14
|
+
inventory_nbt = nbtio.loads_snbt(inventory)
|
|
15
|
+
|
|
16
|
+
template_codes: list[str] = []
|
|
17
|
+
for tag in inventory_nbt['inventory']:
|
|
18
|
+
tag: CompoundTagVariant
|
|
19
|
+
components = tag['components']
|
|
20
|
+
if components.is_null():
|
|
21
|
+
continue
|
|
22
|
+
|
|
23
|
+
custom_data = components['minecraft:custom_data']
|
|
24
|
+
if custom_data.is_null():
|
|
25
|
+
continue
|
|
26
|
+
|
|
27
|
+
pbv_tag = custom_data['PublicBukkitValues']
|
|
28
|
+
if pbv_tag.is_null():
|
|
29
|
+
continue
|
|
30
|
+
|
|
31
|
+
code_template_data = pbv_tag['hypercube:codetemplatedata']
|
|
32
|
+
if code_template_data.is_null():
|
|
33
|
+
continue
|
|
34
|
+
|
|
35
|
+
code_template_json = json.loads(code_template_data.get_string())
|
|
36
|
+
|
|
37
|
+
template_code = code_template_json.get('code')
|
|
38
|
+
if template_code:
|
|
39
|
+
template_codes.append(template_code)
|
|
40
|
+
|
|
41
|
+
if not template_codes:
|
|
42
|
+
print_status('Could not find any templates in the inventory.')
|
|
43
|
+
|
|
44
|
+
write_output_file(output_path, '\n'.join(template_codes))
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from typing import Literal
|
|
2
2
|
import re
|
|
3
|
-
from
|
|
4
|
-
from dfpyre import Variable, Number, String, Text
|
|
3
|
+
from dfpyre import Variable, Number, String, Text, Parameter
|
|
5
4
|
from pyrecli.util import read_input_file, write_output_file, parse_templates_from_string
|
|
6
5
|
|
|
7
6
|
|
|
@@ -22,33 +21,34 @@ def rename_var_in_text_code(s: str, var_to_rename: str, new_var_name: str):
|
|
|
22
21
|
|
|
23
22
|
def rename_command(input_path: str, output_path: str|None,
|
|
24
23
|
var_to_rename: str, new_var_name: str,
|
|
25
|
-
var_to_rename_scope: Literal['game', 'saved', 'local', 'line']|None)
|
|
24
|
+
var_to_rename_scope: Literal['game', 'saved', 'local', 'line']|None):
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return Err(input_result.err_value)
|
|
30
|
-
|
|
31
|
-
templates_result = parse_templates_from_string(input_result.ok_value)
|
|
32
|
-
if templates_result.is_err():
|
|
33
|
-
return Err(templates_result.err_value)
|
|
34
|
-
templates = templates_result.ok_value
|
|
26
|
+
templates_string = read_input_file(input_path)
|
|
27
|
+
templates = parse_templates_from_string(templates_string)
|
|
35
28
|
|
|
36
29
|
for template in templates:
|
|
37
30
|
for codeblock in template.codeblocks:
|
|
38
31
|
for argument in codeblock.args:
|
|
32
|
+
# Try to rename variable
|
|
39
33
|
if isinstance(argument, Variable):
|
|
40
34
|
if argument.name == var_to_rename and (var_to_rename_scope is None or argument.scope == var_to_rename_scope):
|
|
41
35
|
argument.name = new_var_name
|
|
42
36
|
argument.name = rename_var_in_text_code(argument.name, var_to_rename, new_var_name)
|
|
43
37
|
|
|
38
|
+
# Try to rename parameter
|
|
39
|
+
elif isinstance(argument, Parameter) and var_to_rename_scope == 'line':
|
|
40
|
+
if argument.name == var_to_rename:
|
|
41
|
+
argument.name = new_var_name
|
|
42
|
+
|
|
43
|
+
# Check for occurrences of the variable in text codes
|
|
44
44
|
elif isinstance(argument, (Number, String, Text)) and isinstance(argument.value, str):
|
|
45
45
|
argument.value = rename_var_in_text_code(argument.value, var_to_rename, new_var_name)
|
|
46
46
|
|
|
47
|
+
# Check for text codes in function calls
|
|
47
48
|
if codeblock.type in {'call_func', 'start_process'}:
|
|
48
49
|
new_data = rename_var_in_text_code(codeblock.data.get('data'), var_to_rename, new_var_name)
|
|
49
50
|
codeblock.data['data'] = new_data
|
|
50
51
|
|
|
51
52
|
new_file_content = '\n'.join(t.build() for t in templates)
|
|
52
53
|
write_path = output_path if output_path else input_path
|
|
53
|
-
|
|
54
|
-
return write_result
|
|
54
|
+
write_output_file(write_path, new_file_content)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from pyrecli.util import write_output_file, connect_to_codeclient, print_status
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def scan_command(output_path: str, token: str|None=None):
|
|
5
|
+
ws = connect_to_codeclient('read_plot', token)
|
|
6
|
+
|
|
7
|
+
print_status('Scanning plot...')
|
|
8
|
+
ws.send('scan')
|
|
9
|
+
|
|
10
|
+
scan_results = ws.recv()
|
|
11
|
+
print_status('Done.')
|
|
12
|
+
ws.close()
|
|
13
|
+
|
|
14
|
+
write_output_file(output_path, scan_results)
|
|
@@ -1,26 +1,20 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from result import Result, Ok, Err
|
|
3
2
|
from dfpyre import DFTemplate
|
|
4
3
|
from pyrecli.util import read_input_file, write_output_file, parse_templates_from_string
|
|
5
4
|
|
|
6
5
|
|
|
7
|
-
def write_to_directory(dir_name: str, templates: list[DFTemplate], flags: dict[str, int|bool])
|
|
6
|
+
def write_to_directory(dir_name: str, templates: list[DFTemplate], flags: dict[str, int|bool]):
|
|
8
7
|
if not os.path.isdir(dir_name):
|
|
9
8
|
os.mkdir(dir_name)
|
|
10
9
|
|
|
11
10
|
for template in templates:
|
|
12
11
|
script_path = f'{dir_name}/{template._get_template_name()}.py'
|
|
13
12
|
script_string = template.generate_script(**flags)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
f.write(script_string)
|
|
17
|
-
except OSError as e:
|
|
18
|
-
return Err(str(e))
|
|
19
|
-
|
|
20
|
-
return Ok(None)
|
|
13
|
+
with open(script_path, 'w') as f:
|
|
14
|
+
f.write(script_string)
|
|
21
15
|
|
|
22
16
|
|
|
23
|
-
def write_to_single_file(file_path: str, templates: list[DFTemplate], flags: dict[str, int|bool])
|
|
17
|
+
def write_to_single_file(file_path: str, templates: list[DFTemplate], flags: dict[str, int|bool]):
|
|
24
18
|
file_content = []
|
|
25
19
|
for i, template in enumerate(templates):
|
|
26
20
|
if i == 0:
|
|
@@ -29,18 +23,12 @@ def write_to_single_file(file_path: str, templates: list[DFTemplate], flags: dic
|
|
|
29
23
|
template_script = template.generate_script(include_import=False, assign_variable=True, **flags)
|
|
30
24
|
file_content.append(template_script)
|
|
31
25
|
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
write_output_file(file_path, '\n\n'.join(file_content))
|
|
34
27
|
|
|
35
|
-
def script_command(input_path: str, output_path: str, one_file: bool, flags: dict[str, int|bool]) -> Result[None, str]:
|
|
36
|
-
input_result = read_input_file(input_path)
|
|
37
|
-
if input_result.is_err():
|
|
38
|
-
return Err(input_result.err_value)
|
|
39
28
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
templates = templates_result.ok_value
|
|
29
|
+
def script_command(input_path: str, output_path: str, one_file: bool, flags: dict[str, int|bool]):
|
|
30
|
+
templates_string = read_input_file(input_path)
|
|
31
|
+
templates = parse_templates_from_string(templates_string)
|
|
44
32
|
|
|
45
33
|
if one_file or output_path == '-':
|
|
46
34
|
return write_to_single_file(output_path, templates, flags)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from pyrecli.util import connect_to_codeclient, read_input_file, parse_templates_from_string, print_status
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def send_command(input_path: str):
|
|
5
|
+
templates_string = read_input_file(input_path)
|
|
6
|
+
templates = parse_templates_from_string(templates_string)
|
|
7
|
+
|
|
8
|
+
ws = connect_to_codeclient()
|
|
9
|
+
|
|
10
|
+
for template in templates:
|
|
11
|
+
item = template.generate_template_item()
|
|
12
|
+
ws.send(f'give {item.get_snbt()}')
|
|
13
|
+
|
|
14
|
+
ws.close()
|
|
15
|
+
|
|
16
|
+
print_status(f'Sent {len(templates)} template{"s" if len(templates) != 1 else ''} successfully.')
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from pyrecli.util import read_input_file, write_output_file, parse_templates_from_string, NoTemplatesError
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def slice_command(input_path: str, output_path: str, target_length: int):
|
|
5
|
+
templates_string = read_input_file(input_path)
|
|
6
|
+
templates = parse_templates_from_string(templates_string)
|
|
7
|
+
|
|
8
|
+
if not templates:
|
|
9
|
+
raise NoTemplatesError(f'Could not find any templates in {input_path}')
|
|
10
|
+
|
|
11
|
+
first_template = templates[0]
|
|
12
|
+
sliced_templates = first_template.slice(target_length)
|
|
13
|
+
built_templates = [t.build() for t in sliced_templates]
|
|
14
|
+
|
|
15
|
+
write_output_file(output_path, '\n'.join(built_templates))
|
|
@@ -9,6 +9,15 @@ from pyrecli.command.rename import rename_command
|
|
|
9
9
|
from pyrecli.command.grabinv import grabinv_command
|
|
10
10
|
from pyrecli.command.docs import docs_command
|
|
11
11
|
from pyrecli.command.slice import slice_command
|
|
12
|
+
from pyrecli.command.cctoken import cctoken_command
|
|
13
|
+
from pyrecli.util import print_status
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def rename_target_scope(value):
|
|
17
|
+
SCOPES = {'game', 'saved', 'local', 'line'}
|
|
18
|
+
if value not in SCOPES:
|
|
19
|
+
raise argparse.ArgumentTypeError(f'Expected one of {SCOPES} for rename target scope')
|
|
20
|
+
return value
|
|
12
21
|
|
|
13
22
|
|
|
14
23
|
def slice_target_length(value):
|
|
@@ -19,13 +28,14 @@ def slice_target_length(value):
|
|
|
19
28
|
return ivalue
|
|
20
29
|
|
|
21
30
|
|
|
22
|
-
def main():
|
|
31
|
+
def main() -> int:
|
|
23
32
|
parser = argparse.ArgumentParser(prog='pyrecli', description='Command line utilities for DiamondFire templates')
|
|
24
33
|
parser.add_argument('--version', '-v', action='version', version=f'pyrecli {importlib.metadata.version('pyrecli')}')
|
|
25
34
|
subparsers = parser.add_subparsers(dest='command', help='Available commands:', required=True, metavar='<command>')
|
|
26
35
|
|
|
27
36
|
parser_scan = subparsers.add_parser('scan', help='Scan the current plot templates with CodeClient')
|
|
28
37
|
parser_scan.add_argument('output_path', help='The file to output template data to', type=str)
|
|
38
|
+
parser_scan.add_argument('--token', '-t', help='The CodeClient authentication token to use', type=str, default=None)
|
|
29
39
|
|
|
30
40
|
parser_send = subparsers.add_parser('send', help='Send templates to DiamondFire with CodeClient')
|
|
31
41
|
parser_send.add_argument('input_path', help='The file containing template data', type=str)
|
|
@@ -40,20 +50,21 @@ def main():
|
|
|
40
50
|
parser_script.add_argument('--preserve_slots', '-s', help='Save the positions of items within chests', action='store_true')
|
|
41
51
|
parser_script.add_argument('--build_and_send', '-b', help='Add `.build_and_send()` to the end of the generated template(s)', action='store_true')
|
|
42
52
|
|
|
43
|
-
parser_rename = subparsers.add_parser('rename', help='Rename a variable')
|
|
53
|
+
parser_rename = subparsers.add_parser('rename', help='Rename all occurrences of a variable')
|
|
44
54
|
parser_rename.add_argument('input_path', help='The file containing template data', type=str)
|
|
45
55
|
parser_rename.add_argument('var_to_rename', help='The variable to rename', type=str)
|
|
46
56
|
parser_rename.add_argument('new_var_name', help='The new name for the variable', type=str)
|
|
47
|
-
parser_rename.add_argument('--
|
|
57
|
+
parser_rename.add_argument('--var_scope', '-s', help='The scope to match', type=rename_target_scope, default=None)
|
|
48
58
|
parser_rename.add_argument('--output_path', '-o', help='The file to output to', type=str, default=None)
|
|
49
59
|
|
|
50
60
|
parser_grabinv = subparsers.add_parser('grabinv', help='Save all templates in the inventory to a file with CodeClient')
|
|
51
61
|
parser_grabinv.add_argument('output_path', help='The file to output template data to', type=str)
|
|
62
|
+
parser_grabinv.add_argument('--token', '-t', help='The CodeClient authentication token to use', type=str, default=None)
|
|
52
63
|
|
|
53
64
|
parser_docs = subparsers.add_parser('docs', help='Generate markdown documentation from template data')
|
|
54
65
|
parser_docs.add_argument('input_path', help='The file containing template data', type=str)
|
|
55
66
|
parser_docs.add_argument('output_path', help='The file to output to', type=str)
|
|
56
|
-
parser_docs.add_argument('title', help='The title for the docs', type=str)
|
|
67
|
+
parser_docs.add_argument('--title', '-t', help='The title for the docs', type=str, default='Template Docs')
|
|
57
68
|
parser_docs.add_argument('--include_hidden', '-ih', help='Include hidden functions and processes', action='store_true')
|
|
58
69
|
parser_docs.add_argument('--notoc', help='Omit the table of contents', action='store_true')
|
|
59
70
|
|
|
@@ -61,52 +72,61 @@ def main():
|
|
|
61
72
|
parser_slice.add_argument('input_path', help='The file containing template data', type=str)
|
|
62
73
|
parser_slice.add_argument('output_path', help='The file to output template data to', type=str)
|
|
63
74
|
parser_slice.add_argument('target_length', help='The maximum length of each sliced template', type=slice_target_length)
|
|
64
|
-
|
|
75
|
+
|
|
76
|
+
parser_cctoken = subparsers.add_parser('cctoken', help='Request a CodeClient token with the specified scopes')
|
|
77
|
+
parser_cctoken.add_argument('output_path', help='The file to output the token to', type=str)
|
|
78
|
+
parser_cctoken.add_argument('scopes', help='The scopes to request', type=str)
|
|
79
|
+
|
|
80
|
+
|
|
65
81
|
parsed_args = parser.parse_args()
|
|
66
82
|
|
|
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
|
-
|
|
83
|
+
try:
|
|
84
|
+
match parsed_args.command:
|
|
85
|
+
case 'scan':
|
|
86
|
+
scan_command(parsed_args.output_path, parsed_args.token)
|
|
87
|
+
|
|
88
|
+
case 'send':
|
|
89
|
+
send_command(parsed_args.input_path)
|
|
90
|
+
|
|
91
|
+
case 'script':
|
|
92
|
+
scriptgen_flags = {
|
|
93
|
+
'indent_size': parsed_args.indent_size,
|
|
94
|
+
'literal_shorthand': parsed_args.literal_shorthand,
|
|
95
|
+
'var_shorthand': parsed_args.var_shorthand,
|
|
96
|
+
'preserve_slots': parsed_args.preserve_slots,
|
|
97
|
+
'build_and_send': parsed_args.build_and_send
|
|
98
|
+
}
|
|
99
|
+
script_command(parsed_args.input_path, parsed_args.output_path, parsed_args.onefile, scriptgen_flags)
|
|
100
|
+
|
|
101
|
+
case 'rename':
|
|
102
|
+
rename_command(
|
|
103
|
+
parsed_args.input_path, parsed_args.output_path,
|
|
104
|
+
parsed_args.var_to_rename, parsed_args.new_var_name, parsed_args.var_scope
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
case 'grabinv':
|
|
108
|
+
grabinv_command(parsed_args.output_path, parsed_args.token)
|
|
109
|
+
|
|
110
|
+
case 'docs':
|
|
111
|
+
docs_command(
|
|
112
|
+
parsed_args.input_path, parsed_args.output_path,
|
|
113
|
+
parsed_args.title, parsed_args.include_hidden, parsed_args.notoc
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
case 'slice':
|
|
117
|
+
slice_command(
|
|
118
|
+
parsed_args.input_path, parsed_args.output_path,
|
|
119
|
+
parsed_args.target_length
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
case 'cctoken':
|
|
123
|
+
cctoken_command(parsed_args.output_path, parsed_args.scopes)
|
|
104
124
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
125
|
+
except Exception as e:
|
|
126
|
+
print_status(e)
|
|
127
|
+
return 1
|
|
108
128
|
|
|
109
|
-
|
|
129
|
+
return 0
|
|
110
130
|
|
|
111
131
|
if __name__ == '__main__':
|
|
112
|
-
main()
|
|
132
|
+
sys.exit(main())
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import sys
|
|
4
|
+
import websocket
|
|
5
|
+
from dfpyre import DFTemplate
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
CODECLIENT_URL = 'ws://localhost:31375'
|
|
9
|
+
|
|
10
|
+
BASE64_REGEX = re.compile(r'^[A-Za-z0-9+/]+={0,2}$')
|
|
11
|
+
|
|
12
|
+
class TemplateParsingError(Exception):
|
|
13
|
+
"""Exception class for DFTemplate parsing errors"""
|
|
14
|
+
|
|
15
|
+
class NoTemplatesError(Exception):
|
|
16
|
+
"""Exception class for empty template list"""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def print_status(*args, **kwargs):
|
|
20
|
+
"""Prints a message to stderr"""
|
|
21
|
+
print(*args, **kwargs, file=sys.stderr)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def connect_to_codeclient(scopes: str|None=None, token: str|None=None) -> websocket.WebSocket:
|
|
25
|
+
"""
|
|
26
|
+
Tries to connect to the CodeClient websocket server with the specified scopes.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
scopes: The scopes to request
|
|
30
|
+
token: The CodeClient authentication token to use
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
The connected websocket
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
ConnectionRefusedError: If connection to the server could not be established
|
|
37
|
+
PermissionError: If scope authentication fails
|
|
38
|
+
"""
|
|
39
|
+
ws = websocket.WebSocket()
|
|
40
|
+
ws.connect(CODECLIENT_URL)
|
|
41
|
+
|
|
42
|
+
print_status('Connected to CodeClient.')
|
|
43
|
+
|
|
44
|
+
if token:
|
|
45
|
+
ws.send(f'token {token}')
|
|
46
|
+
auth_message = ws.recv()
|
|
47
|
+
elif scopes:
|
|
48
|
+
print_status('Please run /auth in game.')
|
|
49
|
+
ws.send(f'scopes {scopes}')
|
|
50
|
+
auth_message = ws.recv()
|
|
51
|
+
|
|
52
|
+
if (token or scopes):
|
|
53
|
+
if auth_message != 'auth':
|
|
54
|
+
raise PermissionError('Failed to authenticate.')
|
|
55
|
+
else:
|
|
56
|
+
print_status('Authentication successful.')
|
|
57
|
+
|
|
58
|
+
return ws
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def parse_templates_from_string(templates: str) -> list[DFTemplate]:
|
|
62
|
+
"""
|
|
63
|
+
Parses a newline-delimited string of template codes into a list of templates.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
templates: The string of templates to parse
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
The list of parsed templates
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
ValueError: If a template code is not a valid base64 string
|
|
73
|
+
TemplateParsingError: If a template failed to parse
|
|
74
|
+
"""
|
|
75
|
+
template_codes = templates.split('\n')
|
|
76
|
+
|
|
77
|
+
for i, template_code in enumerate(template_codes):
|
|
78
|
+
if not BASE64_REGEX.match(template_code):
|
|
79
|
+
raise ValueError(f'Template code at line {i+1} is not a base64 string.')
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
return [DFTemplate.from_code(c) for c in template_codes]
|
|
83
|
+
except Exception as e:
|
|
84
|
+
raise TemplateParsingError(f'Error while parsing template: {e}') from None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def read_input_file(path: str) -> str:
|
|
88
|
+
"""
|
|
89
|
+
Returns the string content of the file at a specified path.
|
|
90
|
+
If the path is a hyphen ('-'), then input will be read from stdin.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
path: The file path to read
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
The file content or input from stdin
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
FileNotFoundError: If the path is not a file or the file doesn't exist
|
|
100
|
+
OSError: If the file was unable to be opened or read
|
|
101
|
+
"""
|
|
102
|
+
if path == '-':
|
|
103
|
+
try:
|
|
104
|
+
input_string = sys.stdin.read()
|
|
105
|
+
except EOFError:
|
|
106
|
+
pass
|
|
107
|
+
return input_string.strip()
|
|
108
|
+
|
|
109
|
+
if not os.path.isfile(path):
|
|
110
|
+
raise FileNotFoundError(f'"{path}" is not a file.')
|
|
111
|
+
|
|
112
|
+
with open(path, 'r') as f:
|
|
113
|
+
return f.read()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def write_output_file(path: str, content: str):
|
|
117
|
+
"""
|
|
118
|
+
Writes string content to a specified file.
|
|
119
|
+
If the file path is a hyphen ('-') then the content will be printed to stdout.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
path: The file path to write to
|
|
123
|
+
content: The string content to write
|
|
124
|
+
|
|
125
|
+
Raises:
|
|
126
|
+
OSError: If the file was unable to be accessed
|
|
127
|
+
"""
|
|
128
|
+
if path == '-':
|
|
129
|
+
print(content, end='')
|
|
130
|
+
else:
|
|
131
|
+
with open(path, 'w') as f:
|
|
132
|
+
f.write(content)
|
pyrecli-0.2.0/PKG-INFO
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: pyrecli
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Summary: Command line utilities for DiamondFire templates
|
|
5
|
-
Home-page: https://github.com/Amp63/pyrecli
|
|
6
|
-
License: MIT
|
|
7
|
-
Keywords: diamondfire,minecraft,template,cli
|
|
8
|
-
Author: Amp
|
|
9
|
-
Requires-Python: >=3.10,<3.13
|
|
10
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
-
Project-URL: Repository, https://github.com/Amp63/pyrecli
|
|
16
|
-
Description-Content-Type: text/markdown
|
|
17
|
-
|
|
18
|
-
# pyrecli
|
|
19
|
-
|
|
20
|
-
Command line utilities for DiamondFire templates
|
|
21
|
-
|
|
22
|
-
## Installation
|
|
23
|
-
|
|
24
|
-
Run the following command in a terminal:
|
|
25
|
-
|
|
26
|
-
```sh
|
|
27
|
-
pip install pyrecli
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Commands
|
|
31
|
-
|
|
32
|
-
- `scan`: Scan all templates on the plot and dump them to a text file (requires [CodeClient](github.com/DFOnline/CodeClient))
|
|
33
|
-
- `send`: Send template items to DiamondFire (requires [CodeClient](github.com/DFOnline/CodeClient))
|
|
34
|
-
- `rename`: Rename all occurences of a variable (including text codes)
|
|
35
|
-
- `script`: Generate python scripts from template data
|
|
36
|
-
- `grabinv`: Save all templates in your Minecraft inventory to a file (requires [CodeClient](github.com/DFOnline/CodeClient))
|
|
37
|
-
- `docs`: Generate markdown documentation from template data
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
## What is this useful for?
|
|
41
|
-
|
|
42
|
-
- Backing up a plot
|
|
43
|
-
- Getting an accurate text representation of DF code
|
|
44
|
-
- Open sourcing
|
|
45
|
-
- Version control
|
|
46
|
-
- Large scale refactoring
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
## Example Usage
|
|
50
|
-
|
|
51
|
-
These two commands will scan your plot, convert each template into a python script, then place the scripts into a directory called `myplot`.
|
|
52
|
-
|
|
53
|
-
```sh
|
|
54
|
-
pyrecli scan templates.dfts
|
|
55
|
-
pyrecli script templates.dfts myplot
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
If you prefer the templates to be outputted to a single file, use the `--onefile` flag:
|
|
59
|
-
|
|
60
|
-
```sh
|
|
61
|
-
pyrecli scan templates.dfts
|
|
62
|
-
pyrecli script templates.dfts myplot.py --onefile
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
For more information about generating scripts, run `pyrecli script -h`.
|
|
66
|
-
|
pyrecli-0.2.0/README.md
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# pyrecli
|
|
2
|
-
|
|
3
|
-
Command line utilities for DiamondFire templates
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
Run the following command in a terminal:
|
|
8
|
-
|
|
9
|
-
```sh
|
|
10
|
-
pip install pyrecli
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Commands
|
|
14
|
-
|
|
15
|
-
- `scan`: Scan all templates on the plot and dump them to a text file (requires [CodeClient](github.com/DFOnline/CodeClient))
|
|
16
|
-
- `send`: Send template items to DiamondFire (requires [CodeClient](github.com/DFOnline/CodeClient))
|
|
17
|
-
- `rename`: Rename all occurences of a variable (including text codes)
|
|
18
|
-
- `script`: Generate python scripts from template data
|
|
19
|
-
- `grabinv`: Save all templates in your Minecraft inventory to a file (requires [CodeClient](github.com/DFOnline/CodeClient))
|
|
20
|
-
- `docs`: Generate markdown documentation from template data
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
## What is this useful for?
|
|
24
|
-
|
|
25
|
-
- Backing up a plot
|
|
26
|
-
- Getting an accurate text representation of DF code
|
|
27
|
-
- Open sourcing
|
|
28
|
-
- Version control
|
|
29
|
-
- Large scale refactoring
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
## Example Usage
|
|
33
|
-
|
|
34
|
-
These two commands will scan your plot, convert each template into a python script, then place the scripts into a directory called `myplot`.
|
|
35
|
-
|
|
36
|
-
```sh
|
|
37
|
-
pyrecli scan templates.dfts
|
|
38
|
-
pyrecli script templates.dfts myplot
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
If you prefer the templates to be outputted to a single file, use the `--onefile` flag:
|
|
42
|
-
|
|
43
|
-
```sh
|
|
44
|
-
pyrecli scan templates.dfts
|
|
45
|
-
pyrecli script templates.dfts myplot.py --onefile
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
For more information about generating scripts, run `pyrecli script -h`.
|
pyrecli-0.2.0/pyproject.toml
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
[tool.poetry]
|
|
2
|
-
name = "pyrecli"
|
|
3
|
-
version = "0.2.0"
|
|
4
|
-
description = "Command line utilities for DiamondFire templates"
|
|
5
|
-
authors = ["Amp"]
|
|
6
|
-
readme = "README.md"
|
|
7
|
-
license = "MIT"
|
|
8
|
-
repository = "https://github.com/Amp63/pyrecli"
|
|
9
|
-
keywords = ["diamondfire", "minecraft", "template", "cli"]
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
[tool.poetry.dependencies]
|
|
13
|
-
python = ">=3.10,<3.13"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
[tool.poetry.scripts]
|
|
17
|
-
pyrecli = "pyrecli.pyrecli:main"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
[build-system]
|
|
21
|
-
requires = ["poetry-core"]
|
|
22
|
-
build-backend = "poetry.core.masonry.api"
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from result import Result, Ok, Err
|
|
3
|
-
import amulet_nbt
|
|
4
|
-
from amulet_nbt import CompoundTag, StringTag
|
|
5
|
-
from pyrecli.util import write_output_file, connect_to_codeclient
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def grabinv_command(output_path: str) -> Result[None, str]:
|
|
9
|
-
ws_result = connect_to_codeclient('inventory')
|
|
10
|
-
if ws_result.is_err():
|
|
11
|
-
return Err(ws_result.err_value)
|
|
12
|
-
ws = ws_result.ok_value
|
|
13
|
-
|
|
14
|
-
ws.send('inv')
|
|
15
|
-
inventory = ws.recv()
|
|
16
|
-
inventory_nbt = amulet_nbt.from_snbt(inventory)
|
|
17
|
-
|
|
18
|
-
template_codes: list[str] = []
|
|
19
|
-
for tag in inventory_nbt:
|
|
20
|
-
components: CompoundTag = tag.get('components')
|
|
21
|
-
if components is None:
|
|
22
|
-
continue
|
|
23
|
-
|
|
24
|
-
custom_data: CompoundTag = components.get('minecraft:custom_data')
|
|
25
|
-
if custom_data is None:
|
|
26
|
-
continue
|
|
27
|
-
|
|
28
|
-
pbv_tag: CompoundTag = custom_data.get('PublicBukkitValues')
|
|
29
|
-
if pbv_tag is None:
|
|
30
|
-
continue
|
|
31
|
-
|
|
32
|
-
code_template_data: StringTag = pbv_tag.get('hypercube:codetemplatedata')
|
|
33
|
-
if code_template_data is None:
|
|
34
|
-
continue
|
|
35
|
-
|
|
36
|
-
code_template_json = json.loads(str(code_template_data))
|
|
37
|
-
|
|
38
|
-
template_code = code_template_json.get('code')
|
|
39
|
-
if template_code:
|
|
40
|
-
template_codes.append(template_code)
|
|
41
|
-
|
|
42
|
-
if not template_codes:
|
|
43
|
-
return Err('Could not find any templates in the inventory.')
|
|
44
|
-
|
|
45
|
-
output_result = write_output_file(output_path, '\n'.join(template_codes))
|
|
46
|
-
if output_result.is_err():
|
|
47
|
-
return output_result
|
|
48
|
-
|
|
49
|
-
return Ok(None)
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
from result import Result, Err
|
|
2
|
-
from pyrecli.util import write_output_file, connect_to_codeclient
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def scan_command(output_path: str) -> Result[None, str]:
|
|
6
|
-
ws_result = connect_to_codeclient('read_plot')
|
|
7
|
-
if ws_result.is_err():
|
|
8
|
-
return Err(ws_result.err_value)
|
|
9
|
-
ws = ws_result.ok_value
|
|
10
|
-
|
|
11
|
-
print('Scanning plot...')
|
|
12
|
-
ws.send('scan')
|
|
13
|
-
|
|
14
|
-
scan_results = ws.recv()
|
|
15
|
-
print('Done.')
|
|
16
|
-
ws.close()
|
|
17
|
-
|
|
18
|
-
write_result = write_output_file(output_path, scan_results)
|
|
19
|
-
return write_result
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
from result import Result, Ok, Err
|
|
2
|
-
from pyrecli.util import connect_to_codeclient, read_input_file, parse_templates_from_string
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def send_command(input_path: str) -> Result[None, str]:
|
|
6
|
-
input_result = read_input_file(input_path)
|
|
7
|
-
if input_result.is_err():
|
|
8
|
-
return Err(input_result.err_value)
|
|
9
|
-
|
|
10
|
-
templates_result = parse_templates_from_string(input_result.ok_value)
|
|
11
|
-
if templates_result.is_err():
|
|
12
|
-
return Err(templates_result.err_value)
|
|
13
|
-
templates = templates_result.ok_value
|
|
14
|
-
|
|
15
|
-
ws_result = connect_to_codeclient()
|
|
16
|
-
if ws_result.is_err():
|
|
17
|
-
return Err(ws_result.err_value)
|
|
18
|
-
ws = ws_result.ok_value
|
|
19
|
-
|
|
20
|
-
for template in templates:
|
|
21
|
-
item = template.generate_template_item()
|
|
22
|
-
ws.send(f'give {item.get_snbt()}')
|
|
23
|
-
|
|
24
|
-
ws.close()
|
|
25
|
-
print(f'Sent {len(templates)} template{"s" if len(templates) != 1 else ''} successfully.')
|
|
26
|
-
return Ok(None)
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
from result import Result, Err
|
|
2
|
-
from pyrecli.util import read_input_file, write_output_file, parse_templates_from_string
|
|
3
|
-
|
|
4
|
-
def slice_command(input_path: str, output_path: str, target_length: int) -> Result[None, str]:
|
|
5
|
-
input_result = read_input_file(input_path)
|
|
6
|
-
if input_result.is_err():
|
|
7
|
-
return Err(input_result.err_value)
|
|
8
|
-
|
|
9
|
-
templates_result = parse_templates_from_string(input_result.ok_value)
|
|
10
|
-
if templates_result.is_err():
|
|
11
|
-
return Err(templates_result.err_value)
|
|
12
|
-
templates = templates_result.ok_value
|
|
13
|
-
|
|
14
|
-
if not templates:
|
|
15
|
-
return Err(f'Could not find any templates in {input_path}')
|
|
16
|
-
|
|
17
|
-
first_template = templates[0]
|
|
18
|
-
sliced_templates = first_template.slice(target_length)
|
|
19
|
-
built_templates = [t.build() for t in sliced_templates]
|
|
20
|
-
|
|
21
|
-
return write_output_file(output_path, '\n'.join(built_templates))
|
pyrecli-0.2.0/pyrecli/util.py
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import re
|
|
3
|
-
import sys
|
|
4
|
-
from result import Result, Ok, Err
|
|
5
|
-
import websocket
|
|
6
|
-
from dfpyre import DFTemplate
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
CODECLIENT_URL = 'ws://localhost:31375'
|
|
10
|
-
|
|
11
|
-
BASE64_REGEX = re.compile(r'^[A-Za-z0-9+/]+={0,2}$')
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def connect_to_codeclient(scopes: str|None=None) -> Result[websocket.WebSocket, str]:
|
|
15
|
-
ws = websocket.WebSocket()
|
|
16
|
-
try:
|
|
17
|
-
ws.connect(CODECLIENT_URL)
|
|
18
|
-
except ConnectionRefusedError:
|
|
19
|
-
return Err('Failed to connect to CodeClient.')
|
|
20
|
-
|
|
21
|
-
print('Connected to CodeClient.')
|
|
22
|
-
|
|
23
|
-
if scopes:
|
|
24
|
-
print('Please run /auth in game.')
|
|
25
|
-
ws.send(f'scopes {scopes}')
|
|
26
|
-
auth_message = ws.recv()
|
|
27
|
-
|
|
28
|
-
if auth_message != 'auth':
|
|
29
|
-
return Err('Failed to authenticate.')
|
|
30
|
-
print('Authentication received.')
|
|
31
|
-
|
|
32
|
-
return Ok(ws)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def parse_templates_from_string(templates: str) -> Result[list[DFTemplate], str]:
|
|
36
|
-
template_codes = templates.split('\n')
|
|
37
|
-
|
|
38
|
-
for i, template_code in enumerate(template_codes):
|
|
39
|
-
if not BASE64_REGEX.match(template_code):
|
|
40
|
-
return Err(f'Template code at line {i+1} is not a base64 string.')
|
|
41
|
-
|
|
42
|
-
try:
|
|
43
|
-
return Ok([DFTemplate.from_code(c) for c in template_codes])
|
|
44
|
-
except Exception as e:
|
|
45
|
-
return Err(str(e))
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def read_input_file(path: str) -> Result[str, str]:
|
|
49
|
-
if path == '-':
|
|
50
|
-
try:
|
|
51
|
-
input_string = sys.stdin.read()
|
|
52
|
-
except EOFError:
|
|
53
|
-
pass
|
|
54
|
-
return Ok(input_string.strip())
|
|
55
|
-
|
|
56
|
-
if not os.path.isfile(path):
|
|
57
|
-
return Err(f'"{path}" is not a file.')
|
|
58
|
-
|
|
59
|
-
try:
|
|
60
|
-
with open(path, 'r') as f:
|
|
61
|
-
return Ok(f.read())
|
|
62
|
-
except OSError as e:
|
|
63
|
-
return Err(str(e))
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def write_output_file(path: str, content: str) -> Result[None, str]:
|
|
67
|
-
if path == '-':
|
|
68
|
-
print(content, end='')
|
|
69
|
-
else:
|
|
70
|
-
try:
|
|
71
|
-
with open(path, 'w') as f:
|
|
72
|
-
f.write(content)
|
|
73
|
-
except OSError as e:
|
|
74
|
-
return Err(str(e))
|
|
75
|
-
|
|
76
|
-
return Ok(None)
|
|
File without changes
|
|
File without changes
|