Skip to content

Commit 1c78911

Browse files
committed
"Fix" Hypixel's disappearing chests problem on modern clients
We really should not need to do this. Hypixel, *please* fix your protocol translation plugin.
1 parent 0b8eddb commit 1c78911

File tree

3 files changed

+100
-1
lines changed

3 files changed

+100
-1
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package org.embeddedt.modernfix.common.mixin.bugfix.missing_block_entities;
2+
3+
import net.minecraft.client.Minecraft;
4+
import net.minecraft.core.BlockPos;
5+
import net.minecraft.core.Registry;
6+
import net.minecraft.world.level.ChunkPos;
7+
import net.minecraft.world.level.Level;
8+
import net.minecraft.world.level.LevelHeightAccessor;
9+
import net.minecraft.world.level.biome.Biome;
10+
import net.minecraft.world.level.block.entity.BlockEntity;
11+
import net.minecraft.world.level.block.state.BlockBehaviour;
12+
import net.minecraft.world.level.block.state.BlockState;
13+
import net.minecraft.world.level.chunk.ChunkAccess;
14+
import net.minecraft.world.level.chunk.LevelChunk;
15+
import net.minecraft.world.level.chunk.LevelChunkSection;
16+
import net.minecraft.world.level.chunk.UpgradeData;
17+
import net.minecraft.world.level.levelgen.blending.BlendingData;
18+
import org.embeddedt.modernfix.ModernFix;
19+
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
20+
import org.jetbrains.annotations.Nullable;
21+
import org.spongepowered.asm.mixin.Final;
22+
import org.spongepowered.asm.mixin.Mixin;
23+
import org.spongepowered.asm.mixin.Shadow;
24+
import org.spongepowered.asm.mixin.Unique;
25+
import org.spongepowered.asm.mixin.injection.At;
26+
import org.spongepowered.asm.mixin.injection.Inject;
27+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
28+
29+
/**
30+
* Hypixel (and possibly other buggy servers) send chunks to the client that are missing some block entity data, which
31+
* causes these entities to be invisible. We "fix" this by recreating the block entity on the client with default data,
32+
* which is hopefully what the legacy server also expects.
33+
*/
34+
@Mixin(LevelChunk.class)
35+
@ClientOnlyMixin
36+
public abstract class LevelChunkMixin extends ChunkAccess {
37+
@Shadow @Final private Level level;
38+
39+
@Shadow @Nullable public abstract BlockEntity getBlockEntity(BlockPos pos, LevelChunk.EntityCreationType creationType);
40+
41+
public LevelChunkMixin(ChunkPos chunkPos, UpgradeData upgradeData, LevelHeightAccessor levelHeightAccessor, Registry<Biome> biomeRegistry, long inhabitedTime, @Nullable LevelChunkSection[] sections, @Nullable BlendingData blendingData) {
42+
super(chunkPos, upgradeData, levelHeightAccessor, biomeRegistry, inhabitedTime, sections, blendingData);
43+
}
44+
45+
@Inject(method = "replaceWithPacketData", at = @At("RETURN"))
46+
private void validateBlockEntitiesInChunk(CallbackInfo ci) {
47+
// No reason to check in singleplayer or on the integrated server
48+
if (this.level.isClientSide && !Minecraft.getInstance().isLocalServer()) {
49+
for (int i = 0; i < this.sections.length; i++) {
50+
var section = this.sections[i];
51+
try {
52+
if (!section.hasOnlyAir() && section.maybeHas(BlockBehaviour.BlockStateBase::hasBlockEntity)) {
53+
scanSectionForBlockEntities(section, i);
54+
}
55+
} catch(Exception e) {
56+
ModernFix.LOGGER.error("Exception validating data in chunk", e);
57+
return;
58+
}
59+
}
60+
}
61+
}
62+
63+
@Unique
64+
private void scanSectionForBlockEntities(LevelChunkSection section, int i) {
65+
int chunkX = this.chunkPos.x;
66+
int chunkZ = this.chunkPos.z;
67+
BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos();
68+
int sectionY = this.getSectionYFromSectionIndex(i);
69+
for (int y = 0; y < 16; y++) {
70+
for (int z = 0; z < 16; z++) {
71+
for (int x = 0; x < 16; x++) {
72+
var state = section.getBlockState(x, y, z);
73+
if (state.hasBlockEntity()) {
74+
cursor.set(chunkX * 16 + x, sectionY * 16 + y, chunkZ * 16 + z);
75+
makeBlockEntityIfNotExists(state, cursor);
76+
}
77+
}
78+
}
79+
}
80+
}
81+
82+
@Unique
83+
private void makeBlockEntityIfNotExists(BlockState state, BlockPos.MutableBlockPos pos) {
84+
if (this.blockEntities.containsKey(pos) || this.pendingBlockEntities.containsKey(pos)) {
85+
return;
86+
}
87+
88+
BlockEntity blockEntity = this.getBlockEntity(pos.immutable(), LevelChunk.EntityCreationType.IMMEDIATE);
89+
String blockName = state.getBlock().toString();
90+
if (blockEntity != null) {
91+
ModernFix.LOGGER.warn("Created missing block entity for {} at {}", blockName, pos.toShortString());
92+
} else {
93+
ModernFix.LOGGER.error("Block entity is missing for {} at {}, but could not be created", blockName, pos.toShortString());
94+
}
95+
}
96+
}
97+

common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ public DefaultSettingMapBuilder put(String key, Boolean value) {
167167
.put("mixin.bugfix.restore_old_dragon_movement", false)
168168
.put("mixin.perf.worldgen_allocation", false) // experimental
169169
.put("mixin.feature.cause_lag_by_disabling_threads", false)
170+
.put("mixin.bugfix.missing_block_entities", false)
170171
.put("mixin.perf.clear_mixin_classinfo", false)
171172
.put("mixin.perf.deduplicate_climate_parameters", false)
172173
.put("mixin.bugfix.packet_leak", false)

common/src/main/resources/assets/modernfix/lang/en_us.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,5 +134,6 @@
134134
"modernfix.option.mixin.perf.fix_loop_spin_waiting": "Fixes Minecraft's built-in wait function consuming excessive amounts of CPU resources.",
135135
"modernfix.option.mixin.perf.forge_cap_retrieval": "Small micro-optimization that makes retrieving custom entity data slightly more efficient on Forge.",
136136
"modernfix.option.mixin.perf.forge_registry_lambda": "Fixes oversights in Forge that lead to excessive allocation in hot registry methods.",
137-
"modernfix.option.mixin.bugfix.restore_old_dragon_movement": "Fixes MC-272431, which tracks the ender dragon being unable to dive to the portal as it did in 1.13 and older. This causes the dragon to fly quite a bit differently from what modern players are used to and also patches out one-cycling, so it's not enabled by default. Thanks to Jukitsu for identifying the regression in the vanilla code."
137+
"modernfix.option.mixin.bugfix.restore_old_dragon_movement": "Fixes MC-272431, which tracks the ender dragon being unable to dive to the portal as it did in 1.13 and older. This causes the dragon to fly quite a bit differently from what modern players are used to and also patches out one-cycling, so it's not enabled by default. Thanks to Jukitsu for identifying the regression in the vanilla code.",
138+
"modernfix.option.mixin.bugfix.missing_block_entities": "Hypixel sends chunks to the client that are missing some block entity data, which makes chests etc. appear invisible. This 'fixes' the problem by creating the needed data on the client. Has no effect for properly behaved servers or in singleplayer."
138139
}

0 commit comments

Comments
 (0)