ViaRewind-Legacy-Support/src/main/java/com/viaversion/viarewind/legacysupport/injector/BoundingBoxFixer.java

145 lines
7.1 KiB
Java

/*
* This file is part of ViaRewind-Legacy-Support - https://github.com/ViaVersion/ViaRewind-Legacy-Support
* Copyright (C) 2016-2023 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.viarewind.legacysupport.injector;
import com.viaversion.viarewind.legacysupport.reflection.ReflectionAPI;
import com.viaversion.viarewind.legacysupport.BukkitPlugin;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.logging.Level;
public class BoundingBoxFixer {
public static void fixLilyPad() {
try {
final Field boundingBoxField = ReflectionAPI.getFieldAccessible(NMSReflection.getNMSBlock("BlockWaterLily"), "a");
setBoundingBox(boundingBoxField.get(null), 0.0625, 0.0, 0.0625, 0.9375, 0.015625, 0.9375);
} catch (Exception ex) {
BukkitPlugin.getInstance().getLogger().log(Level.SEVERE, "Could not fix lily pad bounding box.", ex);
}
}
public static void fixCarpet(final int serverVersion) {
try {
final Class<?> blockCarpetClass = serverVersion <= ProtocolVersion.v1_16_4.getVersion() ? NMSReflection.getNMSBlock("BlockCarpet") : NMSReflection.getNMSBlock("CarpetBlock");
final Field boundingBoxField = ReflectionAPI.getFieldAccessible(blockCarpetClass, "a");
setBoundingBox(boundingBoxField.get(0), 0.0D, -0.0000001D, 0.0D, 1.0D, 0.0000001D, 1.0D);
} catch (Exception ex) {
BukkitPlugin.getInstance().getLogger().log(Level.SEVERE, "Could not fix carpet bounding box.", ex);
}
}
public static void fixLadder(final int serverVersion) {
try {
final boolean pre1_12_2 = serverVersion <= ProtocolVersion.v1_12_2.getVersion();
final boolean pre1_13_2 = serverVersion <= ProtocolVersion.v1_13_2.getVersion();
final boolean pre1_16_4 = serverVersion <= ProtocolVersion.v1_16_4.getVersion();
final Class<?> blockLadderClass = NMSReflection.getNMSBlock("BlockLadder");
final Field boundingBoxEastField = ReflectionAPI.getFieldAccessible(blockLadderClass, pre1_12_2 ? "b" : pre1_13_2 ? "c" : pre1_16_4 ? "c" : "d");
final Field boundingBoxWestField = ReflectionAPI.getFieldAccessible(blockLadderClass, pre1_12_2 ? "c" : pre1_13_2 ? "o" : pre1_16_4 ? "d" : "e");
final Field boundingBoxSouthField = ReflectionAPI.getFieldAccessible(blockLadderClass, pre1_12_2 ? "d" : pre1_13_2 ? "p" : pre1_16_4 ? "e" : "f");
final Field boundingBoxNorthField = ReflectionAPI.getFieldAccessible(blockLadderClass, pre1_12_2 ? "e" : pre1_13_2 ? "q" : pre1_16_4 ? "f" : "g");
setBoundingBox(boundingBoxEastField.get(null), 0.0D, 0.0D, 0.0D, 0.125D, 1.0D, 1.0D);
setBoundingBox(boundingBoxWestField.get(null), 0.875D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D);
setBoundingBox(boundingBoxSouthField.get(null), 0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.125D);
setBoundingBox(boundingBoxNorthField.get(null), 0.0D, 0.0D, 0.875D, 1.0D, 1.0D, 1.0D);
} catch (Exception ex) {
BukkitPlugin.getInstance().getLogger().log(Level.SEVERE, "Could not fix ladder bounding box.", ex);
}
}
private static void setBoundingBox(Object boundingBox, double... values) throws ReflectiveOperationException {
if (boundingBox.getClass().getSimpleName().equals("VoxelShapeArray")) {
setVoxelShapeArray(boundingBox, values);
return;
}
if (boundingBox.getClass().getSimpleName().equals("AxisAlignedBB")) {
setAxisAlignedBB(boundingBox, values);
return;
}
// Tuinity support
if (boundingBox.getClass().getSimpleName().equals("AABBVoxelShape")) {
setAABBVoxelShape(boundingBox, values);
return;
}
throw new IllegalStateException("Unknown bounding box type: " + boundingBox.getClass().getName());
}
private static void setAABBVoxelShape(Object boundingBox, double[] values) throws ReflectiveOperationException {
for (Field field : boundingBox.getClass().getFields()) {
// Set data for internally used AxisAlignedBB
if (field.getType().getSimpleName().equals("AxisAlignedBB")) {
setBoundingBox(field.get(boundingBox), values);
}
// Clear the cache
if (field.getType().getSimpleName().equals("DoubleList")) {
final Object doubleList = field.get(boundingBox);
doubleList.getClass().getMethod("clear").invoke(doubleList);
}
}
}
private static void setAxisAlignedBB(final Object boundingBox, final double[] values) throws ReflectiveOperationException {
final Field[] doubleFields = Arrays.stream(boundingBox.getClass().getDeclaredFields()).filter(f -> f.getType() == double.class && !Modifier.isStatic(f.getModifiers())).toArray(Field[]::new);
if (doubleFields.length < 6) {
throw new IllegalStateException("Invalid field count for " + boundingBox.getClass().getName() + ": " + doubleFields.length);
}
for (int i = 0; i < 6; i++) {
Field currentField = doubleFields[i];
currentField.setAccessible(true);
currentField.setDouble(boundingBox, values[i]);
}
}
private static void setVoxelShapeArray(final Object voxelShapeArray, final double[] values) throws ReflectiveOperationException {
final Field[] doubleListFields = Arrays.stream(voxelShapeArray.getClass().getDeclaredFields()).filter(f -> f.getType().getSimpleName().equals("DoubleList")).toArray(Field[]::new);
if (doubleListFields.length < 3) {
throw new IllegalStateException("Invalid field count for " + voxelShapeArray.getClass().getName() + ": " + doubleListFields.length);
}
// FastUtil is relocated on Spigot but not on Paper
final String doubleArrayListClass = doubleListFields[0].getType().getName().replace("DoubleList", "DoubleArrayList");
final Method wrapMethod = Class.forName(doubleArrayListClass).getMethod("wrap", double[].class);
for (int i = 0; i < 3; i++) {
final double[] array = {values[i], values[i + 3]};
final Field field = doubleListFields[i];
field.setAccessible(true);
field.set(voxelShapeArray, wrapMethod.invoke(null, (Object) array));
}
}
}