Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,70 @@
import static com.viaversion.viarewind.legacysupport.util.ReflectionUtil.getFieldAccessible;

public class BlockCollisionChanges {

public static void fixLilyPad(final Logger logger, final ProtocolVersion serverVersion) {
try {
// Modern Mojang runtime (1.20.5+ including 26.x)
if (serverVersion.newerThanOrEqualTo(ProtocolVersion.v1_20_5)) {
// Try both names depending on version
Class<?> lilyClass;
try {
lilyClass = Class.forName("net.minecraft.world.level.block.WaterlilyBlock");
} catch (ClassNotFoundException ex) {
lilyClass = Class.forName("net.minecraft.world.level.block.LilyPadBlock");
}

// Build replacement VoxelShape (old-style lily pad box)
Class<?> shapesClass = Class.forName("net.minecraft.world.phys.shapes.Shapes");
Method boxMethod = shapesClass.getMethod(
"box",
double.class, double.class, double.class,
double.class, double.class, double.class
);

Object newShape = boxMethod.invoke(null,
0.0625D, 0.0D, 0.0625D,
0.9375D, 0.015625D, 0.9375D
);

// Bypass: use Unsafe to smash static final SHAPE
Field shapeField = lilyClass.getDeclaredField("SHAPE");
shapeField.setAccessible(true);

Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Object unsafe = theUnsafeField.get(null);

Method staticFieldOffset = unsafeClass.getMethod("staticFieldOffset", Field.class);
Method staticFieldBase = unsafeClass.getMethod("staticFieldBase", Field.class);
Method putObjectVolatile = unsafeClass.getMethod("putObjectVolatile", Object.class, long.class, Object.class);

long offset = (long) staticFieldOffset.invoke(unsafe, shapeField);
Object baseObj = staticFieldBase.invoke(unsafe, shapeField);

// This actually overwrites the static final field
putObjectVolatile.invoke(unsafe, baseObj, offset, newShape);

logger.info("Patched lily pad SHAPE for 1.20.5+ via Unsafe bypass");
return;
}

// Legacy path (pre 1.20.5)
final Field boundingBoxField = getFieldAccessible(getNMSBlockClass("BlockWaterLily"), serverVersion.olderThanOrEqualTo(ProtocolVersion.v1_20_2) ? "a" : "b");

setBoundingBox(boundingBoxField.get(null), 0.0625, 0.0, 0.0625, 0.9375, 0.015625, 0.9375);
} catch (Exception ex) {
logger.log(Level.SEVERE, "Could not fix lily pad bounding box.", ex);
}
}

public static void fixCarpet(final Logger logger, final ProtocolVersion serverVersion) {
try {
// Modern Mojang runtime (1.20.5+ including 26.x)
if (serverVersion.newerThanOrEqualTo(ProtocolVersion.v1_20_5)) {
logger.warning("Skipping carpet collision fix on Mojang 1.20.5+ (SHAPE is static final).");
return;
}
// Legacy path (pre 1.20.5)
final Class<?> blockCarpetClass = serverVersion.olderThanOrEqualTo(ProtocolVersion.v1_16_4) ? getNMSBlockClass("BlockCarpet") : getNMSBlockClass("CarpetBlock");

final Field boundingBoxField = getFieldAccessible(blockCarpetClass, serverVersion.olderThanOrEqualTo(ProtocolVersion.v1_20_2) ? "a" : "b");
Expand All @@ -59,43 +110,46 @@ public static void fixCarpet(final Logger logger, final ProtocolVersion serverVe
public static void fixLadder(final Logger logger, final ProtocolVersion serverVersion) {
try {
if (serverVersion.newerThanOrEqualTo(ProtocolVersion.v1_20_5)) {
final Class<?> blockLadderClass = getNMSBlockClass("BlockLadder");

final Map<String, double[]> overrides = new HashMap<String, double[]>();
overrides.put("EAST", new double[]{0.0D, 0.0D, 0.0D, 0.125D, 1.0D, 1.0D});
overrides.put("WEST", new double[]{0.875D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D});
overrides.put("SOUTH", new double[]{0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.125D});
overrides.put("NORTH", new double[]{0.0D, 0.0D, 0.875D, 1.0D, 1.0D, 1.0D});

Map<Object, Object> shapesMap = null;
for (Field field : blockLadderClass.getDeclaredFields()) {
if (!Modifier.isStatic(field.getModifiers())) {
continue;
}
if (!Map.class.isAssignableFrom(field.getType())) {
continue;
}
field.setAccessible(true);
final Object value = field.get(null);
if (value instanceof Map<?, ?>) {
@SuppressWarnings("unchecked") final Map<Object, Object> casted = (Map<Object, Object>) value;
shapesMap = casted;
break;
}
}

boolean updated = false;
if (shapesMap != null) {
updated = updateShapesMap(shapesMap, overrides);
}

if (!updated) {
updated = updateShapeConstants(blockLadderClass, overrides);
}

if (!updated) {
throw new IllegalStateException("Could not adjust ladder shapes for modern versions");
}

// Mojang class name
Class<?> ladderBlockClass = Class.forName("net.minecraft.world.level.block.LadderBlock");

// Shapes.box(...) for VoxelShape creation
Class<?> shapesClass = Class.forName("net.minecraft.world.phys.shapes.Shapes");
Method boxMethod = shapesClass.getMethod(
"box",
double.class, double.class, double.class,
double.class, double.class, double.class
);

Class<?> directionClass = Class.forName("net.minecraft.core.Direction");
// Direction enum
Object EAST = directionClass.getField("EAST").get(null);
Object WEST = directionClass.getField("WEST").get(null);
Object SOUTH = directionClass.getField("SOUTH").get(null);
Object NORTH = directionClass.getField("NORTH").get(null);

// Build shapes
Object eastShape = boxMethod.invoke(null, 0.0D, 0.0D, 0.0D, 0.125D, 1.0D, 1.0D);
Object westShape = boxMethod.invoke(null, 0.875D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D);
Object southShape = boxMethod.invoke(null, 0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.125D);
Object northShape = boxMethod.invoke(null, 0.0D, 0.0D, 0.875D, 1.0D, 1.0D, 1.0D);

// Replace LadderBlock.SHAPES contents (cannot reassign final field!)
Field shapesField = ladderBlockClass.getDeclaredField("SHAPES");
shapesField.setAccessible(true);

@SuppressWarnings("unchecked")
Map<Object, Object> shapesMap = (Map<Object, Object>) shapesField.get(null);

shapesMap.clear();
shapesMap.put(EAST, eastShape);
shapesMap.put(WEST, westShape);
shapesMap.put(SOUTH, southShape);
shapesMap.put(NORTH, northShape);

logger.info("Successfully patched ladder collision shapes for Mojang 26.2+");
return;
} else {
final boolean pre1_12_2 = serverVersion.olderThanOrEqualTo(ProtocolVersion.v1_12_2);
final boolean pre1_13_2 = serverVersion.olderThanOrEqualTo(ProtocolVersion.v1_13_2);
Expand Down