diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java index 8a2bcf7123..6b341eece4 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -2,6 +2,7 @@ package org.bukkit.craftbukkit; import com.google.common.base.Charsets; import com.google.common.base.Function; +import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; @@ -302,6 +303,7 @@ public final class CraftServer implements Server { public boolean ignoreVanillaPermissions = false; private final List playerView; public int reloadCount; + public Set activeCompatibilities = Collections.emptySet(); static { ConfigurationSerialization.registerClass(CraftOfflinePlayer.class); @@ -377,6 +379,7 @@ public final class CraftServer implements Server { TicketType.PLUGIN.timeout = configuration.getInt("chunk-gc.period-in-ticks"); minimumAPI = ApiVersion.getOrCreateVersion(configuration.getString("settings.minimum-api")); loadIcon(); + loadCompatibilities(); // Set map color cache if (configuration.getBoolean("settings.use-map-color-cache")) { @@ -420,6 +423,25 @@ public final class CraftServer implements Server { } } + private void loadCompatibilities() { + ConfigurationSection compatibilities = configuration.getConfigurationSection("settings.compatibility"); + if (compatibilities == null) { + activeCompatibilities = Collections.emptySet(); + return; + } + + activeCompatibilities = compatibilities + .getKeys(false) + .stream() + .filter(compatibilities::getBoolean) + .collect(Collectors.toSet()); + + if (!activeCompatibilities.isEmpty()) { + logger.info("Using following compatibilities: `" + Joiner.on("`, `").join(activeCompatibilities) + "`, this will affect performance and other plugins behavior."); + logger.info("Only use when necessary and prefer updating plugins if possible."); + } + } + public void loadPlugins() { pluginManager.registerInterface(JavaPluginLoader.class); @@ -896,6 +918,7 @@ public final class CraftServer implements Server { printSaveWarning = false; console.autosavePeriod = configuration.getInt("ticks-per.autosave"); loadIcon(); + loadCompatibilities(); try { playerList.getIpBans().load(); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java index 3609e0cc21..71ad0775a6 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java @@ -12,6 +12,7 @@ import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.craftbukkit.legacy.fieldrename.FieldRenameData; import org.bukkit.craftbukkit.legacy.reroute.DoNotReroute; import org.bukkit.craftbukkit.legacy.reroute.InjectPluginVersion; +import org.bukkit.craftbukkit.legacy.reroute.RequireCompatibility; import org.bukkit.craftbukkit.legacy.reroute.RerouteMethodName; import org.bukkit.craftbukkit.legacy.reroute.RerouteStatic; import org.bukkit.craftbukkit.util.ApiVersion; @@ -55,6 +56,7 @@ public class FieldRename { return Enum.valueOf(enumClass, rename(apiVersion, enumClass.getName().replace('.', '/'), name)); } + @RequireCompatibility("allow-old-keys-in-registry") public static T get(Registry registry, NamespacedKey namespacedKey) { // We don't have version-specific changes, so just use current, and don't inject a version return CraftRegistry.get(registry, namespacedKey, ApiVersion.CURRENT); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RequireCompatibility.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RequireCompatibility.java new file mode 100644 index 0000000000..ff1938c098 --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RequireCompatibility.java @@ -0,0 +1,13 @@ +package org.bukkit.craftbukkit.legacy.reroute; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface RequireCompatibility { + + String value(); +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteBuilder.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteBuilder.java index 883a715a6a..b5f38641d2 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteBuilder.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteBuilder.java @@ -112,6 +112,11 @@ public class RerouteBuilder { boolean inBukkit = !method.isAnnotationPresent(NotInBukkit.class); - return new RerouteMethodData(methodKey, sourceDesc, sourceOwner, methodName, rerouteStatic != null, targetType, Type.getInternalName(method.getDeclaringClass()), method.getName(), arguments, rerouteReturn, inBukkit); + String requiredCompatibility = null; + if (method.isAnnotationPresent(RequireCompatibility.class)) { + requiredCompatibility = method.getAnnotation(RequireCompatibility.class).value(); + } + + return new RerouteMethodData(methodKey, sourceDesc, sourceOwner, methodName, rerouteStatic != null, targetType, Type.getInternalName(method.getDeclaringClass()), method.getName(), arguments, rerouteReturn, inBukkit, requiredCompatibility); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteMethodData.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteMethodData.java index d78b48c72c..19c31a1b31 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteMethodData.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteMethodData.java @@ -1,9 +1,11 @@ package org.bukkit.craftbukkit.legacy.reroute; import java.util.List; +import org.jetbrains.annotations.Nullable; import org.objectweb.asm.Type; public record RerouteMethodData(String source, Type sourceDesc, Type sourceOwner, String sourceName, boolean staticReroute, Type targetType, String targetOwner, String targetName, - List arguments, RerouteReturn rerouteReturn, boolean isInBukkit) { + List arguments, RerouteReturn rerouteReturn, boolean isInBukkit, + @Nullable String requiredCompatibility) { } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java index 227ae3d504..4f06d72c8d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java @@ -6,6 +6,7 @@ import java.io.FileOutputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.List; @@ -117,7 +118,7 @@ public class Commodore { byte[] b = ByteStreams.toByteArray(is); if (entry.getName().endsWith(".class")) { - b = convert(b, "dummy", ApiVersion.NONE); + b = convert(b, "dummy", ApiVersion.NONE, Collections.emptySet()); entry = new JarEntry(entry.getName()); } @@ -135,7 +136,7 @@ public class Commodore { } } - public static byte[] convert(byte[] b, final String pluginName, final ApiVersion pluginVersion) { + public static byte[] convert(byte[] b, final String pluginName, final ApiVersion pluginVersion, final Set activeCompatibilities) { final boolean modern = pluginVersion.isNewerThanOrSameAs(ApiVersion.FLATTENING); ClassReader cr = new ClassReader(b); ClassWriter cw = new ClassWriter(cr, 0); @@ -385,7 +386,7 @@ public class Commodore { } private boolean checkReroute(MethodPrinter visitor, Map rerouteMethodDataMap, int opcode, String owner, String name, String desc, Type samMethodType, Type instantiatedMethodType) { - return rerouteMethods(rerouteMethodDataMap, opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.H_INVOKESTATIC, owner, name, desc, data -> { + return rerouteMethods(activeCompatibilities, rerouteMethodDataMap, opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.H_INVOKESTATIC, owner, name, desc, data -> { visitor.visit(Opcodes.INVOKESTATIC, className, buildMethodName(data), buildMethodDesc(data), isInterface, samMethodType, instantiatedMethodType); rerouteMethodData.add(data); }); @@ -555,7 +556,7 @@ public class Commodore { But since it is only applied for each class and method call once when they get first loaded, it should not be that bad. (Although some load time testing could be done) */ - public static boolean rerouteMethods(Map rerouteMethodDataMap, boolean staticCall, String owner, String name, String desc, Consumer consumer) { + public static boolean rerouteMethods(Set activeCompatibilities, Map rerouteMethodDataMap, boolean staticCall, String owner, String name, String desc, Consumer consumer) { Type ownerType = Type.getObjectType(owner); Class ownerClass; try { @@ -578,6 +579,10 @@ public class Commodore { continue; } + if (data.requiredCompatibility() != null && !activeCompatibilities.contains(data.requiredCompatibility())) { + return false; + } + consumer.accept(data); return true; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java index 848bafdc8d..d81071d712 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java @@ -53,6 +53,7 @@ import org.bukkit.block.data.BlockData; import org.bukkit.craftbukkit.CraftEquipmentSlot; import org.bukkit.craftbukkit.CraftFeatureFlag; import org.bukkit.craftbukkit.CraftRegistry; +import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.attribute.CraftAttribute; import org.bukkit.craftbukkit.attribute.CraftAttributeInstance; import org.bukkit.craftbukkit.block.data.CraftBlockData; @@ -323,7 +324,7 @@ public final class CraftMagicNumbers implements UnsafeValues { @Override public byte[] processClass(PluginDescriptionFile pdf, String path, byte[] clazz) { try { - clazz = Commodore.convert(clazz, pdf.getName(), ApiVersion.getOrCreateVersion(pdf.getAPIVersion())); + clazz = Commodore.convert(clazz, pdf.getName(), ApiVersion.getOrCreateVersion(pdf.getAPIVersion()), ((CraftServer) Bukkit.getServer()).activeCompatibilities); } catch (Exception ex) { Bukkit.getLogger().log(Level.SEVERE, "Fatal error trying to convert " + pdf.getFullName() + ":" + path, ex); } diff --git a/paper-server/src/main/resources/configurations/bukkit.yml b/paper-server/src/main/resources/configurations/bukkit.yml index eef7c125b2..6615fffc4c 100644 --- a/paper-server/src/main/resources/configurations/bukkit.yml +++ b/paper-server/src/main/resources/configurations/bukkit.yml @@ -23,6 +23,8 @@ settings: shutdown-message: Server closed minimum-api: none use-map-color-cache: true + compatibility: + allow-old-keys-in-registry: false spawn-limits: monsters: 70 animals: 10