1# This file allows you to programmatically create blocks in Craft.
2# Please use this wisely. Test on your own server first. Do not abuse it.
3
4import requests
5import socket
6import sqlite3
7import sys
8
9DEFAULT_HOST = '127.0.0.1'
10DEFAULT_PORT = 4080
11
12EMPTY = 0
13GRASS = 1
14SAND = 2
15STONE = 3
16BRICK = 4
17WOOD = 5
18CEMENT = 6
19DIRT = 7
20PLANK = 8
21SNOW = 9
22GLASS = 10
23COBBLE = 11
24LIGHT_STONE = 12
25DARK_STONE = 13
26CHEST = 14
27LEAVES = 15
28CLOUD = 16
29TALL_GRASS = 17
30YELLOW_FLOWER = 18
31RED_FLOWER = 19
32PURPLE_FLOWER = 20
33SUN_FLOWER = 21
34WHITE_FLOWER = 22
35BLUE_FLOWER = 23
36
37OFFSETS = [
38    (-0.5, -0.5, -0.5),
39    (-0.5, -0.5, 0.5),
40    (-0.5, 0.5, -0.5),
41    (-0.5, 0.5, 0.5),
42    (0.5, -0.5, -0.5),
43    (0.5, -0.5, 0.5),
44    (0.5, 0.5, -0.5),
45    (0.5, 0.5, 0.5),
46]
47
48def sphere(cx, cy, cz, r, fill=False, fx=False, fy=False, fz=False):
49    result = set()
50    for x in range(cx - r, cx + r + 1):
51        if fx and x != cx:
52            continue
53        for y in range(cy - r, cy + r + 1):
54            # if y < cy:
55            #     continue # top hemisphere only
56            if fy and y != cy:
57                continue
58            for z in range(cz - r, cz + r + 1):
59                if fz and z != cz:
60                    continue
61                inside = False
62                outside = fill
63                for dx, dy, dz in OFFSETS:
64                    ox, oy, oz = x + dx, y + dy, z + dz
65                    d2 = (ox - cx) ** 2 + (oy - cy) ** 2 + (oz - cz) ** 2
66                    d = d2 ** 0.5
67                    if d < r:
68                        inside = True
69                    else:
70                        outside = True
71                if inside and outside:
72                    result.add((x, y, z))
73    return result
74
75def circle_x(x, y, z, r, fill=False):
76    return sphere(x, y, z, r, fill, fx=True)
77
78def circle_y(x, y, z, r, fill=False):
79    return sphere(x, y, z, r, fill, fy=True)
80
81def circle_z(x, y, z, r, fill=False):
82    return sphere(x, y, z, r, fill, fz=True)
83
84def cylinder_x(x1, x2, y, z, r, fill=False):
85    x1, x2 = sorted((x1, x2))
86    result = set()
87    for x in range(x1, x2 + 1):
88        result |= circle_x(x, y, z, r, fill)
89    return result
90
91def cylinder_y(x, y1, y2, z, r, fill=False):
92    y1, y2 = sorted((y1, y2))
93    result = set()
94    for y in range(y1, y2 + 1):
95        result |= circle_y(x, y, z, r, fill)
96    return result
97
98def cylinder_z(x, y, z1, z2, r, fill=False):
99    z1, z2 = sorted((z1, z2))
100    result = set()
101    for z in range(z1, z2 + 1):
102        result |= circle_z(x, y, z, r, fill)
103    return result
104
105def cuboid(x1, x2, y1, y2, z1, z2, fill=True):
106    x1, x2 = sorted((x1, x2))
107    y1, y2 = sorted((y1, y2))
108    z1, z2 = sorted((z1, z2))
109    result = set()
110    a = (x1 == x2) + (y1 == y2) + (z1 == z2)
111    for x in range(x1, x2 + 1):
112        for y in range(y1, y2 + 1):
113            for z in range(z1, z2 + 1):
114                n = 0
115                n += x in (x1, x2)
116                n += y in (y1, y2)
117                n += z in (z1, z2)
118                if not fill and n <= a:
119                    continue
120                result.add((x, y, z))
121    return result
122
123def pyramid(x1, x2, y, z1, z2, fill=False):
124    x1, x2 = sorted((x1, x2))
125    z1, z2 = sorted((z1, z2))
126    result = set()
127    while x2 >= x1 and z2 >= z2:
128        result |= cuboid(x1, x2, y, y, z1, z2, fill)
129        y, x1, x2, z1, z2 = y + 1, x1 + 1, x2 - 1, z1 + 1, z2 - 1
130    return result
131
132def get_identity():
133    query = (
134        'select username, token from identity_token where selected = 1;'
135    )
136    conn = sqlite3.connect('auth.db')
137    rows = conn.execute(query)
138    for row in rows:
139        return row
140    raise Exception('No identities found.')
141
142class Client(object):
143    def __init__(self, host, port):
144        self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
145        self.conn.connect((host, port))
146        self.authenticate()
147    def authenticate(self):
148        username, identity_token = get_identity()
149        url = 'https://craft.michaelfogleman.com/api/1/identity'
150        payload = {
151            'username': username,
152            'identity_token': identity_token,
153        }
154        response = requests.post(url, data=payload)
155        if response.status_code == 200 and response.text.isalnum():
156            access_token = response.text
157            self.conn.sendall('A,%s,%s\n' % (username, access_token))
158        else:
159            raise Exception('Failed to authenticate.')
160    def set_block(self, x, y, z, w):
161        self.conn.sendall('B,%d,%d,%d,%d\n' % (x, y, z, w))
162    def set_blocks(self, blocks, w):
163        key = lambda block: (block[1], block[0], block[2])
164        for x, y, z in sorted(blocks, key=key):
165            self.set_block(x, y, z, w)
166    def bitmap(self, sx, sy, sz, d1, d2, data, lookup):
167        x, y, z = sx, sy, sz
168        dx1, dy1, dz1 = d1
169        dx2, dy2, dz2 = d2
170        for row in data:
171            x = sx if dx1 else x
172            y = sy if dy1 else y
173            z = sz if dz1 else z
174            for c in row:
175                w = lookup.get(c)
176                if w is not None:
177                    self.set_block(x, y, z, w)
178                x, y, z = x + dx1, y + dy1, z + dz1
179            x, y, z = x + dx2, y + dy2, z + dz2
180
181def get_client():
182    default_args = [DEFAULT_HOST, DEFAULT_PORT]
183    args = sys.argv[1:] + [None] * len(default_args)
184    host, port = [a or b for a, b in zip(args, default_args)]
185    client = Client(host, int(port))
186    return client
187
188def main():
189    client = get_client()
190    set_block = client.set_block
191    set_blocks = client.set_blocks
192    # set_blocks(circle_y(0, 32, 0, 16, True), STONE)
193    # set_blocks(circle_y(0, 33, 0, 16), BRICK)
194    # set_blocks(cuboid(-1, 1, 1, 31, -1, 1), CEMENT)
195    # set_blocks(cuboid(-1024, 1024, 32, 32, -3, 3), STONE)
196    # set_blocks(cuboid(-3, 3, 32, 32, -1024, 1024), STONE)
197    # set_blocks(cuboid(-1024, 1024, 33, 33, -3, -3), BRICK)
198    # set_blocks(cuboid(-1024, 1024, 33, 33, 3, 3), BRICK)
199    # set_blocks(cuboid(-3, -3, 33, 33, -1024, 1024), BRICK)
200    # set_blocks(cuboid(3, 3, 33, 33, -1024, 1024), BRICK)
201    # set_blocks(sphere(0, 32, 0, 16), GLASS)
202    # for y in range(1, 32):
203    #     set_blocks(circle_y(0, y, 0, 4, True), CEMENT)
204    # set_blocks(circle_x(16, 33, 0, 3), BRICK)
205    # set_blocks(circle_x(-16, 33, 0, 3), BRICK)
206    # set_blocks(circle_z(0, 33, 16, 3), BRICK)
207    # set_blocks(circle_z(0, 33, -16, 3), BRICK)
208    # for x in range(0, 1024, 32):
209    #     set_blocks(cuboid(x - 1, x + 1, 31, 32, -1, 1), CEMENT)
210    #     set_blocks(cuboid(-x - 1, -x + 1, 31, 32, -1, 1), CEMENT)
211    #     set_blocks(cuboid(x, x, 1, 32, -1, 1), CEMENT)
212    #     set_blocks(cuboid(-x, -x, 1, 32, -1, 1), CEMENT)
213    # for z in range(0, 1024, 32):
214    #     set_blocks(cuboid(-1, 1, 31, 32, z - 1, z + 1), CEMENT)
215    #     set_blocks(cuboid(-1, 1, 31, 32, -z - 1, -z + 1), CEMENT)
216    #     set_blocks(cuboid(-1, 1, 1, 32, z, z), CEMENT)
217    #     set_blocks(cuboid(-1, 1, 1, 32, -z, -z), CEMENT)
218    # for x in range(0, 1024, 8):
219    #     set_block(x, 32, 0, CEMENT)
220    #     set_block(-x, 32, 0, CEMENT)
221    # for z in range(0, 1024, 8):
222    #     set_block(0, 32, z, CEMENT)
223    #     set_block(0, 32, -z, CEMENT)
224    # set_blocks(pyramid(32, 32+64-1, 12, 32, 32+64-1), COBBLE)
225    # outer = circle_y(0, 11, 0, 176 + 3, True)
226    # inner = circle_y(0, 11, 0, 176 - 3, True)
227    # set_blocks(outer - inner, STONE)
228    # a = sphere(-32, 48, -32, 24, True)
229    # b = sphere(-24, 40, -24, 24, True)
230    # set_blocks(a - b, PLANK)
231    # set_blocks(cylinder_x(-64, 64, 32, 0, 8), STONE)
232    # data = [
233    #     '...............................',
234    #     '..xxx..xxxx...xxx..xxxxx.xxxxx.',
235    #     '.x...x.x...x.x...x.x.......x...',
236    #     '.x.....xxxx..xxxxx.xxx.....x...',
237    #     '.x...x.x..x..x...x.x.......x...',
238    #     '..xxx..x...x.x...x.x.......x...',
239    #     '...............................',
240    # ]
241    # lookup = {
242    #     'x': STONE,
243    #     '.': PLANK,
244    # }
245    # client.bitmap(0, 32, 32, (1, 0, 0), (0, -1, 0), data, lookup)
246
247if __name__ == '__main__':
248    main()
249