From ffb44ca4bd861e046a548431eff7a67425df4b4a Mon Sep 17 00:00:00 2001 From: Logan Saso Date: Sat, 4 Oct 2025 21:47:07 -0700 Subject: [PATCH] Add in-game commands --- README.md | 43 ++++ .../RegionForcefieldPlugin.java | 12 + .../command/ForcefieldCommand.java | 223 ++++++++++++++++++ src/main/resources/plugin.yml | 33 +++ 4 files changed, 311 insertions(+) create mode 100644 src/main/java/loganintech/regionforcefield/command/ForcefieldCommand.java diff --git a/README.md b/README.md index 08a595c..e2563b0 100644 --- a/README.md +++ b/README.md @@ -38,15 +38,33 @@ Once installed, the plugin automatically works with your existing WorldGuard reg 2. Set the entry flag to deny: `/rg flag myregion entry deny` 3. Players who cannot enter the region will see a purple forcefield around it +### Commands + +- `/forcefield debug` or `/ff debug` - Toggle debug mode on/off +- `/forcefield reload` or `/ff reload` - Reload the configuration file +- `/forcefield status` or `/ff status` - View plugin status and settings +- `/forcefield info` or `/ff info` - View information about blocked regions nearby +- `/forcefield help` or `/ff help` - Show command help + ### Permissions Players who are members or owners of a region can enter it even if entry is denied for others. These players will not see the forcefield. +- `regionforcefield.*` - Grants all permissions (default: op) +- `regionforcefield.command` - Allows use of `/forcefield` command (default: true) +- `regionforcefield.debug` - Allows toggling debug mode (default: op) +- `regionforcefield.reload` - Allows reloading config (default: op) +- `regionforcefield.status` - Allows viewing status (default: op) +- `regionforcefield.info` - Allows viewing region info (default: true) + ## Configuration Edit `plugins/RegionForcefield/config.yml`: ```yaml +# Enable debug logging (useful for troubleshooting) +debug: false + # Update frequency (20 ticks = 1 second) update-interval-ticks: 20 @@ -76,6 +94,31 @@ particle-size: 1.0 - Set `render-walls: false` to only show edges - Increase `update-interval-ticks` if you don't need real-time updates +## Troubleshooting + +### Forcefields not appearing + +1. Enable debug mode: `/forcefield debug` or set `debug: true` in config.yml +2. Check the server console for debug messages +3. Use `/forcefield info` to see if regions are being detected +4. Verify the region has `entry deny` set: `/rg info ` +5. Check you're not a member/owner of the region +6. Ensure you're within render distance of the region (default: 100 blocks) + +### Plugin won't load + +1. Verify WorldGuard 7.0.14 or higher is installed +2. Check server logs for error messages +3. Ensure you're running Paper 1.21.8 or higher +4. Verify Java 21 is being used + +### Performance issues + +1. Increase `particle-spacing` (less particles) +2. Reduce `max-render-distance` +3. Set `render-walls: false` (edges only) +4. Increase `update-interval-ticks` (update less frequently) + ## Building from Source ```bash diff --git a/src/main/java/loganintech/regionforcefield/RegionForcefieldPlugin.java b/src/main/java/loganintech/regionforcefield/RegionForcefieldPlugin.java index 12e86d7..128098c 100644 --- a/src/main/java/loganintech/regionforcefield/RegionForcefieldPlugin.java +++ b/src/main/java/loganintech/regionforcefield/RegionForcefieldPlugin.java @@ -1,8 +1,10 @@ package loganintech.regionforcefield; +import loganintech.regionforcefield.command.ForcefieldCommand; import loganintech.regionforcefield.forcefield.ForcefieldRenderer; import loganintech.regionforcefield.region.RegionPermissionChecker; import loganintech.regionforcefield.task.ForcefieldUpdateTask; +import org.bukkit.command.PluginCommand; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; @@ -33,6 +35,16 @@ public final class RegionForcefieldPlugin extends JavaPlugin { this.permissionChecker = new RegionPermissionChecker(this); this.forcefieldRenderer = new ForcefieldRenderer(this); + // Register commands + ForcefieldCommand commandExecutor = new ForcefieldCommand(this); + PluginCommand command = getCommand("forcefield"); + if (command != null) { + command.setExecutor(commandExecutor); + command.setTabCompleter(commandExecutor); + } else { + getLogger().warning("Failed to register /forcefield command!"); + } + // Start the periodic update task this.updateTask = new ForcefieldUpdateTask(this, permissionChecker, forcefieldRenderer); long updateInterval = getConfig().getLong("update-interval-ticks", 20L); diff --git a/src/main/java/loganintech/regionforcefield/command/ForcefieldCommand.java b/src/main/java/loganintech/regionforcefield/command/ForcefieldCommand.java new file mode 100644 index 0000000..c0cee18 --- /dev/null +++ b/src/main/java/loganintech/regionforcefield/command/ForcefieldCommand.java @@ -0,0 +1,223 @@ +package loganintech.regionforcefield.command; + +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import loganintech.regionforcefield.RegionForcefieldPlugin; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +/** + * Handles the /forcefield command and its subcommands. + */ +public class ForcefieldCommand implements CommandExecutor, TabCompleter { + + private final RegionForcefieldPlugin plugin; + + public ForcefieldCommand(@NotNull RegionForcefieldPlugin plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, + @NotNull String label, @NotNull String[] args) { + if (args.length == 0) { + sendHelp(sender); + return true; + } + + switch (args[0].toLowerCase()) { + case "debug": + return handleDebug(sender); + case "reload": + return handleReload(sender); + case "status": + return handleStatus(sender); + case "info": + return handleInfo(sender); + case "help": + sendHelp(sender); + return true; + default: + sender.sendMessage(ChatColor.RED + "Unknown subcommand. Use /forcefield help"); + return true; + } + } + + private boolean handleDebug(@NotNull CommandSender sender) { + if (!sender.hasPermission("regionforcefield.debug")) { + sender.sendMessage(ChatColor.RED + "You don't have permission to use this command."); + return true; + } + + boolean currentDebug = plugin.getConfig().getBoolean("debug", false); + boolean newDebug = !currentDebug; + + plugin.getConfig().set("debug", newDebug); + plugin.saveConfig(); + + sender.sendMessage(ChatColor.GREEN + "Debug mode " + + (newDebug ? ChatColor.YELLOW + "enabled" : ChatColor.GRAY + "disabled") + + ChatColor.GREEN + "."); + + plugin.getLogger().info(sender.getName() + " toggled debug mode: " + newDebug); + return true; + } + + private boolean handleReload(@NotNull CommandSender sender) { + if (!sender.hasPermission("regionforcefield.reload")) { + sender.sendMessage(ChatColor.RED + "You don't have permission to use this command."); + return true; + } + + try { + plugin.reloadConfig(); + sender.sendMessage(ChatColor.GREEN + "Configuration reloaded successfully!"); + plugin.getLogger().info(sender.getName() + " reloaded the configuration."); + } catch (Exception e) { + sender.sendMessage(ChatColor.RED + "Error reloading configuration: " + e.getMessage()); + plugin.getLogger().warning("Error reloading configuration: " + e.getMessage()); + e.printStackTrace(); + } + return true; + } + + private boolean handleStatus(@NotNull CommandSender sender) { + if (!sender.hasPermission("regionforcefield.status")) { + sender.sendMessage(ChatColor.RED + "You don't have permission to use this command."); + return true; + } + + sender.sendMessage(ChatColor.GOLD + "=== RegionForcefield Status ==="); + sender.sendMessage(ChatColor.YELLOW + "Version: " + ChatColor.WHITE + plugin.getDescription().getVersion()); + sender.sendMessage(ChatColor.YELLOW + "Debug Mode: " + ChatColor.WHITE + + (plugin.getConfig().getBoolean("debug", false) ? "Enabled" : "Disabled")); + sender.sendMessage(ChatColor.YELLOW + "Update Interval: " + ChatColor.WHITE + + plugin.getConfig().getLong("update-interval-ticks", 20L) + " ticks"); + sender.sendMessage(ChatColor.YELLOW + "Max Render Distance: " + ChatColor.WHITE + + plugin.getConfig().getInt("max-render-distance", 100) + " blocks"); + sender.sendMessage(ChatColor.YELLOW + "Particle Spacing: " + ChatColor.WHITE + + plugin.getConfig().getDouble("particle-spacing", 0.5) + " blocks"); + sender.sendMessage(ChatColor.YELLOW + "Render Walls: " + ChatColor.WHITE + + (plugin.getConfig().getBoolean("render-walls", true) ? "Yes" : "No")); + + int red = plugin.getConfig().getInt("particle-color.red", 147); + int green = plugin.getConfig().getInt("particle-color.green", 112); + int blue = plugin.getConfig().getInt("particle-color.blue", 219); + sender.sendMessage(ChatColor.YELLOW + "Particle Color: " + ChatColor.WHITE + + "RGB(" + red + ", " + green + ", " + blue + ")"); + + sender.sendMessage(ChatColor.YELLOW + "Online Players: " + ChatColor.WHITE + + plugin.getServer().getOnlinePlayers().size()); + + return true; + } + + private boolean handleInfo(@NotNull CommandSender sender) { + if (!(sender instanceof Player)) { + sender.sendMessage(ChatColor.RED + "This command can only be used by players."); + return true; + } + + if (!sender.hasPermission("regionforcefield.info")) { + sender.sendMessage(ChatColor.RED + "You don't have permission to use this command."); + return true; + } + + Player player = (Player) sender; + + try { + // Get regions the player is blocked from + Set blockedRegions = plugin.getPermissionChecker() + .getBlockedRegions(player, player.getWorld()); + + sender.sendMessage(ChatColor.GOLD + "=== Region Information ==="); + sender.sendMessage(ChatColor.YELLOW + "World: " + ChatColor.WHITE + player.getWorld().getName()); + + // Get all regions in the world + RegionManager regionManager = WorldGuard.getInstance() + .getPlatform() + .getRegionContainer() + .get(com.sk89q.worldedit.bukkit.BukkitAdapter.adapt(player.getWorld())); + + if (regionManager != null) { + int totalRegions = regionManager.getRegions().size(); + sender.sendMessage(ChatColor.YELLOW + "Total Regions: " + ChatColor.WHITE + totalRegions); + sender.sendMessage(ChatColor.YELLOW + "Blocked Regions: " + ChatColor.WHITE + blockedRegions.size()); + + if (!blockedRegions.isEmpty()) { + sender.sendMessage(ChatColor.GOLD + "Regions you cannot enter:"); + for (ProtectedRegion region : blockedRegions) { + double distance = calculateDistance(player, region); + sender.sendMessage(ChatColor.GRAY + " - " + ChatColor.WHITE + region.getId() + + ChatColor.GRAY + " (distance: " + String.format("%.1f", distance) + " blocks)"); + } + } else { + sender.sendMessage(ChatColor.GREEN + "You can enter all regions in this world!"); + } + } else { + sender.sendMessage(ChatColor.RED + "No region manager found for this world."); + } + } catch (Exception e) { + sender.sendMessage(ChatColor.RED + "Error retrieving region information: " + e.getMessage()); + plugin.getLogger().warning("Error in info command: " + e.getMessage()); + e.printStackTrace(); + } + + return true; + } + + private void sendHelp(@NotNull CommandSender sender) { + sender.sendMessage(ChatColor.GOLD + "=== RegionForcefield Commands ==="); + sender.sendMessage(ChatColor.YELLOW + "/forcefield debug " + ChatColor.GRAY + "- Toggle debug mode"); + sender.sendMessage(ChatColor.YELLOW + "/forcefield reload " + ChatColor.GRAY + "- Reload configuration"); + sender.sendMessage(ChatColor.YELLOW + "/forcefield status " + ChatColor.GRAY + "- Show plugin status"); + sender.sendMessage(ChatColor.YELLOW + "/forcefield info " + ChatColor.GRAY + "- Show region information"); + sender.sendMessage(ChatColor.YELLOW + "/forcefield help " + ChatColor.GRAY + "- Show this help message"); + } + + private double calculateDistance(@NotNull Player player, @NotNull ProtectedRegion region) { + double playerX = player.getLocation().getX(); + double playerY = player.getLocation().getY(); + double playerZ = player.getLocation().getZ(); + + double regionCenterX = (region.getMinimumPoint().x() + region.getMaximumPoint().x()) / 2.0; + double regionCenterY = (region.getMinimumPoint().y() + region.getMaximumPoint().y()) / 2.0; + double regionCenterZ = (region.getMinimumPoint().z() + region.getMaximumPoint().z()) / 2.0; + + return Math.sqrt( + Math.pow(playerX - regionCenterX, 2) + + Math.pow(playerY - regionCenterY, 2) + + Math.pow(playerZ - regionCenterZ, 2) + ); + } + + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, + @NotNull String alias, @NotNull String[] args) { + List completions = new ArrayList<>(); + + if (args.length == 1) { + List subcommands = Arrays.asList("debug", "reload", "status", "info", "help"); + String input = args[0].toLowerCase(); + + for (String subcommand : subcommands) { + if (subcommand.startsWith(input)) { + completions.add(subcommand); + } + } + } + + return completions; + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index d348244..af39f1c 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -6,3 +6,36 @@ depend: [WorldGuard] author: loganintech description: Renders visible forcefields around WorldGuard regions that players cannot enter website: https://github.com/loganintech + +commands: + forcefield: + description: Manage RegionForcefield plugin + usage: / [debug|reload|status|info|help] + aliases: [ff, regionforcefield] + permission: regionforcefield.command + +permissions: + regionforcefield.*: + description: Grants all RegionForcefield permissions + default: op + children: + regionforcefield.command: true + regionforcefield.debug: true + regionforcefield.reload: true + regionforcefield.status: true + regionforcefield.info: true + regionforcefield.command: + description: Allows access to the base /forcefield command + default: true + regionforcefield.debug: + description: Allows toggling debug mode + default: op + regionforcefield.reload: + description: Allows reloading the plugin configuration + default: op + regionforcefield.status: + description: Allows viewing plugin status + default: op + regionforcefield.info: + description: Allows viewing region information + default: true