diff --git a/gradle.properties b/gradle.properties index 324dbcb..a4f01a9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.configuration-cache=false # Mod mod_id=video_player -mod_version=0.4.30 +mod_version=0.4.31 maven_group=com.ejclaw.videoplayer archives_base_name=video_player diff --git a/src/main/java/com/ejclaw/videoplayer/command/CommandPermissions.java b/src/main/java/com/ejclaw/videoplayer/command/CommandPermissions.java new file mode 100644 index 0000000..c7c446a --- /dev/null +++ b/src/main/java/com/ejclaw/videoplayer/command/CommandPermissions.java @@ -0,0 +1,45 @@ +package com.ejclaw.videoplayer.command; + +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.permissions.Permissions; +import net.minecraft.world.entity.player.Player; + +/** + * Shared {@code .requires(...)} predicate for all {@code /video*} commands. + * + *

Semantics: + *

+ * + *

The bypass for non-player sources is safe because reaching one of those execution + * contexts already requires server-operator trust: + *

+ */ +public final class CommandPermissions { + private CommandPermissions() {} + + /** + * Returns {@code true} when the source is allowed to run a {@code /video*} command. + * + *

Player → OP only. Anything else → always allowed. + */ + public static boolean opOrServer(CommandSourceStack s) { + if (s.getEntity() instanceof Player) { + return s.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER); + } + return true; + } +} diff --git a/src/main/java/com/ejclaw/videoplayer/command/VideoCacheCommand.java b/src/main/java/com/ejclaw/videoplayer/command/VideoCacheCommand.java index a44fb3e..6d552df 100644 --- a/src/main/java/com/ejclaw/videoplayer/command/VideoCacheCommand.java +++ b/src/main/java/com/ejclaw/videoplayer/command/VideoCacheCommand.java @@ -20,7 +20,6 @@ import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.Style; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.permissions.Permissions; import java.net.URI; import java.util.Map; @@ -32,8 +31,9 @@ import java.util.Map; *
{@code /videocache remove } — drop the entry from server config and tell every client * to delete the matching cache file. * - *

Replaces the old {@code /videopreload}. Same permission gate - * ({@link Permissions#COMMANDS_GAMEMASTER}) so command blocks can drive it. + *

Replaces the old {@code /videopreload}. Permission gate via + * {@link CommandPermissions#opOrServer(CommandSourceStack)} so command blocks and datapack + * functions can drive it without touching {@code functionPermissionLevel}. */ public final class VideoCacheCommand { private VideoCacheCommand() {} @@ -44,7 +44,7 @@ public final class VideoCacheCommand { private static LiteralArgumentBuilder build(String root) { return Commands.literal(root) - .requires(s -> s.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER)) + .requires(CommandPermissions::opOrServer) .then(Commands.literal("add") .then(Commands.argument("name", StringArgumentType.word()) .then(Commands.argument("url", StringArgumentType.greedyString()) diff --git a/src/main/java/com/ejclaw/videoplayer/command/VideoDeleteCommand.java b/src/main/java/com/ejclaw/videoplayer/command/VideoDeleteCommand.java index b2fe6b7..9370fbf 100644 --- a/src/main/java/com/ejclaw/videoplayer/command/VideoDeleteCommand.java +++ b/src/main/java/com/ejclaw/videoplayer/command/VideoDeleteCommand.java @@ -9,7 +9,6 @@ import net.minecraft.commands.arguments.coordinates.BlockPosArgument; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.permissions.Permissions; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; @@ -24,7 +23,7 @@ public final class VideoDeleteCommand { private static com.mojang.brigadier.builder.LiteralArgumentBuilder build(String name) { return Commands.literal(name) - .requires(s -> s.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER)) + .requires(CommandPermissions::opOrServer) .then(Commands.argument("pos", BlockPosArgument.blockPos()) .executes(VideoDeleteCommand::run)); } diff --git a/src/main/java/com/ejclaw/videoplayer/command/VideoMuteCommand.java b/src/main/java/com/ejclaw/videoplayer/command/VideoMuteCommand.java index 873de63..bc87d02 100644 --- a/src/main/java/com/ejclaw/videoplayer/command/VideoMuteCommand.java +++ b/src/main/java/com/ejclaw/videoplayer/command/VideoMuteCommand.java @@ -14,7 +14,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.permissions.Permissions; /** SPEC §4.5.1 — {@code /videoMute } */ public final class VideoMuteCommand { @@ -27,7 +26,7 @@ public final class VideoMuteCommand { private static com.mojang.brigadier.builder.LiteralArgumentBuilder build(String name) { return Commands.literal(name) - .requires(s -> s.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER)) + .requires(CommandPermissions::opOrServer) .then(Commands.argument("pos", BlockPosArgument.blockPos()) .then(Commands.argument("state", StringArgumentType.word()) .executes(VideoMuteCommand::run))); diff --git a/src/main/java/com/ejclaw/videoplayer/command/VideoPlaceCommand.java b/src/main/java/com/ejclaw/videoplayer/command/VideoPlaceCommand.java index 0a6ea76..173981f 100644 --- a/src/main/java/com/ejclaw/videoplayer/command/VideoPlaceCommand.java +++ b/src/main/java/com/ejclaw/videoplayer/command/VideoPlaceCommand.java @@ -19,7 +19,6 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.permissions.Permissions; import net.minecraft.world.level.block.Block; /** @@ -53,7 +52,7 @@ public final class VideoPlaceCommand { private static com.mojang.brigadier.builder.LiteralArgumentBuilder build(String name) { return Commands.literal(name) - .requires(s -> s.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER)) + .requires(CommandPermissions::opOrServer) .then(Commands.argument("pos", BlockPosArgument.blockPos()) .then(Commands.argument("facing", StringArgumentType.word()) .then(Commands.argument("width", IntegerArgumentType.integer(1, 32)) diff --git a/src/main/java/com/ejclaw/videoplayer/command/VideoStickCommand.java b/src/main/java/com/ejclaw/videoplayer/command/VideoStickCommand.java index 2f84b68..1f74046 100644 --- a/src/main/java/com/ejclaw/videoplayer/command/VideoStickCommand.java +++ b/src/main/java/com/ejclaw/videoplayer/command/VideoStickCommand.java @@ -6,18 +6,17 @@ import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.permissions.Permissions; import net.minecraft.world.item.ItemStack; public final class VideoStickCommand { private VideoStickCommand() {} public static void register(CommandDispatcher dispatcher) { - // OP/console/command-block 만 사용 가능. Permissions.COMMANDS_GAMEMASTER = level 2, - // 즉 /op 받은 플레이어(level 4) 와 콘솔(level 4), command block(default level 2) 통과. + // 플레이어는 OP(level 2+) 만, 콘솔/커맨드블럭/함수(/function) 는 무조건 통과. + // 따라서 functionPermissionLevel 같은 gamerule 을 만질 필요가 없다. // 일반 플레이어(level 0) 는 탭 자동완성에도 안 떠야 정상. dispatcher.register(Commands.literal("videoStick") - .requires(s -> s.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER)) + .requires(CommandPermissions::opOrServer) .executes(ctx -> run(ctx.getSource()))); }