OpenRCT2-ObjectCommon 0.1.4__tar.gz → 0.2.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.
Files changed (31) hide show
  1. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/PKG-INFO +5 -4
  2. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/README.md +3 -2
  3. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/openrct2_object_common/blender/lights.py +1 -1
  4. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/openrct2_object_common/blender/mesh_extract.py +1 -2
  5. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/openrct2_object_common/parkobj.py +7 -0
  6. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/openrct2_object_common/placement.py +2 -2
  7. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/pyproject.toml +4 -4
  8. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/tests/test_parkobj.py +21 -0
  9. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/.github/workflows/lint.yml +0 -0
  10. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/.github/workflows/publish.yml +0 -0
  11. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/.github/workflows/pytest.yml +0 -0
  12. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/.gitignore +0 -0
  13. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/.yamllint.yaml +0 -0
  14. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/LICENSE +0 -0
  15. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/openrct2_object_common/__init__.py +0 -0
  16. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/openrct2_object_common/blender/__init__.py +0 -0
  17. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/openrct2_object_common/blender/modal.py +0 -0
  18. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/openrct2_object_common/blender/props.py +0 -0
  19. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/openrct2_object_common/cli.py +0 -0
  20. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/openrct2_object_common/config.py +0 -0
  21. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/openrct2_object_common/objectjson.py +0 -0
  22. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/openrct2_object_common/py.typed +0 -0
  23. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/openrct2_object_common/testing.py +0 -0
  24. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/scripts/ci/set_version.py +0 -0
  25. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/tests/conftest.py +0 -0
  26. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/tests/test_blender.py +0 -0
  27. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/tests/test_blender_shared.py +0 -0
  28. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/tests/test_cli.py +0 -0
  29. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/tests/test_config.py +0 -0
  30. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/tests/test_objectjson.py +0 -0
  31. {openrct2_objectcommon-0.1.4 → openrct2_objectcommon-0.2.0}/tests/test_placement.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OpenRCT2-ObjectCommon
3
- Version: 0.1.4
3
+ Version: 0.2.0
4
4
  Summary: Shared config, CLI, .parkobj, and object.json scaffolding for OpenRCT2 object generators.
5
5
  Project-URL: Homepage, https://github.com/alex-parisi/OpenRCT2-ObjectCommon
6
6
  Project-URL: Repository, https://github.com/alex-parisi/OpenRCT2-ObjectCommon
@@ -9,7 +9,7 @@ License-Expression: GPL-3.0-or-later
9
9
  License-File: LICENSE
10
10
  Requires-Python: >=3.11
11
11
  Requires-Dist: numpy>=1.26
12
- Requires-Dist: openrct2-x7-renderer>=0.3.3
12
+ Requires-Dist: openrct2-x7-renderer>=0.3.4
13
13
  Requires-Dist: pillow>=10.0
14
14
  Requires-Dist: pyyaml>=6.0
15
15
  Provides-Extra: blender
@@ -109,8 +109,9 @@ def export_to(obj, context, parkobj_path, work_dir, skip_render=False):
109
109
 
110
110
  ### Blender helpers
111
111
 
112
- The `blender` subpackage imports `bpy` (only `blender.modal` `blender.lights`
113
- does not), so install the extra when working with it outside Blender:
112
+ The `blender` subpackage imports `bpy` (`blender.modal`, `blender.props`, and
113
+ `blender.mesh_extract` — `blender.lights` does not), so install the extra when
114
+ working with it outside Blender:
114
115
 
115
116
  ```bash
116
117
  pip install "OpenRCT2-ObjectCommon[blender]"
@@ -91,8 +91,9 @@ def export_to(obj, context, parkobj_path, work_dir, skip_render=False):
91
91
 
92
92
  ### Blender helpers
93
93
 
94
- The `blender` subpackage imports `bpy` (only `blender.modal` `blender.lights`
95
- does not), so install the extra when working with it outside Blender:
94
+ The `blender` subpackage imports `bpy` (`blender.modal`, `blender.props`, and
95
+ `blender.mesh_extract` — `blender.lights` does not), so install the extra when
96
+ working with it outside Blender:
96
97
 
97
98
  ```bash
98
99
  pip install "OpenRCT2-ObjectCommon[blender]"
