pylitematic 0.0.3__py3-none-any.whl → 0.0.5__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.
- pylitematic/__init__.py +3 -4
- pylitematic/block_palette.py +129 -0
- pylitematic/block_property.py +13 -13
- pylitematic/block_state.py +34 -12
- pylitematic/geometry.py +67 -103
- pylitematic/property_cache.py +52 -0
- pylitematic/region.py +517 -218
- pylitematic/resource_location.py +6 -10
- pylitematic/schematic.py +24 -13
- pylitematic/test.py +62 -54
- {pylitematic-0.0.3.dist-info → pylitematic-0.0.5.dist-info}/METADATA +4 -1
- pylitematic-0.0.5.dist-info/RECORD +14 -0
- pylitematic-0.0.3.dist-info/RECORD +0 -12
- {pylitematic-0.0.3.dist-info → pylitematic-0.0.5.dist-info}/WHEEL +0 -0
- {pylitematic-0.0.3.dist-info → pylitematic-0.0.5.dist-info}/top_level.txt +0 -0
pylitematic/resource_location.py
CHANGED
@@ -50,8 +50,8 @@ class ResourceLocation:
|
|
50
50
|
def to_string(self) -> str:
|
51
51
|
return str(self)
|
52
52
|
|
53
|
-
@
|
54
|
-
def from_string(string: str) -> ResourceLocation:
|
53
|
+
@classmethod
|
54
|
+
def from_string(cls, string: str) -> ResourceLocation:
|
55
55
|
match = LOCATION_PATTERN.fullmatch(string)
|
56
56
|
if not match:
|
57
57
|
raise ValueError(f"Invalid resource location string {string!r}")
|
@@ -59,15 +59,11 @@ class ResourceLocation:
|
|
59
59
|
namespace = match.group("namespace")
|
60
60
|
path = match.group("path")
|
61
61
|
|
62
|
-
return
|
62
|
+
return cls(path=path, namespace=namespace)
|
63
63
|
|
64
64
|
def to_nbt(self) -> nbtlib.String:
|
65
65
|
return nbtlib.String(self)
|
66
66
|
|
67
|
-
@
|
68
|
-
def from_nbt(nbt: nbtlib.String) -> ResourceLocation:
|
69
|
-
return
|
70
|
-
|
71
|
-
|
72
|
-
class BlockId(ResourceLocation):
|
73
|
-
...
|
67
|
+
@classmethod
|
68
|
+
def from_nbt(cls, nbt: nbtlib.String) -> ResourceLocation:
|
69
|
+
return cls.from_string(str(nbt))
|
pylitematic/schematic.py
CHANGED
@@ -9,7 +9,8 @@ import twos
|
|
9
9
|
from typing import Iterator
|
10
10
|
|
11
11
|
from pylitematic.geometry import BlockPosition, Size3D
|
12
|
-
from pylitematic.region import Region
|
12
|
+
from pylitematic.region import AIR, Region
|
13
|
+
from pylitematic.block_state import AIR
|
13
14
|
|
14
15
|
|
15
16
|
DEFAULT_VERSION_MAJOR: int = 7
|
@@ -70,12 +71,11 @@ class Schematic:
|
|
70
71
|
self._created_at = round(time.time() * 1000)
|
71
72
|
self._modified_at = self._created_at
|
72
73
|
|
74
|
+
# TODO: use packaging.version.Version
|
73
75
|
self.version_major = version_major
|
74
76
|
self.version_minor = version_minor
|
75
77
|
self.mc_version = mc_version
|
76
78
|
|
77
|
-
self.modified: bool = True
|
78
|
-
|
79
79
|
def __getitem__(self, key):
|
80
80
|
return self._regions[key]
|
81
81
|
|
@@ -104,10 +104,11 @@ class Schematic:
|
|
104
104
|
|
105
105
|
@property
|
106
106
|
def bounds(self) -> tuple[BlockPosition, BlockPosition]:
|
107
|
+
# TODO: make cached and update on region add / remove
|
107
108
|
lowers = []
|
108
109
|
uppers = []
|
109
110
|
for reg in self._regions.values():
|
110
|
-
lower, upper = reg.
|
111
|
+
lower, upper = reg.world.bounds
|
111
112
|
lowers.append(lower)
|
112
113
|
uppers.append(upper)
|
113
114
|
return (
|
@@ -121,7 +122,8 @@ class Schematic:
|
|
121
122
|
|
122
123
|
@property
|
123
124
|
def blocks(self) -> int:
|
124
|
-
return sum(
|
125
|
+
return sum(
|
126
|
+
reg.volume - reg.count(AIR) for reg in self._regions.values())
|
125
127
|
|
126
128
|
@property
|
127
129
|
def region_count(self) -> int:
|
@@ -142,9 +144,11 @@ class Schematic:
|
|
142
144
|
|
143
145
|
def add_region(self, name: str, region: Region) -> None:
|
144
146
|
self._regions[name] = region
|
147
|
+
# TODO: re-calculate bounding box
|
145
148
|
|
146
149
|
def remove_region(self, name: str) -> Region:
|
147
150
|
return self._regions.pop(name)
|
151
|
+
# TODO: re-calculate bounding box
|
148
152
|
|
149
153
|
@property
|
150
154
|
def created_at(self) -> datetime:
|
@@ -154,19 +158,26 @@ class Schematic:
|
|
154
158
|
def modified_at(self) -> datetime:
|
155
159
|
return datetime.fromtimestamp(int(self._modified_at / 1000))
|
156
160
|
|
161
|
+
def clear(self) -> None:
|
162
|
+
self._regions = {}
|
163
|
+
|
157
164
|
def save(self, path: pathlib.Path | str) -> None:
|
165
|
+
self._modified_at = int(time.time() * 1000)
|
158
166
|
file = nbtlib.File(self.to_nbt())
|
159
167
|
file.save(path, gzipped=True, byteorder="big")
|
160
168
|
|
161
|
-
@
|
162
|
-
def load(path: pathlib.Path | str) -> Schematic:
|
169
|
+
@classmethod
|
170
|
+
def load(cls, path: pathlib.Path | str) -> Schematic:
|
163
171
|
if isinstance(path, str):
|
164
172
|
path = pathlib.Path(path)
|
165
173
|
nbt = nbtlib.File.load(path.expanduser(), True)
|
166
|
-
return
|
174
|
+
return cls.from_nbt(nbt)
|
167
175
|
|
168
176
|
def to_nbt(self) -> nbtlib.Compound:
|
169
|
-
|
177
|
+
if not self.region_count:
|
178
|
+
raise ValueError(
|
179
|
+
f"Schematic {self.name!r} needs at least one region")
|
180
|
+
|
170
181
|
nbt = nbtlib.Compound()
|
171
182
|
|
172
183
|
# meta data
|
@@ -190,7 +201,7 @@ class Schematic:
|
|
190
201
|
# regions
|
191
202
|
regions = nbtlib.Compound()
|
192
203
|
for name, region in self.regions():
|
193
|
-
region.
|
204
|
+
region.reduce_palette()
|
194
205
|
regions[name] = region.to_nbt()
|
195
206
|
nbt["Regions"] = regions
|
196
207
|
|
@@ -202,8 +213,8 @@ class Schematic:
|
|
202
213
|
|
203
214
|
return nbt
|
204
215
|
|
205
|
-
@
|
206
|
-
def from_nbt(nbt: nbtlib.Compound) -> Schematic:
|
216
|
+
@classmethod
|
217
|
+
def from_nbt(cls, nbt: nbtlib.Compound) -> Schematic:
|
207
218
|
# meta data
|
208
219
|
try:
|
209
220
|
meta = nbt["Metadata"]
|
@@ -247,7 +258,7 @@ class Schematic:
|
|
247
258
|
|
248
259
|
mc_version = nbt.get("MinecraftDataVersion")
|
249
260
|
|
250
|
-
schem =
|
261
|
+
schem = cls(
|
251
262
|
name=name,
|
252
263
|
author=author,
|
253
264
|
description=desc,
|
pylitematic/test.py
CHANGED
@@ -1,71 +1,79 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
from pylitematic import (
|
2
|
+
BlockPosition,
|
3
|
+
BlockId,
|
4
|
+
BlockState,
|
5
|
+
Region,
|
6
|
+
Schematic,
|
7
|
+
Size3D,
|
8
|
+
)
|
4
9
|
|
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
10
|
|
44
11
|
air = BlockState("air")
|
45
12
|
stone = BlockState("stone")
|
46
13
|
dirt = BlockState("dirt")
|
47
14
|
grass = BlockState("grass_block")
|
48
|
-
|
15
|
+
water = BlockState("water")
|
16
|
+
lava = BlockState("lava")
|
17
|
+
sand = BlockState("sand")
|
18
|
+
|
19
|
+
cobble = BlockState("cobblestone")
|
20
|
+
mossy_cobble = BlockState("mossy_cobblestone")
|
21
|
+
|
49
22
|
snow = BlockState("snow_block")
|
23
|
+
ice = BlockState("ice")
|
50
24
|
pumpkin = BlockState("carved_pumpkin", facing="west")
|
25
|
+
jack = BlockState("jack_o_lantern", facing="west")
|
51
26
|
|
52
|
-
ground = Region(size=Size3D(16, 9, 16)
|
53
|
-
ground[:,:5,:] = stone
|
54
|
-
ground[:,5:8,:] = dirt
|
55
|
-
ground[
|
27
|
+
ground = Region(size=Size3D(16, 9, 16))
|
28
|
+
ground.local[:,:5,:] = stone # 4 stone layers
|
29
|
+
ground.local[:,5:8,:] = dirt # 3 dirt layers
|
30
|
+
ground[(ground > dirt) & (ground == BlockId("air"))] = grass # grass above exposed dirt
|
31
|
+
ground.numpy[2:5,-1,2:5] = water # small pond
|
32
|
+
ground[ground.relative_to(water, BlockPosition(4, 0, 0))] = lava # lava pool
|
56
33
|
|
57
34
|
boulder = Region(size=(4, 4, 4), origin=ground.origin+[6, ground.height, 6])
|
58
|
-
boulder[:] =
|
35
|
+
boulder[:] = mossy_cobble # fill with mossy cobblestone
|
36
|
+
boulder.numpy[:,-2:,:] = cobble # upper two layers of cobblestone
|
37
|
+
|
38
|
+
snow_man = Region(
|
39
|
+
size=(1, -3, 1), origin=boulder.origin+[2, boulder.upper.y+3, 1])
|
40
|
+
snow_man.set_default_view(snow_man.numpy)
|
41
|
+
snow_man[...] = snow # fill with snow
|
42
|
+
snow_man[0,-1,0] = pumpkin # pumpkin on top
|
59
43
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
snow_man[0,snow_man.upper.y,0] = pumpkin
|
44
|
+
snow_woman = snow_man.copy(origin=snow_man.origin+[-1, 0, 1])
|
45
|
+
# snow_woman[snow_woman == snow] = ice # replace snow with ice
|
46
|
+
snow_woman.where(snow, ice, jack) # replace snow with ice and rest with lanterns
|
64
47
|
|
65
|
-
|
48
|
+
clones = []
|
49
|
+
start = BlockPosition(1, ground.upper.y, ground.upper.z-1)
|
50
|
+
for i in range(5):
|
51
|
+
clones.append(
|
52
|
+
snow_man.copy(origin=start+(i * 3, 3, -i)))
|
53
|
+
|
54
|
+
cuboid = Region(origin=(14, ground.upper.y+3, 4), size=(-2,-3,-4))
|
55
|
+
cuboid[:,-2:0,:] = sand
|
56
|
+
cuboid.where((cuboid < air) & (cuboid == sand), stone)
|
57
|
+
for pos, block in cuboid.numpy.items():
|
58
|
+
print(f"{pos}:\t{block}")
|
59
|
+
|
60
|
+
schem = Schematic(
|
61
|
+
name="a_scene", author="Boscawinks", description="A simple scene")
|
66
62
|
schem.add_region("ground", ground)
|
67
63
|
schem.add_region("boulder", boulder)
|
68
64
|
schem.add_region("snow_man", snow_man)
|
69
|
-
schem.
|
65
|
+
schem.add_region("snow_woman", snow_woman)
|
66
|
+
for i, clone in enumerate(clones):
|
67
|
+
schem.add_region(f"clone_{i+1}", clone)
|
68
|
+
schem.add_region("cuboid", cuboid)
|
69
|
+
schem.save(
|
70
|
+
f"/mnt/d/minecraft/schematics/Litematica/test/{schem.name}.litematic")
|
71
|
+
|
72
|
+
# print(boulder[...,-1])
|
70
73
|
|
71
|
-
|
74
|
+
# from pathlib import Path
|
75
|
+
# path = Path("/mnt/d/minecraft/schematics/Litematica/turtle/turtle_8x8.litematic")
|
76
|
+
# turtle = Schematic.load(path)
|
77
|
+
# for name, reg in turtle.regions():
|
78
|
+
# reg[reg != BlockState("air")] = BlockState("blue_wool")
|
79
|
+
# turtle.save(path.with_suffix(".blue.litematic"))
|
@@ -1,13 +1,16 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pylitematic
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.5
|
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
|
7
7
|
Project-URL: Homepage, https://github.com/boscawinks/pylitematic
|
8
8
|
Project-URL: Repository, https://github.com/boscawinks/pylitematic
|
9
9
|
Project-URL: Issues, https://github.com/boscawinks/pylitematic/issues
|
10
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
10
11
|
Classifier: Programming Language :: Python :: 3
|
12
|
+
Classifier: Operating System :: OS Independent
|
13
|
+
Classifier: Topic :: Utilities
|
11
14
|
Requires-Python: >=3.10.12
|
12
15
|
Description-Content-Type: text/markdown
|
13
16
|
Requires-Dist: bitpacking>=0.1.0
|
@@ -0,0 +1,14 @@
|
|
1
|
+
pylitematic/__init__.py,sha256=KlmeOfpqQ64bSbOTEj2IcLwARfwzg3kTbUtBpRAgpU0,188
|
2
|
+
pylitematic/block_palette.py,sha256=9k1rzLgmoUK2jkuKo4v0nuE37wVFvDF3jja59NqYY_Y,4280
|
3
|
+
pylitematic/block_property.py,sha256=HGxDSngEr-tcwuZPGn6ohHQQ65OL4MaTP9ytDAvuSCw,7714
|
4
|
+
pylitematic/block_state.py,sha256=4CQ5V6dbXuGY3vRp2wXJCqPIV9xIHZbuzJ7JgY7oLA4,4064
|
5
|
+
pylitematic/geometry.py,sha256=gPsvjnp6tSpQ1uA9hbsKO4EdjdMuxV2wm6KwxFaedSQ,4211
|
6
|
+
pylitematic/property_cache.py,sha256=BFY-fh6xDBpJTZ2QBndeJKYVAObVwMLA4TYfXiiCBcA,1358
|
7
|
+
pylitematic/region.py,sha256=_08u-lbR3MxBQIngRZq-72lS9X73ynUR1Vpu0J_jtN4,20468
|
8
|
+
pylitematic/resource_location.py,sha256=VgTPayrTc0xJNM6smG9vseajFGVccL2wwtLKUyG9qYo,2125
|
9
|
+
pylitematic/schematic.py,sha256=Nm7gV6e0ayYSJK8lhcjnm5_c5KGC-MSRTzpTbqfvKcA,8367
|
10
|
+
pylitematic/test.py,sha256=9OQzQONGjs6TqyinwdSk6o9yysXe2EKSiOeHM45hGIM,2663
|
11
|
+
pylitematic-0.0.5.dist-info/METADATA,sha256=dOGSClod9zp_e8LFQiNHDKM8lzZNE7QIxNZCXyoaT6I,1159
|
12
|
+
pylitematic-0.0.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
13
|
+
pylitematic-0.0.5.dist-info/top_level.txt,sha256=sYUxm6O7Dh5TzuP-kPFe2FHJWUuwHFO69vN2VBiEG4A,12
|
14
|
+
pylitematic-0.0.5.dist-info/RECORD,,
|
@@ -1,12 +0,0 @@
|
|
1
|
-
pylitematic/__init__.py,sha256=-yLTtTzD7kRpeKMbUTYNy67zkFkxUP8V1DnPXhS3ZFY,202
|
2
|
-
pylitematic/block_property.py,sha256=4wwsQrxyNGDcrktpd4tD0Vzv-a1OxWYMj0H05GDiT-I,7739
|
3
|
-
pylitematic/block_state.py,sha256=tMRoY6jPtt2M85DR9U9dnQh2Ukp6pP_65xXzMIFeUEo,3546
|
4
|
-
pylitematic/geometry.py,sha256=kt2buIJovAymAtaGQYmmWj7ypOt1xI8s--LHkbTPUlM,5399
|
5
|
-
pylitematic/region.py,sha256=0bUcK9oVyhNDzfxTjZmVLwMddrIdxvJX-cXkWL8BRSE,11097
|
6
|
-
pylitematic/resource_location.py,sha256=gHVE1RTRHtc8DWSlXa0WUom68tRlJy8LU5dOzuONV98,2186
|
7
|
-
pylitematic/schematic.py,sha256=SLz4NQNBJkLXV9Oj0fVOE-daP4MRsdYAnnl1vKzJv_Q,7928
|
8
|
-
pylitematic/test.py,sha256=9uch5L58o9QVJI50jUcbuulEswYzDgtYnRiIP991jFo,3162
|
9
|
-
pylitematic-0.0.3.dist-info/METADATA,sha256=ES-4UDWi6DQU6fCSNrgf5FLMDX_jh1g7k6kx9fmiX70,1033
|
10
|
-
pylitematic-0.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
11
|
-
pylitematic-0.0.3.dist-info/top_level.txt,sha256=sYUxm6O7Dh5TzuP-kPFe2FHJWUuwHFO69vN2VBiEG4A,12
|
12
|
-
pylitematic-0.0.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|