Example #1
0
    def get_damage_packet(self):
        """
        Make a packet representing the current damage on this chunk.

        This method is not private, but some care should be taken with it,
        since it wraps some fairly cryptic internal data structures.

        If this chunk is currently undamaged, this method will return an empty
        string, which should be safe to treat as a packet. Please check with
        `is_damaged()` before doing this if you need to optimize this case.

        To avoid extra overhead, this method should really be used in
        conjunction with `Factory.broadcast_for_chunk()`.

        Do not forget to clear this chunk's damage! Callers are responsible
        for doing this.

        >>> packet = chunk.get_damage_packet()
        >>> factory.broadcast_for_chunk(packet, chunk.x, chunk.z)
        >>> chunk.clear_damage()

        :returns: str representing a packet
        """

        if len(self.damaged) == 0:
            return ""
        elif len(self.damaged) == 1:
            # Use a single block update packet.
            x, y, z = next(iter(self.damaged))
            index = triplet_to_index((x, y, z))
            x += self.x * 16
            z += self.z * 16
            return make_packet("block", x=x, y=y, z=z,
                type=self.blocks[index], meta=self.metadata[index])
        else:
            # Use a batch update.
            coords = []
            types = []
            metadata = []
            for x, y, z in self.damaged:
                index = triplet_to_index((x, y, z))
                # Coordinates are not quite packed in the same system as the
                # indices for chunk data structures.
                # Chunk data structures are ((x * 16) + z) * 128) + y, or in
                # bit-twiddler's parlance, x << 11 | z << 7 | y. However, for
                # this, we need x << 12 | z << 8 | y, so repack accordingly.
                packed = x << 12 | z << 8 | y
                coords.append(packed)
                types.append(self.blocks[index])
                metadata.append(self.metadata[index])
            return make_packet("batch", x=self.x, z=self.z,
                length=len(coords), coords=coords, types=types,
                metadata=metadata)
Example #2
0
    def set_block(self, coords, block):
        """
        Update a block value.

        :param tuple coords: coordinate triplet
        :param int block: block type
        """

        x, y, z = coords
        index = triplet_to_index(coords)

        if self.blocks[index] != block:
            self.blocks[index] = block

            # Regenerate heightmap at this coordinate. Honestly not sure
            # whether or not this is cheaper than the set of conditional
            # statements required to update it in relative terms instead of
            # absolute terms. Revisit this later, maybe?
            for y in range(127, -1, -1):
                if self.blocks[index]:
                    break
            self.heightmap[x * 16 + z] = y

            self.dirty = True
            self.damaged.add(coords)
Example #3
0
    def get_block(self, coords):
        """
        Look up a block value.

        :param tuple coords: coordinate triplet

        :returns: int representing block type
        """

        index = triplet_to_index(coords)

        return self.blocks[index]
Example #4
0
    def regenerate_heightmap(self):
        """
        Regenerate the height map.

        The height map is merely the position of the tallest block in any
        xz-column.
        """

        for x, z in product(xrange(16), xrange(16)):
            # Get the index for the top of the column, and then exploit the
            # nature of indices to avoid calling triplet_to_index()
            # repeatedly.
            index = triplet_to_index((x, 0, z))
            for y in range(127, -1, -1):
                if self.blocks[index + y]:
                    break

            self.heightmap[x * 16 + z] = y