forked from avdstaaij/gdpc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
4_world_slice.py
executable file
·111 lines (83 loc) · 3.84 KB
/
4_world_slice.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#!/usr/bin/env python3
"""
Load and use a world slice.
"""
import sys
import numpy as np
from gdpc import __url__, Editor, Block
from gdpc.exceptions import InterfaceConnectionError, BuildAreaNotSetError
from gdpc.vector_tools import addY
# Create an editor object.
# The Editor class provides a high-level interface to interact with the Minecraft world.
editor = Editor()
# Check if the editor can connect to the GDMC HTTP interface.
try:
editor.checkConnection()
except InterfaceConnectionError:
print(
f"Error: Could not connect to the GDMC HTTP interface at {editor.host}!\n"
"To use GDPC, you need to use a \"backend\" that provides the GDMC HTTP interface.\n"
"For example, by running Minecraft with the GDMC HTTP mod installed.\n"
f"See {__url__}/README.md for more information."
)
sys.exit(1)
# Get the build area.
try:
buildArea = editor.getBuildArea()
except BuildAreaNotSetError:
print(
"Error: failed to get the build area!\n"
"Make sure to set the build area with the /setbuildarea command in-game.\n"
"For example: /setbuildarea ~0 0 ~0 ~64 200 ~64"
)
sys.exit(1)
# Get a world slice.
#
# A world slice contains all kinds of information about a slice of the world, like blocks, biomes
# and heightmaps. All of its data is extracted directly from Minecraft's chunk format:
# https://minecraft.fandom.com/wiki/Chunk_format. World slices take a while to load, but accessing
# data from them is very fast.
#
# To get a world slice, you need to specify a rectangular XZ-area using a Rect object (the 2D
# equivalent of a Box). Box.toRect() is a convenience function that converts a Box to its XZ-rect.
#
# Note that a world slice is a "snapshot" of the world: any changes you make to the world after
# loading a world slice are not reflected by it.
print("Loading world slice...")
buildRect = buildArea.toRect()
worldSlice = editor.loadWorldSlice(buildRect)
print("World slice loaded!")
# Most of worldSlice's functions have a "local" and a "global" variant. The local variant expects
# coordinates relatve to the rect with which it was constructed, while the global variant expects
# absolute coorndates.
vec = addY(buildRect.center, 30)
print(f"Block at {vec}: {worldSlice.getBlock(vec - buildArea.offset)}")
print(f"Block at {vec}: {worldSlice.getBlockGlobal(vec)}")
# Heightmaps are an easy way to get the uppermost block at any coordinate. They are very useful for
# writing terrain-adaptive generator algorithms.
# World slices provide access to the heightmaps that Minecraft stores in its chunk format, so you
# get their computation for free.
#
# By default, world slices load the following four heightmaps:
# - "WORLD_SURFACE": The top non-air blocks.
# - "MOTION_BLOCKING": The top blocks with a hitbox or fluid.
# - "MOTION_BLOCKING_NO_LEAVES": Like MOTION_BLOCKING, but ignoring leaves.
# - "OCEAN_FLOOR": The top non-air solid blocks.
#
# Heightmaps are loaded into 2D numpy arrays of Y coordinates.
print(f"Available heightmaps: {worldSlice.heightmaps.keys()}")
heightmap = worldSlice.heightmaps["MOTION_BLOCKING_NO_LEAVES"]
print(f"Heightmap shape: {heightmap.shape}")
localCenter = buildRect.size // 2
# When indexing a numpy array with a vector, you need to convert to a tuple first.
centerHeight = heightmap[tuple(localCenter)]
centerTopBlock = worldSlice.getBlock(addY(localCenter, centerHeight - 1))
print(f"Top block at the center of the build area: {centerTopBlock}")
print(f"Average height: {int(np.mean(heightmap))}")
# Place walls of stone bricks on the perimeter of the build area, following the curvature of the
# terrain.
print("Placing walls...")
for point in buildRect.outline:
height = heightmap[tuple(point - buildRect.offset)]
for y in range(height, height + 5):
editor.placeBlock(addY(point, y), Block("stone_bricks"))