def __init__(self, objectgroup, world_dir):
        self.objectgroup = objectgroup
        self.world_dir = world_dir
        self.level_name = 'Converted Object Group'
        self.boundary_block = anvil.Block('minecraft', 'barrier')

        # If True, convert regions that are small enough to structure blocks
        self.region_structure_blocks = True

        # If True, use player heads as playerstart regions instead of structure blocks
        self.playerstart_to_player_head = True
Example #2
0
    def get_replacement_block(self, repl_chunk, x, y, z):
        gold_block = anvil.Block("minecraft", "gold_block")

        b = gold_block
        # if repl_chunk:
        #     new_block = repl_chunk.get_block(x, y, z)
        #     # TODO expand is_solid list
        #     if is_solid(new_block.id):
        #         b = new_block
        #         b = blue_wool

        return b
Example #3
0
    def load(cls, path: Union[Path, str]) -> "Blueprint":
        self = cls()

        with open(path) as f:
            blueprint = json.load(f)

        for point_str, block in blueprint.items():
            point = [int(p) for i, p in enumerate(point_str.split(","))]
            self[(point[0], point[1],
                  point[2])] = anvil.Block("minecraft", block["block"],
                                           block["attrs"])

        return self
Example #4
0
    def __init__(self, objectgroup, world_dir, resources_pack_path=None):
        self.objectgroup = objectgroup
        self.world_dir = world_dir
        self.level_name = 'Converted Object Group'
        self.boundary_block = anvil.Block('minecraft', 'barrier')

        # If Not None, it will convert a MC Dungeon resources pack to a MC resources pack
        self.resources_pack_path = resources_pack_path

        # If True, convert regions that are small enough to structure blocks
        self.region_structure_blocks = True

        # If True, use player heads as playerstart regions instead of structure blocks
        self.playerstart_to_player_head = True
Example #5
0
def repeater(facing: Direction4,
             delay: int = 1,
             powered: bool = False,
             locked: bool = False):
    return anvil.Block(
        "minecraft",
        "repeater",
        {
            "delay": delay,
            "powered": powered,
            "facing": facing,
            "locked": locked
        },
    )
Example #6
0
    def __init__(self, identifier):
        self.identifier = identifier

        self.air = anvil.Block("minecraft", "air")
        self.stone = anvil.Block("minecraft", "stone")
        self.glass = anvil.Block("minecraft", "glass")

        self.torch = anvil.Block("minecraft", "torch")
        self.r_torch = anvil.Block("minecraft", "redstone_torch")
        self.rail = anvil.Block("minecraft", "rail")
        self.powered_rail = anvil.Block("minecraft", "powered_rail")

        # https://tryolabs.com/blog/2013/07/05/run-time-method-patching-python/
        # TODO put this somewhere else?
        anvil.EmptyChunk.save = save_chunk
        anvil.EmptyRegion.save = save_region
Example #7
0
    def place_chests(self, region, new_region, x, y, z, direction):
        # add_x = 1
        # add_z = 0
        chest = anvil.Block("minecraft", "chest")
        chest.properties["waterlogged"] = "false"
        chest.properties["facing"] = "east"
        chest.properties["type"] = "single"

        # if direction == cfg.M_DIR_Z:
        #     add_x = 0
        #     add_z = 1
        #     chest.properties["shape"] = "east_west"

        i = 0
        chest_x = x
        chest_y = y - 1
        chest_z = z

        for item_type in self.item_dict:
            amount = self.item_dict[item_type]

            while amount > 0:
                if direction == cfg.M_DIR_Z:
                    chest_x = x + 1
                    chest_z = z + i
                else:
                    chest_x = x + i
                    chest_z = z + 1

                new_region.set_block(chest, chest_x, chest_y, chest_z)

                item = "minecraft:" + str(item_type)
                block_entity, amount = self.create_chest_block_entity(
                    chest_x, chest_y, chest_z, item, amount)

                chunk_idx_x = chest_x // cfg.CHUNK_B_X
                chunk_idx_z = chest_z // cfg.CHUNK_B_Z
                chunk = region.get_chunk(chunk_idx_x, chunk_idx_z)

                if chunk.data["TileEntities"].tagID != nbt.TAG_Compound.id:
                    chunk.data["TileEntities"] = nbt.TAG_List(
                        name="TileEntities", type=nbt.TAG_Compound)
                chunk.data["TileEntities"].tags.append(block_entity)
                i += 1
