diff --git a/divinemc-server/build.gradle.kts.patch b/divinemc-server/build.gradle.kts.patch index 52bf821..e933b44 100644 --- a/divinemc-server/build.gradle.kts.patch +++ b/divinemc-server/build.gradle.kts.patch @@ -56,7 +56,29 @@ implementation("ca.spottedleaf:concurrentutil:0.0.2") // Paper - Add ConcurrentUtil dependency // Paper start implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+ -@@ -219,14 +_,14 @@ +@@ -207,26 +_,35 @@ + // Paper end - spark + } + ++// DivineMC start - hide irrelevant compilation warnings ++tasks.withType { ++ val compilerArgs = options.compilerArgs ++ compilerArgs.add("-Xlint:-module") ++ compilerArgs.add("-Xlint:-removal") ++ compilerArgs.add("-Xlint:-dep-ann") ++} ++// DivineMC end - hide irrelevant compilation warnings ++ + tasks.jar { + manifest { + val git = Git(rootProject.layout.projectDirectory.path) + val mcVersion = rootProject.providers.gradleProperty("mcVersion").get() + val build = System.getenv("BUILD_NUMBER") ?: null +- val buildTime = if (build != null) Instant.now() else Instant.EPOCH ++ val buildTime = Instant.now() // DivineMC - Build time to current, we dont have a build server rn + val gitHash = git.exec(providers, "rev-parse", "--short=7", "HEAD").get().trim() + val implementationVersion = "$mcVersion-${build ?: "DEV"}-$gitHash" + val date = git.exec(providers, "show", "-s", "--format=%ci", gitHash).get().trim() // Paper val gitBranch = git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim() // Paper attributes( "Main-Class" to "org.bukkit.craftbukkit.Main", diff --git a/divinemc-server/minecraft-patches/features/0002-DivineMC-Configuration.patch b/divinemc-server/minecraft-patches/features/0002-DivineMC-Configuration.patch index 46fdc82..f6a7aad 100644 --- a/divinemc-server/minecraft-patches/features/0002-DivineMC-Configuration.patch +++ b/divinemc-server/minecraft-patches/features/0002-DivineMC-Configuration.patch @@ -5,10 +5,10 @@ Subject: [PATCH] DivineMC Configuration diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 90cdcb8934e28dcc51272c9b40a6d89ac5dec75e..884f86f3a8f8bcc5242a9c9f44cc09506312b8c0 100644 +index b545362d99d39e1fbecf2e38ba4406e7936b9d6e..18b8405ac4de2b96ae8164fd580f9bd164b8db81 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -300,6 +300,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop pluginsBlockingSleep = new java.util.HashSet<>(); // Paper - API to allow/disallow tick sleeping public boolean lagging = false; // Purpur - Lagging threshold -@@ -472,6 +473,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop -Date: Sun, 12 Jan 2025 16:55:31 +0300 -Subject: [PATCH] temporary fix for purpur build failure - - -diff --git a/net/minecraft/world/level/block/EndGatewayBlock.java b/net/minecraft/world/level/block/EndGatewayBlock.java -index 017bc4fda1a43e74acf77860916309ca7a4b0b10..54abeb142e119edd1c1d1c263821b95b1f05c388 100644 ---- a/net/minecraft/world/level/block/EndGatewayBlock.java -+++ b/net/minecraft/world/level/block/EndGatewayBlock.java -@@ -100,7 +100,7 @@ public class EndGatewayBlock extends BaseEntityBlock implements Portal { - // Paper end - call EntityPortalEnterEvent - // Purpur start - Add EntityTeleportHinderedEvent - if (level.purpurConfig.imposeTeleportRestrictionsOnGateways && (entity.isVehicle() || entity.isPassenger())) { -- if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), entity.isPassenger() ? org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_PASSENGER : org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, PlayerTeleportEvent.TeleportCause.END_GATEWAY).callEvent()) { -+ if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), entity.isPassenger() ? org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_PASSENGER : org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.END_GATEWAY).callEvent()) { - return; - } - } -diff --git a/net/minecraft/world/level/block/EndPortalBlock.java b/net/minecraft/world/level/block/EndPortalBlock.java -index a229bf06e34305db5b8aa81bc26b47bfb7d79c5c..7e60bcadd2d343e00fc554dba0b85c9191f7efb6 100644 ---- a/net/minecraft/world/level/block/EndPortalBlock.java -+++ b/net/minecraft/world/level/block/EndPortalBlock.java -@@ -60,7 +60,7 @@ public class EndPortalBlock extends BaseEntityBlock implements Portal { - if (entity.canUsePortal(false)) { - // Purpur start - Add EntityTeleportHinderedEvent - if (level.purpurConfig.imposeTeleportRestrictionsOnEndPortals && (entity.isVehicle() || entity.isPassenger())) { -- if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), entity.isPassenger() ? org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_PASSENGER : org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, PlayerTeleportEvent.TeleportCause.END_PORTAL).callEvent()) { -+ if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), entity.isPassenger() ? org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_PASSENGER : org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.END_PORTAL).callEvent()) { - return; - } - } diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch index 1356932..4753c2c 100644 --- a/divinemc-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch @@ -1,5 +1,13 @@ --- a/net/minecraft/server/dedicated/DedicatedServer.java +++ b/net/minecraft/server/dedicated/DedicatedServer.java +@@ -231,6 +_,7 @@ + org.spigotmc.WatchdogThread.doStart(org.spigotmc.SpigotConfig.timeoutTime, org.spigotmc.SpigotConfig.restartOnCrash); // Paper - start watchdog thread + thread.start(); // Paper - Enhance console tab completions for brigadier commands; start console thread after MinecraftServer.console & PaperConfig are initialized + io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command ++ space.bxteam.divinemc.command.DivineCommands.registerCommands(this); // DivineMC - register commands + this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark + com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics + // Purpur start - Purpur config files @@ -301,7 +_,7 @@ String proxyFlavor = (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled) ? "Velocity" : "BungeeCord"; String proxyLink = (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled) ? "https://docs.papermc.io/velocity/security" : "http://www.spigotmc.org/wiki/firewall-guide/"; diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/server/level/ServerPlayer.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/server/level/ServerPlayer.java.patch index 349a5fe..8272d26 100644 --- a/divinemc-server/minecraft-patches/sources/net/minecraft/server/level/ServerPlayer.java.patch +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/server/level/ServerPlayer.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -2239,6 +_,7 @@ +@@ -2240,6 +_,7 @@ this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, gameMode.getId())); if (gameMode == GameType.SPECTATOR) { this.removeEntitiesOnShoulder(); diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/Entity.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/Entity.java.patch index 601e4bb..e65b197 100644 --- a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/Entity.java.patch +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/Entity.java.patch @@ -8,7 +8,7 @@ public boolean onGround; public boolean horizontalCollision; public boolean verticalCollision; -@@ -1095,6 +_,12 @@ +@@ -1113,6 +_,12 @@ // Paper end - detailed watchdog information public void move(MoverType type, Vec3 movement) { @@ -21,7 +21,7 @@ final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity // Paper start - detailed watchdog information ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot move an entity off-main"); -@@ -4214,6 +_,7 @@ +@@ -4232,6 +_,7 @@ } public final void setBoundingBox(AABB bb) { diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/LivingEntity.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/LivingEntity.java.patch index 980367b..d2e4b23 100644 --- a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/LivingEntity.java.patch +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/LivingEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java -@@ -1358,7 +_,7 @@ +@@ -1377,7 +_,7 @@ player.setRealHealth(health); } @@ -9,7 +9,7 @@ return; } // CraftBukkit end -@@ -2637,6 +_,7 @@ +@@ -2656,6 +_,7 @@ } protected void updateSwingTime() { @@ -17,7 +17,7 @@ int currentSwingDuration = this.getCurrentSwingDuration(); if (this.swinging) { this.swingTime++; -@@ -3120,7 +_,13 @@ +@@ -3139,7 +_,13 @@ } protected float getFlyingSpeed() { @@ -32,7 +32,7 @@ } public float getSpeed() { -@@ -3561,6 +_,7 @@ +@@ -3606,6 +_,7 @@ protected void updateFallFlying() { this.checkSlowFallDistance(); if (!this.level().isClientSide) { diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/player/Player.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/player/Player.java.patch index c2e6d82..193fc82 100644 --- a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/player/Player.java.patch +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/player/Player.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/player/Player.java +++ b/net/minecraft/world/entity/player/Player.java -@@ -1864,6 +_,11 @@ +@@ -1872,6 +_,11 @@ } public void causeFoodExhaustion(float exhaustion, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason reason) { diff --git a/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java.patch b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java.patch new file mode 100644 index 0000000..48afc6a --- /dev/null +++ b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java.patch @@ -0,0 +1,10 @@ +--- a/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java +@@ -5,6 +_,7 @@ + + public final class CraftDefaultPermissions { + private static final String ROOT = "minecraft"; ++ public static final String DIVINEMC_ROOT = "divinemc"; // DivineMC - permission root for commands + + private CraftDefaultPermissions() {} + diff --git a/divinemc-server/purpur-patches/features/0001-Optimize-default-values-for-configs.patch b/divinemc-server/purpur-patches/features/0001-Optimize-default-values-for-configs.patch index b861116..2ac4f5e 100644 --- a/divinemc-server/purpur-patches/features/0001-Optimize-default-values-for-configs.patch +++ b/divinemc-server/purpur-patches/features/0001-Optimize-default-values-for-configs.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Optimize default values for configs diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 3b0875ed8ef29a5cede89e22e86bf90ab99d7f13..c5f0b3a31dfa851363df5f96d193c313474b192b 100644 +index 31f41990623120f8de9f7c842bd8ad4f5affe59f..8f9a73f524ffaa439f0cb59eeb3de3c2ff9fac3d 100644 --- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java +++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -201,7 +201,7 @@ public class PurpurConfig { +@@ -217,7 +217,7 @@ public class PurpurConfig { laggingThreshold = getDouble("settings.lagging-threshold", laggingThreshold); } diff --git a/divinemc-server/purpur-patches/features/0002-Add-missing-purpur-config-options.patch b/divinemc-server/purpur-patches/features/0002-Add-missing-purpur-config-options.patch index 7b06ee4..10bb9d1 100644 --- a/divinemc-server/purpur-patches/features/0002-Add-missing-purpur-config-options.patch +++ b/divinemc-server/purpur-patches/features/0002-Add-missing-purpur-config-options.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add missing purpur config options diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index c5f0b3a31dfa851363df5f96d193c313474b192b..0eb993f2c6b8b7b59ae6995cefb0a921fd3a480a 100644 +index 8f9a73f524ffaa439f0cb59eeb3de3c2ff9fac3d..3ba75f262e7477fc4d1a544599646e1d33fe31aa 100644 --- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java +++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -235,6 +235,7 @@ public class PurpurConfig { +@@ -273,6 +273,7 @@ public class PurpurConfig { } public static int barrelRows = 3; @@ -16,7 +16,7 @@ index c5f0b3a31dfa851363df5f96d193c313474b192b..0eb993f2c6b8b7b59ae6995cefb0a921 public static boolean enderChestSixRows = false; public static boolean enderChestPermissionRows = false; public static boolean cryingObsidianValidForPortalFrame = false; -@@ -265,6 +266,7 @@ public class PurpurConfig { +@@ -306,6 +307,7 @@ public class PurpurConfig { case 1 -> 9; default -> 27; }); @@ -25,10 +25,10 @@ index c5f0b3a31dfa851363df5f96d193c313474b192b..0eb993f2c6b8b7b59ae6995cefb0a921 org.bukkit.event.inventory.InventoryType.ENDER_CHEST.setDefaultSize(enderChestSixRows ? 54 : 27); enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows); diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f52cbfa21b9df10f30216138585218074a495f8a..0be19a242561e58792c2614f2a9c12c225edee9e 100644 +index 278e43c190613a0181211c160e063a514afae1ae..8203ff823587b6a282506620d0b1f6b389f0d22c 100644 --- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java +++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -575,12 +575,20 @@ public class PurpurWorldConfig { +@@ -979,12 +979,20 @@ public class PurpurWorldConfig { public boolean allayControllable = true; public double allayMaxHealth = 20.0D; public double allayScale = 1.0D; @@ -49,7 +49,7 @@ index f52cbfa21b9df10f30216138585218074a495f8a..0be19a242561e58792c2614f2a9c12c2 } public boolean armadilloRidable = false; -@@ -716,6 +724,10 @@ public class PurpurWorldConfig { +@@ -1124,6 +1132,10 @@ public class PurpurWorldConfig { public double camelMovementSpeedMin = 0.09D; public double camelMovementSpeedMax = 0.09D; public int camelBreedingTicks = 6000; @@ -60,7 +60,7 @@ index f52cbfa21b9df10f30216138585218074a495f8a..0be19a242561e58792c2614f2a9c12c2 private void camelSettings() { camelRidableInWater = getBoolean("mobs.camel.ridable-in-water", camelRidableInWater); camelMaxHealthMin = getDouble("mobs.camel.attributes.max_health.min", camelMaxHealthMin); -@@ -725,6 +737,10 @@ public class PurpurWorldConfig { +@@ -1133,6 +1145,10 @@ public class PurpurWorldConfig { camelMovementSpeedMin = getDouble("mobs.camel.attributes.movement_speed.min", camelMovementSpeedMin); camelMovementSpeedMax = getDouble("mobs.camel.attributes.movement_speed.max", camelMovementSpeedMax); camelBreedingTicks = getInt("mobs.camel.breeding-delay-ticks", camelBreedingTicks); @@ -71,7 +71,7 @@ index f52cbfa21b9df10f30216138585218074a495f8a..0be19a242561e58792c2614f2a9c12c2 } public boolean catRidable = false; -@@ -1127,12 +1143,22 @@ public class PurpurWorldConfig { +@@ -1539,12 +1555,22 @@ public class PurpurWorldConfig { public boolean frogControllable = true; public float frogRidableJumpHeight = 0.65F; public int frogBreedingTicks = 6000; @@ -94,7 +94,7 @@ index f52cbfa21b9df10f30216138585218074a495f8a..0be19a242561e58792c2614f2a9c12c2 } public boolean ghastRidable = false; -@@ -1991,6 +2017,10 @@ public class PurpurWorldConfig { +@@ -2439,6 +2465,10 @@ public class PurpurWorldConfig { public double snifferMaxHealth = 14.0D; public double snifferScale = 1.0D; public int snifferBreedingTicks = 6000; @@ -105,7 +105,7 @@ index f52cbfa21b9df10f30216138585218074a495f8a..0be19a242561e58792c2614f2a9c12c2 private void snifferSettings() { snifferRidable = getBoolean("mobs.sniffer.ridable", snifferRidable); snifferRidableInWater = getBoolean("mobs.sniffer.ridable-in-water", snifferRidableInWater); -@@ -1998,6 +2028,10 @@ public class PurpurWorldConfig { +@@ -2446,6 +2476,10 @@ public class PurpurWorldConfig { snifferMaxHealth = getDouble("mobs.sniffer.attributes.max_health", snifferMaxHealth); snifferScale = Mth.clamp(getDouble("mobs.sniffer.attributes.scale", snifferScale), 0.0625D, 16.0D); snifferBreedingTicks = getInt("mobs.sniffer.breeding-delay-ticks", snifferBreedingTicks); @@ -116,7 +116,7 @@ index f52cbfa21b9df10f30216138585218074a495f8a..0be19a242561e58792c2614f2a9c12c2 } public boolean squidRidable = false; -@@ -2091,10 +2125,20 @@ public class PurpurWorldConfig { +@@ -2539,10 +2573,20 @@ public class PurpurWorldConfig { public boolean tadpoleRidable = false; public boolean tadpoleRidableInWater = true; public boolean tadpoleControllable = true; @@ -137,7 +137,7 @@ index f52cbfa21b9df10f30216138585218074a495f8a..0be19a242561e58792c2614f2a9c12c2 } public boolean traderLlamaRidable = false; -@@ -2278,10 +2322,20 @@ public class PurpurWorldConfig { +@@ -2730,10 +2774,20 @@ public class PurpurWorldConfig { public boolean wardenRidable = false; public boolean wardenRidableInWater = true; public boolean wardenControllable = true; diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineCommand.java b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineCommand.java new file mode 100644 index 0000000..2c005a5 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineCommand.java @@ -0,0 +1,160 @@ +package space.bxteam.divinemc.command; + +import io.papermc.paper.command.CommandUtil; +import it.unimi.dsi.fastutil.Pair; +import net.minecraft.Util; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.bukkit.plugin.PluginManager; +import org.jetbrains.annotations.Nullable; +import space.bxteam.divinemc.command.subcommands.*; + +import java.util.*; +import java.util.stream.Collectors; + +import static net.kyori.adventure.text.Component.newline; +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.format.NamedTextColor.GRAY; +import static net.kyori.adventure.text.format.NamedTextColor.RED; + +public final class DivineCommand extends Command { + public static final String COMMAND_LABEL = "divinemc"; + public static final String BASE_PERM = DivineCommands.COMMAND_BASE_PERM + "." + COMMAND_LABEL; + private static final Permission basePermission = new Permission(BASE_PERM, PermissionDefault.TRUE); + + private static final DivineSubCommand RELOAD_SUBCOMMAND = new ReloadCommand(); + private static final DivineSubCommand VERSION_SUBCOMMAND = new VersionCommand(); + private static final Map SUBCOMMANDS = Util.make(() -> { + final Map, DivineSubCommand> commands = new HashMap<>(); + + commands.put(Set.of(ReloadCommand.LITERAL_ARGUMENT), RELOAD_SUBCOMMAND); + commands.put(Set.of(VersionCommand.LITERAL_ARGUMENT), VERSION_SUBCOMMAND); + + return commands.entrySet().stream() + .flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue()))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + }); + // alias -> subcommand label + private static final Map ALIASES = Util.make(() -> { + final Map> aliases = new HashMap<>(); + + aliases.put(VersionCommand.LITERAL_ARGUMENT, Set.of("ver")); + + return aliases.entrySet().stream() + .flatMap(entry -> entry.getValue().stream().map(s -> Map.entry(s, entry.getKey()))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + }); + + private String createUsageMessage(Collection arguments) { + return "/" + COMMAND_LABEL + " [" + String.join(" | ", arguments) + "]"; + } + + public DivineCommand() { + super(COMMAND_LABEL); + this.description = "DivineMC related commands"; + this.usageMessage = this.createUsageMessage(SUBCOMMANDS.keySet()); + final List permissions = SUBCOMMANDS.values().stream().map(DivineSubCommand::getPermission).filter(Objects::nonNull).toList(); + this.setPermission(BASE_PERM); + final PluginManager pluginManager = Bukkit.getServer().getPluginManager(); + pluginManager.addPermission(basePermission); + for (final Permission permission : permissions) { + pluginManager.addPermission(permission); + } + } + + @Override + public List tabComplete( + final CommandSender sender, + final String alias, + final String[] args, + final @Nullable Location location + ) throws IllegalArgumentException { + if (args.length <= 1) { + List subCommandArguments = new ArrayList<>(SUBCOMMANDS.size()); + for (Map.Entry subCommandEntry : SUBCOMMANDS.entrySet()) { + if (subCommandEntry.getValue().testPermission(sender)) { + subCommandArguments.add(subCommandEntry.getKey()); + } + } + return CommandUtil.getListMatchingLast(sender, args, subCommandArguments); + } + + final @Nullable Pair subCommand = resolveCommand(args[0]); + if (subCommand != null && subCommand.second().testPermission(sender)) { + return subCommand.second().tabComplete(sender, subCommand.first(), Arrays.copyOfRange(args, 1, args.length)); + } + + return Collections.emptyList(); + } + + private boolean testHasOnePermission(CommandSender sender) { + for (Map.Entry subCommandEntry : SUBCOMMANDS.entrySet()) { + if (subCommandEntry.getValue().testPermission(sender)) { + return true; + } + } + return false; + } + + @Override + public boolean execute( + final CommandSender sender, + final String commandLabel, + final String[] args + ) { + + // Check if the sender has the base permission and at least one specific permission + if (!sender.hasPermission(basePermission) || !this.testHasOnePermission(sender)) { + sender.sendMessage(Bukkit.permissionMessage()); + return true; + } + + // Determine the usage message with the subcommands they can perform + List subCommandArguments = new ArrayList<>(SUBCOMMANDS.size()); + for (Map.Entry subCommandEntry : SUBCOMMANDS.entrySet()) { + if (subCommandEntry.getValue().testPermission(sender)) { + subCommandArguments.add(subCommandEntry.getKey()); + } + } + String specificUsageMessage = this.createUsageMessage(subCommandArguments); + + // If they did not give a subcommand + if (args.length == 0) { + sender.sendMessage(newline().append(text("Command usage: " + specificUsageMessage, GRAY))); + return false; + } + + // If they do not have permission for the subcommand they gave, or the argument is not a valid subcommand + final @Nullable Pair subCommand = resolveCommand(args[0]); + if (subCommand == null || !subCommand.second().testPermission(sender)) { + sender.sendMessage(text("Usage: " + specificUsageMessage, RED)); + return false; + } + + // Execute the subcommand + final String[] choppedArgs = Arrays.copyOfRange(args, 1, args.length); + return subCommand.second().execute(sender, subCommand.first(), choppedArgs); + } + + private static @Nullable Pair resolveCommand(String label) { + label = label.toLowerCase(Locale.ENGLISH); + @Nullable DivineSubCommand subCommand = SUBCOMMANDS.get(label); + if (subCommand == null) { + final @Nullable String command = ALIASES.get(label); + if (command != null) { + label = command; + subCommand = SUBCOMMANDS.get(command); + } + } + + if (subCommand != null) { + return Pair.of(label, subCommand); + } + + return null; + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineCommands.java b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineCommands.java new file mode 100644 index 0000000..4dd92fd --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineCommands.java @@ -0,0 +1,28 @@ +package space.bxteam.divinemc.command; + +import net.minecraft.server.MinecraftServer; +import org.bukkit.command.Command; +import org.bukkit.craftbukkit.util.permissions.CraftDefaultPermissions; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; + +import java.util.HashMap; +import java.util.Map; + +@DefaultQualifier(NonNull.class) +public final class DivineCommands { + public static final String COMMAND_BASE_PERM = CraftDefaultPermissions.DIVINEMC_ROOT + ".command"; + + private DivineCommands() {} + + private static final Map COMMANDS = new HashMap<>(); + static { + COMMANDS.put(DivineCommand.COMMAND_LABEL, new DivineCommand()); + } + + public static void registerCommands(final MinecraftServer server) { + COMMANDS.forEach((s, command) -> { + server.server.getCommandMap().register(s, "DivineMC", command); + }); + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineSubCommand.java b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineSubCommand.java new file mode 100644 index 0000000..750fd89 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineSubCommand.java @@ -0,0 +1,23 @@ +package space.bxteam.divinemc.command; + +import org.bukkit.command.CommandSender; +import org.bukkit.permissions.Permission; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; + +@DefaultQualifier(NonNull.class) +public interface DivineSubCommand { + boolean execute(CommandSender sender, String subCommand, String[] args); + + default List tabComplete(final CommandSender sender, final String subCommand, final String[] args) { + return Collections.emptyList(); + } + + boolean testPermission(CommandSender sender); + + @Nullable Permission getPermission(); +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineSubCommandPermission.java b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineSubCommandPermission.java new file mode 100644 index 0000000..152ae2b --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineSubCommandPermission.java @@ -0,0 +1,28 @@ +package space.bxteam.divinemc.command; + +import org.bukkit.command.CommandSender; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.jetbrains.annotations.Nullable; + +public abstract class DivineSubCommandPermission implements DivineSubCommand { + public final Permission permission; + + protected DivineSubCommandPermission(Permission permission) { + this.permission = permission; + } + + protected DivineSubCommandPermission(String permission, PermissionDefault permissionDefault) { + this(new Permission(permission, permissionDefault)); + } + + @Override + public boolean testPermission(CommandSender sender) { + return sender.hasPermission(this.permission); + } + + @Override + public @Nullable Permission getPermission() { + return this.permission; + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/command/subcommands/ReloadCommand.java b/divinemc-server/src/main/java/space/bxteam/divinemc/command/subcommands/ReloadCommand.java new file mode 100644 index 0000000..de65e9d --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/command/subcommands/ReloadCommand.java @@ -0,0 +1,39 @@ +package space.bxteam.divinemc.command.subcommands; + +import net.minecraft.server.MinecraftServer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.permissions.PermissionDefault; +import space.bxteam.divinemc.command.DivineCommand; +import space.bxteam.divinemc.command.DivineSubCommandPermission; + +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.format.NamedTextColor.GREEN; +import static net.kyori.adventure.text.format.NamedTextColor.RED; + +public final class ReloadCommand extends DivineSubCommandPermission { + public final static String LITERAL_ARGUMENT = "reload"; + public static final String PERM = DivineCommand.BASE_PERM + "." + LITERAL_ARGUMENT; + + public ReloadCommand() { + super(PERM, PermissionDefault.OP); + } + + @Override + public boolean execute(final CommandSender sender, final String subCommand, final String[] args) { + this.doReload(sender); + return true; + } + + private void doReload(final CommandSender sender) { + Command.broadcastCommandMessage(sender, text("Please note that this command is not supported and may cause issues.", RED)); + Command.broadcastCommandMessage(sender, text("If you encounter any issues please use the /stop command to restart your server.", RED)); + + MinecraftServer server = ((CraftServer) sender.getServer()).getServer(); + server.divineConfigurations.reloadConfigs(server); + server.server.reloadCount++; + + Command.broadcastCommandMessage(sender, text("DivineMC config reload complete.", GREEN)); + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/command/subcommands/VersionCommand.java b/divinemc-server/src/main/java/space/bxteam/divinemc/command/subcommands/VersionCommand.java new file mode 100644 index 0000000..fe93249 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/command/subcommands/VersionCommand.java @@ -0,0 +1,35 @@ +package space.bxteam.divinemc.command.subcommands; + +import net.minecraft.server.MinecraftServer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.permissions.PermissionDefault; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.jetbrains.annotations.Nullable; +import space.bxteam.divinemc.command.DivineCommand; +import space.bxteam.divinemc.command.DivineSubCommandPermission; + +@DefaultQualifier(NonNull.class) +public final class VersionCommand extends DivineSubCommandPermission { + public final static String LITERAL_ARGUMENT = "version"; + public static final String PERM = DivineCommand.BASE_PERM + "." + LITERAL_ARGUMENT; + + public VersionCommand() { + super(PERM, PermissionDefault.TRUE); + } + + @Override + public boolean execute(final CommandSender sender, final String subCommand, final String[] args) { + final @Nullable Command ver = MinecraftServer.getServer().server.getCommandMap().getCommand("version"); + if (ver != null) { + ver.execute(sender, DivineCommand.COMMAND_LABEL, new String[0]); + } + return true; + } + + @Override + public boolean testPermission(CommandSender sender) { + return super.testPermission(sender) && sender.hasPermission("bukkit.command.version"); + } +} diff --git a/gradle.properties b/gradle.properties index 248523d..15a9588 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ group = space.bxteam.divinemc mcVersion = 1.21.4 version = 1.21.4-R0.1-SNAPSHOT -purpurRef = 4b00f67e399eb4decafe5e4fcf559bc04ee1d8f4 +purpurRef = a4e8b4d70b42fbecd7dc4ea82cad847f6fd65282 org.gradle.configuration-cache=true org.gradle.caching = true diff --git a/settings.gradle.kts b/settings.gradle.kts index e44a065..880544d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,9 +12,29 @@ plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" } +if (!file(".git").exists()) { + val errorText = """ + + =====================[ ERROR ]===================== + The DivineMC project directory is not a properly cloned Git repository. + + In order to build DivineMC from source you must clone + the DivineMC repository using Git, not download a code + zip from GitHub. + + Built Gale jars are available for download at + https://github.com/DivineMC/DivineMC/actions + + See https://github.com/PaperMC/Paper/blob/main/CONTRIBUTING.md + for further information on building and modifying Paper forks. + =================================================== + """.trimIndent() + error(errorText) +} + rootProject.name = "DivineMC" -for (name in listOf("divinemc-api", "divinemc-server", "divinemc-api-generator")) { +for (name in listOf("divinemc-api", "divinemc-server")) { val projName = name.lowercase(Locale.ENGLISH) include(projName) findProject(":$projName")!!.projectDir = file(name)