pylitematic 0.0.2__tar.gz → 0.0.3__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.
- {pylitematic-0.0.2 → pylitematic-0.0.3}/PKG-INFO +4 -1
- {pylitematic-0.0.2 → pylitematic-0.0.3}/pyproject.toml +8 -2
- {pylitematic-0.0.2 → pylitematic-0.0.3}/src/pylitematic/__init__.py +2 -2
- {pylitematic-0.0.2 → pylitematic-0.0.3}/src/pylitematic/block_property.py +0 -66
- {pylitematic-0.0.2 → pylitematic-0.0.3}/src/pylitematic/block_state.py +12 -5
- {pylitematic-0.0.2 → pylitematic-0.0.3}/src/pylitematic/geometry.py +14 -0
- {pylitematic-0.0.2 → pylitematic-0.0.3}/src/pylitematic/region.py +56 -49
- {pylitematic-0.0.2 → pylitematic-0.0.3}/src/pylitematic/resource_location.py +4 -0
- {pylitematic-0.0.2 → pylitematic-0.0.3}/src/pylitematic/schematic.py +7 -5
- pylitematic-0.0.3/src/pylitematic/test.py +71 -0
- {pylitematic-0.0.2 → pylitematic-0.0.3}/src/pylitematic.egg-info/PKG-INFO +4 -1
- {pylitematic-0.0.2 → pylitematic-0.0.3}/src/pylitematic.egg-info/SOURCES.txt +2 -1
- {pylitematic-0.0.2 → pylitematic-0.0.3}/src/pylitematic.egg-info/requires.txt +4 -0
- {pylitematic-0.0.2 → pylitematic-0.0.3}/tests/test_block_property.py +0 -42
- pylitematic-0.0.3/tests/test_schematic.py +40 -0
- pylitematic-0.0.2/src/pylitematic/test.py +0 -39
- {pylitematic-0.0.2 → pylitematic-0.0.3}/README.md +0 -0
- {pylitematic-0.0.2 → pylitematic-0.0.3}/setup.cfg +0 -0
- {pylitematic-0.0.2 → pylitematic-0.0.3}/src/pylitematic.egg-info/dependency_links.txt +0 -0
- {pylitematic-0.0.2 → pylitematic-0.0.3}/src/pylitematic.egg-info/top_level.txt +0 -0
- {pylitematic-0.0.2 → pylitematic-0.0.3}/tests/test_resource_location.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pylitematic
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.3
|
4
4
|
Summary: Load, modify, and save Litematica schematics
|
5
5
|
Author-email: Boscawinks <bosca.winks@gmx.de>
|
6
6
|
License: GPL-3.0-only
|
@@ -13,6 +13,9 @@ Description-Content-Type: text/markdown
|
|
13
13
|
Requires-Dist: bitpacking>=0.1.0
|
14
14
|
Requires-Dist: nbtlib>=2.0.4
|
15
15
|
Requires-Dist: numpy>=2.2.6
|
16
|
+
Requires-Dist: twos>=0.0.1
|
17
|
+
Provides-Extra: dev
|
18
|
+
Requires-Dist: pytest; extra == "dev"
|
16
19
|
|
17
20
|
# pylitematic
|
18
21
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "pylitematic"
|
7
|
-
version = "0.0.
|
7
|
+
version = "0.0.3"
|
8
8
|
description = "Load, modify, and save Litematica schematics"
|
9
9
|
authors = [
|
10
10
|
{ name="Boscawinks", email="bosca.winks@gmx.de" }
|
@@ -12,7 +12,8 @@ authors = [
|
|
12
12
|
dependencies = [
|
13
13
|
"bitpacking >=0.1.0",
|
14
14
|
"nbtlib >=2.0.4",
|
15
|
-
"numpy >=2.2.6"
|
15
|
+
"numpy >=2.2.6",
|
16
|
+
"twos >= 0.0.1"
|
16
17
|
]
|
17
18
|
readme = "README.md"
|
18
19
|
requires-python = ">=3.10.12"
|
@@ -21,6 +22,11 @@ classifiers = [
|
|
21
22
|
"Programming Language :: Python :: 3",
|
22
23
|
]
|
23
24
|
|
25
|
+
[project.optional-dependencies]
|
26
|
+
dev = [
|
27
|
+
"pytest"
|
28
|
+
]
|
29
|
+
|
24
30
|
[project.urls]
|
25
31
|
Homepage = "https://github.com/boscawinks/pylitematic"
|
26
32
|
Repository = "https://github.com/boscawinks/pylitematic"
|
@@ -1,7 +1,7 @@
|
|
1
|
-
__version__ = "0.0.
|
1
|
+
__version__ = "0.0.3"
|
2
2
|
|
3
3
|
from .block_state import BlockState
|
4
4
|
from .geometry import BlockPosition, Size3D
|
5
5
|
from .region import Region
|
6
|
-
from .resource_location import
|
6
|
+
from .resource_location import BlockId
|
7
7
|
from .schematic import Schematic
|
@@ -14,72 +14,6 @@ ENUM_VALUE_REGEX: str = r"[a-z]+(_[a-z]+)*" # snake case
|
|
14
14
|
ENUM_VALUE_PATTERN: re.Pattern = re.compile(ENUM_VALUE_REGEX)
|
15
15
|
|
16
16
|
|
17
|
-
class Property():
|
18
|
-
|
19
|
-
__slots__ = ("_name", "_value")
|
20
|
-
|
21
|
-
def __init__(self, name: str, value: Any | PropertyValue) -> None:
|
22
|
-
if not PROPERTY_NAME_PATTERN.fullmatch(name):
|
23
|
-
raise ValueError(f"Invalid property name {name!r}")
|
24
|
-
self._name = name
|
25
|
-
|
26
|
-
if not isinstance(value, PropertyValue):
|
27
|
-
value = PropertyValue.value_factory(value=value)
|
28
|
-
self._value = value
|
29
|
-
|
30
|
-
def __str__(self) -> str:
|
31
|
-
return f"{self._name}={self._value}"
|
32
|
-
|
33
|
-
def __repr__(self) -> str:
|
34
|
-
return (
|
35
|
-
f"{type(self).__name__}("
|
36
|
-
f"name: {self._name}, value: {self._value!r})")
|
37
|
-
|
38
|
-
def __eq__(self, other: Any) -> bool:
|
39
|
-
if not isinstance(other, Property):
|
40
|
-
return NotImplemented
|
41
|
-
return (self.name, self.value) == (other.name, other.value)
|
42
|
-
|
43
|
-
def __lt__(self, other: Any) -> bool:
|
44
|
-
if not isinstance(other, Property):
|
45
|
-
return NotImplemented
|
46
|
-
return (self.name, self.value) < (other.name, other.value)
|
47
|
-
|
48
|
-
@property
|
49
|
-
def name(self) -> str:
|
50
|
-
return self._name
|
51
|
-
|
52
|
-
@property
|
53
|
-
def value(self) -> Any:
|
54
|
-
return self._value.get()
|
55
|
-
|
56
|
-
@value.setter
|
57
|
-
def value(self, value: Any) -> None:
|
58
|
-
self._value.set(value)
|
59
|
-
|
60
|
-
def to_string(self) -> str:
|
61
|
-
return str(self)
|
62
|
-
|
63
|
-
@staticmethod
|
64
|
-
def from_string(string: str, value: str | None = None) -> Property:
|
65
|
-
if value is None:
|
66
|
-
# tread string as "name=value"
|
67
|
-
try:
|
68
|
-
string, value = string.split("=")
|
69
|
-
except ValueError as exc:
|
70
|
-
raise ValueError(f"Invalid property string {string!r}") from exc
|
71
|
-
return Property(name=string, value=PropertyValue.from_string(value))
|
72
|
-
|
73
|
-
def to_nbt(self) -> tuple[str, nbtlib.String]:
|
74
|
-
# return nbtlib.Compound(Name=nbtlib.String(self._name), Value=self._value.to_nbt()})
|
75
|
-
return self._name, self._value.to_nbt()
|
76
|
-
|
77
|
-
@staticmethod
|
78
|
-
def from_nbt(name: str, nbt: nbtlib.String) -> Property:
|
79
|
-
# return Property.from_string(name=nbt["Name"], value=str(nbt["Value"]))
|
80
|
-
return Property.from_string(string=name, value=str(nbt))
|
81
|
-
|
82
|
-
|
83
17
|
class Properties(dict):
|
84
18
|
|
85
19
|
def __init__(self, *args, **kwargs):
|
@@ -4,7 +4,7 @@ from copy import deepcopy
|
|
4
4
|
from nbtlib import Compound
|
5
5
|
from typing import Any, Iterator
|
6
6
|
|
7
|
-
from .resource_location import
|
7
|
+
from .resource_location import BlockId
|
8
8
|
from .block_property import Properties
|
9
9
|
|
10
10
|
|
@@ -12,8 +12,15 @@ class BlockState:
|
|
12
12
|
|
13
13
|
__slots__ = ("_id", "_props")
|
14
14
|
|
15
|
-
def __init__(self, _id: str, **props: Any) -> None:
|
16
|
-
|
15
|
+
def __init__(self, _id: str | BlockId, **props: Any) -> None:
|
16
|
+
if isinstance(_id, str):
|
17
|
+
_id = BlockId.from_string(_id)
|
18
|
+
elif not isinstance(_id, BlockId):
|
19
|
+
raise TypeError(
|
20
|
+
f"'_id' has to be str or {BlockId.__name__}, got"
|
21
|
+
f" {type(_id).__name__}")
|
22
|
+
self._id: BlockId = _id
|
23
|
+
|
17
24
|
self._props: Properties = Properties(**props)
|
18
25
|
|
19
26
|
def __getitem__(self, name: str) -> Any:
|
@@ -58,8 +65,8 @@ class BlockState:
|
|
58
65
|
f"id: {self._id!r}, props: {self._props!r})")
|
59
66
|
|
60
67
|
@property
|
61
|
-
def id(self) ->
|
62
|
-
return
|
68
|
+
def id(self) -> BlockId:
|
69
|
+
return self._id
|
63
70
|
|
64
71
|
def props(self) -> Iterator[tuple[str, Any]]:
|
65
72
|
return self._props.items()
|
@@ -191,3 +191,17 @@ class Size3D(Vec3i):
|
|
191
191
|
return (
|
192
192
|
f"{type(self).__name__}("
|
193
193
|
f"width={self.width}, height={self.height}, length={self.length})")
|
194
|
+
|
195
|
+
def end(self, axis: tuple[int,...] | int | None = None) -> BlockPosition:
|
196
|
+
limit = self - np.sign(self)
|
197
|
+
|
198
|
+
if axis is None:
|
199
|
+
return BlockPosition(*limit)
|
200
|
+
|
201
|
+
if not isinstance(axis, tuple):
|
202
|
+
axis = (axis, )
|
203
|
+
|
204
|
+
ret = np.zeros_like(limit, dtype=int)
|
205
|
+
for ax in axis:
|
206
|
+
ret[ax] = limit[ax]
|
207
|
+
return BlockPosition(*ret)
|
@@ -9,7 +9,7 @@ from typing import Iterator
|
|
9
9
|
|
10
10
|
from .block_state import BlockState
|
11
11
|
from .geometry import BlockPosition, Size3D
|
12
|
-
from .resource_location import
|
12
|
+
from .resource_location import BlockId
|
13
13
|
|
14
14
|
|
15
15
|
AIR = BlockState("air")
|
@@ -28,10 +28,11 @@ class Region:
|
|
28
28
|
self._palette_map: dict[BlockState, int] = {AIR: 0}
|
29
29
|
self._blocks = np.zeros(abs(self._size), dtype=int)
|
30
30
|
|
31
|
-
|
32
|
-
self.
|
33
|
-
self.
|
34
|
-
self.
|
31
|
+
# TODO: Add support
|
32
|
+
self._entities = nbtlib.List[nbtlib.Compound]()
|
33
|
+
self._tile_entities = nbtlib.List[nbtlib.Compound]()
|
34
|
+
self._block_ticks = nbtlib.List[nbtlib.Compound]()
|
35
|
+
self._fluid_ticks = nbtlib.List[nbtlib.Compound]()
|
35
36
|
|
36
37
|
def __contains__(self, item) -> bool:
|
37
38
|
if isinstance(item, BlockPosition):
|
@@ -41,50 +42,26 @@ class Region:
|
|
41
42
|
if index is None:
|
42
43
|
return False
|
43
44
|
return np.any(self._blocks == index)
|
44
|
-
elif isinstance(item,
|
45
|
+
elif isinstance(item, BlockId):
|
45
46
|
return any(
|
46
47
|
(bs.id == item and np.any(self._blocks == idx))
|
47
48
|
for bs, idx in self._palette_map.items())
|
48
49
|
else:
|
49
50
|
return False
|
50
51
|
|
51
|
-
def
|
52
|
-
|
53
|
-
index = (index,)
|
54
|
-
ndim = self._blocks.ndim
|
55
|
-
result = []
|
56
|
-
for item in index:
|
57
|
-
if item is Ellipsis:
|
58
|
-
result.extend([slice(None)] * (ndim - len(index) + 1))
|
59
|
-
else:
|
60
|
-
result.append(item)
|
61
|
-
while len(result) < ndim:
|
62
|
-
result.append(slice(None))
|
63
|
-
return tuple(result)
|
52
|
+
def __eq__(self, other) -> bool:
|
53
|
+
palette = np.array(self._palette, dtype=object)
|
64
54
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
offset = self.lower[i]
|
69
|
-
if isinstance(item, int):
|
70
|
-
index.append(item - offset)
|
71
|
-
elif isinstance(item, slice):
|
72
|
-
start = item.start - offset if item.start is not None else None
|
73
|
-
stop = item.stop - offset if item.stop is not None else None
|
74
|
-
index.append(slice(start, stop, item.step))
|
75
|
-
else:
|
76
|
-
index.append(item)
|
77
|
-
return tuple(index)
|
55
|
+
if isinstance(other, BlockState):
|
56
|
+
matches = np.array([state == other for state in palette])
|
57
|
+
return matches[self._blocks]
|
78
58
|
|
79
|
-
|
80
|
-
|
59
|
+
elif isinstance(other, BlockId):
|
60
|
+
matches = np.array([state.id == other for state in palette])
|
61
|
+
return matches[self._blocks]
|
81
62
|
|
82
|
-
def _key_to_index(self, key):
|
83
|
-
if isinstance(key, BlockPosition):
|
84
|
-
index = tuple(key)
|
85
63
|
else:
|
86
|
-
|
87
|
-
return self._to_internal(index)
|
64
|
+
return NotImplemented
|
88
65
|
|
89
66
|
def __getitem__(self, key):
|
90
67
|
index = self._key_to_index(key)
|
@@ -126,6 +103,44 @@ class Region:
|
|
126
103
|
raise TypeError(
|
127
104
|
"Value must be a BlockState or a list of BlockStates")
|
128
105
|
|
106
|
+
def _expand_index(self, index):
|
107
|
+
if not isinstance(index, tuple):
|
108
|
+
index = (index,)
|
109
|
+
ndim = self._blocks.ndim
|
110
|
+
result = []
|
111
|
+
for item in index:
|
112
|
+
if item is Ellipsis:
|
113
|
+
result.extend([slice(None)] * (ndim - len(index) + 1))
|
114
|
+
else:
|
115
|
+
result.append(item)
|
116
|
+
while len(result) < ndim:
|
117
|
+
result.append(slice(None))
|
118
|
+
return tuple(result)
|
119
|
+
|
120
|
+
def _to_internal(self, pos):
|
121
|
+
index = []
|
122
|
+
for i, item in enumerate(pos):
|
123
|
+
offset = self.lower[i]
|
124
|
+
if isinstance(item, int):
|
125
|
+
index.append(item - offset)
|
126
|
+
elif isinstance(item, slice):
|
127
|
+
start = item.start - offset if item.start is not None else None
|
128
|
+
stop = item.stop - offset if item.stop is not None else None
|
129
|
+
index.append(slice(start, stop, item.step))
|
130
|
+
else:
|
131
|
+
index.append(item)
|
132
|
+
return tuple(index)
|
133
|
+
|
134
|
+
def _from_internal(self, index: tuple[int, int, int]) -> BlockPosition:
|
135
|
+
return self.lower + index
|
136
|
+
|
137
|
+
def _key_to_index(self, key):
|
138
|
+
if isinstance(key, BlockPosition):
|
139
|
+
index = tuple(key)
|
140
|
+
else:
|
141
|
+
index = self._expand_index(key)
|
142
|
+
return self._to_internal(index)
|
143
|
+
|
129
144
|
def compact_palette(self) -> None:
|
130
145
|
idx = np.unique(self._blocks)
|
131
146
|
# always include minecraft:air in a palette
|
@@ -171,14 +186,6 @@ class Region:
|
|
171
186
|
)
|
172
187
|
return nbtlib.LongArray([twos.to_signed(x, 64) for x in chunks])
|
173
188
|
|
174
|
-
@staticmethod
|
175
|
-
def inclusive_end(
|
176
|
-
size: Size3D,
|
177
|
-
pos: BlockPosition = BlockPosition(0, 0, 0),
|
178
|
-
) -> BlockPosition:
|
179
|
-
return pos + (size - np.sign(size))
|
180
|
-
# return pos + np.where(size > 0, size - 1, size + 1)
|
181
|
-
|
182
189
|
@property
|
183
190
|
def size(self) -> Size3D:
|
184
191
|
return self._size
|
@@ -193,7 +200,7 @@ class Region:
|
|
193
200
|
|
194
201
|
@cached_property
|
195
202
|
def limit(self) -> BlockPosition:
|
196
|
-
return self.
|
203
|
+
return self._origin + self._size.end()
|
197
204
|
|
198
205
|
@cached_property
|
199
206
|
def start(self) -> BlockPosition:
|
@@ -201,7 +208,7 @@ class Region:
|
|
201
208
|
|
202
209
|
@cached_property
|
203
210
|
def end(self) -> BlockPosition:
|
204
|
-
return self.
|
211
|
+
return self._size.end()
|
205
212
|
|
206
213
|
@property
|
207
214
|
def width(self) -> int:
|
@@ -8,8 +8,8 @@ import time
|
|
8
8
|
import twos
|
9
9
|
from typing import Iterator
|
10
10
|
|
11
|
-
from .geometry import BlockPosition, Size3D
|
12
|
-
from .region import Region
|
11
|
+
from pylitematic.geometry import BlockPosition, Size3D
|
12
|
+
from pylitematic.region import Region
|
13
13
|
|
14
14
|
|
15
15
|
DEFAULT_VERSION_MAJOR: int = 7
|
@@ -142,7 +142,6 @@ class Schematic:
|
|
142
142
|
|
143
143
|
def add_region(self, name: str, region: Region) -> None:
|
144
144
|
self._regions[name] = region
|
145
|
-
self._update()
|
146
145
|
|
147
146
|
def remove_region(self, name: str) -> Region:
|
148
147
|
return self._regions.pop(name)
|
@@ -214,7 +213,10 @@ class Schematic:
|
|
214
213
|
|
215
214
|
name = meta["Name"].unpack()
|
216
215
|
author = meta["Author"].unpack()
|
217
|
-
|
216
|
+
try:
|
217
|
+
desc = meta["Description"].unpack()
|
218
|
+
except KeyError:
|
219
|
+
desc = ""
|
218
220
|
|
219
221
|
preview = meta.get("PreviewImageData")
|
220
222
|
if preview is not None:
|
@@ -248,7 +250,7 @@ class Schematic:
|
|
248
250
|
schem = Schematic(
|
249
251
|
name=name,
|
250
252
|
author=author,
|
251
|
-
description=
|
253
|
+
description=desc,
|
252
254
|
regions=regions,
|
253
255
|
preview=preview,
|
254
256
|
version_major=major,
|
@@ -0,0 +1,71 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import pathlib
|
3
|
+
from pylitematic import BlockPosition, BlockId, BlockState, Region, Schematic, Size3D
|
4
|
+
|
5
|
+
# path = pathlib.Path("/mnt/d/minecraft/schematics/Litematica/test/subs.litematic")
|
6
|
+
# path = pathlib.Path("/mnt/d/minecraft/schematics/Litematica/test/regions.litematic")
|
7
|
+
# path = pathlib.Path("/mnt/d/minecraft/schematics/Litematica/test/creeper_test.litematic")
|
8
|
+
# stone = BlockState.from_string("minecraft:stone")
|
9
|
+
# dirt = BlockState.from_string("minecraft:dirt")
|
10
|
+
# s = Schematic.load(path)
|
11
|
+
# print(f"{s.volume=} {s.size=} {s.bounds=}")
|
12
|
+
# for name, reg in s.regions():
|
13
|
+
# print(name)
|
14
|
+
# print(f"\t{reg.shape=} {reg.volume=} {reg.block_count=}")
|
15
|
+
# print(f"\t{reg.origin=!s} {reg.limit=!s}")
|
16
|
+
# print(f"\t{reg.start=!s} {reg.end=!s}")
|
17
|
+
# print(f"\t{reg.lower=!s} {reg.upper=!s} {reg.size=}")
|
18
|
+
# # print(f"\t{reg[..., 1, 0]}")
|
19
|
+
# # print(f"\t{reg[:][1][0]}")
|
20
|
+
# # print(f"\t{reg[BlockPosition(0, 1, 0)]}")
|
21
|
+
# # reg[1,1,1] = BlockState.from_string("minecraft:stone")
|
22
|
+
# # print("lol: ", reg[reg.end])
|
23
|
+
# # reg[0,:,0] = BlockState("minecraft:obsidian")
|
24
|
+
# # reg[0,:,0] = [dirt, stone, dirt]
|
25
|
+
# # print(reg[...,0])
|
26
|
+
# # print(reg[np.array([BlockPosition(0, 0, 0), BlockPosition(1, 1, 1)])])
|
27
|
+
# # print(f"\t{reg[:]}")
|
28
|
+
# # for pos, state in reg.blocks(exclude_air=True):
|
29
|
+
# # print(pos, state)
|
30
|
+
# # for pos, state in reg.blocks((BlockState("oak_log", axis="x"), BlockState("spruce_log", axis="z")), ignore_props=True):
|
31
|
+
# # reg[...,-1] = stone
|
32
|
+
# for pos, state in reg.blocks(exclude=BlockState("air")):
|
33
|
+
# print(f"\t{pos} {reg._to_internal(pos)}: {state}")
|
34
|
+
# for pos, state in reg.blocks(include=BlockState("lime_wool")):
|
35
|
+
# reg[pos] = BlockState("minecraft:blue_wool")
|
36
|
+
# for pos, state in reg.blocks(include=BlockState("tripwire"), ignore_props=True):
|
37
|
+
# reg[pos] = BlockState("minecraft:glass")
|
38
|
+
# # print(BlockState("oak_log", axis="x") in reg)
|
39
|
+
# # print(BlockPosition(1, 1, 0) in reg)
|
40
|
+
# # print(ResourceLocation("birch_log") in reg)
|
41
|
+
# # print(reg[0,:,2])
|
42
|
+
# s.save("/mnt/d/minecraft/schematics/Litematica/test/aaa.litematic")
|
43
|
+
|
44
|
+
air = BlockState("air")
|
45
|
+
stone = BlockState("stone")
|
46
|
+
dirt = BlockState("dirt")
|
47
|
+
grass = BlockState("grass_block")
|
48
|
+
cobble = BlockState("mossy_cobblestone")
|
49
|
+
snow = BlockState("snow_block")
|
50
|
+
pumpkin = BlockState("carved_pumpkin", facing="west")
|
51
|
+
|
52
|
+
ground = Region(size=Size3D(16, 9, 16), origin=BlockPosition(0, 0, 0))
|
53
|
+
ground[:,:5,:] = stone
|
54
|
+
ground[:,5:8,:] = dirt
|
55
|
+
ground[:,8:,:] = grass
|
56
|
+
|
57
|
+
boulder = Region(size=(4, 4, 4), origin=ground.origin+[6, ground.height, 6])
|
58
|
+
boulder[:] = cobble
|
59
|
+
|
60
|
+
# snow_man = Region(size=(1, 3, 1), origin=boulder.origin+[1, boulder.height, 1])
|
61
|
+
snow_man = Region(size=(-1, -3, -1), origin=boulder.origin+[1, boulder.height+2, 1])
|
62
|
+
snow_man[:] = snow
|
63
|
+
snow_man[0,snow_man.upper.y,0] = pumpkin
|
64
|
+
|
65
|
+
schem = Schematic(name="scene", author="Boscawinks", description="A simple scene")
|
66
|
+
schem.add_region("ground", ground)
|
67
|
+
schem.add_region("boulder", boulder)
|
68
|
+
schem.add_region("snow_man", snow_man)
|
69
|
+
schem.save(f"/mnt/d/minecraft/schematics/Litematica/test/{schem.name}.litematic")
|
70
|
+
|
71
|
+
print(snow_man == snow)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pylitematic
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.3
|
4
4
|
Summary: Load, modify, and save Litematica schematics
|
5
5
|
Author-email: Boscawinks <bosca.winks@gmx.de>
|
6
6
|
License: GPL-3.0-only
|
@@ -13,6 +13,9 @@ Description-Content-Type: text/markdown
|
|
13
13
|
Requires-Dist: bitpacking>=0.1.0
|
14
14
|
Requires-Dist: nbtlib>=2.0.4
|
15
15
|
Requires-Dist: numpy>=2.2.6
|
16
|
+
Requires-Dist: twos>=0.0.1
|
17
|
+
Provides-Extra: dev
|
18
|
+
Requires-Dist: pytest; extra == "dev"
|
16
19
|
|
17
20
|
# pylitematic
|
18
21
|
|
@@ -6,7 +6,6 @@ from pylitematic.block_property import (
|
|
6
6
|
BooleanValue,
|
7
7
|
EnumValue,
|
8
8
|
IntegerValue,
|
9
|
-
Property,
|
10
9
|
PropertyValue,
|
11
10
|
)
|
12
11
|
|
@@ -50,44 +49,3 @@ def test_value():
|
|
50
49
|
|
51
50
|
with pytest.raises(TypeError):
|
52
51
|
PropertyValue.value_factory(value=3.14)
|
53
|
-
|
54
|
-
|
55
|
-
def test_property():
|
56
|
-
# TODO
|
57
|
-
# * test sorting of properties
|
58
|
-
|
59
|
-
def check_prop(
|
60
|
-
prop: Property,
|
61
|
-
name: str,
|
62
|
-
target: Any,
|
63
|
-
string: str,
|
64
|
-
nbt: tuple[str, String],
|
65
|
-
) -> None:
|
66
|
-
assert prop.name == name
|
67
|
-
assert prop.value == target
|
68
|
-
prop.value = target
|
69
|
-
assert str(prop) == string
|
70
|
-
assert prop.to_string() == string
|
71
|
-
assert prop.to_nbt() == nbt
|
72
|
-
|
73
|
-
proto_props = [
|
74
|
-
("enabled", True, "true", String("true"), "enabled=true"),
|
75
|
-
("facing", "north", "north", String("north"), "facing=north"),
|
76
|
-
("age", 42, "42", String("42"), "age=42"),
|
77
|
-
]
|
78
|
-
|
79
|
-
for name, target, string, nbt, prop_string in proto_props:
|
80
|
-
prop = Property(name=name, value=target)
|
81
|
-
check_prop(prop, name, target, prop_string, (name, nbt))
|
82
|
-
prop = Property.from_string(string=name, value=string)
|
83
|
-
check_prop(prop, name, target, prop_string, (name, nbt))
|
84
|
-
prop = Property.from_string(string=prop_string)
|
85
|
-
check_prop(prop, name, target, prop_string, (name, nbt))
|
86
|
-
prop = Property.from_nbt(name=name, nbt=nbt)
|
87
|
-
check_prop(prop, name, target, prop_string, (name, nbt))
|
88
|
-
|
89
|
-
with pytest.raises(TypeError):
|
90
|
-
Property(name="float", value=3.14)
|
91
|
-
|
92
|
-
with pytest.raises(ValueError):
|
93
|
-
Property(name="Uppercase", value=1)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
from pylitematic import BlockPosition, BlockState, Region, Schematic, Size3D
|
2
|
+
|
3
|
+
|
4
|
+
def test_schematic(tmp_path):
|
5
|
+
air = BlockState("air")
|
6
|
+
stone = BlockState("stone")
|
7
|
+
dirt = BlockState("dirt")
|
8
|
+
grass = BlockState("grass_block")
|
9
|
+
cobble = BlockState("mossy_cobblestone")
|
10
|
+
snow = BlockState("snow_block")
|
11
|
+
pumpkin = BlockState("carved_pumpkin", facing="west")
|
12
|
+
|
13
|
+
ground = Region(size=Size3D(16, 9, 16))
|
14
|
+
ground[:,:5,:] = stone
|
15
|
+
ground[:,5:8,:] = dirt
|
16
|
+
ground[:,8:,:] = grass
|
17
|
+
|
18
|
+
boulder = Region(
|
19
|
+
size=(4, 4, 4), origin=ground.origin+[6, ground.height, 6])
|
20
|
+
for pos, block in boulder.blocks():
|
21
|
+
if block == air:
|
22
|
+
boulder[pos] = cobble
|
23
|
+
# ^ since region is empty, this is equivalent to boulder[:] = cobble
|
24
|
+
|
25
|
+
snow_man = Region(
|
26
|
+
size=(1, 3, 1), origin=boulder.origin+[1, boulder.height, 1])
|
27
|
+
snow_man[:] = snow
|
28
|
+
snow_man[BlockPosition(0, 2, 0)] = pumpkin
|
29
|
+
|
30
|
+
schem = Schematic(
|
31
|
+
name="scene", author="Boscawinks", description="A simple scene")
|
32
|
+
schem.add_region("ground", ground)
|
33
|
+
schem.add_region("boulder", boulder)
|
34
|
+
schem.add_region("snow_man", snow_man)
|
35
|
+
|
36
|
+
save_path = tmp_path / f"{schem.name}.litematic"
|
37
|
+
schem.save(save_path)
|
38
|
+
|
39
|
+
copy = schem.load(save_path)
|
40
|
+
assert copy.to_nbt() == schem.to_nbt()
|
@@ -1,39 +0,0 @@
|
|
1
|
-
import numpy as np
|
2
|
-
import pathlib
|
3
|
-
from pylitematic import BlockPosition, BlockState, ResourceLocation, Schematic
|
4
|
-
|
5
|
-
path = pathlib.Path("/mnt/d/minecraft/schematics/Litematica/test/subs.litematic")
|
6
|
-
path = pathlib.Path("/mnt/d/minecraft/schematics/Litematica/test/creeper_test.litematic")
|
7
|
-
path = pathlib.Path("/mnt/d/minecraft/schematics/Litematica/test/regions.litematic")
|
8
|
-
stone = BlockState.from_string("minecraft:stone")
|
9
|
-
dirt = BlockState.from_string("minecraft:dirt")
|
10
|
-
s = Schematic.load(path)
|
11
|
-
print(f"{s.volume=} {s.size=} {s.bounds=}")
|
12
|
-
for name, reg in s.regions():
|
13
|
-
print(name)
|
14
|
-
print(f"\t{reg.shape=} {reg.volume=} {reg.block_count=}")
|
15
|
-
print(f"\t{reg.origin=!s} {reg.limit=!s}")
|
16
|
-
print(f"\t{reg.start=!s} {reg.end=!s}")
|
17
|
-
print(f"\t{reg.lower=!s} {reg.upper=!s} {reg.size=}")
|
18
|
-
# print(f"\t{reg[..., 1, 0]}")
|
19
|
-
# print(f"\t{reg[:][1][0]}")
|
20
|
-
# print(f"\t{reg[BlockPosition(0, 1, 0)]}")
|
21
|
-
# reg[1,1,1] = BlockState.from_string("minecraft:stone")
|
22
|
-
# print("lol: ", reg[reg.end])
|
23
|
-
reg[0,:,0] = BlockState("minecraft:obsidian")
|
24
|
-
reg[0,:,0] = [dirt, stone, dirt]
|
25
|
-
# print(reg[...,0])
|
26
|
-
# print(reg[np.array([BlockPosition(0, 0, 0), BlockPosition(1, 1, 1)])])
|
27
|
-
# print(f"\t{reg[:]}")
|
28
|
-
# for pos, state in reg.blocks(exclude_air=True):
|
29
|
-
# print(pos, state)
|
30
|
-
# for pos, state in reg.blocks((BlockState("oak_log", axis="x"), BlockState("spruce_log", axis="z")), ignore_props=True):
|
31
|
-
for pos, state in reg.blocks(exclude=BlockState("air")):
|
32
|
-
print(pos, reg._to_internal(pos), state)
|
33
|
-
for pos, state in reg.blocks(include=BlockState("air")):
|
34
|
-
reg[pos] = BlockState("minecraft:netherrack")
|
35
|
-
print(BlockState("oak_log", axis="x") in reg)
|
36
|
-
print(BlockPosition(1, 1, 0) in reg)
|
37
|
-
print(ResourceLocation("birch_log") in reg)
|
38
|
-
# print(reg[0,:,2])
|
39
|
-
s.save("/mnt/d/minecraft/schematics/Litematica/test/aaa.litematic")
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|