Example #8
0
    def convert(self):
        """Creates a Java Edition world in the world directory from the object group."""
        # TODO: Converting to a Java world should be done one region or maybe even
        # one sub-region at a time. Right now, all regions are kept
        # in memory until the conversion process is done, which means the memory
        # usage can be massive for bigger object groups.

        # anvil-parser doesn't actually support loading a region from a file and
        # then editing it and writing it to a file again. Regions loaded from a
        # file are read-only, and the regions that can be edited start out empty.

        region_cache = {}
        block_cache = {}

        def get_region(rx, rz):
            if f'{rx}x{rz}' in region_cache:
                return region_cache[f'{rx}x{rz}']
            else:
                region_cache[f'{rx}x{rz}'] = anvil.EmptyRegion(rx, rz)
                return region_cache[f'{rx}x{rz}']

        structure_block = anvil.Block('minecraft', 'structure_block')
        player_head = anvil.Block('minecraft', 'player_head')

        def find_room_for_structure_block(area, get_block):
            xi = range(area[0])
            zi = range(area[1])

            # Blocks that will break if a stucture block is placed on top of them
            breakable_blocks = [0x3c, 0xc6]

            # Check the area and blocks above it
            for y in range(49):
                for x in xi:
                    for z in zi:
                        if get_block(x, y, z) == 0 and not get_block(
                                x, y - 1, z) in breakable_blocks:
                            return (x, y, z)

            # Check blocks below the area
            for y in range(-1, -49, -1):
                for x in xi:
                    for z in zi:
                        if get_block(x, y, z) == 0 and not get_block(
                                x, y - 1, z) in breakable_blocks:
                            return (x, y, z)

            # No room found :(
            return None

        if isinstance(self.objectgroup, dict):
            og = self.objectgroup

        else:  # If objectgroup is a file path, parse the json file
            with open(self.objectgroup) as json_file:
                og = json.load(json_file)

        for tile_dict in og['objects']:
            if isinstance(tile_dict, Tile):
                tile = tile_dict
            else:
                tile = Tile.from_dict(tile_dict)

            zi = range(tile.size[2])
            yi = range(min(256, tile.size[1]))

            # For each slice of the tile along the X axis...
            for tx in range(tile.size[0]):
                ax = tx + tile.pos[0]
                rx = ax // 512

                # For each column of the slice along the Z axis...
                for tz in zi:
                    az = tz + tile.pos[2]
                    rz = az // 512
                    region = get_region(rx, rz)

                    # For each block in the column along the Y axis...
                    for ty in yi:
                        ay = ty + tile.pos[1]

                        # Skip this block if it's outside of the world bounds
                        if ay < 0 or ay >= 256:
                            continue

                        bidx = tile.get_block_index(tx, ty, tz)

                        # If the block is just air, we don't need to do anything
                        if tile.blocks[bidx] == 0:
                            continue

                        # Get the Java block from the cache if it's there
                        bcid = tile.blocks[bidx] << 4 | tile.block_data[bidx]
                        if bcid in block_cache:
                            java_block = block_cache[bcid]

                        else:  # If not, find it and add it to the cache to speed things up later
                            mapped_block = find_dungeons_block(
                                tile.blocks[bidx], tile.block_data[bidx])

                            if mapped_block is None:
                                print(
                                    f'Warning: {tile.blocks[bidx]}:{tile.block_data[bidx]} is not mapped to anything. It will be replaced by air.'
                                )
                                continue

                            if len(mapped_block['java']) > 1:
                                java_block = anvil.Block(
                                    *mapped_block['java'][0].split(':', 1),
                                    mapped_block['java'][1])
                            else:
                                java_block = anvil.Block(
                                    *mapped_block['java'][0].split(':', 1))

                            block_cache[bcid] = java_block

                        # Once we have the Java block, add it to the region
                        region.set_block(java_block, ax, ay, az)

            # TODO: Block post-processing to fix fences, walls, stairs, and more

            converter_blocks = []

            # Add the tile doors to the world
            for door in tile.doors:

                def get_block(x, y, z):
                    tx = x + door.pos[0]
                    ty = y + door.pos[1]
                    tz = z + door.pos[2]
                    if f'{tx},{ty},{tz}' in converter_blocks:
                        return -1
                    if tx >= 0 and tx < tile.size[
                            0] and ty >= 0 and ty < tile.size[
                                1] and tz >= 0 and tz < tile.size[2]:
                        return tile.get_block_id(tx, ty, tz)
                    else:
                        return 0

                pos = find_room_for_structure_block(door.size[::2], get_block)

                if pos is None:
                    if hasattr(door, 'name'):
                        print(
                            f'Warning: No room to place structure block for door: {door.name}'
                        )
                    else:
                        print(
                            f'Warning: No room to place structure block for unnamed door.'
                        )

                else:
                    tpos = [p + d for p, d in zip(pos, door.pos)]
                    if tpos[0] >= 0 and tpos[0] < tile.size[0] and tpos[
                            1] >= 0 and tpos[1] < tile.size[1] and tpos[
                                2] >= 0 and tpos[2] < tile.size[2]:
                        apos = [p + t for p, t in zip(tpos, tile.pos)]
                        region = get_region(apos[0] // 512, apos[2] // 512)
                        region.set_block(structure_block, *apos)
                        metadata = door.dict()
                        metadata.pop('name', None)
                        metadata.pop('pos', None)
                        metadata.pop('size', None)
                        if hasattr(door, 'name'):
                            tile_entity = structure_block_entity(
                                *apos, 'SAVE', f'door:{door.name}',
                                json.dumps(metadata), *[-v for v in pos],
                                *door.size)
                        else:
                            tile_entity = structure_block_entity(
                                *apos, 'SAVE', 'door:', json.dumps(metadata),
                                *[-v for v in pos], *door.size)
                        region.chunks[apos[2] // 16 % 32 * 32 + apos[0] // 16 %
                                      32].tile_entities.append(tile_entity)
                        converter_blocks.append(
                            f'{tpos[0]},{tpos[1]},{tpos[2]}')

            if self.region_structure_blocks:
                # Add the tile regions to the world
                for tile_region in tile.regions:
                    # playerstart regions just use a player head instead of a structure block
                    if self.playerstart_to_player_head and hasattr(
                            tile_region,
                            'tags') and tile_region.tags == 'playerstart':
                        ax = tile.pos[0] + tile_region.pos[0]
                        ay = tile.pos[1] + tile_region.pos[1]
                        az = tile.pos[2] + tile_region.pos[2]
                        rx = ax // 512
                        rz = az // 512
                        region = get_region(rx, rz)
                        region.set_block(player_head, ax, ay, az)
                        tile_entity = TAG_Compound()
                        tile_entity.tags.extend([
                            TAG_String(name='id', value='minecraft:skull'),
                            TAG_Byte(name='keepPacked', value=0),
                            TAG_Int(name='x', value=ax),
                            TAG_Int(name='y', value=ay),
                            TAG_Int(name='z', value=az)
                        ])
                        region.chunks[az // 16 % 32 * 32 + ax // 16 %
                                      32].tile_entities.append(tile_entity)
                        converter_blocks.append(
                            f'{tile_region.pos[0]},{tile_region.pos[1]},{tile_region.pos[2]}'
                        )

                    elif tile_region.size[0] <= 48 and tile_region.size[
                            1] <= 48 and tile_region.size[2] <= 48:

                        def get_block(x, y, z):
                            tx = x + tile_region.pos[0]
                            ty = y + tile_region.pos[1]
                            tz = z + tile_region.pos[2]
                            if f'{tx},{ty},{tz}' in converter_blocks:
                                return -1
                            if tx >= 0 and tx < tile.size[
                                    0] and ty >= 0 and ty < tile.size[
                                        1] and tz >= 0 and tz < tile.size[2]:
                                return tile.get_block_id(tx, ty, tz)
                            else:
                                return 0

                        pos = find_room_for_structure_block(
                            tile_region.size[::2], get_block)

                        if pos is None:
                            if hasattr(tile_region, 'name'):
                                print(
                                    f'Warning: No room to place structure block for region: {tile_region.name}'
                                )
                            else:
                                print(
                                    f'Warning: No room to place structure block for unnamed region.'
                                )

                        else:
                            tpos = [
                                p + d for p, d in zip(pos, tile_region.pos)
                            ]
                            if tpos[0] >= 0 and tpos[0] < tile.size[
                                    0] and tpos[1] >= 0 and tpos[
                                        1] < tile.size[1] and tpos[
                                            2] >= 0 and tpos[2] < tile.size[2]:
                                apos = [p + t for p, t in zip(tpos, tile.pos)]
                                region = get_region(apos[0] // 512,
                                                    apos[2] // 512)
                                region.set_block(structure_block, *apos)
                                metadata = tile_region.dict()
                                metadata.pop('name', None)
                                metadata.pop('pos', None)
                                metadata.pop('size', None)
                                if hasattr(tile_region, 'name'):
                                    tile_entity = structure_block_entity(
                                        *apos, 'SAVE',
                                        f'region:{tile_region.name}',
                                        json.dumps(metadata),
                                        *[-v for v in pos], *tile_region.size)
                                else:
                                    tile_entity = structure_block_entity(
                                        *apos, 'SAVE', 'region:',
                                        json.dumps(metadata),
                                        *[-v for v in pos], *tile_region.size)
                                region.chunks[
                                    apos[2] // 16 % 32 * 32 + apos[0] // 16 %
                                    32].tile_entities.append(tile_entity)
                                converter_blocks.append(
                                    f'{tpos[0]},{tpos[1]},{tpos[2]}')

            # Add the tile boundaries to the world
            for boundary in tile.boundaries:
                ax = tile.pos[0] + boundary.x
                az = tile.pos[2] + boundary.z
                rx = ax // 512
                rz = az // 512
                region = get_region(rx, rz)

                for by in range(boundary.h):
                    ay = tile.pos[1] + boundary.y + by

                    region.set_block(self.boundary_block, ax, ay, az)

        # Write regions to files
        os.makedirs(os.path.join(self.world_dir, 'region'), exist_ok=True)
        for k in region_cache:
            region_cache[k].save(
                os.path.join(
                    self.world_dir,
                    f'region/r.{region_cache[k].x}.{region_cache[k].z}.mca'))

        # For convenience, write the object group to objectgroup.json in the world
        # directory, so JavaWorldToObjectGroup can convert the world back to an
        # object group without any changes.
        og_copy = json.loads(json.dumps(og))  # faster than copy.deepcopy
        for tile in og_copy['objects']:
            tile.pop('blocks', None)
            tile.pop('boundaries', None)
            tile.pop('doors', None)
            tile.pop('height-plane', None)
            if self.region_structure_blocks and 'regions' in tile:
                # Keep only regions that are too big turn into structure blocks
                tile['regions'] = [
                    r for r in tile['regions'] if r['size'][0] > 48
                    or r['size'][1] > 48 or r['size'][2] > 48
                ]
        with open(os.path.join(self.world_dir, 'objectgroup.json'),
                  'w') as out_file:
            out_file.write(stringify(og_copy))

        # Create level.dat file
        level = NBTFile('level_template.dat', 'rb')
        level['Data']['LevelName'].value = self.level_name
        level['Data']['LastPlayed'].value = int(time.time() * 1000)

        # Place the player spawn above the center of the first tile.
        # This could probably be made a bit smarter, since the center of the tile
        # might still be above the void. For now, this faster solution will have to do.
        level['Data']['SpawnX'].value = int(og['objects'][0]['pos'][0] +
                                            og['objects'][0]['size'][0] * 0.5)
        level['Data']['SpawnY'].value = min(
            255, og['objects'][0]['pos'][1] + og['objects'][0]['size'][1])
        level['Data']['SpawnZ'].value = int(og['objects'][0]['pos'][2] +
                                            og['objects'][0]['size'][2] * 0.5)

        level.write_file(os.path.join(self.world_dir, 'level.dat'))
Example #9
0
from typing import Dict, Tuple, Union

from pathlib import Path
import json

import anvil  # type: ignore

region = anvil.EmptyRegion(0, 0)

stone = anvil.Block("minecraft", "stone")
redstone_wire = anvil.Block("minecraft", "redstone_wire")
air = anvil.Block("minecraft", "air")

for y in range(4):
    for z in range(32):
        for x in range(32):
            region.set_block(stone, x, y, z)

Point = Tuple[int, int, int]


class Blueprint(Dict[Point, anvil.Block]):
    @property
    def dimensions(self) -> Point:
        max_x = max(point[0] for point in self)
        max_y = max(point[1] for point in self)
        max_z = max(point[2] for point in self)
        return (max_x + 1, max_y + 1, max_z + 1)

    def place(self, region: anvil.Region, offset: Point = (0, 0, 0)) -> None:
        for point, block in self.items():
Example #10
0
import _path
import anvil
from random import choice

# Create a new region with the `EmptyRegion` class at 0, 0 (in region coords)
region = anvil.EmptyRegion(0, 0)

# Create `Block` objects that are used to set blocks
stone = anvil.Block('minecraft', 'stone')
dirt = anvil.Block('minecraft', 'dirt')

# Make a 16x16x16 cube of either stone or dirt blocks
for y in range(16):
    for z in range(16):
        for x in range(16):
            region.set_block(choice((stone, dirt)), x, y, z)

# Save to a file
region.save('r.0.0.mca')
Example #11
0
def to_block(name):
    return anvil.Block('minecraft', name)
Example #12
0
#!/usr/bin/env python3

import anvil
import pyfastnoisesimd as fns
import numpy as np
import time


def to_block(name):
    return anvil.Block('minecraft', name)


# Usefull blocks type
grass = anvil.Block('minecraft', 'grass_block')
sand = anvil.Block('minecraft', 'sand')
bedrock = anvil.Block('minecraft', 'bedrock')
dirt = anvil.Block('minecraft', 'dirt')
air = anvil.Block('minecraft', 'air')
glass = anvil.Block('minecraft', 'glass')
pumpkin = anvil.Block('minecraft', 'pumpkin')
diorite = anvil.Block('minecraft', 'diorite')
stone = anvil.Block('minecraft', 'stone')
oak_log_up = anvil.Block('minecraft', 'oak_log', properties={'axis': 'y'})
oak_leaves = anvil.Block('minecraft',
                         'oak_leaves',
                         properties={'persistent': True})
coal = anvil.Block('minecraft', 'coal_ore')
iron = anvil.Block('minecraft', 'iron_ore')
gold = anvil.Block('minecraft', 'gold_ore')
diamond = anvil.Block('minecraft', 'diamond_ore')
redstone = anvil.Block('minecraft', 'redstone_ore')
Example #13
0
def torch(facing: Direction4, lit: bool = True):
    return anvil.Block("minecraft", "redstone_wall_torch", {
        "lit": lit,
        "facing": facing
    })
Example #14
0
    def modify(self, chunk, repl_chunk, new_region, chunk_x, chunk_z):
        x = 0
        y = 0
        z = 0

        # Create `Block` objects that are used to set blocks
        water = anvil.Block("minecraft", "water")
        diamond_block = anvil.Block("minecraft", "diamond_block")
        # gold_block = anvil.Block("minecraft", "gold_block")
        # blue_wool = anvil.Block("minecraft", "blue_wool")

        # Iterate all blocks and select write the new block to the new_chunk
        for block in chunk.stream_chunk():
            b = block

            x_region = chunk_x * cfg.CHUNK_B_X + x
            z_region = chunk_z * cfg.CHUNK_B_Z + z
            x_global = new_region.x * cfg.REGION_B_X + x_region
            z_global = new_region.z * cfg.REGION_B_Z + z_region

            xyz = self.identifier.identified[x_region, y, z_region]
            if xyz == cfg.WATERBLOCK:
                b = water
                print(
                    f"Found water Block ({x},{y},{z}) in Chunk ({chunk_x}, {chunk_z})"
                )
                print(f"GlobalPos: ({x_global}, {y}, {z_global})")
            elif xyz == cfg.AIRPOCKET:
                if repl_chunk:
                    b = self.get_replacement_block(repl_chunk, x, y, z)
                else:
                    b = self.stone
                print(
                    f"Found AIRPOCKET Block ({x},{y},{z}) in Chunk ({chunk_x}, {chunk_z})"
                )
                print(f"GlobalPos: ({x_global}, {y}, {z_global})")
            elif xyz == cfg.SOLIDAREA:
                if repl_chunk:
                    b = self.get_replacement_block(repl_chunk, x, y, z)
                # b = self.get_replacement_block(repl_chunk, x, y, z)
                if repl_chunk:
                    new_block = repl_chunk.get_block(x, y, z)
                    # Replace the block if it is solid but use the original when it is not
                    if is_repl_block(new_block.id):
                        b = new_block
                    # TODO debug version
                    b = diamond_block
            elif xyz != cfg.UNCHANGED:
                print(f"Found UNIDENTIFIED Block ({x},{y},{z}) "
                      f"in Chunk ({chunk_x}, {chunk_z}) with {xyz}.")
                print(f"GlobalPos: ({x_global}, {y}, {z_global})")

            try:
                new_region.set_block(b, x_global, y, z_global)
            except OutOfBoundsCoordinates:
                print(f"could not set Block ({x},{y},{z})")

            # TODO
            if z == 15 and x == 15:
                y += 1
            if x == 15:
                z = (z + 1) % 16
            x = (x + 1) % 16