@@ -43,7 +43,7 @@ def lights_from_items(items: Iterable[_LightItem]) -> list[Light]:
43
43
  rig = [
44
44
  Light(
45
45
  type=LIGHT_TYPE_MAP[item.type],
46
- shadow=int(item.shadow),
46
+ shadow=bool(item.shadow),
47
47
  direction=normalize_direction(list(item.direction)),
48
48
  intensity=item.strength,
49
49
  )
@@ -16,6 +16,7 @@ from collections.abc import Callable
16
16
  import bpy
17
17
  import numpy as np
18
18
  from mathutils import Matrix, Vector
19
+ from openrct2_x7_renderer.constants import MaterialFlag
19
20
  from openrct2_x7_renderer.image import quantize_to_indexed, read_png
20
21
  from openrct2_x7_renderer.mesh import Material, Mesh
21
22
  from openrct2_x7_renderer.types import IndexedImage
@@ -65,8 +66,6 @@ def material_base(
65
66
  *prop_attr*: attribute name on the bpy material (e.g. ``"vg_material"``).
66
67
  *region_map*: maps region enum string → ``(flag_bits, region_id)``.
67
68
  """
68
- from openrct2_x7_renderer.constants import MaterialFlag
69
-
70
69
  m = Material()
71
70
  if bmat is None:
72
71
  return m, None
@@ -73,6 +73,8 @@ def write_images_dat_lgx(
73
73
  ``note`` is appended to the log line (e.g. ``" for 4 tiles"``). Returns the
74
74
  single-element ``["$LGX:images.dat[0..N-1]"]`` list OpenRCT2 expects.
75
75
  """
76
+ if not images:
77
+ raise ValueError("Cannot write images.dat with no sprites")
76
78
  out_path = work_dir / "images.dat"
77
79
  write_images_dat(images, out_path)
78
80
  log.info(
@@ -123,6 +125,11 @@ def assemble_parkobj(
123
125
  parkobj_path.parent.mkdir(parents=True, exist_ok=True)
124
126
  tmp_fd, tmp_path = tempfile.mkstemp(suffix=".parkobj", dir=parkobj_path.parent)
125
127
  os.close(tmp_fd)
128
+ # mkstemp creates the file 0o600; widen to the umask default so the
129
+ # delivered .parkobj has normal file permissions after os.replace.
130
+ umask = os.umask(0)
131
+ os.umask(umask)
132
+ os.chmod(tmp_path, 0o666 & ~umask)
126
133
  try:
127
134
  with zipfile.ZipFile(tmp_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
128
135
  zf.write(work_dir / "object.json", "object.json")
@@ -24,8 +24,8 @@ __all__ = ["add_model_to_scene", "orientation_to_matrix"]
24
24
  def orientation_to_matrix(orientation_deg: NDArray[np.float64]) -> NDArray[np.float64]:
25
25
  """A MeshFrame orientation ``(angle_y, angle_z, angle_x)`` in degrees as a
26
26
  ``(3, 3)`` rotation matrix, applied as ``rotate_y @ rotate_z @ rotate_x``."""
27
- rx, ry, rz = orientation_deg * (math.pi / 180.0)
28
- return rotate_y(rx) @ rotate_z(ry) @ rotate_x(rz)
27
+ ry, rz, rx = orientation_deg * (math.pi / 180.0)
28
+ return rotate_y(ry) @ rotate_z(rz) @ rotate_x(rx)
29
29
 
30
30
 
31
31
  def add_model_to_scene(
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "OpenRCT2-ObjectCommon"
3
- version = "0.1.4"
3
+ version = "0.2.0"
4
4
  description = "Shared config, CLI, .parkobj, and object.json scaffolding for OpenRCT2 object generators."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -9,7 +9,7 @@ authors = [
9
9
  { name = "Alex Parisi", email = "alex@atparisi.com" }
10
10
  ]
11
11
  dependencies = [
12
- "openrct2-x7-renderer>=0.3.3",
12
+ "openrct2-x7-renderer>=0.3.4",
13
13
  "numpy>=1.26",
14
14
  "pillow>=10.0",
15
15
  "pyyaml>=6.0",
@@ -19,8 +19,8 @@ dependencies = [
19
19
  Homepage = "https://github.com/alex-parisi/OpenRCT2-ObjectCommon"
20
20
  Repository = "https://github.com/alex-parisi/OpenRCT2-ObjectCommon"
21
21
 
22
- # The Blender helpers (openrct2_object_common.blender.modal) import bpy; the rest
23
- # of the package does not. Only the add-ons need this extra.
22
+ # The Blender helpers (openrct2_object_common.blender, except .lights) import
23
+ # bpy; the rest of the package does not. Only the add-ons need this extra.
24
24
  [project.optional-dependencies]
25
25
  blender = ["bpy>=4.0"]
26
26
 
@@ -1,6 +1,7 @@
1
1
  """Tests for .parkobj assembly and the images.dat helper."""
2
2
 
3
3
  import json
4
+ import os
4
5
  import zipfile
5
6
 
6
7
  import numpy as np
@@ -26,6 +27,13 @@ def test_write_images_dat_lgx_writes_blob_and_ref(tmp_path):
26
27
  assert (tmp_path / "images.dat").exists()
27
28
 
28
29
 
30
+ def test_write_images_dat_lgx_rejects_empty_list(tmp_path):
31
+ # An empty list would otherwise emit a malformed "$LGX:images.dat[0..-1]".
32
+ with pytest.raises(ValueError, match="no sprites"):
33
+ write_images_dat_lgx([], tmp_path)
34
+ assert not (tmp_path / "images.dat").exists()
35
+
36
+
29
37
  def test_assemble_renders_and_zips(tmp_path):
30
38
  work = tmp_path / "object"
31
39
  parkobj = tmp_path / "out" / "thing.parkobj"
@@ -58,6 +66,19 @@ def test_assemble_cleans_up_temp_on_zip_failure(tmp_path):
58
66
  assert list(out.glob("*.parkobj")) == []
59
67
 
60
68
 
69
+ def test_assemble_parkobj_respects_umask(tmp_path):
70
+ # The temp file behind the atomic replace is mkstemp'd (0o600); the final
71
+ # .parkobj must carry normal umask-derived permissions instead.
72
+ work = tmp_path / "object"
73
+ parkobj = tmp_path / "thing.parkobj"
74
+ old_umask = os.umask(0o022)
75
+ try:
76
+ assemble_parkobj({"id": "rct2.thing"}, parkobj, work, _render_two_pixels)
77
+ finally:
78
+ os.umask(old_umask)
79
+ assert parkobj.stat().st_mode & 0o777 == 0o644
80
+
81
+
61
82
  def test_skip_render_reuses_previous_images(tmp_path):
62
83
  work = tmp_path / "object"
63
84
  work.mkdir()