Actually fix signs and start on campfire/container translators

Since signs are not sent as block (tile) entities in chunks when it comes to later Minecraft versions, caching and sending the signs after the chunk packet has been sent was the only way to fix this. Sign data sending has intentionally been delayed in JavaUpdateTileEntityTranslator in the event that a chunk takes a long time to send and the block entity data is sent first.
This commit is contained in:
RednedEpic 2019-12-30 21:55:17 -06:00
parent e1ba1fa3e8
commit da8bd8a659
11 changed files with 204 additions and 66 deletions

View file

@ -46,6 +46,7 @@ import lombok.Getter;
import org.geysermc.connector.network.translators.bedrock.*;
import org.geysermc.connector.network.translators.block.BlockTranslator;
import org.geysermc.connector.network.translators.block.entity.BlockEntityTranslator;
import org.geysermc.connector.network.translators.block.entity.ContainerBlockEntityTranslator;
import org.geysermc.connector.network.translators.block.entity.EmptyBlockEntityTranslator;
import org.geysermc.connector.network.translators.block.entity.SignBlockEntityTranslator;
import org.geysermc.connector.network.translators.inventory.GenericInventoryTranslator;
@ -174,8 +175,9 @@ public class TranslatorsInit {
}
private static void registerBlockEntityTranslators() {
blockEntityTranslators.put("Empty", new EmptyBlockEntityTranslator());
blockEntityTranslators.put("Sign", new SignBlockEntityTranslator());
blockEntityTranslators.put("Empty", new EmptyBlockEntityTranslator("empty", "Empty"));
blockEntityTranslators.put("Sign", new SignBlockEntityTranslator("minecraft:sign", "Sign"));
blockEntityTranslators.put("Campfire", new ContainerBlockEntityTranslator("minecraft:campfire", "Campfire"));
}
private static void registerInventoryTranslators() {

View file

@ -30,43 +30,48 @@ import com.github.steveice10.opennbt.tag.builtin.IntTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.nukkitx.nbt.CompoundTagBuilder;
import com.nukkitx.nbt.tag.Tag;
import org.geysermc.connector.utils.BlockEntityUtils;
import lombok.AllArgsConstructor;
import java.util.List;
@AllArgsConstructor
public abstract class BlockEntityTranslator {
protected String javaId;
protected String bedrockId;
public abstract List<Tag<?>> translateTag(CompoundTag tag);
public abstract CompoundTag getDefaultJavaTag(int x, int y, int z);
public abstract com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(int x, int y, int z);
public com.nukkitx.nbt.tag.CompoundTag getBlockEntityTag(CompoundTag tag, String id) {
public com.nukkitx.nbt.tag.CompoundTag getBlockEntityTag(CompoundTag tag) {
int x = Integer.parseInt(String.valueOf(tag.getValue().get("x").getValue()));
int y = Integer.parseInt(String.valueOf(tag.getValue().get("y").getValue()));
int z = Integer.parseInt(String.valueOf(tag.getValue().get("z").getValue()));
CompoundTagBuilder tagBuilder = getConstantBedrockTag(id, x, y, z).toBuilder();
CompoundTagBuilder tagBuilder = getConstantBedrockTag(x, y, z).toBuilder();
translateTag(tag).forEach(tagBuilder::tag);
return tagBuilder.buildRootTag();
}
protected CompoundTag getConstantJavaTag(String id, int x, int y, int z) {
protected CompoundTag getConstantJavaTag(int x, int y, int z) {
CompoundTag tag = new CompoundTag("");
tag.put(new IntTag("x", x));
tag.put(new IntTag("y", y));
tag.put(new IntTag("z", z));
tag.put(new StringTag("id", id));
tag.put(new StringTag("id", javaId));
return tag;
}
protected com.nukkitx.nbt.tag.CompoundTag getConstantBedrockTag(String id, int x, int y, int z) {
protected com.nukkitx.nbt.tag.CompoundTag getConstantBedrockTag(int x, int y, int z) {
CompoundTagBuilder tagBuilder = CompoundTagBuilder.builder()
.intTag("x", x)
.intTag("y", y)
.intTag("z", z)
.stringTag("id", id);
.stringTag("id", bedrockId);
return tagBuilder.buildRootTag();
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2019 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.network.translators.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.nukkitx.nbt.CompoundTagBuilder;
import com.nukkitx.nbt.tag.IntArrayTag;
import com.nukkitx.nbt.tag.Tag;
import org.geysermc.connector.network.translators.TranslatorsInit;
import org.geysermc.connector.network.translators.item.ItemEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ContainerBlockEntityTranslator extends BlockEntityTranslator {
public ContainerBlockEntityTranslator(String javaId, String bedrockId) {
super(javaId, bedrockId);
}
@Override
public List<Tag<?>> translateTag(CompoundTag tag) {
List<Tag<?>> tags = new ArrayList<>();
ListTag items = tag.get("Items");
List<com.nukkitx.nbt.tag.CompoundTag> tagsList = new ArrayList<>();
for (com.github.steveice10.opennbt.tag.builtin.Tag itemTag : items.getValue()) {
tagsList.add(getItem((CompoundTag) itemTag));
}
int[] fakeCookingTime = new int[4];
Arrays.fill(fakeCookingTime, 3);
com.nukkitx.nbt.tag.ListTag<com.nukkitx.nbt.tag.CompoundTag> bedrockItems =
new com.nukkitx.nbt.tag.ListTag<>("Items", com.nukkitx.nbt.tag.CompoundTag.class, tagsList);
tags.add(bedrockItems);
tags.add(new IntArrayTag("CookingTimes", fakeCookingTime));
tags.add(new IntArrayTag("CookingTotalTimes", fakeCookingTime));
return tags;
}
@Override
public CompoundTag getDefaultJavaTag(int x, int y, int z) {
CompoundTag tag = getConstantJavaTag(x, y, z);
tag.put(new ListTag("Items"));
return tag;
}
@Override
public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(int x, int y, int z) {
CompoundTagBuilder tagBuilder = getConstantBedrockTag(x, y, z).toBuilder();
tagBuilder.listTag("Items", com.nukkitx.nbt.tag.CompoundTag.class, new ArrayList<>());
return tagBuilder.buildRootTag();
}
protected com.nukkitx.nbt.tag.CompoundTag getItem(CompoundTag tag) {
ItemEntry entry = TranslatorsInit.getItemTranslator().getItemEntry((String) tag.get("id").getValue());
CompoundTagBuilder tagBuilder = CompoundTagBuilder.builder()
.shortTag("id", (short) entry.getBedrockId())
.byteTag("Count", (byte) tag.get("Count").getValue())
.shortTag("Damage", (short) entry.getBedrockData())
.byteTag("Slot", (byte) tag.get("Slot").getValue())
.tag(CompoundTagBuilder.builder().build("tag"));
return tagBuilder.buildRootTag();
}
}

View file

@ -33,6 +33,10 @@ import java.util.List;
public class EmptyBlockEntityTranslator extends BlockEntityTranslator {
public EmptyBlockEntityTranslator(String javaId, String bedrockId) {
super(javaId, bedrockId);
}
@Override
public List<Tag<?>> translateTag(CompoundTag tag) {
return new ArrayList<>();
@ -40,11 +44,11 @@ public class EmptyBlockEntityTranslator extends BlockEntityTranslator {
@Override
public CompoundTag getDefaultJavaTag(int x, int y, int z) {
return getConstantJavaTag("empty", x, y, z);
return getConstantJavaTag(x, y, z);
}
@Override
public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(int x, int y, int z) {
return getConstantBedrockTag("Empty", x, y, z);
return getConstantBedrockTag(x, y, z);
}
}

View file

@ -28,9 +28,9 @@ package org.geysermc.connector.network.translators.block.entity;
import com.github.steveice10.mc.protocol.data.message.Message;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.nbt.CompoundTagBuilder;
import com.nukkitx.nbt.tag.ByteTag;
import com.nukkitx.nbt.tag.StringTag;
import com.nukkitx.nbt.tag.Tag;
import org.geysermc.connector.utils.MessageUtils;
import java.util.ArrayList;
@ -38,6 +38,10 @@ import java.util.List;
public class SignBlockEntityTranslator extends BlockEntityTranslator {
public SignBlockEntityTranslator(String javaId, String bedrockId) {
super(javaId, bedrockId);
}
@Override
public List<Tag<?>> translateTag(CompoundTag tag) {
List<Tag<?>> tags = new ArrayList<>();
@ -53,13 +57,12 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator {
+ "\n" + MessageUtils.getBedrockMessage(Message.fromString(line4))
));
tags.add(new ByteTag("isMovable", (byte) 0));
return tags;
}
@Override
public CompoundTag getDefaultJavaTag(int x, int y, int z) {
CompoundTag tag = getConstantJavaTag("minecraft:sign", x, y, z);
CompoundTag tag = getConstantJavaTag(x, y, z);
tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text1", "{\"text\":\"\"}"));
tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text2", "{\"text\":\"\"}"));
tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text3", "{\"text\":\"\"}"));
@ -69,9 +72,8 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator {
@Override
public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(int x, int y, int z) {
CompoundTagBuilder tagBuilder = getConstantBedrockTag("Sign", x, y, z).toBuilder();
CompoundTagBuilder tagBuilder = getConstantBedrockTag(x, y, z).toBuilder();
tagBuilder.stringTag("Text", "");
tagBuilder.byteTag("isMovable", (byte) 0);
return tagBuilder.buildRootTag();
}
}

View file

@ -41,6 +41,7 @@ import com.github.steveice10.opennbt.tag.builtin.ShortTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.protocol.bedrock.data.ItemData;
import org.geysermc.connector.console.GeyserLogger;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.utils.Toolbox;
@ -52,6 +53,8 @@ import java.util.Map;
public class ItemTranslator {
private Map<String, ItemEntry> javaIdentifierMap = new HashMap<>();
public ItemStack translateToJava(ItemData data) {
ItemEntry javaItem = getItem(data);
@ -89,6 +92,11 @@ public class ItemTranslator {
return ItemEntry.AIR;
}
public ItemEntry getItemEntry(String javaIdentifier) {
return javaIdentifierMap.computeIfAbsent(javaIdentifier, key -> Toolbox.ITEM_ENTRIES.values()
.stream().filter(itemEntry -> itemEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null));
}
private CompoundTag translateToJavaNBT(com.nukkitx.nbt.tag.CompoundTag tag) {
CompoundTag javaTag = new CompoundTag(tag.getName());
Map<String, Tag> javaValue = javaTag.getValue();

View file

@ -1,5 +1,7 @@
package org.geysermc.connector.network.translators.java.world;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerChunkDataPacket;
import com.nukkitx.math.vector.Vector2i;
import com.nukkitx.math.vector.Vector3f;
@ -9,9 +11,13 @@ import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.network.VarInts;
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import org.geysermc.api.Geyser;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
@ -22,7 +28,6 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
@Override
public void translate(ServerChunkDataPacket packet, GeyserSession session) {
// Not sure if this is safe or not, however without this the client usually times out
Geyser.getConnector().getGeneralThreadPool().execute(() -> {
Vector2i chunkPos = session.getLastChunkPosition();
Vector3f position = session.getPlayerEntity().getPosition();
@ -33,7 +38,6 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
chunkPublisherUpdatePacket.setPosition(position.toInt());
chunkPublisherUpdatePacket.setRadius(session.getRenderDistance() << 4);
session.getUpstream().sendPacket(chunkPublisherUpdatePacket);
session.setLastChunkPosition(newChunkPos);
}
@ -75,6 +79,16 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
levelChunkPacket.setChunkZ(packet.getColumn().getZ());
levelChunkPacket.setData(payload);
session.getUpstream().sendPacket(levelChunkPacket);
// Signs have to be sent after the chunk since in later versions they aren't included in the block entities
for (Int2ObjectMap.Entry<CompoundTag> blockEntityEntry : chunkData.signs.int2ObjectEntrySet()) {
int x = blockEntityEntry.getValue().getAsInt("x");
int y = blockEntityEntry.getValue().getAsInt("y");
int z = blockEntityEntry.getValue().getAsInt("z");
ChunkUtils.updateBlock(session, new BlockState(blockEntityEntry.getIntKey()), new Position(x, y, z));
}
chunkData.signs.clear();
} catch (Exception ex) {
ex.printStackTrace();
}

View file

@ -26,26 +26,29 @@
package org.geysermc.connector.network.translators.java.world;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdateTileEntityPacket;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.block.entity.BlockEntityTranslator;
import org.geysermc.connector.utils.BlockEntityUtils;
import java.util.concurrent.TimeUnit;
public class JavaUpdateTileEntityTranslator extends PacketTranslator<ServerUpdateTileEntityPacket> {
@Override
public void translate(ServerUpdateTileEntityPacket packet, GeyserSession session) {
BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket();
blockEntityPacket.setBlockPosition(Vector3i.from(packet.getPosition().getX(),
packet.getPosition().getY(),
packet.getPosition().getZ())
);
String id = BlockEntityUtils.getBedrockBlockEntityId(packet.getType().name());
BlockEntityTranslator translator = BlockEntityUtils.getBlockEntityTranslator(id);
blockEntityPacket.setData(translator.getBlockEntityTag(packet.getNbt(), id));
session.getUpstream().sendPacket(blockEntityPacket);
if (id.equalsIgnoreCase("Sign")) {
// Delay so chunks can finish sending
session.getConnector().getGeneralThreadPool().schedule(() ->
BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(packet.getNbt()), packet.getPosition()),
5,
TimeUnit.SECONDS
);
} else {
BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(packet.getNbt()), packet.getPosition());
}
}
}

View file

@ -1,5 +1,10 @@
package org.geysermc.connector.utils;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.TranslatorsInit;
import org.geysermc.connector.network.translators.block.entity.BlockEntityTranslator;
@ -10,8 +15,9 @@ public class BlockEntityUtils {
if (id.contains("piston_head"))
return "PistonArm";
id = id.replace("minecraft:", "");
id = id.replace("_", " ");
id = id.toLowerCase()
.replace("minecraft:", "")
.replace("_", " ");
String[] words = id.split(" ");
for (int i = 0; i < words.length; i++) {
words[i] = words[i].substring(0, 1).toUpperCase() + words[i].substring(1).toLowerCase();
@ -29,4 +35,11 @@ public class BlockEntityUtils {
return blockEntityTranslator;
}
public static void updateBlockEntity(GeyserSession session, com.nukkitx.nbt.tag.CompoundTag blockEntity, Position position) {
BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket();
blockEntityPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ()));
blockEntityPacket.setData(blockEntity);
session.getUpstream().sendPacket(blockEntityPacket);
}
}

View file

@ -3,35 +3,31 @@ package org.geysermc.connector.utils;
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
import com.github.steveice10.mc.protocol.data.game.chunk.Column;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockChangeRecord;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.geysermc.connector.console.GeyserLogger;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.geysermc.connector.console.GeyserLogger;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.TranslatorsInit;
import org.geysermc.connector.network.translators.block.BlockEntry;
import org.geysermc.connector.network.translators.block.entity.BlockEntityTranslator;
import org.geysermc.connector.network.translators.block.entity.SignBlockEntityTranslator;
import org.geysermc.connector.world.chunk.ChunkPosition;
import org.geysermc.connector.world.chunk.ChunkSection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ChunkUtils {
public static ChunkData translateToBedrock(Column column) {
ChunkData chunkData = new ChunkData();
Chunk[] chunks = column.getChunks();
int chunkSectionCount = chunks.length;
chunkData.sections = new ChunkSection[chunkSectionCount];
chunkData.sections = new ChunkSection[chunks.length];
List<CompoundTag> blockEntities = new ArrayList<>(Arrays.asList(column.getTileEntities()));
for (int chunkY = 0; chunkY < chunkSectionCount; chunkY++) {
CompoundTag[] blockEntities = column.getTileEntities();
for (int chunkY = 0; chunkY < chunks.length; chunkY++) {
chunkData.sections[chunkY] = new ChunkSection();
Chunk chunk = chunks[chunkY];
@ -39,15 +35,17 @@ public class ChunkUtils {
continue;
ChunkSection section = chunkData.sections[chunkY];
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
BlockState blockState = chunk.get(x, y, z);
BlockEntry block = TranslatorsInit.getBlockTranslator().getBlockEntry(blockState);
section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z),
block.getBedrockRuntimeId());
if (block.getJavaIdentifier().contains("sign[")) {
Position pos = new ChunkPosition(column.getX(), column.getZ()).getBlock(x, (chunkY << 4) + y, z);
chunkData.signs.put(block.getJavaId(), TranslatorsInit.getBlockEntityTranslators().get("Sign").getDefaultBedrockTag(pos.getX(), pos.getY(), pos.getZ()));
} else {
section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), block.getBedrockRuntimeId());
}
if (block.isWaterlogged()) {
BlockEntry water = TranslatorsInit.getBlockTranslator().getBlockEntry("minecraft:water[level=0]");
@ -58,17 +56,20 @@ public class ChunkUtils {
}
}
List<com.nukkitx.nbt.tag.CompoundTag> bedrockBlockEntities = new ArrayList<>();
for (CompoundTag tag : blockEntities) {
Tag idTag = tag.get("id");
if (idTag == null && !tag.contains("Sign")) {
com.nukkitx.nbt.tag.CompoundTag[] bedrockBlockEntities = new com.nukkitx.nbt.tag.CompoundTag[blockEntities.length];
for (int i = 0; i < bedrockBlockEntities.length; i++) {
CompoundTag tag = blockEntities[i];
String tagName;
if (!tag.contains("id")) {
GeyserLogger.DEFAULT.debug("Got tag with no id: " + tag.getValue());
continue;
tagName = "Empty";
} else {
tagName = (String) tag.get("id").getValue();
}
String id = idTag == null ? "Sign" : BlockEntityUtils.getBedrockBlockEntityId((String) idTag.getValue());
String id = BlockEntityUtils.getBedrockBlockEntityId(tagName);
BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(id);
bedrockBlockEntities.add(blockEntityTranslator.getBlockEntityTag(tag, id));
bedrockBlockEntities[i] = blockEntityTranslator.getBlockEntityTag(tag);
}
chunkData.blockEntities = bedrockBlockEntities;
@ -102,6 +103,8 @@ public class ChunkUtils {
public ChunkSection[] sections;
public byte[] biomes = new byte[256];
public List<com.nukkitx.nbt.tag.CompoundTag> blockEntities = new ArrayList<>();
public com.nukkitx.nbt.tag.CompoundTag[] blockEntities = new com.nukkitx.nbt.tag.CompoundTag[0];
public Int2ObjectMap<com.nukkitx.nbt.tag.CompoundTag> signs = new Int2ObjectOpenHashMap<>();
}
}

View file

@ -18,17 +18,9 @@ public class ChunkPosition {
}
public Position getChunkBlock(int x, int y, int z) {
int chunkX = x % 16;
int chunkY = y % 16;
int chunkZ = z % 16;
if (chunkX < 0)
chunkX = -chunkX;
if (chunkY < 0)
chunkY = -chunkY;
if (chunkZ < 0)
chunkZ = -chunkZ;
int chunkX = x & 15;
int chunkY = y & 15;
int chunkZ = z & 15;
return new Position(chunkX, chunkY, chunkZ);
}
}