Fix a few chunk errors

This does not fix chunks on its own, they're still very much a work in progress, however this commit should hopefully resolve a few issues. The client might still crash due to some invalid mappings (most likely), but this should bring us one step closer.

With hardcoded values for the chunks, the client wouldn't crash (which leads me to the conclusion stated above) and on the occasions that it would not time out, the chunks were empty.

Co-authored-by: SupremeMortal <suprememortal@users.noreply.github.com>
This commit is contained in:
RednedEpic 2019-09-08 15:46:10 -05:00 committed by RednedEpic
parent bd8a00b5eb
commit 383429d71b
5 changed files with 104 additions and 97 deletions

View File

@ -100,7 +100,7 @@ import org.geysermc.connector.network.translators.java.entity.spawn.JavaSpawnPla
import org.geysermc.connector.network.translators.java.scoreboard.JavaDisplayScoreboardTranslator;
import org.geysermc.connector.network.translators.java.scoreboard.JavaScoreboardObjectiveTranslator;
import org.geysermc.connector.network.translators.java.scoreboard.JavaUpdateScoreTranslator;
import org.geysermc.connector.network.translators.java.world.JavaChunkDataPacket;
import org.geysermc.connector.network.translators.java.world.JavaChunkDataTranslator;
import org.geysermc.connector.network.translators.java.world.JavaNotifyClientTranslator;
import org.geysermc.connector.network.translators.java.window.JavaOpenWindowTranslator;
import org.geysermc.connector.network.translators.java.window.JavaSetSlotTranslator;
@ -168,7 +168,7 @@ public class TranslatorsInit {
Registry.registerJava(ServerPlayerHealthPacket.class, new JavaPlayerHealthTranslator());
Registry.registerJava(ServerNotifyClientPacket.class, new JavaNotifyClientTranslator());
Registry.registerJava(ServerChunkDataPacket.class, new JavaChunkDataPacket());
Registry.registerJava(ServerChunkDataPacket.class, new JavaChunkDataTranslator());
Registry.registerJava(ServerEntityDestroyPacket.class, new JavaEntityDestroyTranslator());
Registry.registerJava(ServerWindowItemsPacket.class, new JavaWindowItemsTranslator());
Registry.registerJava(ServerOpenWindowPacket.class, new JavaOpenWindowTranslator());

View File

@ -1,55 +0,0 @@
package org.geysermc.connector.network.translators.java.world;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerChunkDataPacket;
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.geysermc.connector.console.GeyserLogger;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.utils.ChunkUtils;
import org.geysermc.connector.world.chunk.ChunkSection;
public class JavaChunkDataPacket extends PacketTranslator<ServerChunkDataPacket> {
@Override
public void translate(ServerChunkDataPacket packet, GeyserSession session) {
try {
byte[] buffer = new byte[32];
ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(packet.getColumn());
int count = 0;
ChunkSection[] sections = chunkData.sections;
for (int i = sections.length - 1; i >= 0; i--) {
if (sections[i].isEmpty())
continue;
count = i + 1;
break;
}
for (int i = 0; i < count; i++) {
ChunkUtils.putBytes(count, buffer, new byte[]{(byte) 0});
ChunkSection section = chunkData.sections[i];
ByteBuf byteBuf = Unpooled.buffer();
section.writeToNetwork(byteBuf);
byte[] byteData = byteBuf.array();
ChunkUtils.putBytes(count, buffer, byteData);
}
LevelChunkPacket levelChunkPacket = new LevelChunkPacket();
levelChunkPacket.setSubChunksLength(16);
levelChunkPacket.setCachingEnabled(true);
levelChunkPacket.setChunkX(packet.getColumn().getX());
levelChunkPacket.setChunkZ(packet.getColumn().getZ());
levelChunkPacket.setData(buffer);
session.getUpstream().sendPacket(levelChunkPacket);
} catch (Exception ex) {
ex.printStackTrace();
}
GeyserLogger.DEFAULT.info("Sent chunk packet!");
}
}

View File

@ -0,0 +1,59 @@
package org.geysermc.connector.network.translators.java.world;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerChunkDataPacket;
import com.nukkitx.network.VarInts;
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.geysermc.api.Geyser;
import org.geysermc.connector.console.GeyserLogger;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.utils.ChunkUtils;
import org.geysermc.connector.world.chunk.ChunkSection;
public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPacket> {
@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(() -> {
try {
ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(packet.getColumn());
ByteBuf byteBuf = Unpooled.buffer(32);
int count = 0;
ChunkSection[] sections = chunkData.sections;
for (int i = sections.length - 1; i >= 0; i--) {
if (sections[i].isEmpty())
continue;
count = i + 1;
break;
}
for (int i = 0; i < count; i++) {
ChunkSection section = chunkData.sections[i];
section.writeToNetwork(byteBuf);
}
byteBuf.writeBytes(chunkData.biomes); // Biomes - 256 bytes
byteBuf.writeByte(0); // Border blocks - Edu edition only
VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now
byte[] payload = new byte[byteBuf.writerIndex()];
byteBuf.readBytes(payload);
LevelChunkPacket levelChunkPacket = new LevelChunkPacket();
levelChunkPacket.setSubChunksLength(count);
levelChunkPacket.setCachingEnabled(false);
levelChunkPacket.setChunkX(packet.getColumn().getX());
levelChunkPacket.setChunkZ(packet.getColumn().getZ());
levelChunkPacket.setData(payload);
session.getUpstream().sendPacket(levelChunkPacket);
} catch (Exception ex) {
ex.printStackTrace();
}
GeyserLogger.DEFAULT.info("Sent chunk packet!");
});
}
}

