Exemplo n.º 1
0
    def run_build(self, builddata):
        block, metadata, x, y, z, face = builddata

        # Don't place items as blocks.
        if block.slot not in blocks:
            raise BuildError("Couldn't build item %r as block" % block)

        # Check for orientable blocks.
        if not metadata and block.orientable():
            metadata = block.orientation(face)
            if metadata is None:
                # Oh, I guess we can't even place the block on this face.
                raise BuildError("Couldn't orient block %r on face %s" %
                    (block, face))

        # Make sure we can remove it from the inventory first.
        if not self.player.inventory.consume((block.slot, 0),
            self.player.equipped):
            # Okay, first one was a bust; maybe we can consume the related
            # block for dropping instead?
            if not self.player.inventory.consume((block.drop, 0),
                self.player.equipped):
                raise BuildError("Couldn't consume %r from inventory" % block)

        # Offset coords according to face.
        adjust_coords_for_face((x, y, z), face)

        # Set the block and data.
        dl = [self.factory.world.set_block((x, y, z), block.slot)]
        if metadata:
            dl.append(self.factory.world.set_metadata((x, y, z), metadata))

        return DeferredList(dl)
Exemplo n.º 2
0
    def update_powered_block(self, x, y, z, enabled):
        """
        Update a powered non-redstone block.
        """

        neighbors = ((x - 1, y, z), (x + 1, y, z), (x, y, z - 1),
            (x, y, z + 1))

        affected = []

        for neighbor in neighbors:
            block = factory.world.sync_get_block(neighbor)
            if block == blocks["redstone-wire"].slot:
                args = neighbor + (enabled,)
                self.update_wires(*args)
            elif block == blocks["redstone-torch"].slot and enabled:
                metadata = factory.world.sync_get_metadata(neighbor)
                face = blocks["redstone-torch"].face(metadata)
                target = adjust_coords_for_face(neighbor, face)
                if target == (x, y, z):
                    # We should turn off this torch.
                    factory.world.sync_set_block(neighbor,
                        blocks["redstone-torch-off"].slot)
                    affected.append(neighbor)

        return affected
Exemplo n.º 3
0
    def run_build(self, builddata):
        block, metadata, x, y, z, face = builddata

        # Don't place items as blocks.
        if block.slot not in blocks:
            raise BuildError("Couldn't build item %r as block" % block)

        # Check for orientable blocks.
        if not metadata and block.orientable():
            metadata = block.orientation(face)
            if metadata is None:
                # Oh, I guess we can't even place the block on this face.
                raise BuildError("Couldn't orient block %r on face %s" %
                                 (block, face))

        # Make sure we can remove it from the inventory first.
        if not self.player.inventory.consume(
            (block.slot, 0), self.player.equipped):
            # Okay, first one was a bust; maybe we can consume the related
            # block for dropping instead?
            if not self.player.inventory.consume(block.drop,
                                                 self.player.equipped):
                raise BuildError("Couldn't consume %r from inventory" % block)

        # Offset coords according to face.
        x, y, z = adjust_coords_for_face((x, y, z), face)

        # Set the block and data.
        dl = [self.factory.world.set_block((x, y, z), block.slot)]
        if metadata:
            dl.append(self.factory.world.set_metadata((x, y, z), metadata))

        return DeferredList(dl)
