diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftHumanEntityMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftHumanEntityMixin.java index 191133d2..fc5a5337 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftHumanEntityMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftHumanEntityMixin.java @@ -1,5 +1,7 @@ package io.izzel.arclight.common.mixin.bukkit; +import io.izzel.arclight.common.mod.server.ArclightForgePermissible; +import io.izzel.arclight.i18n.ArclightConfig; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import org.bukkit.craftbukkit.v.CraftServer; @@ -7,10 +9,14 @@ import org.bukkit.craftbukkit.v.entity.CraftEntity; import org.bukkit.craftbukkit.v.entity.CraftHumanEntity; import org.bukkit.craftbukkit.v.inventory.CraftInventory; import org.bukkit.craftbukkit.v.inventory.CraftInventoryPlayer; +import org.bukkit.permissions.PermissibleBase; +import org.bukkit.permissions.ServerOperator; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; @Mixin(value = CraftHumanEntity.class, remap = false) public abstract class CraftHumanEntityMixin extends CraftEntity { @@ -24,6 +30,15 @@ public abstract class CraftHumanEntityMixin extends CraftEntity { super(server, entity); } + @Redirect(method = "", at = @At(value = "NEW", target = "org/bukkit/permissions/PermissibleBase")) + private PermissibleBase arclight$forwardPerm(ServerOperator opable) { + if (ArclightConfig.spec().getCompat().isForwardPermissionReverse()) { + return new ArclightForgePermissible(opable); + } else { + return new PermissibleBase(opable); + } + } + @Override public void setHandle(Entity entity) { super.setHandle(entity); diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/ArclightForgePermissible.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/ArclightForgePermissible.java new file mode 100644 index 00000000..f31e3552 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/ArclightForgePermissible.java @@ -0,0 +1,77 @@ +package io.izzel.arclight.common.mod.server; + +import io.izzel.arclight.api.Unsafe; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.server.permission.PermissionAPI; +import net.minecraftforge.server.permission.handler.IPermissionHandler; +import net.minecraftforge.server.permission.nodes.PermissionDynamicContextKey; +import net.minecraftforge.server.permission.nodes.PermissionNode; +import net.minecraftforge.server.permission.nodes.PermissionType; +import net.minecraftforge.server.permission.nodes.PermissionTypes; +import org.bukkit.craftbukkit.v.entity.CraftHumanEntity; +import org.bukkit.permissions.PermissibleBase; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.ServerOperator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; + +public class ArclightForgePermissible extends PermissibleBase { + + private final CraftHumanEntity player; + + public ArclightForgePermissible(@Nullable ServerOperator opable) { + super(opable); + this.player = (CraftHumanEntity) opable; + } + + @Override + public boolean hasPermission(@NotNull String inName) { + var node = newNode(inName, (player, playerUUID, context) -> super.hasPermission(inName)); + if (player.getHandle() instanceof ServerPlayer player) { + return getHandler().getPermission(player, node); + } else { + return getHandler().getOfflinePermission(player.getUniqueId(), node); + } + } + + @Override + public boolean hasPermission(@NotNull Permission perm) { + var node = newNode(perm.getName(), (player, playerUUID, context) -> super.hasPermission(perm)); + if (player.getHandle() instanceof ServerPlayer player) { + return getHandler().getPermission(player, node); + } else { + return getHandler().getOfflinePermission(player.getUniqueId(), node); + } + } + + private static final MethodHandle H_handler, H_newNode; + + private static IPermissionHandler getHandler() { + try { + return (IPermissionHandler) H_handler.invokeExact(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("unchecked") + private static PermissionNode newNode(String nodeName, PermissionNode.PermissionResolver defaultResolver, PermissionDynamicContextKey... dynamics) { + try { + return (PermissionNode) H_newNode.invokeExact(nodeName, PermissionTypes.BOOLEAN, defaultResolver, dynamics); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + static { + try { + H_handler = Unsafe.lookup().findStaticGetter(PermissionAPI.class, "activeHandler", IPermissionHandler.class); + H_newNode = Unsafe.lookup().findConstructor(PermissionNode.class, MethodType.methodType(void.class, String.class, PermissionType.class, PermissionNode.PermissionResolver.class, PermissionDynamicContextKey[].class)); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } +} diff --git a/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/CompatSpec.java b/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/CompatSpec.java index 65e33a6c..4977af99 100644 --- a/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/CompatSpec.java +++ b/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/CompatSpec.java @@ -5,6 +5,7 @@ import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; @ConfigSerializable @@ -23,7 +24,7 @@ public class CompatSpec { private List extraLogicWorlds; @Setting("forward-permission") - private boolean forwardPermission; + private String forwardPermission; public Map getMaterials() { return materials; @@ -50,6 +51,10 @@ public class CompatSpec { } public boolean isForwardPermission() { - return forwardPermission; + return Objects.equals(forwardPermission, "true"); + } + + public boolean isForwardPermissionReverse() { + return Objects.equals(forwardPermission, "reverse"); } } diff --git a/i18n-config/src/main/resources/META-INF/i18n/en_us.conf b/i18n-config/src/main/resources/META-INF/i18n/en_us.conf index bff889c0..e8e1fa12 100644 --- a/i18n-config/src/main/resources/META-INF/i18n/en_us.conf +++ b/i18n-config/src/main/resources/META-INF/i18n/en_us.conf @@ -115,5 +115,10 @@ comments { "Extra worlds running logic" "If any mods do not function well, try search class names in logs related to [EXT_LOGIC] and add them here" ] + forward-permission.comment = [ + "true - Forward Forge permission query to Bukkit" + "false - Disable permission forward" + "reverse - Forward Bukkit player perimission query to Forge" + ] } } diff --git a/i18n-config/src/main/resources/META-INF/i18n/zh_cn.conf b/i18n-config/src/main/resources/META-INF/i18n/zh_cn.conf index befd005d..9d8e6648 100644 --- a/i18n-config/src/main/resources/META-INF/i18n/zh_cn.conf +++ b/i18n-config/src/main/resources/META-INF/i18n/zh_cn.conf @@ -115,5 +115,10 @@ comments { "额外运行逻辑的维度类名" "如果有模组世界/功能运行不正常,尝试在日志中搜索和 [EXT_LOGIC] 有关的对应类名并添加" ] + forward-permission.comment = [ + "true - 将 Forge 权限查询请求转发至 Bukkit" + "false - 不启用权限转发" + "reverse - 将 Bukkit 玩家权限查询请求转发至 Forge" + ] } }