Improve 1.8 -> 1.9 chunk translation and fix #2065 (#2669)

This commit is contained in:
RK_01 2021-08-28 10:02:27 +02:00 committed by GitHub
parent 533572e8cd
commit e7a0b4cf04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 483 additions and 315 deletions

View File

@ -230,6 +230,13 @@ public interface ViaVersionConfig {
*/
int getPistonReplacementId();
/**
* Fix 1.9+ clients not rendering the far away chunks
*
* @return true to fix chunk borders
*/
boolean isChunkBorderFix();
/**
* Force json transform
*
@ -417,7 +424,7 @@ public interface ViaVersionConfig {
boolean isForcedUse1_17ResourcePack();
/**
* Get the message that is sent when a user displays a resource pack prompt.
* Get the message that is sent when a user displays a resource pack prompt.
*
* @return cached serialized component
*/

View File

@ -33,6 +33,8 @@ public enum BlockFace {
TOP((byte) 0, (byte) 1, (byte) 0, EnumAxis.Y),
BOTTOM((byte) 0, (byte) -1, (byte) 0, EnumAxis.Y);
public static final BlockFace[] HORIZONTAL = new BlockFace[]{NORTH, SOUTH, EAST, WEST};
private static final Map<BlockFace, BlockFace> opposites = new HashMap<>();
static {

View File

@ -20,41 +20,23 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.chunks;
package com.viaversion.viaversion.api.type.types.minecraft;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.type.Type;
import java.util.ArrayList;
import java.util.List;
public abstract class BaseChunkBulkType extends Type<Chunk[]> {
public class Chunk1_8 extends BaseChunk {
private boolean unloadPacket;
public Chunk1_8(int x, int z, boolean groundUp, int bitmask, ChunkSection[] sections, int[] biomeData, List<CompoundTag> blockEntities) {
super(x, z, groundUp, false, bitmask, sections, biomeData, blockEntities);
protected BaseChunkBulkType() {
super(Chunk[].class);
}
/**
* Chunk unload.
*
* @param x coord
* @param z coord
*/
public Chunk1_8(int x, int z) {
this(x, z, true, 0, new ChunkSection[16], null, new ArrayList<>());
this.unloadPacket = true;
protected BaseChunkBulkType(String typeName) {
super(typeName, Chunk[].class);
}
/**
* Does this chunks have biome data
*
* @return True if the chunks has biome data
*/
public boolean hasBiomeData() {
return biomeData != null && fullChunk;
}
public boolean isUnloadPacket() {
return unloadPacket;
@Override
public Class<? extends Type> getBaseClass() {
return BaseChunkBulkType.class;
}
}

View File

@ -55,6 +55,15 @@ public class ChunkSectionType1_8 extends Type<ChunkSection> {
@Override
public void write(ByteBuf buffer, ChunkSection chunkSection) throws Exception {
throw new UnsupportedOperationException();
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
int block = chunkSection.getFlatBlock(x, y, z);
buffer.writeByte(block);
buffer.writeByte(block >> 8);
}
}
}
}
}

View File

@ -52,6 +52,7 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf
private boolean nmsPlayerTicking;
private boolean replacePistons;
private int pistonReplacementId;
private boolean chunkBorderFix;
private boolean autoTeam;
private boolean forceJsonTransform;
private boolean nbtArrayFix;
@ -114,6 +115,7 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf
nmsPlayerTicking = getBoolean("nms-player-ticking", true);
replacePistons = getBoolean("replace-pistons", false);
pistonReplacementId = getInt("replacement-piston-id", 0);
chunkBorderFix = getBoolean("chunk-border-fix", false);
autoTeam = getBoolean("auto-team", true);
forceJsonTransform = getBoolean("force-json-transform", false);
nbtArrayFix = getBoolean("chat-nbt-fix", true);
@ -277,6 +279,11 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf
return pistonReplacementId;
}
@Override
public boolean isChunkBorderFix() {
return chunkBorderFix;
}
@Override
public boolean isAutoTeam() {
// Collision has to be enabled first

View File

@ -17,11 +17,13 @@
*/
package com.viaversion.viaversion.protocols.protocol1_10to1_9_3;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.metadata.Metadata;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.protocol.remapper.ValueTransformer;
@ -30,8 +32,10 @@ import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.version.Types1_9;
import com.viaversion.viaversion.protocols.protocol1_10to1_9_3.packets.InventoryPackets;
import com.viaversion.viaversion.protocols.protocol1_10to1_9_3.storage.ResourcePackTracker;
import com.viaversion.viaversion.protocols.protocol1_9_1_2to1_9_3_4.types.Chunk1_9_3_4Type;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.ClientboundPackets1_9_3;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.ServerboundPackets1_9_3;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@ -145,6 +149,66 @@ public class Protocol1_10To1_9_3_4 extends AbstractProtocol<ClientboundPackets1_
}
});
// Join Game
registerClientbound(ClientboundPackets1_9_3.JOIN_GAME, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.INT); // 0 - Entity ID
map(Type.UNSIGNED_BYTE); // 1 - Gamemode
map(Type.INT); // 2 - Dimension
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
int dimensionId = wrapper.get(Type.INT, 1);
clientWorld.setEnvironment(dimensionId);
}
});
}
});
// Respawn
registerClientbound(ClientboundPackets1_9_3.RESPAWN, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.INT); // 0 - Dimension ID
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
int dimensionId = wrapper.get(Type.INT, 0);
clientWorld.setEnvironment(dimensionId);
}
});
}
});
// Chunk Data
registerClientbound(ClientboundPackets1_9_3.CHUNK_DATA, new PacketRemapper() {
@Override
public void registerMap() {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
Chunk chunk = wrapper.passthrough(new Chunk1_9_3_4Type(clientWorld));
if (Via.getConfig().isReplacePistons()) {
int replacementId = Via.getConfig().getPistonReplacementId();
for (ChunkSection section : chunk.getSections()) {
if (section == null) continue;
section.replacePaletteEntry(36, replacementId);
}
}
}
});
}
});
// Packet Send ResourcePack
registerClientbound(ClientboundPackets1_9_3.RESOURCE_PACK, new PacketRemapper() {
@Override
@ -196,6 +260,10 @@ public class Protocol1_10To1_9_3_4 extends AbstractProtocol<ClientboundPackets1_
@Override
public void init(UserConnection userConnection) {
userConnection.put(new ResourcePackTracker());
if (!userConnection.has(ClientWorld.class)) {
userConnection.put(new ClientWorld(userConnection));
}
}
@Override

View File

@ -137,9 +137,9 @@ public class Protocol1_9_3To1_9_1_2 extends AbstractProtocol<ClientboundPackets1
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ClientWorld clientChunks = wrapper.user().get(ClientWorld.class);
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
int dimensionId = wrapper.get(Type.INT, 1);
clientChunks.setEnvironment(dimensionId);
clientWorld.setEnvironment(dimensionId);
}
});
}

