From 18a87fe28db72a8d631ba08c4f91c4c04ca4f612 Mon Sep 17 00:00:00 2001 From: Gk0Wk Date: Fri, 26 Mar 2021 16:20:48 +0800 Subject: [PATCH] First commit --- .github/workflows/maven.yml | 50 +++++ .gitignore | 113 +++++++++++ pom.xml | 103 ++++++++++ .../AreaCommandBlocker.java | 190 ++++++++++++++++++ .../AreaCommandBlockerCommands.java | 30 +++ .../gk0wk/areacommandblocker/area/Area.java | 31 +++ .../area/AreaContainer.java | 13 ++ .../gk0wk/areacommandblocker/area/Point.java | 12 ++ .../area/VanillaAreaContainer.java | 96 +++++++++ src/main/resources/config.yml | 22 ++ src/main/resources/plugin.yml | 7 + 11 files changed, 667 insertions(+) create mode 100644 .github/workflows/maven.yml create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/io/github/gk0wk/areacommandblocker/AreaCommandBlocker.java create mode 100644 src/main/java/io/github/gk0wk/areacommandblocker/AreaCommandBlockerCommands.java create mode 100644 src/main/java/io/github/gk0wk/areacommandblocker/area/Area.java create mode 100644 src/main/java/io/github/gk0wk/areacommandblocker/area/AreaContainer.java create mode 100644 src/main/java/io/github/gk0wk/areacommandblocker/area/Point.java create mode 100644 src/main/java/io/github/gk0wk/areacommandblocker/area/VanillaAreaContainer.java create mode 100644 src/main/resources/config.yml create mode 100644 src/main/resources/plugin.yml diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..3b09d85 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,50 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Run Maven and publish + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Pull source + uses: actions/checkout@v1 + + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + server-id: city.newnan.NewNanPlus # Value of the distributionManagement/repository/id field of the pom.xml + settings-path: ${{ github.workspace }} # location for the settings.xml file + + - name: Restore Maven cache + uses: skjolber/maven-cache-github-action@v1 + with: + step: restore + + - name: Build with Maven + run: mvn clean && mvn -B package --file pom.xml + + - name: Set Release version env variable + run: echo "RELEASE_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV + + - name: Save Maven cache + uses: skjolber/maven-cache-github-action@v1 + with: + step: save + + - name: Upload to Release + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: target/MCron-*.jar + tag: ${{ env.RELEASE_VERSION }} + overwrite: true + file_glob: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4788b4b --- /dev/null +++ b/.gitignore @@ -0,0 +1,113 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +target/ + +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next + +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar +.flattened-pom.xml + +# Common working directory +run/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..625052d --- /dev/null +++ b/pom.xml @@ -0,0 +1,103 @@ + + + 4.0.0 + + io.github.gk0wk + AreaCommandBlocker + 1.0.0 + jar + + AreaCommandBlocker + + Block command bu areas. + + 1.8 + UTF-8 + + https://gk0wk.github.io/ + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + true + false + false + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + + src/main/resources + true + + + + + + + spigotmc-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + jitpack.io + https://jitpack.io + + + aikar + https://repo.aikar.co/content/groups/aikar/ + + + + + + org.bukkit + bukkit + 1.12-R0.1-SNAPSHOT + provided + + + co.aikar + acf-paper + 0.5.0-SNAPSHOT + compile + + + com.github.NewNanCity + Violet + 1.0.5 + + + diff --git a/src/main/java/io/github/gk0wk/areacommandblocker/AreaCommandBlocker.java b/src/main/java/io/github/gk0wk/areacommandblocker/AreaCommandBlocker.java new file mode 100644 index 0000000..f60a77b --- /dev/null +++ b/src/main/java/io/github/gk0wk/areacommandblocker/AreaCommandBlocker.java @@ -0,0 +1,190 @@ +package io.github.gk0wk.areacommandblocker; + +import co.aikar.commands.PaperCommandManager; +import io.github.gk0wk.areacommandblocker.area.Area; +import io.github.gk0wk.areacommandblocker.area.AreaContainer; +import io.github.gk0wk.areacommandblocker.area.VanillaAreaContainer; +import io.github.gk0wk.violet.config.ConfigManager; +import io.github.gk0wk.violet.config.ConfigUtil; +import io.github.gk0wk.violet.i18n.LanguageManager; +import io.github.gk0wk.violet.message.MessageManager; +import me.lucko.helper.Events; +import me.lucko.helper.plugin.ExtendedJavaPlugin; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; + +import java.io.IOException; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class AreaCommandBlocker extends ExtendedJavaPlugin { + + protected ConfigManager configManager; + private LanguageManager languageManager; + protected MessageManager messageManager; + + private static AreaCommandBlocker instance = null; + public static AreaCommandBlocker getInstance() { + return instance; + } + + @Override + protected void load() { + // 初始化ConfigManager + configManager = new ConfigManager(this); + configManager.touch("config.yml"); + + // 初始化LanguageManager + try { + Locale locale = new Locale("config"); + languageManager = new LanguageManager(this) + .register(locale, "config.yml") + .setMajorLanguage(locale); + } catch (LanguageManager.FileNotFoundException | ConfigManager.UnknownConfigFileFormatException | IOException e) { + e.printStackTrace(); + this.onDisable(); + } + + // 初始化MessageManager + messageManager = new MessageManager(this) + .setLanguageProvider(languageManager); + messageManager.setPlayerPrefix(messageManager.sprintf("$msg.prefix$")); + + instance = this; + } + + @Override + protected void enable() { + // 初始化CommandManager - 不能在load()里面初始化! + PaperCommandManager commandManager = new PaperCommandManager(this); + commandManager.usePerIssuerLocale(true, false); + try { + commandManager.getLocales().loadYamlLanguageFile("config.yml", new Locale("config")); + } catch (IOException | InvalidConfigurationException e) { + e.printStackTrace(); + this.onDisable(); + } + + // 注册指令 + commandManager.registerCommand(new AreaCommandBlockerCommands()); + + // 注册事件 + Events.subscribe(PlayerCommandPreprocessEvent.class, EventPriority.HIGHEST) + .filter(e -> !e.getPlayer().isOp()) + .filter(this::checkDeny) + .handler(e -> { + messageManager.printf(e.getPlayer(), "$msg.command-deny$"); + e.setCancelled(true); + }); + + // Containers + areaContainers.add(new VanillaAreaContainer()); + + // 载入配置 + reload(); + } + + private final Map commandGroups = new HashMap<>(); + + public String[][] getGroup(String groupName) { + return commandGroups.get(groupName); + } + + private final Set areaContainers = new HashSet<>(); + + protected void reload() { + commandGroups.clear(); + areaContainers.forEach(AreaContainer::clear); + try { + // Read Command Group + configManager.get("config.yml").getNode("command-group").getChildrenMap().forEach((key, value) -> { + if (key instanceof String) { + Set tmpGroup = new HashSet<>(); + ConfigUtil.setListIfNull(value).getList(Object::toString).forEach(command -> { + String[] tmpCommand = AreaCommandBlocker.normalizeCommand(command); + if (tmpCommand != null) { + tmpGroup.add(tmpCommand); + } + }); + commandGroups.put((String) key, tmpGroup.toArray(new String[0][0])); + } + }); + + // Read Areas + configManager.get("config.yml").getNode("area").getChildrenMap().forEach((key, value) -> { + if (key instanceof String) { + String area = (String) key; + String name = value.getNode("main").getString(null); + boolean whitelist = value.getNode("mode").getString("allow").equals("allow"); + List areaCommands = ConfigUtil.setListIfNull(value.getNode("list")).getList(Object::toString); + + boolean accepted = false; + for (AreaContainer container : areaContainers) { + if (container.register(area, name, areaCommands, whitelist)) { + accepted = true; + break; + } + } + + if (!accepted) { + messageManager.printf("$msg.load-failed$", area); + } + } + }); + } catch (IOException | ConfigManager.UnknownConfigFileFormatException e) { + e.printStackTrace(); + this.onDisable(); + } + + } + + protected void listArea(CommandSender sender) { + messageManager.printf(sender, "$msg.list-head$"); + areaContainers.forEach(containers -> + containers.getAll().forEach(area -> { + messageManager.printf(sender, (area.whitelist ? "$msg.list-area-whitelist$" : "$msg.list-area-blacklist$"), area.name); + for (String[] pattern : area.contents) { + messageManager.printf(sender, "$msg.list-command$", pattern[0], pattern[1]); + } + }) + ); + } + + private boolean checkDeny(PlayerCommandPreprocessEvent event) { + for (AreaContainer container : areaContainers) { + Area area = container.check(event.getPlayer().getLocation()); + if (area != null) { + // 权限检查 + if (area.name != null && event.getPlayer().hasPermission("areacommandblocker.bypass." + area.name)) { + return false; + } + String[] commands = event.getMessage().split(" ")[0].split(":"); + String plugin = (commands.length > 1) ? commands[0] : ""; + String command = (commands.length > 1) ? commands[1] : commands[0]; + for (String[] patterns : area.contents) { + if (!patterns[0].isEmpty() && !plugin.matches(patterns[0])) { + continue; + } + if (command.matches(patterns[1])) { + return !area.whitelist; + } + } + return area.whitelist; + } + } + return false; + } + + private static final Pattern commandPattern = Pattern.compile("^/((?[^:]*):)?(?\\S+)$"); + public static String[] normalizeCommand(String command) { + Matcher matcher = commandPattern.matcher(command); + if (matcher.find()) { + return new String[]{matcher.group("plugin"), matcher.group("command")}; + } else { + return null; + } + } +} diff --git a/src/main/java/io/github/gk0wk/areacommandblocker/AreaCommandBlockerCommands.java b/src/main/java/io/github/gk0wk/areacommandblocker/AreaCommandBlockerCommands.java new file mode 100644 index 0000000..7e31bc0 --- /dev/null +++ b/src/main/java/io/github/gk0wk/areacommandblocker/AreaCommandBlockerCommands.java @@ -0,0 +1,30 @@ +package io.github.gk0wk.areacommandblocker; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.CommandHelp; +import co.aikar.commands.annotation.*; +import org.bukkit.command.CommandSender; + +@CommandAlias("areacommandblocker|acb") +public class AreaCommandBlockerCommands extends BaseCommand { + @Subcommand("reload") + @CommandPermission("areacommandblocker.reload") + @Description("{@@msg.help-reload}") + public static void onReload(CommandSender sender) { + AreaCommandBlocker.getInstance().reload(); + AreaCommandBlocker.getInstance().messageManager.printf(sender, "$msg.reload$"); + } + + @Subcommand("ls|list|show") + @CommandPermission("areacommandblocker.list") + @Description("{@@msg.help-list}") + public static void listCron(CommandSender sender) { + AreaCommandBlocker.getInstance().listArea(sender); + } + + @HelpCommand + public static void onHelp(CommandSender sender, CommandHelp help) { + AreaCommandBlocker.getInstance().messageManager.printf(sender, "$msg.help-head$"); + help.showHelp(); + } +} diff --git a/src/main/java/io/github/gk0wk/areacommandblocker/area/Area.java b/src/main/java/io/github/gk0wk/areacommandblocker/area/Area.java new file mode 100644 index 0000000..9a6e7f1 --- /dev/null +++ b/src/main/java/io/github/gk0wk/areacommandblocker/area/Area.java @@ -0,0 +1,31 @@ +package io.github.gk0wk.areacommandblocker.area; + +import io.github.gk0wk.areacommandblocker.AreaCommandBlocker; + +import java.util.*; + +public class Area { + public final String[][] contents; + public final String name; + public final boolean whitelist; + public Area(String name, List contents, boolean whitelist) { + this.whitelist = whitelist; + this.name = name; + + Set tmp = new HashSet<>(); + contents.forEach(content -> { + if (content.charAt(0) == '/') { + String[] tmpCommand = AreaCommandBlocker.normalizeCommand(content); + if (tmpCommand != null) { + tmp.add(tmpCommand); + } + } else { + String[][] group = AreaCommandBlocker.getInstance().getGroup(content); + if (group != null) { + tmp.addAll(Arrays.asList(group)); + } + } + }); + this.contents = tmp.toArray(new String[0][0]); + } +} diff --git a/src/main/java/io/github/gk0wk/areacommandblocker/area/AreaContainer.java b/src/main/java/io/github/gk0wk/areacommandblocker/area/AreaContainer.java new file mode 100644 index 0000000..49ac54d --- /dev/null +++ b/src/main/java/io/github/gk0wk/areacommandblocker/area/AreaContainer.java @@ -0,0 +1,13 @@ +package io.github.gk0wk.areacommandblocker.area; + +import org.bukkit.Location; + +import java.util.List; +import java.util.Set; + +public interface AreaContainer { + boolean register(String area, String name, List contents, boolean whitelist); + Area check(Location location); + Set getAll(); + void clear(); +} diff --git a/src/main/java/io/github/gk0wk/areacommandblocker/area/Point.java b/src/main/java/io/github/gk0wk/areacommandblocker/area/Point.java new file mode 100644 index 0000000..aeb2f7f --- /dev/null +++ b/src/main/java/io/github/gk0wk/areacommandblocker/area/Point.java @@ -0,0 +1,12 @@ +package io.github.gk0wk.areacommandblocker.area; + +public class Point { + public int x; + public int y; + public int z; + public Point(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } +} diff --git a/src/main/java/io/github/gk0wk/areacommandblocker/area/VanillaAreaContainer.java b/src/main/java/io/github/gk0wk/areacommandblocker/area/VanillaAreaContainer.java new file mode 100644 index 0000000..dfd0ee5 --- /dev/null +++ b/src/main/java/io/github/gk0wk/areacommandblocker/area/VanillaAreaContainer.java @@ -0,0 +1,96 @@ +package io.github.gk0wk.areacommandblocker.area; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class VanillaAreaContainer implements AreaContainer { + + private static final Pattern acceptableAreaNamePattern = Pattern.compile( + "^(?.+):(?[+-]?[0-9]+(\\.[0-9]+)?),(?[+-]?[0-9]+(\\.[0-9]+)?),(?[+-]?[0-9]+(\\.[0-9]+)?):(?[+-]?[0-9]+(\\.[0-9]+)?),(?[+-]?[0-9]+(\\.[0-9]+)?),(?[+-]?[0-9]+(\\.[0-9]+)?)$" + ); + + // The data-structure and its algorithm is extremely inelegant, but I tried. Using 4-level linked-map is too complex. + private final Map> areas = new HashMap<>(); + private final Map anotherPoint = new HashMap<>(); + + @Override + public boolean register(String area, String name, List contents, boolean whitelist) { + Matcher matcher = acceptableAreaNamePattern.matcher(area); + if (matcher.find()) { + Area _area = new Area(name, contents, whitelist); + + // Parse + World world = Bukkit.getWorld(matcher.group("world")); + int x1 = Double.valueOf(matcher.group("x1")).intValue(); + int y1 = Double.valueOf(matcher.group("y1")).intValue(); + int z1 = Double.valueOf(matcher.group("z1")).intValue(); + int x2 = Double.valueOf(matcher.group("x2")).intValue(); + int y2 = Double.valueOf(matcher.group("y2")).intValue(); + int z2 = Double.valueOf(matcher.group("z2")).intValue(); + + // Swap & Arrange + int tmp; + if (x1 > x2) { + tmp = x1; + x1 = x2; + x2 = tmp; + } + if (y1 > y2) { + tmp = y1; + y1 = y2; + y2 = tmp; + } + if (z1 > z2) { + tmp = z1; + z1 = z2; + z2 = tmp; + } + + Point p1 = new Point(x1, y1, z1); + Point p2 = new Point(x2, y2, z2); + + if (!areas.containsKey(world)) { + areas.put(world, new HashMap<>()); + } + + areas.get(world).put(_area, p1); + anotherPoint.put(_area, p2); + + return true; + } + return false; + } + + @Override + public Area check(Location location) { + Map tmpMap = areas.get(location.getWorld()); + if (tmpMap != null) { + for (Map.Entry entry : tmpMap.entrySet()) { + Point p1 = entry.getValue(); + if (location.getBlockY() >= p1.y && location.getBlockX() >= p1.x && location.getBlockZ() >= p1.z) { + Point p2 = anotherPoint.get(entry.getKey()); + if (location.getBlockY() <= p2.y && location.getBlockX() <= p2.x && location.getBlockZ() <= p2.z) { + return entry.getKey(); + } + } + } + } + return null; + } + + @Override + public Set getAll() { + return anotherPoint.keySet(); + } + + @Override + public void clear() { + areas.clear(); + anotherPoint.clear(); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..f44cf0a --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,22 @@ +version: ${project.version} +command-group: + teleport: + - '/tp\S*' +area: + 'world:0,0,0:10,10,10': + name: main + mode: deny + list: + - 'teleport' +msg: + prefix: '§l[ACB]§r ' + reload: 'ACB has been reloaded.' + load-failed: 'Failed loading unsupported area: {0}' + command-deny: 'Sorry, but you don''t have permission to do so.' + list-head: '§3======== §3§l[ACB List] §r§3========' + list-area-blacklist: '§a§l{0} §c§l[Blacklist]§r' + list-area-whitelist: '§a§l{0} §a§l[Whitelist]§r' + list-command: '§6- §r{0}:{1}§r' + help-head: '§3======== §3§l[ACB Help] §r§3========' + help-reload: 'Reload all areas in the config.yml' + help-list: 'List all areas with their commands.' \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..4942c28 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,7 @@ +name: AreaCommandBlocker +version: ${project.version} +main: io.github.gk0wk.areacommandblocker.AreaCommandBlocker +softdepend: [ WorldGuard, Resident, Towny, PlotGuard ] +authors: [ Gk0Wk ] +description: Block command bu areas. +website: https://gk0wk.github.io/