mirror of
https://github.com/mcMMO-Dev/mcMMO.git
synced 2025-08-02 12:35:27 +02:00
CompatibilityLayer framework
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
package com.gmail.nossr50.util.compat;
|
||||
|
||||
/**
|
||||
* Compatibility Layers should be named after the functionality they serve
|
||||
*/
|
||||
public interface CompatibilityLayer {
|
||||
/**
|
||||
* Whether or not this CompatibilityLayer successfully initialized and in theory should be functional
|
||||
* @return true if this CompatibilityLayer is functional
|
||||
*/
|
||||
boolean noErrorsOnInitialize();
|
||||
}
|
@@ -0,0 +1,125 @@
|
||||
package com.gmail.nossr50.util.compat;
|
||||
|
||||
import com.gmail.nossr50.locale.LocaleLoader;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.StringUtils;
|
||||
import com.gmail.nossr50.util.compat.layers.PlayerAttackCooldownExploitPreventionLayer;
|
||||
import com.gmail.nossr50.util.nms.NMSVersion;
|
||||
import com.gmail.nossr50.util.platform.MinecraftGameVersion;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* These classes are a band-aid solution for adding NMS support into 2.1.XXX
|
||||
* In 2.2 we are switching to modules and that will clean things up significantly
|
||||
*
|
||||
*/
|
||||
public class CompatibilityManager {
|
||||
private HashMap<CompatibilityType, Boolean> supportedLayers;
|
||||
private boolean isFullyCompatibleServerSoftware = true; //true if all compatibility layers load successfully
|
||||
private final MinecraftGameVersion minecraftGameVersion;
|
||||
private final NMSVersion nmsVersion;
|
||||
|
||||
/* Compatibility Layers */
|
||||
private PlayerAttackCooldownExploitPreventionLayer playerAttackCooldownExploitPreventionLayer;
|
||||
|
||||
public CompatibilityManager(MinecraftGameVersion minecraftGameVersion) {
|
||||
mcMMO.p.getLogger().info("Loading compatibility layers...");
|
||||
this.minecraftGameVersion = minecraftGameVersion;
|
||||
this.nmsVersion = determineNMSVersion();
|
||||
init();
|
||||
mcMMO.p.getLogger().info("Finished loading compatibility layers.");
|
||||
}
|
||||
|
||||
private void init() {
|
||||
initSupportedLayersMap();
|
||||
initCompatibilityLayers();
|
||||
}
|
||||
|
||||
private void initSupportedLayersMap() {
|
||||
supportedLayers = new HashMap<>(); //Init map
|
||||
|
||||
for(CompatibilityType compatibilityType : CompatibilityType.values()) {
|
||||
supportedLayers.put(compatibilityType, false); //All layers are set to false when initialized
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all necessary compatibility layers
|
||||
* For any unsupported layers, load a dummy layer
|
||||
*/
|
||||
private void initCompatibilityLayers() {
|
||||
if(nmsVersion == NMSVersion.UNSUPPORTED) {
|
||||
mcMMO.p.getLogger().info("NMS not supported for this version of Minecraft, possible solutions include updating mcMMO or updating your server software. NMS Support is not available on every version of Minecraft.");
|
||||
mcMMO.p.getLogger().info("Certain features of mcMMO that require NMS will be disabled, you can check what is disabled by running the /mmocompat command!");
|
||||
//Load dummy compatibility layers
|
||||
isFullyCompatibleServerSoftware = false;
|
||||
loadDummyCompatibilityLayers();
|
||||
} else {
|
||||
playerAttackCooldownExploitPreventionLayer = new PlayerAttackCooldownExploitPreventionLayer(nmsVersion);
|
||||
|
||||
//Mark as operational
|
||||
if(playerAttackCooldownExploitPreventionLayer.noErrorsOnInitialize()) {
|
||||
supportedLayers.put(CompatibilityType.PLAYER_ATTACK_COOLDOWN_EXPLOIT_PREVENTION, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadDummyCompatibilityLayers() {
|
||||
|
||||
}
|
||||
|
||||
public void reportCompatibilityStatus(CommandSender commandSender) {
|
||||
if(isFullyCompatibleServerSoftware) {
|
||||
commandSender.sendMessage(LocaleLoader.getString("mcMMO.Template.Prefix",
|
||||
"mcMMO is fully compatible with the currently running server software."));
|
||||
} else {
|
||||
//TODO: Better messages for each incompatible layer
|
||||
for(CompatibilityType compatibilityType : CompatibilityType.values()) {
|
||||
if(!supportedLayers.get(compatibilityType)) {
|
||||
commandSender.sendMessage(LocaleLoader.getString("mcMMO.Template.Prefix",
|
||||
"Support layer for " + StringUtils.getCapitalized(compatibilityType.toString()) + "is not supported on this version of Minecraft."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commandSender.sendMessage(LocaleLoader.getString("mcMMO.Template.Prefix", "NMS Status - " + nmsVersion.toString()));
|
||||
}
|
||||
|
||||
public boolean isCompatibilityLayerOperational(CompatibilityType compatibilityType) {
|
||||
return supportedLayers.get(compatibilityType);
|
||||
}
|
||||
|
||||
public boolean isFullyCompatibleServerSoftware() {
|
||||
return isFullyCompatibleServerSoftware;
|
||||
}
|
||||
|
||||
public NMSVersion getNmsVersion() {
|
||||
return nmsVersion;
|
||||
}
|
||||
|
||||
private NMSVersion determineNMSVersion() {
|
||||
switch(minecraftGameVersion.getMajorVersion().asInt()) {
|
||||
case 1:
|
||||
switch(minecraftGameVersion.getMinorVersion().asInt()) {
|
||||
case 12:
|
||||
return NMSVersion.NMS_1_12_2;
|
||||
case 13:
|
||||
return NMSVersion.NMS_1_13_2;
|
||||
case 14:
|
||||
return NMSVersion.NMS_1_14_4;
|
||||
case 15:
|
||||
return NMSVersion.NMS_1_15_2;
|
||||
}
|
||||
}
|
||||
|
||||
return NMSVersion.UNSUPPORTED;
|
||||
}
|
||||
|
||||
public PlayerAttackCooldownExploitPreventionLayer getPlayerAttackCooldownExploitPreventionLayer() {
|
||||
return playerAttackCooldownExploitPreventionLayer;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package com.gmail.nossr50.util.compat;
|
||||
|
||||
public enum CompatibilityType {
|
||||
PLAYER_ATTACK_COOLDOWN_EXPLOIT_PREVENTION
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
package com.gmail.nossr50.util.compat.layers;
|
||||
|
||||
import com.gmail.nossr50.util.compat.CompatibilityLayer;
|
||||
import com.gmail.nossr50.util.nms.NMSVersion;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
*
|
||||
* These classes are a band-aid solution for adding NMS support into 2.1.XXX
|
||||
* In 2.2 we are switching to modules and that will clean things up significantly
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractCompatibilityLayer implements CompatibilityLayer {
|
||||
|
||||
protected boolean noErrorsOnInitialize = true;
|
||||
protected final @NotNull NMSVersion nmsVersion;
|
||||
|
||||
public AbstractCompatibilityLayer(@NotNull NMSVersion nmsVersion) {
|
||||
this.nmsVersion = nmsVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the CompatibilityLayer
|
||||
* @return true if the CompatibilityLayer initialized and should be functional
|
||||
*/
|
||||
public abstract boolean initializeLayer();
|
||||
|
||||
@Override
|
||||
public boolean noErrorsOnInitialize() {
|
||||
return noErrorsOnInitialize;
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package com.gmail.nossr50.util.compat.layers;
|
||||
|
||||
import com.gmail.nossr50.util.nms.NMSVersion;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public class DummyPlayerAttackCooldownExploitPreventionLayer extends PlayerAttackCooldownExploitPreventionLayer {
|
||||
public DummyPlayerAttackCooldownExploitPreventionLayer() {
|
||||
super(NMSVersion.UNSUPPORTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initializeLayer() {
|
||||
return noErrorsOnInitialize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAttackStrength(Player player) throws InvocationTargetException, IllegalAccessException {
|
||||
return 1.0F; //Always full strength
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getCooldownValue(Player player) throws InvocationTargetException, IllegalAccessException {
|
||||
return 0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetAttackStrength(Player player) throws InvocationTargetException, IllegalAccessException {
|
||||
//Do nothing
|
||||
return;
|
||||
}
|
||||
}
|
@@ -0,0 +1,198 @@
|
||||
package com.gmail.nossr50.util.compat.layers;
|
||||
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.nms.NMSConstants;
|
||||
import com.gmail.nossr50.util.nms.NMSVersion;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
*
|
||||
* These classes are a band-aid solution for adding NMS support into 2.1.XXX
|
||||
* In 2.2 we are switching to modules and that will clean things up significantly
|
||||
*
|
||||
*/
|
||||
public class PlayerAttackCooldownExploitPreventionLayer extends AbstractCompatibilityLayer implements PlayerAttackCooldownMethods{
|
||||
|
||||
private final String cbNMSVersionPath;
|
||||
|
||||
protected Class<?> craftPlayerClass;
|
||||
protected Class<?> entityHumanClass;
|
||||
|
||||
protected Method playerAttackCooldownMethod;
|
||||
protected Method playerAttackStrengthMethod;
|
||||
protected Method resetPlayerAttackCooldownMethod;
|
||||
protected Method getHandleMethod;
|
||||
|
||||
public PlayerAttackCooldownExploitPreventionLayer(@NotNull NMSVersion nmsVersion) {
|
||||
super(nmsVersion);
|
||||
mcMMO.p.getLogger().info("Loading Compatibility Layer... (Player Attack Cooldown Exploit Prevention)");
|
||||
if(!isCompatibleWithMinecraftVersion()) {
|
||||
mcMMO.p.getLogger().severe("this version of mcMMO does not support NMS for this version of Minecraft, try updating mcMMO or updating Minecraft. Not all versions of Minecraft will have NMS support built into mcMMO.");
|
||||
cbNMSVersionPath = "";
|
||||
} else {
|
||||
if(NMSConstants.getCraftBukkitVersionPath(nmsVersion) != null) {
|
||||
cbNMSVersionPath = NMSConstants.getCraftBukkitVersionPath(nmsVersion);
|
||||
noErrorsOnInitialize = initializeLayer();
|
||||
|
||||
if(noErrorsOnInitialize) {
|
||||
mcMMO.p.getLogger().info("Successfully Loaded Compatibility Layer! (Player Attack Cooldown Exploit Prevention)");
|
||||
}
|
||||
} else {
|
||||
mcMMO.p.getLogger().info("Failed to load - CL (Player Attack Cooldown Exploit Prevention) Could not find CB NMS path for CL");
|
||||
flagErrorsDuringStartup();
|
||||
mcMMO.p.getLogger().warning("Could not wire NMS package path for CraftBukkit!");
|
||||
cbNMSVersionPath = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCompatibleWithMinecraftVersion() {
|
||||
switch(nmsVersion) {
|
||||
case NMS_1_13_2:
|
||||
case NMS_1_14_4:
|
||||
case NMS_1_15_2:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache all reflection methods/types/classes needed for the NMS of this CompatibilityLayer
|
||||
* @param cooldownMethodName the cooldown method name
|
||||
* @param attackStrengthMethodName the attack strength method name
|
||||
* @param resetAttackCooldownMethodName the reset attack cooldown method name
|
||||
* @param getHandleMethodName the get handle method name
|
||||
* @return true if NMS was successfully wired
|
||||
*/
|
||||
public boolean wireNMS(@NotNull String cooldownMethodName, @NotNull String attackStrengthMethodName, @NotNull String resetAttackCooldownMethodName, @NotNull String getHandleMethodName) {
|
||||
entityHumanClass = initEntityHumanClass();
|
||||
craftPlayerClass = initCraftPlayerClass();
|
||||
|
||||
try {
|
||||
this.playerAttackCooldownMethod = entityHumanClass.getMethod(cooldownMethodName);
|
||||
this.playerAttackStrengthMethod = entityHumanClass.getMethod(attackStrengthMethodName, float.class);
|
||||
this.resetPlayerAttackCooldownMethod = entityHumanClass.getMethod(resetAttackCooldownMethodName);
|
||||
if (craftPlayerClass != null) {
|
||||
this.getHandleMethod = craftPlayerClass.getMethod(getHandleMethodName);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
flagErrorsDuringStartup();
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached player attack cooldown method
|
||||
* @return the cached player attack cooldown method
|
||||
*/
|
||||
private @Nullable Method getPlayerAttackCooldownMethod() {
|
||||
return playerAttackCooldownMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached player attack strength method
|
||||
* @return the cached player attack strength method
|
||||
*/
|
||||
private @Nullable Method getPlayerAttackStrengthMethod() {
|
||||
return playerAttackStrengthMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached player attack cooldown reset method
|
||||
* @return the cached player attack cooldown reset method
|
||||
*/
|
||||
private @Nullable Method getResetPlayerAttackCooldownMethod() {
|
||||
return resetPlayerAttackCooldownMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab the CraftPlayer class type from NMS
|
||||
* @return the CraftPlayer class type from NMS
|
||||
*/
|
||||
private @Nullable Class<?> initCraftPlayerClass() {
|
||||
try {
|
||||
return Class.forName(NMSConstants.getCraftPlayerClassPath(cbNMSVersionPath));
|
||||
} catch (ClassNotFoundException e) {
|
||||
flagErrorsDuringStartup();
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab the EntityHuman class type from NMS
|
||||
* @return the EntityHuman class type from NMS
|
||||
*/
|
||||
private @Nullable Class<?> initEntityHumanClass() {
|
||||
try {
|
||||
return Class.forName(NMSConstants.getEntityHumanClassPath(cbNMSVersionPath));
|
||||
} catch (ClassNotFoundException e) {
|
||||
flagErrorsDuringStartup();
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void flagErrorsDuringStartup() {
|
||||
noErrorsOnInitialize = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs the attack strength for a player
|
||||
* Should be noted that as of today there is no way to capture a players current attack strength in spigot when they attack an entity outside of network packet listening
|
||||
* @param player target player
|
||||
* @return the float value of the player's attack strength
|
||||
*/
|
||||
@Override
|
||||
public float getAttackStrength(Player player) throws InvocationTargetException, IllegalAccessException {
|
||||
Object craftPlayer = craftPlayerClass.cast(player);
|
||||
Object entityHuman = entityHumanClass.cast(getHandleMethod.invoke(craftPlayer));
|
||||
|
||||
return (float) playerAttackStrengthMethod.invoke(entityHuman, 0F); //Add no adjustment ticks
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getCooldownValue(Player player) throws InvocationTargetException, IllegalAccessException {
|
||||
Object craftPlayer = craftPlayerClass.cast(player);
|
||||
Object entityHuman = entityHumanClass.cast(getHandleMethod.invoke(craftPlayer));
|
||||
|
||||
return (float) playerAttackCooldownMethod.invoke(entityHuman); //Add no adjustment ticks
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetAttackStrength(Player player) throws InvocationTargetException, IllegalAccessException {
|
||||
Object craftPlayer = craftPlayerClass.cast(player);
|
||||
Object entityHuman = entityHumanClass.cast(getHandleMethod.invoke(craftPlayer));
|
||||
|
||||
resetPlayerAttackCooldownMethod.invoke(entityHuman);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initializeLayer() {
|
||||
switch(nmsVersion) {
|
||||
case NMS_1_12_2:
|
||||
return wireNMS("dr", "n", "ds", "getHandle");
|
||||
case NMS_1_13_2:
|
||||
return wireNMS("dG", "r", "dH", "getHandle");
|
||||
case NMS_1_14_4:
|
||||
return wireNMS("dY", "s", "dZ", "getHandle");
|
||||
case NMS_1_15_2:
|
||||
return wireNMS("ex", "s", "ey", "getHandle");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package com.gmail.nossr50.util.compat.layers;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public interface PlayerAttackCooldownMethods {
|
||||
/**
|
||||
* Grabs the attack strength for a player
|
||||
* Should be noted that as of today there is no way to capture a players current attack strength in spigot when they attack an entity outside of network packet listening
|
||||
* @param player target player
|
||||
* @return the float value of the player's attack strength
|
||||
*/
|
||||
float getAttackStrength(Player player) throws InvocationTargetException, IllegalAccessException;
|
||||
|
||||
float getCooldownValue(Player player) throws InvocationTargetException, IllegalAccessException;
|
||||
|
||||
void resetAttackStrength(Player player) throws InvocationTargetException, IllegalAccessException;
|
||||
}
|
Reference in New Issue
Block a user