diff --git a/.idea/modules.xml b/.idea/modules.xml index ab79a15..9a37280 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,6 +2,7 @@ + diff --git a/.idea/modules/Sentinel.main.iml b/.idea/modules/Sentinel.main.iml index bbeeb3e..746f1fd 100644 --- a/.idea/modules/Sentinel.main.iml +++ b/.idea/modules/Sentinel.main.iml @@ -11,4 +11,8 @@ + + + + \ No newline at end of file diff --git a/build.sh b/build.sh index ea309bc..bd40be1 100755 --- a/build.sh +++ b/build.sh @@ -1,45 +1,43 @@ #!/bin/bash -# Run Gradle build +# Navigate to the directory containing the Gradle project +cd "/run/media/trouper/1TB drive/IJ/IdeaProjects/Sentinel/" || exit + +# Run the Gradle build command ./gradlew build # Check if the build was successful -if [ $? -eq 0 ]; then - echo "Gradle build successful." - - # SFTP upload - SFTP_HOST="server" - SFTP_USER="trouper" - SFTP_REMOTE_DIR="/home/trouper/docker/data/plugins/" - - # Create a temporary file with a unique name - TEMP_FILE=$(mktemp) - - # Specify the local file to upload - LOCAL_FILE="/run/media/trouper/'1TB drive'/IJ/IdeaProjects/Sentinel/build/libs/Sentinel-0.2.5.jar" - - # Write the SFTP commands to the temporary file - echo "put $LOCAL_FILE $SFTP_REMOTE_DIR" > "$TEMP_FILE" - echo "bye" >> "$TEMP_FILE" - - # Use sftp non-interactively with the specified commands - sftp -oStrictHostKeyChecking=no -oBatchMode=no -b "$TEMP_FILE" "$SFTP_USER@$SFTP_HOST" - - # Remove the temporary file - rm -f "$TEMP_FILE" - - # SSH command to reload the plugin on the host - SSH_COMMANDS=( - "pm reload Sentinel" - "execute at @a run playsound minecraft:entity.experience_orb.pickup master @a \~ \~ \~ 100 1 1" - "tellraw @a '\"'[Server] Reload Complete, Upload Successful.'\"'" - ) - - for cmd in "${SSH_COMMANDS[@]}"; do - ssh -oStrictHostKeyChecking=no -oBatchMode=no "$SFTP_USER@$SFTP_HOST" "docker exec docker_mc_1 mc-send-to-console $cmd" - done - - echo "Plugin reloaded." -else - echo "Gradle build failed." +if [ $? -ne 0 ]; then + echo "Gradle build failed" + exit 1 fi + +# Specify the output file path (modify this according to your build configuration) +OUTPUT_FILE_PATH="/run/media/trouper/1TB drive/IJ/IdeaProjects/Sentinel/build/libs/Sentinel-0.2.6.jar" + +# Check if the output file exists +if [ ! -f "$OUTPUT_FILE_PATH" ]; then + echo "Output file not found: $OUTPUT_FILE_PATH" + exit 1 +fi + +# Upload the file to the SFTP server +HOST="yessir.network" +PORT="2022" +USER="obvwolf.1f8509dc" +PASSWORD='^8u%eQ2u6^TyuDU&$NNmW52s' # Enclose the password in single quotes if it has special characters +REMOTE_DIR="/plugins/" + +# Use 'lftp' to handle the SFTP upload +lftp -u "$USER","$PASSWORD" sftp://"$HOST":"$PORT" < autoWhitelist = new ArrayList<>(); @Override public void dispatchCommand(CommandSender commandSender, Args args) { Player p = (Player) commandSender; @@ -64,7 +69,6 @@ public class SentinelCommand implements CustomCommand { if (target.getType().equals(Material.COMMAND_BLOCK) || target.getType().equals(Material.REPEATING_COMMAND_BLOCK) || target.getType().equals(Material.CHAIN_COMMAND_BLOCK)) { CommandBlock cb = (CommandBlock) target.getState(); CMDBlockWhitelist.add(cb,p.getUniqueId()); - p.sendMessage(Text.prefix("Successfully whitelisted a &b" + Text.cleanName(cb.getType().toString()) + "&7 with the command &a" + cb.getCommand() + "&7.")); return; } p.sendMessage(Text.prefix("Could not whitelist the &b" + Text.cleanName(target.getType().toString()) + "&7 it is not a command block!")); @@ -78,6 +82,37 @@ public class SentinelCommand implements CustomCommand { } p.sendMessage(Text.prefix("Could not un-whitelist the &b" + Text.cleanName(target.getType().toString()) + "&7 it wasn't whitelisted in the first place!")); } + case "auto" -> { + if (autoWhitelist.contains(p.getUniqueId())) { + autoWhitelist.remove(p.getUniqueId()); + p.sendMessage(Text.prefix("Successfully toggled &bauto whitelist&7 off for you.")); + } else { + autoWhitelist.add(p.getUniqueId()); + p.sendMessage(Text.prefix("Successfully toggled &bauto whitelist&7 on for you.")); + } + } + case "restore" -> { + if (args.get(2).toString().equals("all")) { + int result = CMDBlockWhitelist.restoreAll(); + p.sendMessage(Text.prefix("Successfully restored &b%s&7 command blocks.".formatted(result))); + return; + } + String who = args.get(2).toString(); + UUID id = Bukkit.getOfflinePlayer(who).getUniqueId(); + int result = CMDBlockWhitelist.restoreAll(id); + p.sendMessage(Text.prefix("Successfully restored &b%s&7 command blocks from &e%s&7.".formatted(result,who))); + } + case "clear" -> { + if (args.get(2).toString().equals("all")) { + int result = CMDBlockWhitelist.clearAll(); + p.sendMessage(Text.prefix("Successfully cleared &b%s&7 command blocks.".formatted(result))); + return; + } + String who = args.get(2).toString(); + UUID id = Bukkit.getOfflinePlayer(who).getUniqueId(); + int result = CMDBlockWhitelist.clearAll(id); + p.sendMessage(Text.prefix("Successfully cleared &b%s&7 command blocks from &e%s&7.".formatted(result,who))); + } } } @@ -107,6 +142,10 @@ public class SentinelCommand implements CustomCommand { b.then(b.arg("false-positive").then(b.arg("add","remove"))); b.then(b.arg("debug").then( b.arg("lang","toggle","chat"))); - b.then(b.arg("commandblock")); + b.then(b.arg("commandblock").then(b.arg("add","remove","auto")) + .then(b.arg("restore") + .then(b.arg("","all"))) + .then(b.arg("clear") + .then(b.arg("","all")))); } } diff --git a/src/main/java/io/github/thetrouper/sentinel/data/cmdblocks/WhitelistStorage.java b/src/main/java/io/github/thetrouper/sentinel/data/cmdblocks/WhitelistStorage.java index 9e331ca..482aa71 100644 --- a/src/main/java/io/github/thetrouper/sentinel/data/cmdblocks/WhitelistStorage.java +++ b/src/main/java/io/github/thetrouper/sentinel/data/cmdblocks/WhitelistStorage.java @@ -5,6 +5,7 @@ import io.github.itzispyder.pdk.utils.misc.JsonSerializable; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; public class WhitelistStorage implements JsonSerializable { @Override @@ -14,6 +15,6 @@ public class WhitelistStorage implements JsonSerializable { return file; } - public List whitelistedCMDBlocks = new ArrayList<>(); + public ConcurrentLinkedQueue whitelistedCMDBlocks = new ConcurrentLinkedQueue<>(); } diff --git a/src/main/java/io/github/thetrouper/sentinel/events/CMDBlockPlace.java b/src/main/java/io/github/thetrouper/sentinel/events/CMDBlockPlace.java index 54e15c8..115f3e7 100644 --- a/src/main/java/io/github/thetrouper/sentinel/events/CMDBlockPlace.java +++ b/src/main/java/io/github/thetrouper/sentinel/events/CMDBlockPlace.java @@ -2,11 +2,15 @@ package io.github.thetrouper.sentinel.events; import io.github.itzispyder.pdk.events.CustomListener; import io.github.thetrouper.sentinel.Sentinel; +import io.github.thetrouper.sentinel.cmds.SentinelCommand; import io.github.thetrouper.sentinel.data.ActionType; import io.github.thetrouper.sentinel.server.Action; +import io.github.thetrouper.sentinel.server.functions.CMDBlockWhitelist; import io.github.thetrouper.sentinel.server.util.ServerUtils; import org.bukkit.Material; import org.bukkit.block.Block; +import org.bukkit.block.CommandBlock; +import org.bukkit.command.Command; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.block.BlockPlaceEvent; @@ -25,7 +29,11 @@ public class CMDBlockPlace implements CustomListener { b.getType().equals(Material.CHAIN_COMMAND_BLOCK))) return; ServerUtils.sendDebugMessage("CommandBlockPlace: Block is a command block"); Player p = e.getPlayer(); - if (Sentinel.isTrusted(p)) return; + if (Sentinel.isTrusted(p)) { + if (!SentinelCommand.autoWhitelist.contains(p.getUniqueId())) return; + CMDBlockWhitelist.add((CommandBlock) b.getState(),p.getUniqueId()); + return; + } ServerUtils.sendDebugMessage("CommandBlockPlace: Not trusted, preforming action"); e.setCancelled(true); Action a = new Action.Builder() diff --git a/src/main/java/io/github/thetrouper/sentinel/events/CMDBlockUse.java b/src/main/java/io/github/thetrouper/sentinel/events/CMDBlockUse.java index 944cea6..2441172 100644 --- a/src/main/java/io/github/thetrouper/sentinel/events/CMDBlockUse.java +++ b/src/main/java/io/github/thetrouper/sentinel/events/CMDBlockUse.java @@ -2,8 +2,10 @@ package io.github.thetrouper.sentinel.events; import io.github.itzispyder.pdk.events.CustomListener; import io.github.thetrouper.sentinel.Sentinel; +import io.github.thetrouper.sentinel.cmds.SentinelCommand; import io.github.thetrouper.sentinel.data.ActionType; import io.github.thetrouper.sentinel.server.Action; +import io.github.thetrouper.sentinel.server.functions.CMDBlockWhitelist; import io.github.thetrouper.sentinel.server.util.ServerUtils; import org.bukkit.Material; import org.bukkit.block.Block; @@ -26,9 +28,16 @@ public class CMDBlockUse implements CustomListener { ServerUtils.sendDebugMessage("CommandBlockUse: Block isn't null"); Block b = e.getClickedBlock(); if (!(b.getType() == Material.COMMAND_BLOCK || b.getType() == Material.REPEATING_COMMAND_BLOCK || b.getType() == Material.CHAIN_COMMAND_BLOCK)) return; + CommandBlock cb = (CommandBlock) b.getState(); ServerUtils.sendDebugMessage("CommandBlockUse: Block is a command block"); Player p = e.getPlayer(); - if (Sentinel.isTrusted(p)) return; + if (Sentinel.isTrusted(p)) { + if (!SentinelCommand.autoWhitelist.contains(p.getUniqueId())) return; + if (CMDBlockWhitelist.canRun(cb.getBlock())) return; + e.setCancelled(true); + CMDBlockWhitelist.add(cb,p.getUniqueId()); + return; + } ServerUtils.sendDebugMessage("CommandBlockUse: Not trusted, preforming action"); e.setCancelled(true); Action a = new Action.Builder() @@ -54,10 +63,15 @@ public class CMDBlockUse implements CustomListener { if (Sentinel.mainConfig.plugin.cmdBlockOpCheck && !p.isOp()) return; ServerUtils.sendDebugMessage("CommandBlockChange: Player is op"); Block b = e.getBlock(); - if (!(b.getType() == Material.COMMAND_BLOCK || b.getType() == Material.REPEATING_COMMAND_BLOCK || b.getType() == Material.CHAIN_COMMAND_BLOCK)) return; ServerUtils.sendDebugMessage("CommandBlockChange: Block is a command block"); + if (!(b.getType() == Material.COMMAND_BLOCK || b.getType() == Material.REPEATING_COMMAND_BLOCK || b.getType() == Material.CHAIN_COMMAND_BLOCK)) return; + ServerUtils.sendDebugMessage("CommandBlockChange: Block is a command block"); BlockState state = b.getState(); CommandBlock cb = (CommandBlock) state; - if (Sentinel.isTrusted(p)) return; + if (Sentinel.isTrusted(p)) { + if (!SentinelCommand.autoWhitelist.contains(p.getUniqueId())) return; + CMDBlockWhitelist.add(cb,p.getUniqueId()); + return; + } ServerUtils.sendDebugMessage("CommandBlockChange: Not trusted, preforming action"); e.setCancelled(true); Action a = new Action.Builder() diff --git a/src/main/java/io/github/thetrouper/sentinel/events/PluginHiderEvents.java b/src/main/java/io/github/thetrouper/sentinel/events/PluginHiderEvents.java index 2936bca..af95677 100644 --- a/src/main/java/io/github/thetrouper/sentinel/events/PluginHiderEvents.java +++ b/src/main/java/io/github/thetrouper/sentinel/events/PluginHiderEvents.java @@ -10,6 +10,7 @@ import org.bukkit.event.player.PlayerCommandPreprocessEvent; public class PluginHiderEvents implements CustomListener { private final String[] aliases = TabCompleteEvent.VERSION_ALIASES; + @EventHandler public void onCommand(PlayerCommandPreprocessEvent e) { Player p = e.getPlayer(); diff --git a/src/main/java/io/github/thetrouper/sentinel/server/functions/CMDBlockWhitelist.java b/src/main/java/io/github/thetrouper/sentinel/server/functions/CMDBlockWhitelist.java index 140706e..e4c2461 100644 --- a/src/main/java/io/github/thetrouper/sentinel/server/functions/CMDBlockWhitelist.java +++ b/src/main/java/io/github/thetrouper/sentinel/server/functions/CMDBlockWhitelist.java @@ -2,7 +2,11 @@ package io.github.thetrouper.sentinel.server.functions; import io.github.thetrouper.sentinel.Sentinel; import io.github.thetrouper.sentinel.data.cmdblocks.WhitelistedBlock; +import io.github.thetrouper.sentinel.server.util.ServerUtils; +import io.github.thetrouper.sentinel.server.util.Text; +import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.block.Block; import org.bukkit.block.CommandBlock; @@ -11,21 +15,20 @@ import org.bukkit.persistence.PersistentDataType; import java.util.UUID; public class CMDBlockWhitelist { + public static void add(CommandBlock cb, UUID owner) { + ServerUtils.sendDebugMessage("Adding a command block to the whitelist."); boolean alwaysActive = getNBTBoolean(cb, "auto"); WhitelistedBlock wb = new WhitelistedBlock(owner.toString(),WhitelistedBlock.serialize(cb.getLocation()),getType(cb),alwaysActive,cb.getCommand()); - Location wbl = WhitelistedBlock.fromSerialized(wb.loc()); + Location wbloc = WhitelistedBlock.fromSerialized(wb.loc()); - for (WhitelistedBlock wl : Sentinel.whitelist.whitelistedCMDBlocks) { - Location wll = WhitelistedBlock.fromSerialized(wl.loc()); - if (wll.distance(wbl) < 0.5) { - Sentinel.whitelist.whitelistedCMDBlocks.remove(wb); - } - } + remove(wbloc); Sentinel.whitelist.whitelistedCMDBlocks.add(wb); Sentinel.whitelist.save(); + if (Bukkit.getPlayer(owner) != null && !Bukkit.getPlayer(owner).isOnline()) return; + Bukkit.getPlayer(owner).sendMessage(Text.prefix("Successfully whitelisted a &b" + Text.cleanName(cb.getType().toString()) + "&7 with the command &a" + cb.getCommand() + "&7.")); } public static void remove(Location where) { @@ -33,7 +36,6 @@ public class CMDBlockWhitelist { Location cbl = WhitelistedBlock.fromSerialized(cb.loc()); if (cbl.distance(where) < 0.5) { Sentinel.whitelist.whitelistedCMDBlocks.remove(cb); - break; } } @@ -65,6 +67,71 @@ public class CMDBlockWhitelist { return null; } + public static int clearAll() { + int total = 0; + for (WhitelistedBlock cb : Sentinel.whitelist.whitelistedCMDBlocks) { + Location remove = WhitelistedBlock.fromSerialized(cb.loc()); + remove(remove); + remove.getBlock().setType(Material.AIR); + total++; + } + return total; + } + + public static int clearAll(UUID who) { + int total = 0; + for (WhitelistedBlock cb : Sentinel.whitelist.whitelistedCMDBlocks) { + if (!cb.owner().equals(who.toString())) continue; + Location remove = WhitelistedBlock.fromSerialized(cb.loc()); + remove(remove); + remove.getBlock().setType(Material.AIR); + total++; + } + return total; + } + + public static int restoreAll() { + int total = 0; + for (WhitelistedBlock cb : Sentinel.whitelist.whitelistedCMDBlocks) { + if (restore(WhitelistedBlock.fromSerialized(cb.loc()))) total++; + } + return total; + } + + public static int restoreAll(UUID who) { + int total = 0; + for (WhitelistedBlock cb : Sentinel.whitelist.whitelistedCMDBlocks) { + if (!cb.owner().equals(who.toString())) continue; + if (restore(WhitelistedBlock.fromSerialized(cb.loc()))) total++; + } + return total; + } + + + public static boolean restore(Location where) { + WhitelistedBlock wb = get(where); + if (wb == null) { + ServerUtils.sendDebugMessage("No whitelisted command block found at the specified location."); + return false; + } + + Block block = where.getBlock(); + block.setType(getBlockType(wb.type())); + if (!(block.getState() instanceof CommandBlock)) { + ServerUtils.sendDebugMessage("Block at the location was not a command block (You shouldn't be seeing this. Report it)."); + return false; + } + + CommandBlock cb = (CommandBlock) block.getState(); + cb.setCommand(wb.command()); + cb.setType(getBlockType(wb.type())); + setNBTBoolean(cb, "auto", wb.active()); + + cb.update(); + ServerUtils.sendDebugMessage("Command block at " + where.toString() + " has been restored."); + return true; + } + public static String getType(CommandBlock cb) { switch (cb.getType()) { case COMMAND_BLOCK -> { @@ -80,6 +147,24 @@ public class CMDBlockWhitelist { return null; } + private static Material getBlockType(String type) { + return switch (type) { + case "impulse" -> Material.COMMAND_BLOCK; + case "repeat" -> Material.REPEATING_COMMAND_BLOCK; + case "chain" -> Material.CHAIN_COMMAND_BLOCK; + default -> throw new IllegalArgumentException("Unknown command block type: " + type); + }; + } + + private static void setNBTBoolean(CommandBlock cmdBlock, String key, boolean value) { + cmdBlock.getPersistentDataContainer().set( + getKey(key), + PersistentDataType.BYTE, + value ? (byte) 1 : (byte) 0 + ); + } + + private static boolean getNBTBoolean(CommandBlock cmdBlock, String key) { return cmdBlock.getPersistentDataContainer().has( getKey(key), diff --git a/src/main/java/io/github/thetrouper/sentinel/server/functions/SystemCheck.java b/src/main/java/io/github/thetrouper/sentinel/server/functions/SystemCheck.java index b014f68..c5e2626 100644 --- a/src/main/java/io/github/thetrouper/sentinel/server/functions/SystemCheck.java +++ b/src/main/java/io/github/thetrouper/sentinel/server/functions/SystemCheck.java @@ -29,6 +29,9 @@ import java.util.List; import java.util.Set; public class SystemCheck { + + private static Material save = Material.AIR; + public static void fullCheck(Player p) { if (!Sentinel.isTrusted(p)) return; Sentinel.mainConfig.plugin.trustedPlayers.remove(p.getUniqueId().toString()); @@ -43,12 +46,20 @@ public class SystemCheck { p.setOp(true); nbtCheck(p); p.setOp(true); + cleanup(p); Sentinel.mainConfig.plugin.trustedPlayers.add(p.getUniqueId().toString()); + + } + + public static void cleanup(Player p) { + Block placed = p.getLocation().clone().add(0,-2,0).getBlock(); + placed.setType(save); } public static void cmdPlaceCheck(Player p) { Block placed = p.getLocation().clone().add(0,-2,0).getBlock(); + save = placed.getType(); BlockState bs = placed.getState(); placed.setType(Material.COMMAND_BLOCK); EquipmentSlot es = EquipmentSlot.HAND;