diff --git a/build.gradle.kts b/build.gradle.kts index 7b6f9d4..cef1933 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -28,6 +28,7 @@ repositories { dependencies { paperweight.paperDevBundle("1.20.1-R0.1-SNAPSHOT") implementation("xyz.jpenilla:reflection-remapper:0.1.0-SNAPSHOT") + implementation("org.incendo:cloud-paper:2.0.0-beta.2") // Zstd Compression Library implementation("com.github.luben:zstd-jni:1.5.5-4") diff --git a/src/main/java/com/moulberry/axiom/AxiomPaper.java b/src/main/java/com/moulberry/axiom/AxiomPaper.java index 7481b52..b9a16b8 100644 --- a/src/main/java/com/moulberry/axiom/AxiomPaper.java +++ b/src/main/java/com/moulberry/axiom/AxiomPaper.java @@ -3,6 +3,7 @@ package com.moulberry.axiom; import com.google.common.util.concurrent.RateLimiter; import com.moulberry.axiom.blueprint.ServerBlueprintManager; import com.moulberry.axiom.buffer.CompressedBlockEntity; +import com.moulberry.axiom.commands.AxiomDebugCommand; import com.moulberry.axiom.event.AxiomCreateWorldPropertiesEvent; import com.moulberry.axiom.event.AxiomModifyWorldEvent; import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration; @@ -26,6 +27,7 @@ import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket; import net.minecraft.server.MinecraftServer; import net.minecraft.world.level.block.state.BlockState; import org.bukkit.*; +import org.bukkit.command.CommandSender; import org.bukkit.configuration.Configuration; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -34,6 +36,9 @@ import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.messaging.Messenger; import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.bukkit.CloudBukkitCapabilities; +import org.incendo.cloud.execution.ExecutionCoordinator; +import org.incendo.cloud.paper.PaperCommandManager; import org.jetbrains.annotations.Nullable; import java.io.FileReader; @@ -279,6 +284,17 @@ public class AxiomPaper extends JavaPlugin implements Listener { Bukkit.getScheduler().scheduleSyncRepeatingTask(this, () -> { WorldExtension.tick(MinecraftServer.getServer(), sendMarkers, maxChunkRelightsPerTick, maxChunkSendsPerTick); }, 1, 1); + + PaperCommandManager manager = PaperCommandManager.createNative( + this, + ExecutionCoordinator.simpleCoordinator() + ); + + if (manager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) { + manager.registerBrigadier(); + } + + AxiomDebugCommand.register(this, manager); } public boolean logLargeBlockBufferChanges() { diff --git a/src/main/java/com/moulberry/axiom/Restrictions.java b/src/main/java/com/moulberry/axiom/Restrictions.java index 5dd0180..4b6c25c 100644 --- a/src/main/java/com/moulberry/axiom/Restrictions.java +++ b/src/main/java/com/moulberry/axiom/Restrictions.java @@ -61,4 +61,16 @@ public class Restrictions { } } + @Override + public String toString() { + return "Restrictions{" + + "canImportBlocks=" + canImportBlocks + + ", canUseEditor=" + canUseEditor + + ", canEditDisplayEntities=" + canEditDisplayEntities + + ", maxSectionsPerSecond=" + maxSectionsPerSecond + + ", boundsMin=" + boundsMin + + ", boundsMax=" + boundsMax + + ", lastPlotBounds=" + lastPlotBounds + + '}'; + } } diff --git a/src/main/java/com/moulberry/axiom/commands/AxiomDebugCommand.java b/src/main/java/com/moulberry/axiom/commands/AxiomDebugCommand.java new file mode 100644 index 0000000..5172953 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/commands/AxiomDebugCommand.java @@ -0,0 +1,139 @@ +package com.moulberry.axiom.commands; + +import com.moulberry.axiom.AxiomPaper; +import com.moulberry.axiom.Restrictions; +import com.moulberry.axiom.integration.Integration; +import com.moulberry.axiom.integration.plotsquared.PlotSquaredIntegration; +import com.moulberry.axiom.integration.worldguard.WorldGuardIntegration; +import net.kyori.adventure.text.Component; +import org.bukkit.block.Block; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.bukkit.BukkitCommandManager; +import org.incendo.cloud.parser.standard.EnumParser; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.permission.PredicatePermission; + +import java.util.UUID; + +public class AxiomDebugCommand { + + /** + * Command requires either the axiom.debug permission or for you to have the UUID d0e05de7-6067-454d-beae-c6d19d886191 + * The command isn't capable of modifying the world, only checking various properties for debugging purposes + * It should be 100% safe to give to any player, but is locked behind some restrictions due to the potential + * for lagging the server by spamming certain commands + */ + + private static final UUID MOULBERRY_UUID = UUID.fromString("d0e05de7-6067-454d-beae-c6d19d886191"); + + public static void register(AxiomPaper axiomPaper, BukkitCommandManager manager) { + manager.command( + base(manager, "hasAxiomPermission").handler(context -> { + boolean hasAxiomPermission = axiomPaper.hasAxiomPermission(context.sender()); + context.sender().sendMessage(Component.text("hasAxiomPermission: " + hasAxiomPermission)); + }) + ); + manager.command( + base(manager, "canUseAxiom").handler(context -> { + boolean canUseAxiom = axiomPaper.canUseAxiom(context.sender()); + context.sender().sendMessage(Component.text("canUseAxiom: " + canUseAxiom)); + }) + ); + manager.command( + base(manager, "isMismatchedDataVersion").handler(context -> { + boolean isMismatchedDataVersion = axiomPaper.isMismatchedDataVersion(context.sender().getUniqueId()); + context.sender().sendMessage(Component.text("isMismatchedDataVersion: " + isMismatchedDataVersion)); + }) + ); + manager.command( + base(manager, "canModifyWorld").handler(context -> { + boolean canModifyWorld = axiomPaper.canModifyWorld(context.sender(), context.sender().getWorld()); + context.sender().sendMessage(Component.text("canModifyWorld: " + canModifyWorld)); + }) + ); + manager.command( + base(manager, "isClientListening").required("channel", StringParser.greedyStringParser()).handler(context -> { + String channel = context.get("channel"); + boolean isClientListening = context.sender().getListeningPluginChannels().contains(channel); + context.sender().sendMessage(Component.text("listening to " + channel +": " + isClientListening)); + }) + ); + manager.command( + base(manager, "hasPermission").required("permission", StringParser.greedyStringParser()).handler(context -> { + String permission = context.get("permission"); + boolean hasPermission = context.sender().hasPermission(permission); + context.sender().sendMessage(Component.text("has permission " + permission +": " + hasPermission)); + }) + ); + manager.command( + base(manager, "getRestrictions").handler(context -> { + Restrictions restrictions = axiomPaper.playerRestrictions.get(context.sender().getUniqueId()); + if (restrictions == null) { + context.sender().sendMessage(Component.text("no restrictions")); + } else { + context.sender().sendMessage(Component.text("restrictions: " + restrictions)); + } + }) + ); + enum IntegrationType { + PLOT_SQUARED, + WORLD_GUARD + } + manager.command( + base(manager, "canBreakBlockAtCurrentPosition").optional("type", EnumParser.enumParser(IntegrationType.class)).handler(context -> { + IntegrationType integrationType = (IntegrationType) context.optional("type").orElse(null); + + Block block = context.sender().getWorld().getBlockAt(context.sender().getLocation()); + + boolean canBreakBlock; + if (integrationType == IntegrationType.PLOT_SQUARED) { + canBreakBlock = PlotSquaredIntegration.canBreakBlock(context.sender(), block); + } else if (integrationType == IntegrationType.WORLD_GUARD) { + canBreakBlock = WorldGuardIntegration.canBreakBlock(context.sender(), block.getLocation()); + } else { + canBreakBlock = Integration.canBreakBlock(context.sender(), block); + } + context.sender().sendMessage(Component.text("canBreakBlock: " + canBreakBlock)); + }) + ); + manager.command( + base(manager, "canPlaceBlockAtCurrentPosition").optional("type", EnumParser.enumParser(IntegrationType.class)).handler(context -> { + IntegrationType integrationType = (IntegrationType) context.optional("type").orElse(null); + + boolean canPlaceBlock; + if (integrationType == IntegrationType.PLOT_SQUARED) { + canPlaceBlock = PlotSquaredIntegration.canPlaceBlock(context.sender(), context.sender().getLocation()); + } else if (integrationType == IntegrationType.WORLD_GUARD) { + canPlaceBlock = WorldGuardIntegration.canPlaceBlock(context.sender(), context.sender().getLocation()); + } else { + canPlaceBlock = Integration.canPlaceBlock(context.sender(), context.sender().getLocation()); + } + context.sender().sendMessage(Component.text("canPlaceBlock: " + canPlaceBlock)); + }) + ); + manager.command( + base(manager, "isPlotWorld").handler(context -> { + boolean isPlotWorld = PlotSquaredIntegration.isPlotWorld(context.sender().getWorld()); + context.sender().sendMessage(Component.text("isPlotWorld: " + isPlotWorld)); + }) + ); + manager.command( + base(manager, "getCurrentEditablePlot").handler(context -> { + PlotSquaredIntegration.PlotBounds plotBounds = PlotSquaredIntegration.getCurrentEditablePlot(context.sender()); + context.sender().sendMessage(Component.text("plotBounds: " + plotBounds)); + }) + ); + } + + private static Command.Builder base(BukkitCommandManager manager, String subcommand) { + return manager.commandBuilder("axiompaperdebug") + .literal(subcommand) + .senderType(Player.class) + .permission(PredicatePermission.of(sender -> sender.hasPermission("axiom.debug") || sender.getUniqueId().equals(MOULBERRY_UUID))); + } + + + +} diff --git a/src/main/java/com/moulberry/axiom/integration/worldguard/WorldGuardIntegrationImpl.java b/src/main/java/com/moulberry/axiom/integration/worldguard/WorldGuardIntegrationImpl.java index 3110655..4787e18 100644 --- a/src/main/java/com/moulberry/axiom/integration/worldguard/WorldGuardIntegrationImpl.java +++ b/src/main/java/com/moulberry/axiom/integration/worldguard/WorldGuardIntegrationImpl.java @@ -34,15 +34,19 @@ public class WorldGuardIntegrationImpl { private static boolean testBuild(Player player, org.bukkit.Location loc, StateFlag flag) { WorldGuardPlatform platform = WorldGuard.getInstance().getPlatform(); + com.sk89q.worldedit.world.World worldEditWorld = BukkitAdapter.adapt(loc.getWorld()); + LocalPlayer worldGuardPlayer = WorldGuardPlugin.inst().wrapPlayer(player); + + if (platform.getSessionManager().hasBypass(worldGuardPlayer, worldEditWorld)) { + return true; + } + RegionContainer regionContainer = platform.getRegionContainer(); if (regionContainer == null) { return true; } RegionQuery query = regionContainer.createQuery(); - - LocalPlayer worldGuardPlayer = WorldGuardPlugin.inst().wrapPlayer(player); - return query.testBuild(BukkitAdapter.adapt(loc), worldGuardPlayer, flag); } @@ -118,12 +122,12 @@ public class WorldGuardIntegrationImpl { BlockVector3 regionMin = region.getMinimumPoint(); BlockVector3 regionMax = region.getMaximumPoint(); - int regionMinX = Math.max(regionMin.getBlockX(), cx*16) - minX; - int regionMinY = Math.max(regionMin.getBlockY(), cy*16) - minY; - int regionMinZ = Math.max(regionMin.getBlockZ(), cz*16) - minZ; - int regionMaxX = Math.min(regionMax.getBlockX(), cx*16+15) - minX; - int regionMaxY = Math.min(regionMax.getBlockY(), cy*16+15) - minY; - int regionMaxZ = Math.min(regionMax.getBlockZ(), cz*16+15) - minZ; + int regionMinX = Math.max(regionMin.getX(), cx*16) - minX; + int regionMinY = Math.max(regionMin.getY(), cy*16) - minY; + int regionMinZ = Math.max(regionMin.getZ(), cz*16) - minZ; + int regionMaxX = Math.min(regionMax.getX(), cx*16+15) - minX; + int regionMaxY = Math.min(regionMax.getY(), cy*16+15) - minY; + int regionMaxZ = Math.min(regionMax.getZ(), cz*16+15) - minZ; Box box = new Box(regionMinX, regionMinY, regionMinZ, regionMaxX, regionMaxY, regionMaxZ); if (value == StateFlag.State.DENY) { diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 2bbc099..8afb8ab 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -8,8 +8,10 @@ api-version: "$apiVersion" permissions: axiom.*: description: Allows use of all default Axiom features - default: true # op - + default: op + axiom.debug: + description: Allows use of the /axiompaperdebug command + default: op axiom.entity.*: description: Allows use of all entity-related features (spawning, manipulating, deleting) default: op