View File

@ -8,12 +8,8 @@ import org.geysermc.connector.network.translators.TranslatorsInit;
import org.geysermc.connector.network.translators.item.BedrockItem;
import org.geysermc.connector.world.chunk.ChunkSection;
import java.util.Arrays;
public class ChunkUtils {
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
public static ChunkData translateToBedrock(Column column) {
ChunkData chunkData = new ChunkData();
chunkData.sections = new ChunkSection[16];
@ -21,6 +17,7 @@ public class ChunkUtils {
chunkData.sections[i] = new ChunkSection();
}
/*
for (int y = 0; y < 256; y++) {
int chunkY = y >> 4;
@ -37,55 +34,61 @@ public class ChunkUtils {
BlockStorage storage = chunk.getBlocks();
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
BlockState block = storage.get(x, y & 0xF, z);
BlockState block = storage.get(x, chunkY, z);
if (block == null)
block = new BlockState(0);
BedrockItem bedrockBlock = TranslatorsInit.getItemTranslator().getBedrockBlock(block);
ChunkSection section = chunkData.sections[chunkY];
org.geysermc.connector.world.chunk.BlockStorage blockStorage = new org.geysermc.connector.world.chunk.BlockStorage();
blockStorage.setFullBlock(ChunkSection.blockPosition(x, y, z), bedrockBlock.getId());
//org.geysermc.connector.world.chunk.BlockStorage blockStorage = new org.geysermc.connector.world.chunk.BlockStorage();
int runtimeId = GlobalBlockPalette.getOrCreateRuntimeId(bedrockBlock.getId(), bedrockBlock.getData());
section.setFullBlock(x, y >> 4, z, 0, runtimeId << 2 | bedrockBlock.getData());
section.getBlockStorageArray()[0] = blockStorage;
section.getBlockStorageArray()[1] = blockStorage;
//section.getBlockStorageArray()[0] = blockStorage;
//section.getBlockStorageArray()[1] = blockStorage;
}
}
}
*/
for (int chunkY = 0; chunkY < 16; chunkY++) {
Chunk chunk = null;
try {
chunk = column.getChunks()[chunkY];
} catch (Exception ex) {
ex.printStackTrace();
}
if (chunk == null || chunk.isEmpty())
continue;
BlockStorage storage = chunk.getBlocks();
ChunkSection section = chunkData.sections[chunkY];
section.getBlockStorageArray()[0] = new org.geysermc.connector.world.chunk.BlockStorage();
section.getBlockStorageArray()[1] = new org.geysermc.connector.world.chunk.BlockStorage();
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
BlockState block = storage.get(x, y, z);
BedrockItem bedrockBlock = TranslatorsInit.getItemTranslator().getBedrockBlock(block);
section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), bedrockBlock.getId() << 4 | bedrockBlock.getData());
}
}
}
}
return chunkData;
}
public static final class ChunkData {
public ChunkSection[] sections;
}
public static void putBytes(int count, byte[] buffer, byte[] bytes) {
if (bytes == null) {
return;
}
int minCapacity = count + bytes.length;
if ((minCapacity) - buffer.length > 0) {
int oldCapacity = buffer.length;
int newCapacity = oldCapacity << 1;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
if (newCapacity - MAX_ARRAY_SIZE > 0) {
newCapacity = hugeCapacity(minCapacity);
}
buffer = Arrays.copyOf(buffer, newCapacity);
}
System.arraycopy(bytes, 0, buffer, count, bytes.length);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) { // overflow
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
public byte[] biomes = new byte[256];
public byte[] blockEntities = new byte[0];
}
}

View File

@ -40,7 +40,7 @@ public class Toolbox {
ByteBuf b = Unpooled.buffer();
VarInts.writeUnsignedInt(b, entries.size());
for (Map<String, Object> e : entries) {
GlobalBlockPalette.registerMapping((int) e.get("id"));
GlobalBlockPalette.registerMapping((int) e.get("id") << 4 | (int) e.get("data"));
BedrockUtils.writeString(b, (String) e.get("name"));
b.writeShortLE((int) e.get("data"));
b.writeShortLE((int) e.get("id"));