text: # This file allows you to programmatically create blocks in Craft.
  # Please use this wisely. Test on your own server first. Do not abuse it.
  
  import requests
  import socket
  import sqlite3
  import sys
  
  DEFAULT_HOST = '127.0.0.1'
  DEFAULT_PORT = 4080
  
  EMPTY = 0
  GRASS = 1
  SAND = 2
  STONE = 3
  BRICK = 4
  WOOD = 5
  CEMENT = 6
  DIRT = 7
  PLANK = 8
  SNOW = 9
  GLASS = 10
  COBBLE = 11
  LIGHT_STONE = 12
  DARK_STONE = 13
  CHEST = 14
  LEAVES = 15
  CLOUD = 16
  TALL_GRASS = 17
  YELLOW_FLOWER = 18
  RED_FLOWER = 19
  PURPLE_FLOWER = 20
  SUN_FLOWER = 21
  WHITE_FLOWER = 22
  BLUE_FLOWER = 23
  
  OFFSETS = [
      (-0.5, -0.5, -0.5),
      (-0.5, -0.5, 0.5),
      (-0.5, 0.5, -0.5),
      (-0.5, 0.5, 0.5),
      (0.5, -0.5, -0.5),
      (0.5, -0.5, 0.5),
      (0.5, 0.5, -0.5),
      (0.5, 0.5, 0.5),
  ]
  
  def sphere(cx, cy, cz, r, fill=False, fx=False, fy=False, fz=False):
      result = set()
      for x in range(cx - r, cx + r + 1):
          if fx and x != cx:
              continue
          for y in range(cy - r, cy + r + 1):
              # if y < cy:
              #     continue # top hemisphere only
              if fy and y != cy:
                  continue
              for z in range(cz - r, cz + r + 1):
                  if fz and z != cz:
                      continue
                  inside = False
                  outside = fill
                  for dx, dy, dz in OFFSETS:
                      ox, oy, oz = x + dx, y + dy, z + dz
                      d2 = (ox - cx) ** 2 + (oy - cy) ** 2 + (oz - cz) ** 2
                      d = d2 ** 0.5
                      if d < r:
                          inside = True
                      else:
                          outside = True
                  if inside and outside:
                      result.add((x, y, z))
      return result
  
  def circle_x(x, y, z, r, fill=False):
      return sphere(x, y, z, r, fill, fx=True)
  
  def circle_y(x, y, z, r, fill=False):
      return sphere(x, y, z, r, fill, fy=True)
  
  def circle_z(x, y, z, r, fill=False):
      return sphere(x, y, z, r, fill, fz=True)
  
  def cylinder_x(x1, x2, y, z, r, fill=False):
      x1, x2 = sorted((x1, x2))
      result = set()
      for x in range(x1, x2 + 1):
          result |= circle_x(x, y, z, r, fill)
      return result
  
  def cylinder_y(x, y1, y2, z, r, fill=False):
      y1, y2 = sorted((y1, y2))
      result = set()
      for y in range(y1, y2 + 1):
          result |= circle_y(x, y, z, r, fill)
      return result
  
  def cylinder_z(x, y, z1, z2, r, fill=False):
      z1, z2 = sorted((z1, z2))
      result = set()
      for z in range(z1, z2 + 1):
          result |= circle_z(x, y, z, r, fill)
      return result
  
  def cuboid(x1, x2, y1, y2, z1, z2, fill=True):
      x1, x2 = sorted((x1, x2))
      y1, y2 = sorted((y1, y2))
      z1, z2 = sorted((z1, z2))
      result = set()
      a = (x1 == x2) + (y1 == y2) + (z1 == z2)
      for x in range(x1, x2 + 1):
          for y in range(y1, y2 + 1):
              for z in range(z1, z2 + 1):
                  n = 0
                  n += x in (x1, x2)
                  n += y in (y1, y2)
                  n += z in (z1, z2)
                  if not fill and n <= a:
                      continue
                  result.add((x, y, z))
      return result
  
  def pyramid(x1, x2, y, z1, z2, fill=False):
      x1, x2 = sorted((x1, x2))
      z1, z2 = sorted((z1, z2))
      result = set()
      while x2 >= x1 and z2 >= z2:
          result |= cuboid(x1, x2, y, y, z1, z2, fill)
          y, x1, x2, z1, z2 = y + 1, x1 + 1, x2 - 1, z1 + 1, z2 - 1
      return result
  
  def get_identity():
      query = (
          'select username, token from identity_token where selected = 1;'
      )
      conn = sqlite3.connect('auth.db')
      rows = conn.execute(query)
      for row in rows:
          return row
      raise Exception('No identities found.')
  
  class Client(object):
      def __init__(self, host, port):
          self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
          self.conn.connect((host, port))
          self.authenticate()
      def authenticate(self):
          username, identity_token = get_identity()
          url = 'https://craft.michaelfogleman.com/api/1/identity'
          payload = {
              'username': username,
              'identity_token': identity_token,
          }
          response = requests.post(url, data=payload)
          if response.status_code == 200 and response.text.isalnum():
              access_token = response.text
              self.conn.sendall('A,%s,%s\n' % (username, access_token))
          else:
              raise Exception('Failed to authenticate.')
      def set_block(self, x, y, z, w):
          self.conn.sendall('B,%d,%d,%d,%d\n' % (x, y, z, w))
      def set_blocks(self, blocks, w):
          key = lambda block: (block[1], block[0], block[2])
          for x, y, z in sorted(blocks, key=key):
              self.set_block(x, y, z, w)
      def bitmap(self, sx, sy, sz, d1, d2, data, lookup):
          x, y, z = sx, sy, sz
          dx1, dy1, dz1 = d1
          dx2, dy2, dz2 = d2
          for row in data:
              x = sx if dx1 else x
              y = sy if dy1 else y
              z = sz if dz1 else z
              for c in row:
                  w = lookup.get(c)
                  if w is not None:
                      self.set_block(x, y, z, w)
                  x, y, z = x + dx1, y + dy1, z + dz1
              x, y, z = x + dx2, y + dy2, z + dz2
  
  def get_client():
      default_args = [DEFAULT_HOST, DEFAULT_PORT]
      args = sys.argv[1:] + [None] * len(default_args)
      host, port = [a or b for a, b in zip(args, default_args)]
      client = Client(host, int(port))
      return client
  
  def main():
      client = get_client()
      set_block = client.set_block
      set_blocks = client.set_blocks
      # set_blocks(circle_y(0, 32, 0, 16, True), STONE)
      # set_blocks(circle_y(0, 33, 0, 16), BRICK)
      # set_blocks(cuboid(-1, 1, 1, 31, -1, 1), CEMENT)
      # set_blocks(cuboid(-1024, 1024, 32, 32, -3, 3), STONE)
      # set_blocks(cuboid(-3, 3, 32, 32, -1024, 1024), STONE)
      # set_blocks(cuboid(-1024, 1024, 33, 33, -3, -3), BRICK)
      # set_blocks(cuboid(-1024, 1024, 33, 33, 3, 3), BRICK)
      # set_blocks(cuboid(-3, -3, 33, 33, -1024, 1024), BRICK)
      # set_blocks(cuboid(3, 3, 33, 33, -1024, 1024), BRICK)
      # set_blocks(sphere(0, 32, 0, 16), GLASS)
      # for y in range(1, 32):
      #     set_blocks(circle_y(0, y, 0, 4, True), CEMENT)
      # set_blocks(circle_x(16, 33, 0, 3), BRICK)
      # set_blocks(circle_x(-16, 33, 0, 3), BRICK)
      # set_blocks(circle_z(0, 33, 16, 3), BRICK)
      # set_blocks(circle_z(0, 33, -16, 3), BRICK)
      # for x in range(0, 1024, 32):
      #     set_blocks(cuboid(x - 1, x + 1, 31, 32, -1, 1), CEMENT)
      #     set_blocks(cuboid(-x - 1, -x + 1, 31, 32, -1, 1), CEMENT)
      #     set_blocks(cuboid(x, x, 1, 32, -1, 1), CEMENT)
      #     set_blocks(cuboid(-x, -x, 1, 32, -1, 1), CEMENT)
      # for z in range(0, 1024, 32):
      #     set_blocks(cuboid(-1, 1, 31, 32, z - 1, z + 1), CEMENT)
      #     set_blocks(cuboid(-1, 1, 31, 32, -z - 1, -z + 1), CEMENT)
      #     set_blocks(cuboid(-1, 1, 1, 32, z, z), CEMENT)
      #     set_blocks(cuboid(-1, 1, 1, 32, -z, -z), CEMENT)
      # for x in range(0, 1024, 8):
      #     set_block(x, 32, 0, CEMENT)
      #     set_block(-x, 32, 0, CEMENT)
      # for z in range(0, 1024, 8):
      #     set_block(0, 32, z, CEMENT)
      #     set_block(0, 32, -z, CEMENT)
      # set_blocks(pyramid(32, 32+64-1, 12, 32, 32+64-1), COBBLE)
      # outer = circle_y(0, 11, 0, 176 + 3, True)
      # inner = circle_y(0, 11, 0, 176 - 3, True)
      # set_blocks(outer - inner, STONE)
      # a = sphere(-32, 48, -32, 24, True)
      # b = sphere(-24, 40, -24, 24, True)
      # set_blocks(a - b, PLANK)
      # set_blocks(cylinder_x(-64, 64, 32, 0, 8), STONE)
      # data = [
      #     '...............................',
      #     '..xxx..xxxx...xxx..xxxxx.xxxxx.',
      #     '.x...x.x...x.x...x.x.......x...',
      #     '.x.....xxxx..xxxxx.xxx.....x...',
      #     '.x...x.x..x..x...x.x.......x...',
      #     '..xxx..x...x.x...x.x.......x...',
      #     '...............................',
      # ]
      # lookup = {
      #     'x': STONE,
      #     '.': PLANK,
      # }
      # client.bitmap(0, 32, 32, (1, 0, 0), (0, -1, 0), data, lookup)
  
  if __name__ == '__main__':
      main()
  