pyneoforge 0.1.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.
- pyneoforge/__init__.py +12 -0
- pyneoforge/build.py +20 -0
- pyneoforge/cli.py +239 -0
- pyneoforge/registry.py +604 -0
- pyneoforge/templates/neoforge-mod/build.gradle +64 -0
- pyneoforge/templates/neoforge-mod/gradle.properties +13 -0
- pyneoforge/templates/neoforge-mod/gradlew +2 -0
- pyneoforge/templates/neoforge-mod/gradlew.bat +2 -0
- pyneoforge/templates/neoforge-mod/settings.gradle +22 -0
- pyneoforge/templates/neoforge-mod/src/main/java/com/pyneoforge/generated/GeneratedContent.java +115 -0
- pyneoforge/templates/neoforge-mod/src/main/java/com/pyneoforge/generated/GeneratedContentLoader.java +86 -0
- pyneoforge/templates/neoforge-mod/src/main/java/com/pyneoforge/generated/PythonForNeoForge.java +13 -0
- pyneoforge/templates/neoforge-mod/src/main/resources/META-INF/neoforge.mods.toml +9 -0
- pyneoforge/templates/neoforge-mod/src/main/resources/pack.mcmeta +6 -0
- pyneoforge-0.1.0.dist-info/METADATA +7 -0
- pyneoforge-0.1.0.dist-info/RECORD +20 -0
- pyneoforge-0.1.0.dist-info/WHEEL +5 -0
- pyneoforge-0.1.0.dist-info/entry_points.txt +2 -0
- pyneoforge-0.1.0.dist-info/licenses/LICENSE +674 -0
- pyneoforge-0.1.0.dist-info/top_level.txt +1 -0
pyneoforge/__init__.py
ADDED
pyneoforge/build.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
from .cli import build_resources
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main() -> None:
|
|
9
|
+
parser = argparse.ArgumentParser(description="Build Python NeoForge definitions into resources.")
|
|
10
|
+
parser.add_argument("script", help="Python mod definition script to execute")
|
|
11
|
+
parser.add_argument("--mod-id", default="python_for_neoforge")
|
|
12
|
+
parser.add_argument("--mod-version", help="Minecraft target version code, for example 1211 -> 1.21.1")
|
|
13
|
+
parser.add_argument("--resources", default="neoforge-mod/src/main/resources")
|
|
14
|
+
args = parser.parse_args()
|
|
15
|
+
|
|
16
|
+
build_resources(args)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
if __name__ == "__main__":
|
|
20
|
+
main()
|
pyneoforge/cli.py
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
from importlib import resources
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import runpy
|
|
9
|
+
import shutil
|
|
10
|
+
import subprocess
|
|
11
|
+
|
|
12
|
+
from .registry import build, clear
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
DEFAULT_MOD_ID = "python_for_neoforge"
|
|
16
|
+
DEFAULT_NEO_VERSION = "21.1.200"
|
|
17
|
+
DEFAULT_JAVA_HOME = "C:/Program Files/Java/jdk-21"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def main() -> None:
|
|
21
|
+
parser = argparse.ArgumentParser(prog="pyneoforge")
|
|
22
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
23
|
+
|
|
24
|
+
init_parser = subparsers.add_parser("init", help="Create a new Python NeoForge mod project.")
|
|
25
|
+
init_parser.add_argument("path", help="Directory to create")
|
|
26
|
+
init_parser.add_argument("--mod-id", default="my_python_mod")
|
|
27
|
+
init_parser.add_argument("--mod-name", default="My Python Mod")
|
|
28
|
+
|
|
29
|
+
build_parser = subparsers.add_parser("build", help="Generate NeoForge resources from a Python script.")
|
|
30
|
+
build_parser.add_argument("script", help="Python mod definition script")
|
|
31
|
+
build_parser.add_argument("--mod-id", default=DEFAULT_MOD_ID)
|
|
32
|
+
build_parser.add_argument("--mod-version", help="Minecraft target version code, for example 1211 -> 1.21.1")
|
|
33
|
+
build_parser.add_argument("--resources", default="neoforge-mod/src/main/resources")
|
|
34
|
+
|
|
35
|
+
package_parser = subparsers.add_parser("package", help="Build a distributable NeoForge jar from a Python script.")
|
|
36
|
+
package_parser.add_argument("script", nargs="?", default="mod.py", help="Python mod definition script")
|
|
37
|
+
package_parser.add_argument("--mod-id")
|
|
38
|
+
package_parser.add_argument("--mod-name")
|
|
39
|
+
package_parser.add_argument("--mod-version", help="Your mod's version, not the Minecraft version")
|
|
40
|
+
package_parser.add_argument("--mc-version", "--minecraft-version", dest="minecraft_version")
|
|
41
|
+
package_parser.add_argument("--neo-version", default=DEFAULT_NEO_VERSION)
|
|
42
|
+
package_parser.add_argument("--java-home")
|
|
43
|
+
package_parser.add_argument("--output", default="dist")
|
|
44
|
+
package_parser.add_argument("--work-dir", default=".pyneoforge/build")
|
|
45
|
+
package_parser.add_argument("--keep-work-dir", action="store_true")
|
|
46
|
+
|
|
47
|
+
args = parser.parse_args()
|
|
48
|
+
if args.command == "init":
|
|
49
|
+
init_project(args)
|
|
50
|
+
elif args.command == "build":
|
|
51
|
+
build_resources(args)
|
|
52
|
+
elif args.command == "package":
|
|
53
|
+
package_mod(args)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def init_project(args: argparse.Namespace) -> None:
|
|
57
|
+
project_dir = Path(args.path)
|
|
58
|
+
project_dir.mkdir(parents=True, exist_ok=False)
|
|
59
|
+
(project_dir / "textures").mkdir()
|
|
60
|
+
(project_dir / "mod.py").write_text(
|
|
61
|
+
_example_mod_py(mod_id=args.mod_id, mod_name=args.mod_name),
|
|
62
|
+
encoding="utf-8",
|
|
63
|
+
)
|
|
64
|
+
(project_dir / "pyneoforge.toml").write_text(
|
|
65
|
+
"\n".join(
|
|
66
|
+
[
|
|
67
|
+
f'mod_id = "{args.mod_id}"',
|
|
68
|
+
f'mod_name = "{args.mod_name}"',
|
|
69
|
+
'minecraft_version = "1211"',
|
|
70
|
+
'mod_version = "0.1.0"',
|
|
71
|
+
"",
|
|
72
|
+
]
|
|
73
|
+
),
|
|
74
|
+
encoding="utf-8",
|
|
75
|
+
)
|
|
76
|
+
print(f"Created {project_dir}")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def build_resources(args: argparse.Namespace) -> None:
|
|
80
|
+
output = _generate_resources(
|
|
81
|
+
script=Path(args.script),
|
|
82
|
+
resources_dir=Path(args.resources),
|
|
83
|
+
mod_id=args.mod_id,
|
|
84
|
+
minecraft_version=args.mod_version,
|
|
85
|
+
)
|
|
86
|
+
print(f"Generated {output}")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def package_mod(args: argparse.Namespace) -> None:
|
|
90
|
+
script = Path(args.script).resolve()
|
|
91
|
+
config = _read_project_config(script.parent / "pyneoforge.toml")
|
|
92
|
+
mod_id = args.mod_id or config.get("mod_id")
|
|
93
|
+
mod_name = args.mod_name or config.get("mod_name")
|
|
94
|
+
mod_version = args.mod_version or config.get("mod_version", "0.1.0")
|
|
95
|
+
minecraft_version = args.minecraft_version or config.get("minecraft_version", "1211")
|
|
96
|
+
java_home = args.java_home or config.get("java_home", DEFAULT_JAVA_HOME if os.name == "nt" else "")
|
|
97
|
+
|
|
98
|
+
if not mod_id:
|
|
99
|
+
raise SystemExit("missing --mod-id or pyneoforge.toml mod_id")
|
|
100
|
+
if not mod_name:
|
|
101
|
+
raise SystemExit("missing --mod-name or pyneoforge.toml mod_name")
|
|
102
|
+
|
|
103
|
+
work_root = Path(args.work_dir).resolve()
|
|
104
|
+
project_dir = work_root / mod_id
|
|
105
|
+
output_dir = Path(args.output).resolve()
|
|
106
|
+
|
|
107
|
+
if project_dir.exists():
|
|
108
|
+
shutil.rmtree(project_dir)
|
|
109
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
110
|
+
|
|
111
|
+
_copy_template(project_dir)
|
|
112
|
+
_apply_template_values(
|
|
113
|
+
project_dir=project_dir,
|
|
114
|
+
mod_id=mod_id,
|
|
115
|
+
mod_name=mod_name,
|
|
116
|
+
mod_version=mod_version,
|
|
117
|
+
neo_version=args.neo_version,
|
|
118
|
+
java_home=java_home,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
_generate_resources(
|
|
122
|
+
script=script,
|
|
123
|
+
resources_dir=project_dir / "src" / "main" / "resources",
|
|
124
|
+
mod_id=mod_id,
|
|
125
|
+
minecraft_version=minecraft_version,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
_run_gradle_build(project_dir)
|
|
129
|
+
jar_path = _find_built_jar(project_dir)
|
|
130
|
+
output_path = output_dir / jar_path.name
|
|
131
|
+
shutil.copyfile(jar_path, output_path)
|
|
132
|
+
|
|
133
|
+
if not args.keep_work_dir:
|
|
134
|
+
shutil.rmtree(project_dir)
|
|
135
|
+
|
|
136
|
+
print(f"Built {output_path}")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _copy_template(destination: Path) -> None:
|
|
140
|
+
template = resources.files("pyneoforge").joinpath("templates", "neoforge-mod")
|
|
141
|
+
with resources.as_file(template) as template_path:
|
|
142
|
+
shutil.copytree(template_path, destination)
|
|
143
|
+
gradlew = destination / "gradlew"
|
|
144
|
+
if gradlew.exists():
|
|
145
|
+
gradlew.chmod(0o755)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _apply_template_values(
|
|
149
|
+
*,
|
|
150
|
+
project_dir: Path,
|
|
151
|
+
mod_id: str,
|
|
152
|
+
mod_name: str,
|
|
153
|
+
mod_version: str,
|
|
154
|
+
neo_version: str,
|
|
155
|
+
java_home: str,
|
|
156
|
+
) -> None:
|
|
157
|
+
java_home_line = ""
|
|
158
|
+
if java_home:
|
|
159
|
+
normalized_java_home = java_home.replace("\\", "/")
|
|
160
|
+
java_home_line = f"org.gradle.java.home={normalized_java_home}"
|
|
161
|
+
|
|
162
|
+
replacements = {
|
|
163
|
+
"__MOD_ID__": mod_id,
|
|
164
|
+
"__MOD_NAME__": mod_name,
|
|
165
|
+
"__MOD_VERSION__": mod_version,
|
|
166
|
+
"__NEO_VERSION__": neo_version,
|
|
167
|
+
"__JAVA_HOME_LINE__": java_home_line,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
for path in project_dir.rglob("*"):
|
|
171
|
+
if not path.is_file() or path.suffix.lower() in {".jar", ".png"}:
|
|
172
|
+
continue
|
|
173
|
+
text = path.read_text(encoding="utf-8")
|
|
174
|
+
for key, value in replacements.items():
|
|
175
|
+
text = text.replace(key, value)
|
|
176
|
+
path.write_text(text, encoding="utf-8")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _generate_resources(*, script: Path, resources_dir: Path, mod_id: str, minecraft_version: str | None) -> Path:
|
|
180
|
+
clear()
|
|
181
|
+
with _pushd(script.parent):
|
|
182
|
+
runpy.run_path(str(script), run_name="__main__")
|
|
183
|
+
return build(mod_id=mod_id, resources_dir=resources_dir, mod_version=minecraft_version)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _run_gradle_build(project_dir: Path) -> None:
|
|
187
|
+
gradlew = project_dir / ("gradlew.bat" if os.name == "nt" else "gradlew")
|
|
188
|
+
command = [str(gradlew if gradlew.exists() else "gradle"), "build"]
|
|
189
|
+
subprocess.run(command, cwd=project_dir, check=True)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _find_built_jar(project_dir: Path) -> Path:
|
|
193
|
+
jars = sorted((project_dir / "build" / "libs").glob("*.jar"))
|
|
194
|
+
if not jars:
|
|
195
|
+
raise FileNotFoundError("Gradle build did not produce a jar")
|
|
196
|
+
return jars[0]
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _read_project_config(path: Path) -> dict[str, str]:
|
|
200
|
+
if not path.exists():
|
|
201
|
+
return {}
|
|
202
|
+
|
|
203
|
+
values: dict[str, str] = {}
|
|
204
|
+
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
|
205
|
+
line = raw_line.split("#", 1)[0].strip()
|
|
206
|
+
if not line or "=" not in line:
|
|
207
|
+
continue
|
|
208
|
+
key, value = line.split("=", 1)
|
|
209
|
+
values[key.strip()] = value.strip().strip('"').strip("'")
|
|
210
|
+
return values
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@contextmanager
|
|
214
|
+
def _pushd(path: Path):
|
|
215
|
+
previous = Path.cwd()
|
|
216
|
+
os.chdir(path)
|
|
217
|
+
try:
|
|
218
|
+
yield
|
|
219
|
+
finally:
|
|
220
|
+
os.chdir(previous)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _example_mod_py(*, mod_id: str, mod_name: str) -> str:
|
|
224
|
+
return f'''from pyneoforge import block, config, creative_tab, item, shaped_recipe
|
|
225
|
+
|
|
226
|
+
config(mod_version=1211)
|
|
227
|
+
|
|
228
|
+
item("ruby", display_name="Ruby", texture_file="textures/ruby.png")
|
|
229
|
+
block("ruby_block", display_name="Ruby Block", hardness=5.0, texture_file="textures/ruby_block.png")
|
|
230
|
+
|
|
231
|
+
creative_tab("main", display_name="{mod_name}", icon="ruby", entries=["ruby", "ruby_block"])
|
|
232
|
+
|
|
233
|
+
shaped_recipe(
|
|
234
|
+
"ruby_block",
|
|
235
|
+
result="ruby_block",
|
|
236
|
+
pattern=["RRR", "RRR", "RRR"],
|
|
237
|
+
key={{"R": "ruby"}},
|
|
238
|
+
)
|
|
239
|
+
'''
|