Exemplo n.º 4
0
    def pre_build_hook(self, player, builddata):
        item, metadata, x, y, z, face = builddata

        if item.slot != blocks["chest"].slot:
            returnValue((True, builddata, False))

        x, y, z = adjust_coords_for_face((x, y, z), face)
        bigx, smallx, bigz, smallz = split_coords(x, z)

        # chest orientation according to players position
        if face == "-y" or face == "+y":
            orientation = ('+x', '+z', '-x', '-z')[((int(player.location.yaw) \
                                                - 45 + 360) % 360) / 90]
        else:
            orientation = face

        # Chests have some restrictions on building:
        # you cannot connect more than two chests. (notchian)
        ccs = chestsAround(self.factory, (x, y, z))
        ccn = len(ccs)
        if ccn > 1:
            # cannot build three or more connected chests
            returnValue((False, builddata, True))

        chunk = yield self.factory.world.request_chunk(bigx, bigz)

        if ccn == 0:
            metadata = blocks["chest"].orientation(orientation)
        elif ccn == 1:
            # check gonna-be-connected chest is not connected already
            n = len(chestsAround(self.factory, ccs[0]))
            if n != 0:
                returnValue((False, builddata, True))

            # align both blocks correctly (since 1.8)
            # get second block
            x2, y2, z2 = ccs[0]
            bigx2, smallx2, bigz2, smallz2 = split_coords(x2, z2)
            # new chests orientation axis according to blocks position
            pair = x - x2, z - z2
            ornt = {(0, 1): "x", (0, -1): "x", (1, 0): "z", (-1, 0): "z"}[pair]
            # if player is faced another direction, fix it
            if orientation[1] != ornt:
                # same sign with proper orientation
                # XXX Probably notchian logic is different here
                #     but this one works well enough
                orientation = orientation[0] + ornt
            metadata = blocks["chest"].orientation(orientation)
            # update second block's metadata
            if bigx == bigx2 and bigz == bigz2:
                # both blocks are in same chunk
                chunk2 = chunk
            else:
                chunk2 = yield self.factory.world.request_chunk(bigx2, bigz2)
            chunk2.set_metadata((smallx2, y2, smallz2), metadata)

        # Not much to do, just tell the chunk about this tile.
        chunk.tiles[smallx, y, smallz] = ChestTile(smallx, y, smallz)
        builddata = builddata._replace(metadata=metadata)
        returnValue((True, builddata, False))
