Non-full-chunk support (#574)

This commit adds non-full chunk support if chunk caching is enabled.
This commit is contained in:
Camotoy 2020-09-03 19:00:36 -04:00 committed by GitHub
parent 4c58568eb4
commit 5b76a85895
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 320 additions and 17 deletions

View file

@ -42,7 +42,7 @@ public class ChunkCache {
private final boolean cache;
@Getter
private Map<ChunkPosition, Column> chunks = new HashMap<>();
private final Map<ChunkPosition, Column> chunks = new HashMap<>();
public ChunkCache(GeyserSession session) {
if (session.getConnector().getWorldManager().getClass() == GeyserBootstrap.DEFAULT_CHUNK_MANAGER.getClass()) {
@ -57,6 +57,15 @@ public class ChunkCache {
return;
}
ChunkPosition position = new ChunkPosition(chunk.getX(), chunk.getZ());
if (chunk.getBiomeData() == null && chunks.containsKey(position)) {
Column newColumn = chunk;
chunk = chunks.get(position);
for (int i = 0; i < newColumn.getChunks().length; i++) {
if (newColumn.getChunks()[i] != null) {
chunk.getChunks()[i] = newColumn.getChunks()[i];
}
}
}
chunks.put(position, chunk);
}

View file

@ -45,18 +45,33 @@ import org.geysermc.connector.utils.ChunkUtils;
@Translator(packet = ServerChunkDataPacket.class)
public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPacket> {
/**
* Determines if we should process non-full chunks
*/
private final boolean isCacheChunks;
public JavaChunkDataTranslator() {
isCacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks();
}
@Override
public void translate(ServerChunkDataPacket packet, GeyserSession session) {
if (session.isSpawned()) {
ChunkUtils.updateChunkPosition(session, session.getPlayerEntity().getPosition().toInt());
}
if (packet.getColumn().getBiomeData() == null) //Non-full chunk
if (packet.getColumn().getBiomeData() == null && !isCacheChunks) {
// Non-full chunk without chunk caching
session.getConnector().getLogger().debug("Not sending non-full chunk because chunk caching is off.");
return;
}
// Non-full chunks don't have all the chunk data, and Bedrock won't accept that
final boolean isNonFullChunk = (packet.getColumn().getBiomeData() == null);
GeyserConnector.getInstance().getGeneralThreadPool().execute(() -> {
try {
ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(packet.getColumn());
ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(session, packet.getColumn(), isNonFullChunk);
ByteBuf byteBuf = Unpooled.buffer(32);
ChunkSection[] sections = chunkData.sections;
@ -71,7 +86,12 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
section.writeToNetwork(byteBuf);
}
byte[] bedrockBiome = BiomeTranslator.toBedrockBiome(packet.getColumn().getBiomeData());
byte[] bedrockBiome;
if (packet.getColumn().getBiomeData() == null) {
bedrockBiome = BiomeTranslator.toBedrockBiome(session.getConnector().getWorldManager().getBiomeDataAt(session, packet.getColumn().getX(), packet.getColumn().getZ()));
} else {
bedrockBiome = BiomeTranslator.toBedrockBiome(packet.getColumn().getBiomeData());
}
byteBuf.writeBytes(bedrockBiome); // Biomes - 256 bytes
byteBuf.writeByte(0); // Border blocks - Edu edition only

View file

@ -32,6 +32,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.ClientChatPacket;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.chunk.ChunkPosition;
import org.geysermc.connector.utils.GameRule;
public class GeyserWorldManager extends WorldManager {
@ -43,6 +44,13 @@ public class GeyserWorldManager extends WorldManager {
return session.getChunkCache().getBlockAt(new Position(x, y, z));
}
@Override
public int[] getBiomeDataAt(GeyserSession session, int x, int z) {
if (!session.getConnector().getConfig().isCacheChunks())
return new int[1024];
return session.getChunkCache().getChunks().get(new ChunkPosition(x, z)).getBiomeData();
}
@Override
public void setGameRule(GeyserSession session, String name, Object value) {
session.sendDownstreamPacket(new ClientChatPacket("/gamerule " + name + " " + value));

View file

@ -74,6 +74,16 @@ public abstract class WorldManager {
*/
public abstract int getBlockAt(GeyserSession session, int x, int y, int z);
/**
* Gets the biome data for the specified chunk.
*
* @param session the session of the player
* @param x the chunk's X coordinate
* @param z the chunk's Z coordinate
* @return the biome data for the specified region with a length of 1024.
*/
public abstract int[] getBiomeDataAt(GeyserSession session, int x, int z);
/**
* Updates a gamerule value on the Java server
*

View file

@ -81,7 +81,7 @@ public class ChunkUtils {
}
}
public static ChunkData translateToBedrock(Column column) {
public static ChunkData translateToBedrock(GeyserSession session, Column column, boolean isNonFullChunk) {
ChunkData chunkData = new ChunkData();
Chunk[] chunks = column.getChunks();
chunkData.sections = new ChunkSection[chunks.length];
@ -97,14 +97,26 @@ public class ChunkUtils {
chunkData.sections[chunkY] = new ChunkSection();
Chunk chunk = chunks[chunkY];
if (chunk == null || chunk.isEmpty())
// Chunk is null and caching chunks is off or this isn't a non-full chunk
if (chunk == null && (!session.getConnector().getConfig().isCacheChunks() || !isNonFullChunk))
continue;
// If chunk is empty then no need to process
if (chunk != null && chunk.isEmpty())
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++) {
int blockState = chunk.get(x, y, z);
int blockState;
// If a non-full chunk, then grab the block that should be here to create a 'full' chunk
if (chunk == null) {
Position pos = new ChunkPosition(column.getX(), column.getZ()).getBlock(x, (chunkY << 4) + y, z);
blockState = session.getConnector().getWorldManager().getBlockAt(session, pos.getX(), pos.getY(), pos.getZ());
} else {
blockState = chunk.get(x, y, z);
}
int id = BlockTranslator.getBedrockBlockId(blockState);
// Check to see if the name is in BlockTranslator.getBlockEntityString, and therefore must be handled differently