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.
@@ -50,8 +50,8 @@ class ResourceLocation:
50
50
  def to_string(self) -> str:
51
51
  return str(self)
52
52
 
53
- @staticmethod
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 ResourceLocation(path=path, namespace=namespace)
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
- @staticmethod
68
- def from_nbt(nbt: nbtlib.String) -> ResourceLocation:
69
- return ResourceLocation.from_string(str(nbt))
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.global_bounds
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(reg.block_count for reg in self._regions.values())
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
- @staticmethod
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 Schematic.from_nbt(nbt)
174
+ return cls.from_nbt(nbt)
167
175
 
168
176
  def to_nbt(self) -> nbtlib.Compound:
169
- # self._update()
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.compact_palette()
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
- @staticmethod
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 = Schematic(
261
+ schem = cls(
251
262
  name=name,
252
263
  author=author,
253
264
  description=desc,
pylitematic/test.py CHANGED
@@ -1,71 +1,79 @@
1
- import numpy as np
2
- import pathlib
3
- from pylitematic import BlockPosition, BlockId, BlockState, Region, Schematic, Size3D
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
- cobble = BlockState("mossy_cobblestone")
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), origin=BlockPosition(0, 0, 0))
53
- ground[:,:5,:] = stone
54
- ground[:,5:8,:] = dirt
55
- ground[:,8:,:] = grass
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[:] = cobble
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
- # 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
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
- schem = Schematic(name="scene", author="Boscawinks", description="A simple scene")
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.save(f"/mnt/d/minecraft/schematics/Litematica/test/{schem.name}.litematic")
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
- print(snow_man == snow)
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
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,,