View File

@ -18,7 +18,6 @@
package com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.types;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.minecraft.Environment;
import com.viaversion.viaversion.api.minecraft.chunks.BaseChunk;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
@ -27,7 +26,6 @@ import com.viaversion.viaversion.api.type.PartialType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.minecraft.BaseChunkType;
import com.viaversion.viaversion.api.type.types.version.Types1_9;
import com.viaversion.viaversion.protocols.protocol1_10to1_9_3.Protocol1_10To1_9_3_4;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import io.netty.buffer.ByteBuf;
@ -42,9 +40,6 @@ public class Chunk1_9_1_2Type extends PartialType<Chunk, ClientWorld> {
@Override
public Chunk read(ByteBuf input, ClientWorld world) throws Exception {
boolean replacePistons = world.getUser().getProtocolInfo().getPipeline().contains(Protocol1_10To1_9_3_4.class) && Via.getConfig().isReplacePistons();
int replacementId = Via.getConfig().getPistonReplacementId();
int chunkX = input.readInt();
int chunkZ = input.readInt();
@ -71,9 +66,6 @@ public class Chunk1_9_1_2Type extends PartialType<Chunk, ClientWorld> {
if (world.getEnvironment() == Environment.NORMAL) {
section.getLight().readSkyLight(input);
}
if (replacePistons) {
section.replacePaletteEntry(36, replacementId);
}
}
int[] biomeData = groundUp ? new int[256] : null;

View File

@ -32,18 +32,11 @@ import com.viaversion.viaversion.api.rewriter.EntityRewriter;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_8.ClientboundPackets1_8;
import com.viaversion.viaversion.protocols.protocol1_8.ServerboundPackets1_8;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.metadata.MetadataRewriter1_9To1_8;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.packets.EntityPackets;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.packets.InventoryPackets;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.packets.PlayerPackets;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.packets.SpawnPackets;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.packets.WorldPackets;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.packets.*;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.*;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.ClientChunks;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.CommandBlockStorage;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.EntityTracker1_9;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.InventoryTracker;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.MovementTracker;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.*;
import com.viaversion.viaversion.util.GsonUtil;
public class Protocol1_9To1_8 extends AbstractProtocol<ClientboundPackets1_8, ClientboundPackets1_9, ServerboundPackets1_8, ServerboundPackets1_9> {
@ -152,6 +145,10 @@ public class Protocol1_9To1_8 extends AbstractProtocol<ClientboundPackets1_8, Cl
userConnection.put(new InventoryTracker());
// CommandBlock storage
userConnection.put(new CommandBlockStorage());
if (!userConnection.has(ClientWorld.class)) {
userConnection.put(new ClientWorld(userConnection));
}
}
@Override

View File

@ -26,6 +26,7 @@ import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_8.ClientboundPackets1_8;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.ItemRewriter;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.PlayerMovementMapper;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
@ -189,12 +190,18 @@ public class PlayerPackets {
tracker.setGameMode(GameMode.getById(wrapper.get(Type.UNSIGNED_BYTE, 0))); //Set player gamemode
});
// Track player's dimension
handler(wrapper -> {
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
int dimensionId = wrapper.get(Type.BYTE, 0);
clientWorld.setEnvironment(dimensionId);
});
// Gotta fake their op
handler(wrapper -> {
CommandBlockProvider provider = Via.getManager().getProviders().get(CommandBlockProvider.class);
provider.sendPermission(wrapper.user());
}
);
});
// Scoreboard will be cleared when join game is received
handler(wrapper -> {
@ -300,22 +307,6 @@ public class PlayerPackets {
}
});
protocol.registerClientbound(ClientboundPackets1_8.UPDATE_HEALTH, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.FLOAT); // 0 - Health
handler(wrapper -> {
float health = wrapper.get(Type.FLOAT, 0);
if (health <= 0) {
// Client unloads chunks on respawn, take note
ClientChunks cc = wrapper.user().get(ClientChunks.class);
cc.getBulkChunks().clear();
cc.getLoadedChunks().clear();
}
});
}
});
protocol.registerClientbound(ClientboundPackets1_8.RESPAWN, new PacketRemapper() {
@Override
public void registerMap() {
@ -324,11 +315,19 @@ public class PlayerPackets {
map(Type.UNSIGNED_BYTE); // 2 - GameMode
map(Type.STRING); // 3 - Level Type
// Track player's dimension
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
int dimensionId = wrapper.get(Type.INT, 0);
clientWorld.setEnvironment(dimensionId);
}
});
handler(wrapper -> {
// Client unloads chunks on respawn, take note
ClientChunks cc = wrapper.user().get(ClientChunks.class);
cc.getBulkChunks().clear();
cc.getLoadedChunks().clear();
// Client unloads chunks on respawn
wrapper.user().get(ClientChunks.class).getLoadedChunks().clear();
int gamemode = wrapper.get(Type.UNSIGNED_BYTE, 0);
EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);

View File

@ -20,8 +20,11 @@ package com.viaversion.viaversion.protocols.protocol1_9to1_8.packets;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk1_8;
import com.viaversion.viaversion.api.minecraft.chunks.BaseChunk;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.item.DataItem;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.protocol.Protocol;
@ -29,8 +32,9 @@ import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.CustomByteType;
import com.viaversion.viaversion.protocols.protocol1_8.ClientboundPackets1_8;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.types.Chunk1_9_1_2Type;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.ClientboundPackets1_9;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.ItemRewriter;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
@ -40,10 +44,11 @@ import com.viaversion.viaversion.protocols.protocol1_9to1_8.sounds.Effect;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.sounds.SoundEffect;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.ClientChunks;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.EntityTracker1_9;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.types.Chunk1_9to1_8Type;
import io.netty.buffer.ByteBuf;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.types.Chunk1_8Type;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.types.ChunkBulk1_8Type;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.Optional;
public class WorldPackets {
@ -132,22 +137,54 @@ public class WorldPackets {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
ClientChunks clientChunks = wrapper.user().get(ClientChunks.class);
Chunk1_9to1_8Type type = new Chunk1_9to1_8Type(clientChunks);
Chunk1_8 chunk = (Chunk1_8) wrapper.read(type);
if (chunk.isUnloadPacket()) {
wrapper.setPacketType(ClientboundPackets1_9.UNLOAD_CHUNK);
Chunk chunk = wrapper.read(new Chunk1_8Type(clientWorld));
// Check if the chunk should be handled as an unload packet
if (chunk.isFullChunk() && chunk.getBitmask() == 0) {
wrapper.setPacketType(ClientboundPackets1_9.UNLOAD_CHUNK);
wrapper.write(Type.INT, chunk.getX());
wrapper.write(Type.INT, chunk.getZ());
// Remove commandBlocks on chunk unload
CommandBlockProvider provider = Via.getManager().getProviders().get(CommandBlockProvider.class);
provider.unloadChunk(wrapper.user(), chunk.getX(), chunk.getZ());
clientChunks.getLoadedChunks().remove(ClientChunks.toLong(chunk.getX(), chunk.getZ()));
// Unload the empty chunks
if (Via.getConfig().isChunkBorderFix()) {
for (BlockFace face : BlockFace.HORIZONTAL) {
int chunkX = chunk.getX() + face.getModX();
int chunkZ = chunk.getZ() + face.getModZ();
if (!clientChunks.getLoadedChunks().contains(ClientChunks.toLong(chunkX, chunkZ))) {
PacketWrapper unloadChunk = wrapper.create(ClientboundPackets1_9.UNLOAD_CHUNK);
unloadChunk.write(Type.INT, chunkX);
unloadChunk.write(Type.INT, chunkZ);
unloadChunk.send(Protocol1_9To1_8.class);
}
}
}
} else {
wrapper.write(type, chunk);
// eat any other data (Usually happens with unload packets)
wrapper.write(new Chunk1_9_1_2Type(clientWorld), chunk);
clientChunks.getLoadedChunks().add(ClientChunks.toLong(chunk.getX(), chunk.getZ()));
// Send empty chunks surrounding the loaded chunk to force 1.9+ clients to render the new chunk
if (Via.getConfig().isChunkBorderFix()) {
for (BlockFace face : BlockFace.HORIZONTAL) {
int chunkX = chunk.getX() + face.getModX();
int chunkZ = chunk.getZ() + face.getModZ();
if (!clientChunks.getLoadedChunks().contains(ClientChunks.toLong(chunkX, chunkZ))) {
PacketWrapper emptyChunk = wrapper.create(ClientboundPackets1_9.CHUNK_DATA);
Chunk c = new BaseChunk(chunkX, chunkZ, true, false, 0, new ChunkSection[16], new int[256], new ArrayList<>());
emptyChunk.write(new Chunk1_9_1_2Type(wrapper.user().get(ClientWorld.class)), c);
emptyChunk.send(Protocol1_9To1_8.class);
}
}
}
}
wrapper.read(Type.REMAINING_BYTES);
}
});
}
@ -158,41 +195,29 @@ public class WorldPackets {
public void registerMap() {
handler(wrapper -> {
wrapper.cancel(); // Cancel the packet from being sent
boolean skyLight = wrapper.read(Type.BOOLEAN);
int count = wrapper.read(Type.VAR_INT);
ChunkBulkSection[] chunks = new ChunkBulkSection[count];
for (int i = 0; i < count; i++) {
chunks[i] = new ChunkBulkSection(wrapper, skyLight);
}
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
ClientChunks clientChunks = wrapper.user().get(ClientChunks.class);
for (ChunkBulkSection chunk : chunks) {
// Data is at the end
CustomByteType customByteType = new CustomByteType(chunk.getLength());
chunk.setData(wrapper.read(customByteType));
Chunk[] chunks = wrapper.read(new ChunkBulk1_8Type(clientWorld));
clientChunks.getBulkChunks().add(ClientChunks.toLong(chunk.getX(), chunk.getZ())); // Store for later
// Split into multiple chunk packets
for (Chunk chunk : chunks) {
PacketWrapper chunkData = wrapper.create(ClientboundPackets1_9.CHUNK_DATA);
chunkData.write(new Chunk1_9_1_2Type(clientWorld), chunk);
chunkData.send(Protocol1_9To1_8.class);
// Construct chunk packet
ByteBuf buffer = null;
try {
buffer = wrapper.user().getChannel().alloc().buffer();
clientChunks.getLoadedChunks().add(ClientChunks.toLong(chunk.getX(), chunk.getZ()));
Type.INT.write(buffer, chunk.getX());
Type.INT.write(buffer, chunk.getZ());
Type.BOOLEAN.write(buffer, true); // Always ground-up
Type.UNSIGNED_SHORT.write(buffer, chunk.getBitMask());
Type.VAR_INT.writePrimitive(buffer, chunk.getLength());
customByteType.write(buffer, chunk.getData());
// Send through this protocol again
PacketWrapper chunkPacket = PacketWrapper.create(ClientboundPackets1_8.CHUNK_DATA, buffer, wrapper.user());
chunkPacket.send(Protocol1_9To1_8.class, false);
} finally {
if (buffer != null) {
buffer.release();
// Send empty chunks surrounding the loaded chunk to force 1.9+ clients to render the new chunk
if (Via.getConfig().isChunkBorderFix()) {
for (BlockFace face : BlockFace.HORIZONTAL) {
int chunkX = chunk.getX() + face.getModX();
int chunkZ = chunk.getZ() + face.getModZ();
if (!clientChunks.getLoadedChunks().contains(ClientChunks.toLong(chunkX, chunkZ))) {
PacketWrapper emptyChunk = wrapper.create(ClientboundPackets1_9.CHUNK_DATA);
Chunk c = new BaseChunk(chunkX, chunkZ, true, false, 0, new ChunkSection[16], new int[256], new ArrayList<>());
emptyChunk.write(new Chunk1_9_1_2Type(wrapper.user().get(ClientWorld.class)), c);
emptyChunk.send(Protocol1_9To1_8.class);
}
}
}
}
@ -237,14 +262,6 @@ public class WorldPackets {
}
});
protocol.registerClientbound(ClientboundPackets1_8.BLOCK_CHANGE, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.POSITION);
map(Type.VAR_INT);
}
});
/* Incoming Packets */
protocol.registerServerbound(ServerboundPackets1_9.UPDATE_SIGN, new PacketRemapper() {

View File

@ -25,21 +25,17 @@ import java.util.Set;
public class ClientChunks extends StoredObject {
private final Set<Long> loadedChunks = Sets.newConcurrentHashSet();
private final Set<Long> bulkChunks = Sets.newConcurrentHashSet();
public ClientChunks(UserConnection connection) {
super(connection);
}
public static long toLong(int msw, int lsw) {
return ((long) msw << 32) + lsw - -2147483648L;
return ((long) msw << 32) + lsw + 2147483648L;
}
public Set<Long> getLoadedChunks() {
return loadedChunks;
}
public Set<Long> getBulkChunks() {
return bulkChunks;
}
}

View File

@ -0,0 +1,148 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.protocol1_9to1_8.types;
import com.viaversion.viaversion.api.minecraft.Environment;
import com.viaversion.viaversion.api.minecraft.chunks.BaseChunk;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.type.PartialType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.minecraft.BaseChunkType;
import com.viaversion.viaversion.api.type.types.version.Types1_8;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.util.ArrayList;
public class Chunk1_8Type extends PartialType<Chunk, ClientWorld> {
public Chunk1_8Type(ClientWorld param) {
super(param, Chunk.class);
}
@Override
public Class<? extends Type> getBaseClass() {
return BaseChunkType.class;
}
@Override
public Chunk read(ByteBuf input, ClientWorld world) throws Exception {
final int chunkX = input.readInt();
final int chunkZ = input.readInt();
final boolean fullChunk = input.readBoolean();
final int bitmask = input.readUnsignedShort();
final int dataLength = Type.VAR_INT.readPrimitive(input);
final byte[] data = new byte[dataLength];
input.readBytes(data);
// Check if the chunk is an unload packet and return early
if (fullChunk && bitmask == 0) {
return new BaseChunk(chunkX, chunkZ, true, false, 0, new ChunkSection[16], null, new ArrayList<>());
}
return deserialize(chunkX, chunkZ, fullChunk, world.getEnvironment() == Environment.NORMAL, bitmask, data);
}
@Override
public void write(ByteBuf output, ClientWorld world, Chunk chunk) throws Exception {
output.writeInt(chunk.getX());
output.writeInt(chunk.getZ());
output.writeBoolean(chunk.isFullChunk());
output.writeShort(chunk.getBitmask());
final byte[] data = serialize(chunk);
Type.VAR_INT.writePrimitive(output, data.length);
output.writeBytes(data);
}
// Used for normal and bulk chunks
public static Chunk deserialize(final int chunkX, final int chunkZ, final boolean fullChunk, final boolean skyLight, final int bitmask, final byte[] data) throws Exception {
final ByteBuf input = Unpooled.wrappedBuffer(data);
final ChunkSection[] sections = new ChunkSection[16];
int[] biomeData = null;
// Read blocks
for (int i = 0; i < sections.length; i++) {
if ((bitmask & 1 << i) == 0) continue;
sections[i] = Types1_8.CHUNK_SECTION.read(input);
}
// Read block light
for (int i = 0; i < sections.length; i++) {
if ((bitmask & 1 << i) == 0) continue;
sections[i].getLight().readBlockLight(input);
}
// Read sky light
if (skyLight) {
for (int i = 0; i < sections.length; i++) {
if ((bitmask & 1 << i) == 0) continue;
sections[i].getLight().readSkyLight(input);
}
}
// Read biome data
if (fullChunk) {
biomeData = new int[256];
for (int i = 0; i < 256; i++) {
biomeData[i] = input.readUnsignedByte();
}
}
input.release();
return new BaseChunk(chunkX, chunkZ, fullChunk, false, bitmask, sections, biomeData, new ArrayList<>());
}
// Used for normal and bulk chunks
public static byte[] serialize(final Chunk chunk) throws Exception {
final ByteBuf output = Unpooled.buffer();
// Write blocks
for (int i = 0; i < chunk.getSections().length; i++) {
if ((chunk.getBitmask() & 1 << i) == 0) continue;
Types1_8.CHUNK_SECTION.write(output, chunk.getSections()[i]);
}
// Write block light
for (int i = 0; i < chunk.getSections().length; i++) {
if ((chunk.getBitmask() & 1 << i) == 0) continue;
chunk.getSections()[i].getLight().writeBlockLight(output);
}
// Write sky light
for (int i = 0; i < chunk.getSections().length; i++) {
if ((chunk.getBitmask() & 1 << i) == 0) continue;
if (chunk.getSections()[i].getLight().hasSkyLight()) chunk.getSections()[i].getLight().writeSkyLight(output);
}
// Write biome data
if (chunk.isFullChunk() && chunk.getBiomeData() != null) {
for (int biome : chunk.getBiomeData()) {
output.writeByte((byte) biome);
}
}
final byte[] data = new byte[output.readableBytes()];
output.readBytes(data);
output.release();
return data;
}
}

View File

@ -1,185 +0,0 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.protocol1_9to1_8.types;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk1_8;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionLight;
import com.viaversion.viaversion.api.type.PartialType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.minecraft.BaseChunkType;
import com.viaversion.viaversion.api.type.types.version.Types1_8;
import com.viaversion.viaversion.api.type.types.version.Types1_9;
import com.viaversion.viaversion.protocols.protocol1_10to1_9_3.Protocol1_10To1_9_3_4;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.ClientChunks;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.logging.Level;
public class Chunk1_9to1_8Type extends PartialType<Chunk, ClientChunks> {
/**
* Amount of sections in a chunks.
*/
public static final int SECTION_COUNT = 16;
/**
* size of each chunks section (16x16x16).
*/
private static final int SECTION_SIZE = 16;
/**
* Length of biome data.
*/
private static final int BIOME_DATA_LENGTH = 256;
public Chunk1_9to1_8Type(ClientChunks chunks) {
super(chunks, Chunk.class);
}
private static long toLong(int msw, int lsw) {
return ((long) msw << 32) + lsw - -2147483648L;
}
@Override
public Class<? extends Type> getBaseClass() {
return BaseChunkType.class;
}
@Override
public Chunk read(ByteBuf input, ClientChunks param) throws Exception {
boolean replacePistons = param.getUser().getProtocolInfo().getPipeline().contains(Protocol1_10To1_9_3_4.class) && Via.getConfig().isReplacePistons();
int replacementId = Via.getConfig().getPistonReplacementId();
int chunkX = input.readInt();
int chunkZ = input.readInt();
long chunkHash = toLong(chunkX, chunkZ);
boolean fullChunk = input.readByte() != 0;
int bitmask = input.readUnsignedShort();
int dataLength = Type.VAR_INT.readPrimitive(input);
// Data to be read
BitSet usedSections = new BitSet(16);
ChunkSection[] sections = new ChunkSection[16];
int[] biomeData = null;
// Calculate section count from bitmask
for (int i = 0; i < 16; i++) {
if ((bitmask & (1 << i)) != 0) {
usedSections.set(i);
}
}
int sectionCount = usedSections.cardinality(); // the amount of sections set
// If the chunks is from a chunks bulk, it is never an unload packet
// Other wise, if it has no data, it is :)
boolean isBulkPacket = param.getBulkChunks().remove(chunkHash);
if (sectionCount == 0 && fullChunk && !isBulkPacket && param.getLoadedChunks().contains(chunkHash)) {
// This is a chunks unload packet
param.getLoadedChunks().remove(chunkHash);
return new Chunk1_8(chunkX, chunkZ);
}
int startIndex = input.readerIndex();
param.getLoadedChunks().add(chunkHash); // mark chunks as loaded
// Read blocks
for (int i = 0; i < SECTION_COUNT; i++) {
if (!usedSections.get(i)) continue; // Section not set
ChunkSection section = Types1_8.CHUNK_SECTION.read(input);
sections[i] = section;
if (replacePistons) {
section.replacePaletteEntry(36, replacementId);
}
}
// Read block light
for (int i = 0; i < SECTION_COUNT; i++) {
if (!usedSections.get(i)) continue; // Section not set, has no light
sections[i].getLight().readBlockLight(input);
}
// Read sky light
int bytesLeft = dataLength - (input.readerIndex() - startIndex);
if (bytesLeft >= ChunkSectionLight.LIGHT_LENGTH) {
for (int i = 0; i < SECTION_COUNT; i++) {
if (!usedSections.get(i)) continue; // Section not set, has no light
sections[i].getLight().readSkyLight(input);
bytesLeft -= ChunkSectionLight.LIGHT_LENGTH;
}
}
// Read biome data
if (bytesLeft >= BIOME_DATA_LENGTH) {
biomeData = new int[BIOME_DATA_LENGTH];
for (int i = 0; i < BIOME_DATA_LENGTH; i++) {
biomeData[i] = input.readByte() & 0xFF;
}
bytesLeft -= BIOME_DATA_LENGTH;
}
// Check remaining bytes
if (bytesLeft > 0) {
Via.getPlatform().getLogger().log(Level.WARNING, bytesLeft + " Bytes left after reading chunks! (" + fullChunk + ")");
}
// Return chunks
return new Chunk1_8(chunkX, chunkZ, fullChunk, bitmask, sections, biomeData, new ArrayList<CompoundTag>());
}
@Override
public void write(ByteBuf output, ClientChunks param, Chunk input) throws Exception {
if (!(input instanceof Chunk1_8)) throw new Exception("Incompatible chunk, " + input.getClass());
Chunk1_8 chunk = (Chunk1_8) input;
// Write primary info
output.writeInt(chunk.getX());
output.writeInt(chunk.getZ());
if (chunk.isUnloadPacket()) return;
output.writeByte(chunk.isFullChunk() ? 0x01 : 0x00);
Type.VAR_INT.writePrimitive(output, chunk.getBitmask());
ByteBuf buf = output.alloc().buffer();
try {
for (int i = 0; i < SECTION_COUNT; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null) continue; // Section not set
Types1_9.CHUNK_SECTION.write(buf, section);
section.getLight().writeBlockLight(buf);
if (!section.getLight().hasSkyLight()) continue; // No sky light, we're done here.
section.getLight().writeSkyLight(buf);
}
buf.readerIndex(0);
Type.VAR_INT.writePrimitive(output, buf.readableBytes() + (chunk.hasBiomeData() ? 256 : 0));
output.writeBytes(buf);
} finally {
buf.release(); // release buffer
}
// Write biome data
if (chunk.hasBiomeData()) {
for (int biome : chunk.getBiomeData()) {
output.writeByte((byte) biome);
}
}
}
}

View File

@ -0,0 +1,127 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.protocol1_9to1_8.types;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.type.PartialType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.minecraft.BaseChunkBulkType;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import io.netty.buffer.ByteBuf;
public class ChunkBulk1_8Type extends PartialType<Chunk[], ClientWorld> {
private static final int BLOCKS_PER_SECTION = 16 * 16 * 16;
private static final int BLOCKS_BYTES = BLOCKS_PER_SECTION * 2;
private static final int LIGHT_BYTES = BLOCKS_PER_SECTION / 2;
private static final int BIOME_BYTES = 16 * 16;
public ChunkBulk1_8Type(final ClientWorld clientWorld) {
super(clientWorld, Chunk[].class);
}
@Override
public Class<? extends Type> getBaseClass() {
return BaseChunkBulkType.class;
}
@Override
public Chunk[] read(ByteBuf input, ClientWorld world) throws Exception {
final boolean skyLight = input.readBoolean();
final int count = Type.VAR_INT.readPrimitive(input);
final Chunk[] chunks = new Chunk[count];
final ChunkBulkSection[] chunkInfo = new ChunkBulkSection[count];
// Read metadata
for (int i = 0; i < chunkInfo.length; i++) {
chunkInfo[i] = new ChunkBulkSection(input, skyLight);
}
// Read data
for (int i = 0; i < chunks.length; i++) {
final ChunkBulkSection chunkBulkSection = chunkInfo[i];
chunkBulkSection.readData(input);
chunks[i] = Chunk1_8Type.deserialize(chunkBulkSection.chunkX, chunkBulkSection.chunkZ, true, skyLight, chunkBulkSection.bitmask, chunkBulkSection.getData());
}
return chunks;
}
@Override
public void write(ByteBuf output, ClientWorld world, Chunk[] chunks) throws Exception {
boolean skyLight = false;
for (Chunk c : chunks) {
for (ChunkSection section : c.getSections()) {
if (section != null) {
if (section.getLight().hasSkyLight()) {
skyLight = true;
}
}
}
}
output.writeBoolean(skyLight);
Type.VAR_INT.writePrimitive(output, chunks.length);
// Write metadata
for (Chunk c : chunks) {
output.writeInt(c.getX());
output.writeInt(c.getZ());
output.writeShort(c.getBitmask());
}
// Write data
for (Chunk c : chunks) {
output.writeBytes(Chunk1_8Type.serialize(c));
}
}
public static final class ChunkBulkSection {
private final int chunkX;
private final int chunkZ;
private final int bitmask;
private final byte[] data;
public ChunkBulkSection(final ByteBuf input, final boolean skyLight) {
this.chunkX = input.readInt();
this.chunkZ = input.readInt();
this.bitmask = input.readUnsignedShort();
final int setSections = Integer.bitCount(this.bitmask);
this.data = new byte[setSections * (BLOCKS_BYTES + (skyLight ? 2 * LIGHT_BYTES : LIGHT_BYTES)) + BIOME_BYTES];
}
public void readData(final ByteBuf input) {
input.readBytes(this.data);
}
public int getChunkX() {
return this.chunkX;
}
public int getChunkZ() {
return this.chunkZ;
}
public int getBitmask() {
return this.bitmask;
}
public byte[] getData() {
return this.data;
}
}
}

View File

@ -276,7 +276,7 @@ public abstract class EntityRewriter<T extends Protocol> extends RewriterBase<T>
* @param entityType entity type
* @param intType int type of the entity id
*/
public void registerTracker(ClientboundPacketType packetType, EntityType entityType, Type<?> intType) {
public void registerTracker(ClientboundPacketType packetType, EntityType entityType, Type<? extends Integer> intType) {
protocol.registerClientbound(packetType, new PacketRemapper() {
@Override
public void registerMap() {

View File

@ -197,6 +197,8 @@ anti-xray-patch: true
replace-pistons: false
# What id should we replace with, default is air. (careful of players getting stuck standing on them)
replacement-piston-id: 0
# Fix 1.9+ clients not rendering the far away chunks and improve chunk rendering when moving fast (Increases network usage and decreases client fps slightly)
chunk-border-fix: false
# Force the string -> json transform
force-json-transform: false
# Minimize the cooldown animation in 1.8 servers