diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..c9c926e --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,27 @@ +version: 2 + +jobs: + build: + docker: + - image: circleci/openjdk:8-jdk-stretch + + steps: + - checkout + + - restore_cache: + keys: + - v1-dependencies-{{ checksum "pom.xml" }} + + - run: + command: | + mvn dependency:go-offline + mvn clean package + mkdir artifact + cp target/*.jar artifact + - save_cache: + paths: + - ~/.m2 + key: v1-dependencies-{{ checksum "pom.xml" }} + + - store_artifacts: + path: artifact diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8147bf8 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +*.java text=auto eol=lf diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..643f9f3 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,35 @@ +name: Java CI + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + - name: Cache dependencies + uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Build with Maven + run: mvn clean package + + - name: Copy artifacts + run: | + mkdir artifact + cp target/*.jar artifact + - name: Archive artifacts + uses: actions/upload-artifact@v1 + with: + name: artifacts + path: artifact diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5a92ac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,225 @@ +# Created by https://www.gitignore.io/api/java,maven,eclipse,netbeans,intellij+all +# Edit at https://www.gitignore.io/?templates=java,maven,eclipse,netbeans,intellij+all + +### Eclipse ### +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +### Eclipse Patch ### +# Eclipse Core +.project + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Annotation Processing +.apt_generated + +.sts4-cache/ + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# 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* + +### Maven ### +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 + +### NetBeans ### +**/nbproject/private/ +**/nbproject/Makefile-*.mk +**/nbproject/Package-*.bash +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +# End of https://www.gitignore.io/api/java,maven,eclipse,netbeans,intellij+all + +# Windows +desktop.ini +*/desktop.ini +Thumbs.db +*/Thumbs.db +ehthumbs.db +*/ehthumbs.db + +# Mac +.DS_Store +*/.DS_Store +__MACOSX +__MACOSX/* +*/__MACOSX +*/__MACOSX/* + +# Java +*.MF diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..af9aba3 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: java + +jdk: + - openjdk8 + +install: + - mvn clean package + +cache: + directories: + - '$HOME/.m2/repository' diff --git a/README.md b/README.md index fb34119..0d1391f 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,39 @@ -# Sanctioner -[![](https://i.loli.net/2019/06/02/5cf3ac062cc3478578.png)](http://www.mcbbs.net/thread-870745-1-1.html "制裁者") - -Sanctioner plugin for Nukkit +# Sanctioner for Nukkit +[![Nukkit](https://img.shields.io/badge/Nukkit-1.0-green)](https://github.com/NukkitX/Nukkit) +[![Build](https://img.shields.io/circleci/build/github/wode490390/Sanctioner/master)](https://circleci.com/gh/wode490390/Sanctioner/tree/master) +[![Release](https://img.shields.io/github/v/release/wode490390/Sanctioner)](https://github.com/wode490390/Sanctioner/releases) +[![Release date](https://img.shields.io/github/release-date/wode490390/Sanctioner)](https://github.com/wode490390/Sanctioner/releases) + Allows banned players to join the game, but everything they do is in vain. Quietly punishes them, they may not know they are banned :/ -Please see [mcbbs](http://www.mcbbs.net/thread-870745-1-1.html) for more information. +![](https://i.loli.net/2019/06/02/5cf3ac062cc3478578.png) + +If you found any bugs or have any suggestions, please open an issue on [GitHub Issues](https://github.com/wode490390/Sanctioner/issues). + +If you like this plugin, please star it on [GitHub](https://github.com/wode490390/Sanctioner). + ## Commands | Command | Permission | Description | Default | | - | - | - | - | | /crash | sanctioner.crash | Crashs the player's client | OP | +| /stuck | sanctioner.stuck | Stucks the player's client | OP | + +## Download +- [Releases](https://github.com/wode490390/Sanctioner/releases) +- [Snapshots](https://circleci.com/gh/wode490390/Sanctioner) + +## Compiling +1. Install [Maven](https://maven.apache.org/). +2. Fork and clone the repo. +3. Run `mvn clean package`. The compiled JAR can be found in the `target/` directory. + +## Metrics Collection + +This plugin uses [bStats](https://github.com/wode490390/bStats-Nukkit). You can opt out using the global bStats config; see the [official website](https://bstats.org/getting-started) for more details. + + + +###### If I have any grammar and/or term errors, please correct them :) diff --git a/pom.xml b/pom.xml index 0a4b6f2..2319611 100644 --- a/pom.xml +++ b/pom.xml @@ -1,31 +1,62 @@ - + 4.0.0 + cn.wode490390.nukkit sanctioner - 1.0.0 + jar + 1.1.0 Sanctioner Sanctioner plugin for Nukkit - 2019 http://wode490390.cn/ - jar + 2019 + GNU General Public License, Version 3.0 http://www.gnu.org/licenses/gpl.html + + + GitHub + https://github.com/wode490390/Sanctioner/issues + + + + CircleCI + https://circleci.com/gh/wode490390/Sanctioner + + + + scm:git:https://github.com/wode490390/Sanctioner.git + scm:git:git@github.com:wode490390/Sanctioner.git + https://github.com/wode490390/Sanctioner + + + + + github + github-releases + https://maven.pkg.github.com/wode490390/Sanctioner + + + 1.8 1.8 UTF-8 + - nukkitx - http://repo.nukkitx.com/main/ + nukkitx-repo + https://repo.nukkitx.com/main/ + cn.nukkit @@ -34,6 +65,7 @@ provided + wodeSanctioner-${project.version} clean package @@ -47,5 +79,52 @@ + + + pl.project13.maven + git-commit-id-plugin + 4.0.1 + + + get-the-git-infos + + revision + + + + + ${project.basedir}/.git + git + yyyy.MM.dd '@' HH:mm:ss z + ${user.timezone} + true + true + ${project.build.outputDirectory}/git.properties + properties + true + false + false + false + false + true + + git.user.* + + + false + 7 + flat + + false + false + 7 + -dirty + * + false + false + + + + diff --git a/src/main/java/cn/wode490390/nukkit/sanctioner/CrashCommand.java b/src/main/java/cn/wode490390/nukkit/sanctioner/CrashCommand.java index a5469ea..2137136 100644 --- a/src/main/java/cn/wode490390/nukkit/sanctioner/CrashCommand.java +++ b/src/main/java/cn/wode490390/nukkit/sanctioner/CrashCommand.java @@ -6,22 +6,13 @@ import cn.nukkit.command.PluginIdentifiableCommand; import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; -import cn.nukkit.item.Item; import cn.nukkit.lang.TranslationContainer; -import cn.nukkit.network.protocol.InventoryContentPacket; -import cn.nukkit.network.protocol.types.ContainerIds; +import cn.nukkit.network.protocol.MovePlayerPacket; import cn.nukkit.plugin.Plugin; import cn.nukkit.utils.TextFormat; public class CrashCommand extends Command implements PluginIdentifiableCommand { - private static final InventoryContentPacket CRASH_PACKET = new InventoryContentPacket(); - - static { - CRASH_PACKET.inventoryId = ContainerIds.CREATIVE; - CRASH_PACKET.slots = new Item[]{Item.get(230)}; - } - private final Plugin plugin; public CrashCommand(Plugin plugin) { @@ -39,10 +30,16 @@ public boolean execute(CommandSender sender, String label, String[] args) { if (!this.plugin.isEnabled() || !this.testPermission(sender)) { return false; } + if (args.length > 0) { Player player = plugin.getServer().getPlayer(args[0]); if (player != null) { - player.dataPacket(CRASH_PACKET); + MovePlayerPacket pk = new MovePlayerPacket(); + pk.eid = player.getId(); + pk.x = pk.y = pk.z = Float.MAX_VALUE; + pk.mode = MovePlayerPacket.MODE_TELEPORT; + player.dataPacket(pk); + Command.broadcastCommandMessage(sender, TextFormat.YELLOW + "Successfully crashed " + args[0] + "'s client"); } else { sender.sendMessage("No targets matched selector"); @@ -50,6 +47,7 @@ public boolean execute(CommandSender sender, String label, String[] args) { } else { sender.sendMessage(new TranslationContainer("commands.generic.usage", this.getUsage())); } + return true; } diff --git a/src/main/java/cn/wode490390/nukkit/sanctioner/MetricsLite.java b/src/main/java/cn/wode490390/nukkit/sanctioner/MetricsLite.java index 8b6550c..3a237e3 100644 --- a/src/main/java/cn/wode490390/nukkit/sanctioner/MetricsLite.java +++ b/src/main/java/cn/wode490390/nukkit/sanctioner/MetricsLite.java @@ -10,12 +10,13 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; + +import javax.net.ssl.HttpsURLConnection; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -29,7 +30,6 @@ import java.util.TimerTask; import java.util.UUID; import java.util.zip.GZIPOutputStream; -import javax.net.ssl.HttpsURLConnection; /** * bStats collects some data for plugin authors. @@ -76,14 +76,20 @@ public class MetricsLite { // The plugin private final Plugin plugin; + // The plugin id + private final int pluginId; + /** * Class constructor. * * @param plugin The plugin which stats should be submitted. + * @param pluginId The id of the plugin. + * It can be found at What is my plugin id? */ - public MetricsLite(Plugin plugin) { + public MetricsLite(Plugin plugin, int pluginId) { Preconditions.checkNotNull(plugin); this.plugin = plugin; + this.pluginId = pluginId; // Get the config file File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); @@ -192,6 +198,7 @@ public JsonObject getPluginData() { String pluginVersion = plugin.getDescription().getVersion(); data.addProperty("pluginName", pluginName); // Append the name of the plugin + data.addProperty("id", pluginId); // Append the id of the plugin data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin JsonArray customCharts = new JsonArray(); @@ -210,6 +217,7 @@ private JsonObject getServerData() { int playerAmount = Server.getInstance().getOnlinePlayers().size(); int onlineMode = Server.getInstance().getPropertyBoolean("xbox-auth", false) ? 1 : 0; String minecraftVersion = Server.getInstance().getVersion(); + String softwareName = Server.getInstance().getName(); // OS/Java specific data String javaVersion = System.getProperty("java.version"); @@ -225,6 +233,7 @@ private JsonObject getServerData() { data.addProperty("playerAmount", playerAmount); data.addProperty("onlineMode", onlineMode); data.addProperty("bukkitVersion", minecraftVersion); + data.addProperty("bukkitName", softwareName); data.addProperty("javaVersion", javaVersion); data.addProperty("osName", osName); @@ -305,7 +314,7 @@ private static void sendData(Plugin plugin, JsonObject data) throws Exception { throw new IllegalAccessException("This method must not be called from the main thread!"); } if (logSentData) { - plugin.getLogger().info("Sending data to bStats: " + data.toString()); + plugin.getLogger().info("Sending data to bStats: " + data); } HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); @@ -325,20 +334,18 @@ private static void sendData(Plugin plugin, JsonObject data) throws Exception { connection.setDoOutput(true); try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { outputStream.write(compressedData); - outputStream.flush(); } - InputStream inputStream = connection.getInputStream(); - StringBuilder builder; - try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { - builder = new StringBuilder(); + StringBuilder builder = new StringBuilder(); + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { String line; while ((line = bufferedReader.readLine()) != null) { builder.append(line); } } + if (logResponseStatusText) { - plugin.getLogger().info("Sent data to bStats and received response: " + builder.toString()); + plugin.getLogger().info("Sent data to bStats and received response: " + builder); } } diff --git a/src/main/java/cn/wode490390/nukkit/sanctioner/Sanctioner.java b/src/main/java/cn/wode490390/nukkit/sanctioner/Sanctioner.java index 8dfd98c..ae24d4d 100644 --- a/src/main/java/cn/wode490390/nukkit/sanctioner/Sanctioner.java +++ b/src/main/java/cn/wode490390/nukkit/sanctioner/Sanctioner.java @@ -9,67 +9,82 @@ import cn.nukkit.event.player.PlayerPreLoginEvent; import cn.nukkit.event.player.PlayerQuitEvent; import cn.nukkit.event.server.DataPacketReceiveEvent; +import cn.nukkit.network.protocol.DataPacket; import cn.nukkit.network.protocol.ProtocolInfo; +import cn.nukkit.network.protocol.TextPacket; import cn.nukkit.permission.BanEntry; import cn.nukkit.permission.BanList; import cn.nukkit.plugin.PluginBase; import cn.nukkit.utils.TextFormat; -import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; + import java.util.List; public class Sanctioner extends PluginBase implements Listener { - private static final List IGNORE_PACKETS = ImmutableList.builder() - .add(ProtocolInfo.ADVENTURE_SETTINGS_PACKET) - .add(ProtocolInfo.ANIMATE_PACKET) - .add(ProtocolInfo.BLOCK_ENTITY_DATA_PACKET) - .add(ProtocolInfo.BLOCK_PICK_REQUEST_PACKET) - .add(ProtocolInfo.BOOK_EDIT_PACKET) - .add(ProtocolInfo.BOSS_EVENT_PACKET) - .add(ProtocolInfo.COMMAND_BLOCK_UPDATE_PACKET) - .add(ProtocolInfo.COMMAND_REQUEST_PACKET) - .add(ProtocolInfo.CONTAINER_CLOSE_PACKET) - .add(ProtocolInfo.CRAFTING_EVENT_PACKET) - .add(ProtocolInfo.ENTITY_EVENT_PACKET) - .add(ProtocolInfo.ENTITY_FALL_PACKET) - .add(ProtocolInfo.ENTITY_PICK_REQUEST_PACKET) - .add(ProtocolInfo.INTERACT_PACKET) - .add(ProtocolInfo.INVENTORY_TRANSACTION_PACKET) - .add(ProtocolInfo.ITEM_FRAME_DROP_ITEM_PACKET) - //.add(ProtocolInfo.LAB_TABLE_PACKET) - .add(ProtocolInfo.LECTERN_UPDATE_PACKET) - .add(ProtocolInfo.LEVEL_SOUND_EVENT_PACKET) - .add(ProtocolInfo.LEVEL_SOUND_EVENT_PACKET_V1) - .add(ProtocolInfo.LEVEL_SOUND_EVENT_PACKET_V2) - .add(ProtocolInfo.MAP_CREATE_LOCKED_COPY_PACKET) - .add(ProtocolInfo.MAP_INFO_REQUEST_PACKET) - .add(ProtocolInfo.MOB_ARMOR_EQUIPMENT_PACKET) - .add(ProtocolInfo.MOB_EQUIPMENT_PACKET) - .add(ProtocolInfo.MODAL_FORM_RESPONSE_PACKET) - .add(ProtocolInfo.MOVE_ENTITY_ABSOLUTE_PACKET) - .add(ProtocolInfo.MOVE_PLAYER_PACKET) - .add(ProtocolInfo.NETWORK_STACK_LATENCY_PACKET) - .add(ProtocolInfo.NPC_REQUEST_PACKET) - .add(ProtocolInfo.PLAYER_ACTION_PACKET) - .add(ProtocolInfo.PLAYER_HOTBAR_PACKET) - .add(ProtocolInfo.PLAYER_INPUT_PACKET) - .add(ProtocolInfo.PLAYER_SKIN_PACKET) - .add(ProtocolInfo.PURCHASE_RECEIPT_PACKET) - .add(ProtocolInfo.REQUEST_CHUNK_RADIUS_PACKET) - .add(ProtocolInfo.RIDER_JUMP_PACKET) - .add(ProtocolInfo.SCRIPT_CUSTOM_EVENT_PACKET) - .add(ProtocolInfo.SERVER_SETTINGS_REQUEST_PACKET) - .add(ProtocolInfo.SET_DEFAULT_GAME_TYPE_PACKET) - .add(ProtocolInfo.SET_DIFFICULTY_PACKET) - .add(ProtocolInfo.SET_ENTITY_DATA_PACKET) - .add(ProtocolInfo.SET_PLAYER_GAME_TYPE_PACKET) - .add(ProtocolInfo.SHOW_CREDITS_PACKET) - .add(ProtocolInfo.SIMPLE_EVENT_PACKET) - .add(ProtocolInfo.SPAWN_EXPERIENCE_ORB_PACKET) - .add(ProtocolInfo.STRUCTURE_BLOCK_UPDATE_PACKET) - .add(ProtocolInfo.TEXT_PACKET) - .build(); + private static final boolean[] IGNORE_PACKETS = new boolean[256]; + + static { + IGNORE_PACKETS[ProtocolInfo.ADVENTURE_SETTINGS_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.ANIMATE_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.ANVIL_DAMAGE_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.BLOCK_ENTITY_DATA_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.BLOCK_PICK_REQUEST_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.BOOK_EDIT_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.BOSS_EVENT_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.COMMAND_BLOCK_UPDATE_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.COMMAND_REQUEST_PACKET & 0xff] = true; + //IGNORE_PACKETS[ProtocolInfo.CONTAINER_CLOSE_PACKET & 0xff] = false; // 1.16 inventory :( + IGNORE_PACKETS[ProtocolInfo.CRAFTING_EVENT_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.DEBUG_INFO_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.EMOTE_LIST_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.EMOTE_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.ENTITY_EVENT_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.ENTITY_FALL_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.ENTITY_PICK_REQUEST_PACKET & 0xff] = true; + //IGNORE_PACKETS[ProtocolInfo.INTERACT_PACKET & 0xff] = false; // 1.16 inventory :( + IGNORE_PACKETS[ProtocolInfo.INVENTORY_TRANSACTION_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.ITEM_FRAME_DROP_ITEM_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.ITEM_STACK_REQUEST_PACKET & 0xff] = true; + IGNORE_PACKETS[0x6d & 0xff] = true; //ProtocolInfo.LAB_TABLE_PACKET + IGNORE_PACKETS[ProtocolInfo.LECTERN_UPDATE_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.LEVEL_SOUND_EVENT_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.LEVEL_SOUND_EVENT_PACKET_V1 & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.LEVEL_SOUND_EVENT_PACKET_V2 & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.MAP_CREATE_LOCKED_COPY_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.MAP_INFO_REQUEST_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.MOB_ARMOR_EQUIPMENT_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.MOB_EQUIPMENT_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.MODAL_FORM_RESPONSE_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.MOVE_ENTITY_ABSOLUTE_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.MOVE_PLAYER_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.MULTIPLAYER_SETTINGS_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.NETWORK_STACK_LATENCY_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.NPC_REQUEST_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.PACKET_VIOLATION_WARNING_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.PLAYER_ACTION_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.PLAYER_AUTH_INPUT_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.PLAYER_HOTBAR_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.PLAYER_INPUT_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.PLAYER_SKIN_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.POS_TRACKING_CLIENT_REQUEST_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.PURCHASE_RECEIPT_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.REQUEST_CHUNK_RADIUS_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.RESPAWN_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.RIDER_JUMP_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.SCRIPT_CUSTOM_EVENT_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.SERVER_SETTINGS_REQUEST_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.SET_DEFAULT_GAME_TYPE_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.SET_DIFFICULTY_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.SET_ENTITY_DATA_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.SET_PLAYER_GAME_TYPE_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.SHOW_CREDITS_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.SIMPLE_EVENT_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.SPAWN_EXPERIENCE_ORB_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.STRUCTURE_BLOCK_UPDATE_PACKET & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.STRUCTURE_TEMPLATE_DATA_EXPORT_REQUEST & 0xff] = true; + IGNORE_PACKETS[ProtocolInfo.TEXT_PACKET & 0xff] = true; + } private final List banned = Lists.newArrayList(); private BanList ban; @@ -77,13 +92,16 @@ public class Sanctioner extends PluginBase implements Listener { @Override public void onEnable() { try { - new MetricsLite(this); - } catch (Exception ignore) { + new MetricsLite(this, 4838); + } catch (Throwable ignore) { } + this.ban = this.getServer().getNameBans(); + this.getServer().getPluginManager().registerEvents(this, this); this.getServer().getCommandMap().register("sanctioner", new CrashCommand(this)); + this.getServer().getCommandMap().register("sanctioner", new StuckCommand(this)); } @Override @@ -92,6 +110,12 @@ public void onDisable() { this.ban.add(entry); this.banned.remove(entry); } + + this.getServer().getOnlinePlayers().values().forEach(player -> { + if (this.ban.isBanned(player.getName())) { + player.close("", "", false); + } + }); } @EventHandler(priority = EventPriority.MONITOR) @@ -119,10 +143,33 @@ public void onPlayerJoin(PlayerJoinEvent event) { @EventHandler(priority = EventPriority.MONITOR) public void onDataPacketReceive(DataPacketReceiveEvent event) { Player player = event.getPlayer(); - if (player != null && IGNORE_PACKETS.contains(event.getPacket().pid())) { + DataPacket packet = event.getPacket(); + int id = packet.pid(); + if (player != null && IGNORE_PACKETS[id & 0xff]) { for (BanEntry entry : this.banned) { if (entry.getName().equalsIgnoreCase(player.getName())) { event.setCancelled(); + + switch (id) { + case ProtocolInfo.TEXT_PACKET: + TextPacket textPacket = (TextPacket) packet; + if (textPacket.type == TextPacket.TYPE_CHAT) { + TextPacket pk = new TextPacket(); + pk.type = TextPacket.TYPE_RAW; + pk.message = this.getServer().getLanguage().translateString("chat.type.text", new String[]{player.getDisplayName(), textPacket.message}); + player.dataPacket(pk); // feedback + } + break; + case ProtocolInfo.COMMAND_REQUEST_PACKET: + TextPacket pk = new TextPacket(); + pk.type = TextPacket.TYPE_RAW; + pk.message = TextFormat.RED + "An unknown error occurred while attempting to perform this command: java.lang.NullPointerException"; + player.dataPacket(pk); // fake feedback + break; + default: + break; + } + break; } } diff --git a/src/main/java/cn/wode490390/nukkit/sanctioner/StuckCommand.java b/src/main/java/cn/wode490390/nukkit/sanctioner/StuckCommand.java new file mode 100644 index 0000000..13a45ed --- /dev/null +++ b/src/main/java/cn/wode490390/nukkit/sanctioner/StuckCommand.java @@ -0,0 +1,62 @@ +package cn.wode490390.nukkit.sanctioner; + +import cn.nukkit.Player; +import cn.nukkit.command.Command; +import cn.nukkit.command.CommandSender; +import cn.nukkit.command.PluginIdentifiableCommand; +import cn.nukkit.command.data.CommandParamType; +import cn.nukkit.command.data.CommandParameter; +import cn.nukkit.lang.TranslationContainer; +import cn.nukkit.level.Level; +import cn.nukkit.network.protocol.ChangeDimensionPacket; +import cn.nukkit.plugin.Plugin; +import cn.nukkit.utils.TextFormat; + +public class StuckCommand extends Command implements PluginIdentifiableCommand { + + private static final ChangeDimensionPacket STUCK_PACKET = new ChangeDimensionPacket(); + + static { + STUCK_PACKET.dimension = Level.DIMENSION_OVERWORLD; + STUCK_PACKET.encode(); + } + + private final Plugin plugin; + + public StuckCommand(Plugin plugin) { + super("stuck", "Stucks the player's client", "/stuck "); + this.setPermission("sanctioner.stuck"); + this.getCommandParameters().clear(); + this.addCommandParameters("default", new CommandParameter[]{ + new CommandParameter("player", CommandParamType.TARGET, false) + }); + this.plugin = plugin; + } + + @Override + public boolean execute(CommandSender sender, String label, String[] args) { + if (!this.plugin.isEnabled() || !this.testPermission(sender)) { + return false; + } + + if (args.length > 0) { + Player player = plugin.getServer().getPlayer(args[0]); + if (player != null) { + player.dataPacket(STUCK_PACKET); + + Command.broadcastCommandMessage(sender, TextFormat.YELLOW + "Successfully stuck " + args[0] + "'s client"); + } else { + sender.sendMessage("No targets matched selector"); + } + } else { + sender.sendMessage(new TranslationContainer("commands.generic.usage", this.getUsage())); + } + + return true; + } + + @Override + public Plugin getPlugin() { + return this.plugin; + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index da6ba68..55308a6 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,12 +1,16 @@ main: cn.wode490390.nukkit.sanctioner.Sanctioner name: "Sanctioner" description: "Sanctioner plugin for Nukkit" -author: "wode490390" +authors: ["wode490390", "Wodor"] website: "http://wode490390.cn/" version: "${pom.version}" -api: ["1.0.0"] +api: ["1.0.6"] load: POSTWORLD + permissions: sanctioner.crash: - description: "Allows player to use /crash" + description: "Allows a player to use /crash" + default: op + sanctioner.stuck: + description: "Allows a player to use /stuck" default: op