Exemplo n.º 5
0
    def pre_build_hook(self, player, builddata):
        item, metadata, x, y, z, face = builddata

        if item.slot == items["sign"].slot:
            # Buildin' a sign, puttin' it on a wall...
            builddata = builddata._replace(block=blocks["wall-sign"])

            # Offset coords according to face.
            if face == "-x":
                builddata = builddata._replace(metadata=0x4)
                x -= 1
            elif face == "+x":
                builddata = builddata._replace(metadata=0x5)
                x += 1
            elif face == "-y":
                # Ceiling Sign is watching you read.
                returnValue((False, builddata))
            elif face == "+y":
                # Put +Y signs on signposts. We're fancy that way. Also,
                # calculate the proper orientation based on player
                # orientation.
                # 180 degrees around to orient the signs correctly, and then
                # 23 degrees to get the sign to midpoint correctly.
                metadata = ((player.location.yaw + 180) * 16 // 360) % 0xf
                builddata = builddata._replace(block=blocks["signpost"],
                    metadata=metadata)
                y += 1
            elif face == "-z":
                builddata = builddata._replace(metadata=0x2)
                z -= 1
            elif face == "+z":
                builddata = builddata._replace(metadata=0x3)
                z += 1

            bigx, smallx, bigz, smallz = split_coords(x, z)

            # Let's build a sign!
            chunk = yield factory.world.request_chunk(bigx, bigz)
            s = Sign(smallx, y, smallz)
            chunk.tiles[smallx, y, smallz] = s

        elif item.slot == blocks["chest"].slot:
            x, y, z = adjust_coords_for_face((x, y, z), face)

            bigx, smallx, bigz, smallz = split_coords(x, z)

           # Not much to do, just tell the chunk about this chest.
            chunk = yield factory.world.request_chunk(bigx, bigz)
            c = Chest(smallx, y, smallz)
            chunk.tiles[smallx, y, smallz] = c

        returnValue((True, builddata))
Exemplo n.º 6
0
    def pre_build_hook(self, player, builddata):
        item, metadata, x, y, z, face = builddata

        if item.slot != blocks["furnace"].slot:
            returnValue((True, builddata, False))

        x, y, z = adjust_coords_for_face((x, y, z), face)
        bigx, smallx, bigz, smallz = split_coords(x, z)

        # the furnace cannot be oriented up or down
        if face == "-y" or face == "+y":
            orientation = ('+x', '+z', '-x', '-z')[((int(player.location.yaw) \
                                                - 45 + 360) % 360) / 90]
            metadata = blocks["furnace"].orientation(orientation)
            builddata = builddata._replace(metadata=metadata)

        # Not much to do, just tell the chunk about this tile.
        chunk = yield self.factory.world.request_chunk(bigx, bigz)
        chunk.tiles[smallx, y, smallz] = FurnaceTile(smallx, y, smallz)
        returnValue((True, builddata, False))
Exemplo n.º 7
0
    def pre_build_hook(self, player, builddata):
        item, metadata, x, y, z, face = builddata

        if item.slot != blocks["furnace"].slot:
            returnValue((True, builddata, False))

        x, y, z = adjust_coords_for_face((x, y, z), face)
        bigx, smallx, bigz, smallz = split_coords(x, z)

        # the furnace cannot be oriented up or down
        if face == "-y" or face == "+y":
            orientation = ('+x', '+z', '-x', '-z')[((int(player.location.yaw) \
                                                - 45 + 360) % 360) / 90]
            metadata = blocks["furnace"].orientation(orientation)
            builddata = builddata._replace(metadata=metadata)

        # Not much to do, just tell the chunk about this tile.
        chunk = yield self.factory.world.request_chunk(bigx, bigz)
        chunk.tiles[smallx, y, smallz] = FurnaceTile(smallx, y, smallz)
        returnValue((True, builddata, False))
Exemplo n.º 8
0
    def use_hook(self, player, target, button):
        # Block coordinates.
        x, y, z = target.location.x, target.location.y, target.location.z

        # Offset coords according to direction. A painting does not occupy a
        # block, therefore we drop the pickup right in front of the block it
        # is attached to.
        face = direction_to_face[target.direction]
        x, y, z = adjust_coords_for_face((x, y, z), face)

        # Pixel coordinates.
        coords = (x * 32 + 16, y * 32, z * 32 + 16)

        self.factory.destroy_entity(target)
        self.factory.give(coords, (items["paintings"].slot, 0), 1)

        packet = make_packet("destroy", count=1, eid=[target.eid])
        self.factory.broadcast(packet)

        # Force the chunk (with its entities) to be saved to disk.
        self.factory.world.mark_dirty((x, y, z))
Exemplo n.º 9
0
    def use_hook(self, player, target, button):
        # Block coordinates.
        x, y, z = target.location.x, target.location.y, target.location.z

        # Offset coords according to direction. A painting does not occupy a
        # block, therefore we drop the pickup right in front of the block it
        # is attached to.
        face = direction_to_face[target.direction]
        x, y, z = adjust_coords_for_face((x, y, z), face)

        # Pixel coordinates.
        coords = (x * 32 + 16, y * 32, z * 32 + 16)

        self.factory.destroy_entity(target)
        self.factory.give(coords, (items["paintings"].slot, 0), 1)

        packet = make_packet("destroy", count=1, eid=[target.eid])
        self.factory.broadcast(packet)

        # Force the chunk (with its entities) to be saved to disk.
        self.factory.world.mark_dirty((x, y, z))
Exemplo n.º 10
0
    def pre_build_hook(self, player, builddata):
        item, metadata, x, y, z, face = builddata

        if item.slot != blocks["chest"].slot:
            returnValue((True, builddata, False))

        x, y, z = adjust_coords_for_face((x, y, z), face)
        bigx, smallx, bigz, smallz = split_coords(x, z)

        # chest orientation according to players position
        if face == "-y" or face == "+y":
            orientation = ('+x', '+z', '-x', '-z')[((int(player.location.yaw) \
                                                - 45 + 360) % 360) / 90]
        else:
            orientation = face

        # Chests have some restrictions on building:
        # you cannot connect more than two chests. (notchian)
        ccs = chestsAround(self.factory, (x, y, z))
        ccn = len(ccs)
        if ccn > 1:
            # cannot build three or more connected chests
            returnValue((False, builddata, True))

        chunk = yield self.factory.world.request_chunk(bigx, bigz)

        if ccn == 0:
            metadata = blocks["chest"].orientation(orientation)
        elif ccn == 1:
            # check gonna-be-connected chest is not connected already
            n = len(chestsAround(self.factory, ccs[0]))
            if n != 0:
                returnValue((False, builddata, True))

            # align both blocks correctly (since 1.8)
            # get second block
            x2, y2, z2 = ccs[0]
            bigx2, smallx2, bigz2, smallz2 = split_coords(x2, z2)
            # new chests orientation axis according to blocks position
            pair = x - x2, z - z2
            ornt = {(0, 1): "x", (0, -1): "x",
                    (1, 0): "z", (-1, 0): "z"}[pair]
            # if player is faced another direction, fix it
            if orientation[1] != ornt:
                # same sign with proper orientation
                # XXX Probably notchian logic is different here
                #     but this one works well enough
                orientation = orientation[0] + ornt
            metadata = blocks["chest"].orientation(orientation)
            # update second block's metadata
            if bigx == bigx2 and bigz == bigz2:
                # both blocks are in same chunk
                chunk2 = chunk
            else:
                chunk2 = yield self.factory.world.request_chunk(bigx2, bigz2)
            chunk2.set_metadata((smallx2, y2, smallz2), metadata)

        # Not much to do, just tell the chunk about this tile.
        chunk.tiles[smallx, y, smallz] = ChestTile(smallx, y, smallz)
        builddata = builddata._replace(metadata=metadata)
        returnValue((True, builddata, False))
Exemplo n.º 11
0
    def run_circuit(self, x, y, z):
        """
        Iterate through a circuit, starting at the given block, and return a
        list of circuits which have been affected.
        """

        world = factory.world
        block = factory.world.sync_get_block((x, y, z))
        neighbors = ((x - 1, y, z), (x + 1, y, z), (x, y, z - 1),
            (x, y, z + 1))

        affected = set()

        if block == blocks["lever"].slot:
            # Power/depower the block the lever is attached to.
            metadata = factory.world.sync_get_metadata((x, y, z))
            powered = metadata & 0x8
            face = blocks["lever"].face(metadata & ~0x8)
            target = adjust_coords_for_face((x, y, z), face)

            if powered:
                self.powered.add(target)
            else:
                self.powered.discard(target)

            affected.add(target)

        else:
            # Let's update anything around us.
            l = self.update_powered_block(x, y, z, (x, y, z) in self.powered)
            affected.update(l)

        if block == blocks["redstone-torch"].slot:
            # Turn on neighboring wires, as appropriate.
            for coords in neighbors:
                if (world.get_block(coords) ==
                    blocks["redstone-wire"].slot):
                    self.update_wires(factory, coords[0], coords[1],
                        coords[2], True)

        if block == blocks["redstone-torch-off"].slot:
            # Turn off neighboring wires, as appropriate.
            for coords in neighbors:
                if (world.get_block(coords) ==
                    blocks["redstone-wire"].slot):
                    self.update_wires(factory, coords[0], coords[1],
                        coords[2], False)

        elif block == blocks["redstone-wire"].slot:
            # Get wire status from neighbors.
            if any(world.get_block(coords) ==
                blocks["redstone-torch"].slot
                for coords in neighbors):
                # We should probably be lit.
                self.update_wires(factory, x, y, z, True)
            else:
                # Find the strongest neighboring wire, and use that.
                new_level = max(factory.world.get_metadata(coords)
                    for coords in neighbors
                    if factory.world.get_block(coords) ==
                        blocks["redstone-wire"].slot)
                if new_level > 0x0:
                    new_level -= 1
                world.set_metadata((x, y, z), new_level)

        return affected
Exemplo n.º 12
0
    def build(self, container):
        if container.x == -1 and container.z == -1 and container.y == 255:
            # Lala-land build packet. Discard it for now.
            return

        # Is the target being selected?
        bigx, smallx, bigz, smallz = split_coords(container.x, container.z)
        try:
            chunk = self.chunks[bigx, bigz]
        except KeyError:
            self.error("Couldn't select in chunk (%d, %d)!" % (bigx, bigz))
            return

        # Try to open it first
        for hook in self.open_hooks:
            window = yield maybeDeferred(hook.open_hook, self, container,
                           chunk.get_block((smallx, container.y, smallz)))
            if window:
                self.write_packet("window-open", wid=window.wid,
                    type=window.identifier, title=window.title,
                    slots=window.slots_num)
                packet = window.save_to_packet()
                self.transport.write(packet)
                # window opened
                return

        # Ignore clients that think -1 is placeable.
        if container.primary == -1:
            return

        # Special case when face is "noop": Update the status of the currently
        # held block rather than placing a new block.
        if container.face == "noop":
            return

        if container.primary in blocks:
            block = blocks[container.primary]
        elif container.primary in items:
            block = items[container.primary]
        else:
            log.err("Ignoring request to place unknown block %d" %
                container.primary)
            return

        # it's the top of the world, you can't build here
        if container.y == 127 and container.face == '+y':
            return

        # Run pre-build hooks. These hooks are able to interrupt the build
        # process.
        builddata = BuildData(block, 0x0, container.x, container.y,
            container.z, container.face)

        for hook in self.pre_build_hooks:
            cont, builddata, cancel = yield maybeDeferred(hook.pre_build_hook,
                self.player, builddata)
            if cancel:
                # Flush damaged chunks.
                for chunk in self.chunks.itervalues():
                    self.factory.flush_chunk(chunk)
                return
            if not cont:
                break

        # Run the build.
        try:
            yield maybeDeferred(self.run_build, builddata)
        except BuildError:
            return

        newblock = builddata.block.slot
        coords = adjust_coords_for_face(
            (builddata.x, builddata.y, builddata.z), builddata.face)

        # Run post-build hooks. These are merely callbacks which cannot
        # interfere with the build process, largely because the build process
        # already happened.
        for hook in self.post_build_hooks:
            yield maybeDeferred(hook.post_build_hook, self.player, coords,
                builddata.block)

        # Feed automatons.
        for automaton in self.factory.automatons:
            if newblock in automaton.blocks:
                automaton.feed(coords)

        # Re-send inventory.
        # XXX this could be optimized if/when inventories track damage.
        packet = self.inventory.save_to_packet()
        self.transport.write(packet)

        # Flush damaged chunks.
        for chunk in self.chunks.itervalues():
            self.factory.flush_chunk(chunk)
Exemplo n.º 13
0
    def build(self, container):
        """
        Handle a build packet.

        Several things must happen. First, the packet's contents need to be
        examined to ensure that the packet is valid. A check is done to see if
        the packet is opening a windowed object. If not, then a build is
        run.
        """

        # Is the target within our purview? We don't do a very strict
        # containment check, but we *do* require that the chunk be loaded.
        bigx, smallx, bigz, smallz = split_coords(container.x, container.z)
        try:
            chunk = self.chunks[bigx, bigz]
        except KeyError:
            self.error("Couldn't select in chunk (%d, %d)!" % (bigx, bigz))
            return

        target = blocks[chunk.get_block((smallx, container.y, smallz))]

        # If it's a chest, hax.
        if target.name == "chest":
            from bravo.policy.windows import Chest
            w = Chest()
            self.windows[self.wid] = w

            w.open()
            self.write_packet("window-open", wid=self.wid, type=w.identifier,
                              title=w.title, slots=w.slots)

            self.wid += 1
            return
        elif target.name == "workbench":
            from bravo.policy.windows import Workbench
            w = Workbench()
            self.windows[self.wid] = w

            w.open()
            self.write_packet("window-open", wid=self.wid, type=w.identifier,
                              title=w.title, slots=w.slots)

            self.wid += 1
            return

        # Try to open it first
        for hook in self.open_hooks:
            window = yield maybeDeferred(hook.open_hook, self, container,
                           chunk.get_block((smallx, container.y, smallz)))
            if window:
                self.write_packet("window-open", wid=window.wid,
                    type=window.identifier, title=window.title,
                    slots=window.slots_num)
                packet = window.save_to_packet()
                self.transport.write(packet)
                # window opened
                return

        # Ignore clients that think -1 is placeable.
        if container.primary == -1:
            return

        # Special case when face is "noop": Update the status of the currently
        # held block rather than placing a new block.
        if container.face == "noop":
            return

        # If the target block is vanishable, then adjust our aim accordingly.
        if target.vanishes:
            container.face = "+y"
            container.y -= 1

        if container.primary in blocks:
            block = blocks[container.primary]
        elif container.primary in items:
            block = items[container.primary]
        else:
            log.err("Ignoring request to place unknown block %d" %
                container.primary)
            return

        # Run pre-build hooks. These hooks are able to interrupt the build
        # process.
        builddata = BuildData(block, 0x0, container.x, container.y,
            container.z, container.face)

        for hook in self.pre_build_hooks:
            cont, builddata, cancel = yield maybeDeferred(hook.pre_build_hook,
                self.player, builddata)
            if cancel:
                # Flush damaged chunks.
                for chunk in self.chunks.itervalues():
                    self.factory.flush_chunk(chunk)
                return
            if not cont:
                break

        # Run the build.
        try:
            yield maybeDeferred(self.run_build, builddata)
        except BuildError:
            return

        newblock = builddata.block.slot
        coords = adjust_coords_for_face(
            (builddata.x, builddata.y, builddata.z), builddata.face)

        # Run post-build hooks. These are merely callbacks which cannot
        # interfere with the build process, largely because the build process
        # already happened.
        for hook in self.post_build_hooks:
            yield maybeDeferred(hook.post_build_hook, self.player, coords,
                builddata.block)

        # Feed automatons.
        for automaton in self.factory.automatons:
            if newblock in automaton.blocks:
                automaton.feed(coords)

        # Re-send inventory.
        # XXX this could be optimized if/when inventories track damage.
        packet = self.inventory.save_to_packet()
        self.transport.write(packet)

        # Flush damaged chunks.
        for chunk in self.chunks.itervalues():
            self.factory.flush_chunk(chunk)
Exemplo n.º 14
0
    def build(self, container):
        if container.x == -1 and container.z == -1 and container.y == 255:
            # Lala-land build packet. Discard it for now.
            return

        # Is the target being selected?
        bigx, smallx, bigz, smallz = split_coords(container.x, container.z)
        try:
            chunk = self.chunks[bigx, bigz]
        except KeyError:
            self.error("Couldn't select in chunk (%d, %d)!" % (bigx, bigz))
            return

        # Try to open it first
        for hook in self.open_hooks:
            window = yield maybeDeferred(
                hook.open_hook, self, container,
                chunk.get_block((smallx, container.y, smallz)))
            if window:
                self.write_packet("window-open",
                                  wid=window.wid,
                                  type=window.identifier,
                                  title=window.title,
                                  slots=window.slots_num)
                packet = window.save_to_packet()
                self.transport.write(packet)
                # window opened
                return

        # Ignore clients that think -1 is placeable.
        if container.primary == -1:
            return

        # Special case when face is "noop": Update the status of the currently
        # held block rather than placing a new block.
        if container.face == "noop":
            return

        if container.primary in blocks:
            block = blocks[container.primary]
        elif container.primary in items:
            block = items[container.primary]
        else:
            log.err("Ignoring request to place unknown block %d" %
                    container.primary)
            return

        # it's the top of the world, you can't build here
        if container.y == 127 and container.face == '+y':
            return

        # Run pre-build hooks. These hooks are able to interrupt the build
        # process.
        builddata = BuildData(block, 0x0, container.x, container.y,
                              container.z, container.face)

        for hook in self.pre_build_hooks:
            cont, builddata, cancel = yield maybeDeferred(
                hook.pre_build_hook, self.player, builddata)
            if cancel:
                # Flush damaged chunks.
                for chunk in self.chunks.itervalues():
                    self.factory.flush_chunk(chunk)
                return
            if not cont:
                break

        # Run the build.
        try:
            yield maybeDeferred(self.run_build, builddata)
        except BuildError:
            return

        newblock = builddata.block.slot
        coords = adjust_coords_for_face(
            (builddata.x, builddata.y, builddata.z), builddata.face)

        # Run post-build hooks. These are merely callbacks which cannot
        # interfere with the build process, largely because the build process
        # already happened.
        for hook in self.post_build_hooks:
            yield maybeDeferred(hook.post_build_hook, self.player, coords,
                                builddata.block)

        # Feed automatons.
        for automaton in self.factory.automatons:
            if newblock in automaton.blocks:
                automaton.feed(coords)

        # Re-send inventory.
        # XXX this could be optimized if/when inventories track damage.
        packet = self.inventory.save_to_packet()
        self.transport.write(packet)

        # Flush damaged chunks.
        for chunk in self.chunks.itervalues():
            self.factory.flush_chunk(chunk)
Exemplo n.º 15
0
    def pre_build_hook(self, player, builddata):
        item, metadata, x, y, z, face = builddata

        if item.slot == items["sign"].slot:
            # Buildin' a sign, puttin' it on a wall...
            builddata = builddata._replace(block=blocks["wall-sign"])

            # Offset coords according to face.
            if face == "-x":
                builddata = builddata._replace(metadata=0x4)
                x -= 1
            elif face == "+x":
                builddata = builddata._replace(metadata=0x5)
                x += 1
            elif face == "-y":
                # Ceiling Sign is watching you read.
                returnValue((False, builddata, False))
            elif face == "+y":
                # Put +Y signs on signposts. We're fancy that way. Also,
                # calculate the proper orientation based on player
                # orientation.
                # 180 degrees around to orient the signs correctly, and then
                # 23 degrees to get the sign to midpoint correctly.
                metadata = ((player.location.yaw + 180) * 16 // 360) % 0xf
                builddata = builddata._replace(block=blocks["signpost"],
                    metadata=metadata)
                y += 1
            elif face == "-z":
                builddata = builddata._replace(metadata=0x2)
                z -= 1
            elif face == "+z":
                builddata = builddata._replace(metadata=0x3)
                z += 1

            bigx, smallx, bigz, smallz = split_coords(x, z)

            # Let's build a sign!
            chunk = yield factory.world.request_chunk(bigx, bigz)
            s = Sign(smallx, y, smallz)
            chunk.tiles[smallx, y, smallz] = s

        elif item.slot in self.block_to_tile:
            x, y, z = adjust_coords_for_face((x, y, z), face)
            bigx, smallx, bigz, smallz = split_coords(x, z)

            if item.slot == blocks["chest"].slot:
                # Chests have some restrictions on building:
                # you cannot connect more than two chests.
                # (notchian)
                ccs = chestsAround(factory, (x, y, z))
                ccn = len(ccs)
                if ccn == 1:
                    # check gonna-be-connected chest is not connected already
                    n = len(chestsAround(factory, ccs[0]))
                    if n != 0:
                        returnValue((False, builddata, True))
                elif ccn > 1:
                    # cannot build three or more connected chests
                    returnValue((False, builddata, True))
            elif item.slot == blocks["furnace"].slot:
                # the furnace cannot be oriented up or down
                if face == "-y" or face == "+y":
                    orientation = ('+x', '+z', '-x', '-z')[((int(player.location.yaw) \
                                                        - 45 + 360) % 360) / 90]
                    metadata = blocks["furnace"].orientation(orientation)
                    builddata = builddata._replace(metadata=metadata)

            chunk = yield factory.world.request_chunk(bigx, bigz)

            # Not much to do, just tell the chunk about this tile.
            tileClass = self.block_to_tile[item.slot]
            tile = tileClass(smallx, y, smallz)
            chunk.tiles[smallx, y, smallz] = tile

        returnValue((True, builddata, False))
Exemplo n.º 16
0
    def test_adjust_plusx(self):
        coords = range(3)

        adjusted = adjust_coords_for_face(coords, "+x")

        self.assertEqual(adjusted, (1, 1, 2))
Exemplo n.º 17
0
    def build(self, container):
        """
        Handle a build packet.

        Several things must happen. First, the packet's contents need to be
        examined to ensure that the packet is valid. A check is done to see if
        the packet is opening a windowed object. If not, then a build is
        run.
        """

        # Is the target within our purview? We don't do a very strict
        # containment check, but we *do* require that the chunk be loaded.
        bigx, smallx, bigz, smallz = split_coords(container.x, container.z)
        try:
            chunk = self.chunks[bigx, bigz]
        except KeyError:
            self.error("Couldn't select in chunk (%d, %d)!" % (bigx, bigz))
            return

        target = blocks[chunk.get_block((smallx, container.y, smallz))]

        # Attempt to open a window.
        from bravo.policy.windows import window_for_block
        window = window_for_block(target)
        if window is not None:
            # We have a window!
            self.windows[self.wid] = window
            identifier, title, slots = window.open()
            self.write_packet("window-open", wid=self.wid, type=identifier,
                              title=title, slots=slots)
            self.wid += 1
            return

        # Try to open it first
        for hook in self.open_hooks:
            window = yield maybeDeferred(hook.open_hook, self, container,
                           chunk.get_block((smallx, container.y, smallz)))
            if window:
                self.write_packet("window-open", wid=window.wid,
                    type=window.identifier, title=window.title,
                    slots=window.slots_num)
                packet = window.save_to_packet()
                self.transport.write(packet)
                # window opened
                return

        # Ignore clients that think -1 is placeable.
        if container.primary == -1:
            return

        # Special case when face is "noop": Update the status of the currently
        # held block rather than placing a new block.
        if container.face == "noop":
            return

        # If the target block is vanishable, then adjust our aim accordingly.
        if target.vanishes:
            container.face = "+y"
            container.y -= 1

        if container.primary in blocks:
            block = blocks[container.primary]
        elif container.primary in items:
            block = items[container.primary]
        else:
            log.err("Ignoring request to place unknown block 0x%x" %
                    container.primary)
            return

        # Run pre-build hooks. These hooks are able to interrupt the build
        # process.
        builddata = BuildData(block, 0x0, container.x, container.y,
            container.z, container.face)

        for hook in self.pre_build_hooks:
            cont, builddata, cancel = yield maybeDeferred(hook.pre_build_hook,
                self.player, builddata)
            if cancel:
                # Flush damaged chunks.
                for chunk in self.chunks.itervalues():
                    self.factory.flush_chunk(chunk)
                return
            if not cont:
                break

        # Run the build.
        try:
            yield maybeDeferred(self.run_build, builddata)
        except BuildError:
            return

        newblock = builddata.block.slot
        coords = adjust_coords_for_face(
            (builddata.x, builddata.y, builddata.z), builddata.face)

        # Run post-build hooks. These are merely callbacks which cannot
        # interfere with the build process, largely because the build process
        # already happened.
        for hook in self.post_build_hooks:
            yield maybeDeferred(hook.post_build_hook, self.player, coords,
                builddata.block)

        # Feed automatons.
        for automaton in self.factory.automatons:
            if newblock in automaton.blocks:
                automaton.feed(coords)

        # Re-send inventory.
        # XXX this could be optimized if/when inventories track damage.
        packet = self.inventory.save_to_packet()
        self.transport.write(packet)

        # Flush damaged chunks.
        for chunk in self.chunks.itervalues():
            self.factory.flush_chunk(chunk)