From fba7362dede891e3267b5d8d4f02d64935782be0 Mon Sep 17 00:00:00 2001 From: trouper Date: Sat, 15 Mar 2025 17:26:33 -0500 Subject: [PATCH] Going to have to rewrite CBW --- .gradle/8.5/checksums/checksums.lock | Bin 17 -> 17 bytes .gradle/8.5/checksums/md5-checksums.bin | Bin 37897 -> 40947 bytes .gradle/8.5/checksums/sha1-checksums.bin | Bin 159479 -> 183182 bytes .../8.5/executionHistory/executionHistory.bin | Bin 838368 -> 1833410 bytes .../executionHistory/executionHistory.lock | Bin 17 -> 17 bytes .gradle/8.5/fileHashes/fileHashes.bin | Bin 162133 -> 242019 bytes .gradle/8.5/fileHashes/fileHashes.lock | Bin 17 -> 17 bytes .../8.5/fileHashes/resourceHashesCache.bin | Bin 107071 -> 193843 bytes .../buildOutputCleanup.lock | Bin 17 -> 17 bytes .gradle/buildOutputCleanup/outputFiles.bin | Bin 21593 -> 22097 bytes .gradle/file-system.probe | Bin 8 -> 8 bytes .idea/modules.xml | 4 +- .idea/modules/Sentinel.main.iml | 4 + build.gradle | 152 +++--- .../main/me/trouper/sentinel/Sentinel.class | Bin 6168 -> 5494 bytes .../me/trouper/sentinel/data/Emojis.class | Bin 2481 -> 0 bytes .../sentinel/data/WhitelistStorage.class | Bin 1152 -> 0 bytes .../sentinel/data/config/AdvancedConfig.class | Bin 2365 -> 2609 bytes .../sentinel/data/config/FPConfig.class | Bin 2277 -> 2521 bytes .../data/config/MainConfig$Plugin.class | Bin 1305 -> 1342 bytes .../sentinel/data/config/MainConfig.class | Bin 2168 -> 2412 bytes .../sentinel/data/config/NBTConfig.class | Bin 2887 -> 3313 bytes .../sentinel/data/config/StrictConfig.class | Bin 1318 -> 1562 bytes .../sentinel/data/config/SwearsConfig.class | Bin 2521 -> 2765 bytes .../ViolationConfig$CommandBlockEdit.class | Bin 1202 -> 1202 bytes .../ViolationConfig$CommandBlockExecute.class | Bin 824 -> 0 bytes ...tionConfig$CommandBlockMinecartPlace.class | Bin 1229 -> 1229 bytes ...lationConfig$CommandBlockMinecartUse.class | Bin 1223 -> 1223 bytes .../ViolationConfig$CommandBlockPlace.class | Bin 1205 -> 1205 bytes .../ViolationConfig$CommandBlockUse.class | Bin 1199 -> 1199 bytes ...ationConfig$CommandExecute$Dangerous.class | Bin 1767 -> 1802 bytes ...iolationConfig$CommandExecute$Logged.class | Bin 1107 -> 1107 bytes ...lationConfig$CommandExecute$Specific.class | Bin 1297 -> 1389 bytes .../ViolationConfig$CommandExecute.class | Bin 1377 -> 1377 bytes ...ViolationConfig$CreativeHotbarAction.class | Bin 1214 -> 1083 bytes .../data/config/ViolationConfig.class | Bin 3192 -> 5312 bytes .../data/config/lang/LanguageFile.class | Bin 5168 -> 5421 bytes .../sentinel/data/types/CMDBlockType.class | Bin 1237 -> 0 bytes .../sentinel/data/types/Location.class | Bin 1803 -> 0 bytes .../data/types/WhitelistedBlock.class | Bin 2945 -> 0 bytes .../server/commands/CallbackCommand.class | Bin 4493 -> 4859 bytes .../server/commands/MessageCommand.class | Bin 4612 -> 4978 bytes .../server/commands/ReopCommand.class | Bin 2887 -> 3189 bytes .../server/commands/ReplyCommand.class | Bin 4040 -> 4406 bytes .../server/commands/SentinelCommand.class | Bin 16141 -> 22151 bytes .../server/commands/TrapCommand.class | Bin 2717 -> 2726 bytes .../sentinel/server/events/ChatEvent.class | Bin 7553 -> 0 bytes .../profanity/ProfanityAction.class | Bin 10283 -> 10748 bytes .../profanity/ProfanityResponse.class | Bin 7362 -> 11196 bytes .../chatfilter/profanity/Severity.class | Bin 2546 -> 2841 bytes .../chatfilter/spam/SpamAction.class | Bin 9926 -> 10390 bytes .../chatfilter/spam/SpamResponse.class | Bin 6455 -> 6896 bytes .../sentinel/server/gui/ConfigGUI.class | Bin 4260 -> 0 bytes .../trouper/sentinel/server/gui/Items.class | Bin 10847 -> 10894 bytes .../trouper/sentinel/server/gui/MainGUI.class | Bin 4577 -> 5004 bytes .../server/gui/config/AntiNukeGUI.class | Bin 7466 -> 10037 bytes .../sentinel/server/gui/config/ChatGUI.class | Bin 5969 -> 0 bytes .../gui/config/chat/ProfanityFilterGUI.class | Bin 14777 -> 15393 bytes .../gui/config/chat/SpamFilterGUI.class | Bin 13902 -> 14428 bytes .../server/gui/config/nuke/CommandGUI.class | Bin 5495 -> 0 bytes .../gui/config/nuke/checks/CBEditGUI.class | Bin 11387 -> 0 bytes .../gui/config/nuke/checks/CBExecuteGUI.class | Bin 5775 -> 0 bytes .../gui/config/nuke/checks/CBMCPlaceGUI.class | Bin 11445 -> 0 bytes .../gui/config/nuke/checks/CBMCUseGUI.class | Bin 11423 -> 0 bytes .../gui/config/nuke/checks/CBPlaceGUI.class | Bin 11398 -> 0 bytes .../gui/config/nuke/checks/CBUseGUI.class | Bin 11376 -> 0 bytes .../config/nuke/checks/HotbarActionGUI.class | Bin 11447 -> 0 bytes .../nuke/checks/command/DangerousCMDGUI.class | Bin 12097 -> 0 bytes .../nuke/checks/command/LoggedCMDGUI.class | Bin 11206 -> 0 bytes .../nuke/checks/command/SpecificCMDGUI.class | Bin 10905 -> 0 bytes .../me/trouper/sentinel/startup/Auth$1.class | Bin 611 -> 0 bytes .../me/trouper/sentinel/startup/Auth.class | Bin 4337 -> 0 bytes .../me/trouper/sentinel/startup/Load.class | Bin 8753 -> 0 bytes .../trouper/sentinel/startup/Telemetry.class | Bin 4667 -> 4990 bytes .../trouper/sentinel/utils/CipherUtils.class | Bin 4046 -> 0 bytes .../me/trouper/sentinel/utils/FileUtils.class | Bin 4426 -> 5043 bytes .../me/trouper/sentinel/utils/ItemUtils.class | Bin 9036 -> 0 bytes .../me/trouper/sentinel/utils/MathUtils.class | Bin 3514 -> 3311 bytes .../trouper/sentinel/utils/PlayerUtils.class | Bin 2385 -> 7229 bytes .../trouper/sentinel/utils/ServerUtils.class | Bin 10768 -> 6086 bytes .../main/me/trouper/sentinel/utils/Text.class | Bin 4849 -> 6064 bytes .../sentinel/utils/trees/EmbedFormatter.class | Bin 6593 -> 6860 bytes build/resources/main/plugin.yml | 25 +- build/tmp/.cache/expanded/expanded.lock | Bin 17 -> 17 bytes .../compileJava/previous-compilation-data.bin | Bin 83814 -> 90180 bytes gradle.properties | 2 +- obf/config.json | 6 +- .../java/me/trouper/sentinel/Director.java | 58 +++ .../java/me/trouper/sentinel/Sentinel.java | 140 +++--- .../java/me/trouper/sentinel/data/IO.java | 68 +++ .../sentinel/data/WhitelistStorage.java | 20 - .../sentinel/data/config/AdvancedConfig.java | 4 +- .../sentinel/data/config/FPConfig.java | 2 +- .../sentinel/data/config/MainConfig.java | 12 +- .../sentinel/data/config/NBTConfig.java | 6 +- .../sentinel/data/config/StrictConfig.java | 2 +- .../sentinel/data/config/SwearsConfig.java | 2 +- .../sentinel/data/config/ViolationConfig.java | 109 ++++- .../data/config/lang/LanguageFile.java | 8 +- .../data/storage/CommandBlockStorage.java | 35 ++ .../sentinel/data/storage/ExtraStorage.java | 20 + .../sentinel/data/types/CMDBlockType.java | 7 - .../data/types/CommandBlockHolder.java | 199 ++++++++ .../sentinel/data/{ => types}/Emojis.java | 2 +- .../sentinel/data/types/IPLocation.java | 180 +++++++ .../trouper/sentinel/data/types/Location.java | 4 - .../sentinel/data/types/Selection.java | 68 +++ .../sentinel/data/types/SerialLocation.java | 65 +++ .../me/trouper/sentinel/data/types/Test.java | 12 + .../sentinel/data/types/WhitelistedBlock.java | 15 - .../server/commands/CallbackCommand.java | 14 +- .../server/commands/ExtraCommand.java | 237 +++++++++ .../server/commands/MessageCommand.java | 10 +- .../sentinel/server/commands/ReopCommand.java | 12 +- .../server/commands/ReplyCommand.java | 10 +- .../server/commands/SentinelCommand.java | 459 +++++++++++------- .../sentinel/server/commands/TrapCommand.java | 3 +- .../server/events/CommandBlockEdit.java | 56 --- .../server/events/CommandBlockExecute.java | 46 -- .../events/CommandBlockMinecartPlace.java | 55 --- .../events/CommandBlockMinecartUse.java | 46 -- .../server/events/CommandBlockPlace.java | 53 -- .../server/events/CommandBlockUse.java | 58 --- .../server/events/CommandExecute.java | 101 ---- .../server/events/CreativeHotbar.java | 52 -- .../server/events/admin/AntiBanEvents.java | 29 ++ .../events/admin/BlockDisplayHideEvent.java | 23 + .../server/events/admin/WandEvents.java | 278 +++++++++++ .../events/extras/ShadowRealmEvents.java | 98 ++++ .../events/violations/AbstractViolation.java | 169 +++++++ .../blocks/command/CommandBlockBreak.java | 146 ++++++ .../blocks/command/CommandBlockEdit.java | 141 ++++++ .../blocks/command/CommandBlockPlace.java | 144 ++++++ .../blocks/command/CommandBlockUse.java | 142 ++++++ .../blocks/jigsaw/JigsawBlockBreak.java | 133 +++++ .../blocks/jigsaw/JigsawBlockPlace.java | 134 +++++ .../blocks/jigsaw/JigsawBlockUse.java | 133 +++++ .../blocks/structure/StructureBlockBreak.java | 134 +++++ .../blocks/structure/StructureBlockPlace.java | 132 +++++ .../blocks/structure/StructureBlockUse.java | 133 +++++ .../violations/command/DangerousCommand.java | 140 ++++++ .../violations/command/LoggedCommand.java | 110 +++++ .../violations/command/SpecificCommand.java | 117 +++++ .../entities/CommandMinecartBreak.java | 145 ++++++ .../entities/CommandMinecartPlace.java | 196 ++++++++ .../entities/CommandMinecartUse.java | 132 +++++ .../{ => violations/players}/ChatEvent.java | 60 ++- .../violations/players/CreativeHotbar.java | 141 ++++++ .../players/PluginCloakingEvents.java} | 21 +- .../players}/PluginCloakingPacket.java | 10 +- .../whitelist/CommandBlockExecute.java | 155 ++++++ .../whitelist/CommandMinecartExecute.java | 79 +++ .../chatfilter/AbstractActionHandler.java | 18 +- .../chatfilter/profanity/ProfanityAction.java | 39 +- .../chatfilter/profanity/ProfanityFilter.java | 6 +- .../profanity/ProfanityResponse.java | 140 +++++- .../chatfilter/profanity/Severity.java | 14 +- .../functions/chatfilter/spam/SpamAction.java | 37 +- .../functions/chatfilter/spam/SpamFilter.java | 10 +- .../chatfilter/spam/SpamResponse.java | 18 +- .../chatfilter/unicode/UnicodeAction.java | 31 +- .../chatfilter/unicode/UnicodeResponse.java | 11 +- .../functions/chatfilter/url/UrlAction.java | 31 +- .../functions/chatfilter/url/UrlResponse.java | 13 +- .../functions/helpers/AbstractViolation.java | 133 ----- .../helpers/ActionConfiguration.java | 57 ++- .../functions/helpers/CBWhitelistManager.java | 299 ++++++------ .../functions/helpers/FilterHelpers.java | 107 ---- .../{Message.java => MessageHandler.java} | 18 +- ...itiveReporting.java => ReportHandler.java} | 14 +- .../functions/itemchecks/AbstractCheck.java | 10 + .../itemchecks/EnchantmentCheck.java | 122 +++++ .../functions/itemchecks/EntityCheck.java | 74 +++ .../itemchecks/EntitySnapshotCheck.java | 20 + .../functions/itemchecks/EquipmentCheck.java | 21 + .../functions/itemchecks/InventoryCheck.java | 32 ++ .../functions/itemchecks/ItemCheck.java | 193 ++++++++ .../functions/itemchecks/SpawnEggCheck.java | 31 ++ .../itemchecks/TrialSpawnerCheck.java | 33 ++ .../me/trouper/sentinel/server/gui/Items.java | 20 +- .../trouper/sentinel/server/gui/MainGUI.java | 13 +- .../server/gui/config/AntiNukeGUI.java | 144 +++--- .../server/gui/{ => config}/ConfigGUI.java | 9 +- .../server/gui/config/{ => chat}/ChatGUI.java | 8 +- .../gui/config/chat/ProfanityFilterGUI.java | 66 ++- .../server/gui/config/chat/SpamFilterGUI.java | 61 ++- .../gui/config/chat/UnicodeFilterGUI.java | 35 +- .../server/gui/config/chat/UrlFilterGUI.java | 43 +- .../server/gui/config/nuke/CommandGUI.java | 67 --- .../gui/config/nuke/checks/CBEditGUI.java | 120 ----- .../gui/config/nuke/checks/CBExecuteGUI.java | 74 --- .../gui/config/nuke/checks/CBMCPlaceGUI.java | 119 ----- .../gui/config/nuke/checks/CBMCUseGUI.java | 120 ----- .../gui/config/nuke/checks/CBPlaceGUI.java | 120 ----- .../gui/config/nuke/checks/CBUseGUI.java | 120 ----- .../config/nuke/checks/HotbarActionGUI.java | 121 ----- .../nuke/checks/command/DangerousCMDGUI.java | 132 ----- .../nuke/checks/command/LoggedCMDGUI.java | 105 ---- .../nuke/checks/command/SpecificCMDGUI.java | 109 ----- .../server/gui/whitelist/WhitelistGUI.java | 375 ++++++++++++++ .../sentinel/startup/BackdoorDetection.java | 45 +- .../sentinel/startup/IndirectLaunch.java | 26 - .../trouper/sentinel/startup/Injection.java | 8 +- .../trouper/sentinel/startup/Telemetry.java | 20 +- .../me/trouper/sentinel/startup/Vaccine.java | 108 +---- .../sentinel/startup/{ => drm}/Auth.java | 28 +- .../startup/{Load.java => drm/Loader.java} | 113 +++-- .../trouper/sentinel/utils/CipherUtils.java | 124 ----- .../trouper/sentinel/utils/DisplayUtils.java | 228 +++++++++ .../me/trouper/sentinel/utils/FileUtils.java | 27 +- .../me/trouper/sentinel/utils/HashUtils.java | 55 +++ .../me/trouper/sentinel/utils/IPUtils.java | 68 +++ .../me/trouper/sentinel/utils/ImageUtils.java | 40 ++ .../sentinel/utils/InventoryUtils.java | 29 ++ .../me/trouper/sentinel/utils/ItemUtils.java | 214 -------- .../me/trouper/sentinel/utils/MathUtils.java | 79 ++- .../trouper/sentinel/utils/PlayerUtils.java | 75 ++- .../utils/{Randomizer.java => Random.java} | 18 +- .../trouper/sentinel/utils/ServerUtils.java | 111 ++--- .../java/me/trouper/sentinel/utils/Text.java | 29 +- .../utils/display/BlockDisplayRaytracer.java | 163 +++++++ .../utils/display/CustomDisplayRaytracer.java | 140 ++++++ .../sentinel/utils/display/RaycastUtils.java | 73 +++ .../sentinel/utils/trees/EmbedFormatter.java | 8 +- src/main/resources/plugin.yml | 23 +- 225 files changed, 7515 insertions(+), 3568 deletions(-) delete mode 100644 build/classes/java/main/me/trouper/sentinel/data/Emojis.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/data/WhitelistStorage.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockExecute.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/data/types/CMDBlockType.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/data/types/Location.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/data/types/WhitelistedBlock.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/server/events/ChatEvent.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/server/gui/ConfigGUI.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/server/gui/config/ChatGUI.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/CommandGUI.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/CBEditGUI.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/CBExecuteGUI.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/CBMCPlaceGUI.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/CBMCUseGUI.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/CBPlaceGUI.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/CBUseGUI.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/HotbarActionGUI.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/command/DangerousCMDGUI.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/command/LoggedCMDGUI.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/command/SpecificCMDGUI.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/startup/Auth$1.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/startup/Auth.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/startup/Load.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/utils/CipherUtils.class delete mode 100644 build/classes/java/main/me/trouper/sentinel/utils/ItemUtils.class create mode 100644 src/main/java/me/trouper/sentinel/Director.java create mode 100644 src/main/java/me/trouper/sentinel/data/IO.java delete mode 100644 src/main/java/me/trouper/sentinel/data/WhitelistStorage.java create mode 100644 src/main/java/me/trouper/sentinel/data/storage/CommandBlockStorage.java create mode 100644 src/main/java/me/trouper/sentinel/data/storage/ExtraStorage.java delete mode 100644 src/main/java/me/trouper/sentinel/data/types/CMDBlockType.java create mode 100644 src/main/java/me/trouper/sentinel/data/types/CommandBlockHolder.java rename src/main/java/me/trouper/sentinel/data/{ => types}/Emojis.java (98%) create mode 100644 src/main/java/me/trouper/sentinel/data/types/IPLocation.java delete mode 100644 src/main/java/me/trouper/sentinel/data/types/Location.java create mode 100644 src/main/java/me/trouper/sentinel/data/types/Selection.java create mode 100644 src/main/java/me/trouper/sentinel/data/types/SerialLocation.java create mode 100644 src/main/java/me/trouper/sentinel/data/types/Test.java delete mode 100644 src/main/java/me/trouper/sentinel/data/types/WhitelistedBlock.java create mode 100644 src/main/java/me/trouper/sentinel/server/commands/ExtraCommand.java delete mode 100644 src/main/java/me/trouper/sentinel/server/events/CommandBlockEdit.java delete mode 100644 src/main/java/me/trouper/sentinel/server/events/CommandBlockExecute.java delete mode 100644 src/main/java/me/trouper/sentinel/server/events/CommandBlockMinecartPlace.java delete mode 100644 src/main/java/me/trouper/sentinel/server/events/CommandBlockMinecartUse.java delete mode 100644 src/main/java/me/trouper/sentinel/server/events/CommandBlockPlace.java delete mode 100644 src/main/java/me/trouper/sentinel/server/events/CommandBlockUse.java delete mode 100644 src/main/java/me/trouper/sentinel/server/events/CommandExecute.java delete mode 100644 src/main/java/me/trouper/sentinel/server/events/CreativeHotbar.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/admin/AntiBanEvents.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/admin/BlockDisplayHideEvent.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/admin/WandEvents.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/extras/ShadowRealmEvents.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/AbstractViolation.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockBreak.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockEdit.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockPlace.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockUse.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/blocks/jigsaw/JigsawBlockBreak.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/blocks/jigsaw/JigsawBlockPlace.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/blocks/jigsaw/JigsawBlockUse.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/blocks/structure/StructureBlockBreak.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/blocks/structure/StructureBlockPlace.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/blocks/structure/StructureBlockUse.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/command/DangerousCommand.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/command/LoggedCommand.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/command/SpecificCommand.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartBreak.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartPlace.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartUse.java rename src/main/java/me/trouper/sentinel/server/events/{ => violations/players}/ChatEvent.java (54%) create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/players/CreativeHotbar.java rename src/main/java/me/trouper/sentinel/server/events/{PluginCloakingEvent.java => violations/players/PluginCloakingEvents.java} (59%) rename src/main/java/me/trouper/sentinel/server/events/{ => violations/players}/PluginCloakingPacket.java (87%) create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/whitelist/CommandBlockExecute.java create mode 100644 src/main/java/me/trouper/sentinel/server/events/violations/whitelist/CommandMinecartExecute.java delete mode 100644 src/main/java/me/trouper/sentinel/server/functions/helpers/AbstractViolation.java delete mode 100644 src/main/java/me/trouper/sentinel/server/functions/helpers/FilterHelpers.java rename src/main/java/me/trouper/sentinel/server/functions/helpers/{Message.java => MessageHandler.java} (57%) rename src/main/java/me/trouper/sentinel/server/functions/helpers/{FalsePositiveReporting.java => ReportHandler.java} (81%) create mode 100644 src/main/java/me/trouper/sentinel/server/functions/itemchecks/AbstractCheck.java create mode 100644 src/main/java/me/trouper/sentinel/server/functions/itemchecks/EnchantmentCheck.java create mode 100644 src/main/java/me/trouper/sentinel/server/functions/itemchecks/EntityCheck.java create mode 100644 src/main/java/me/trouper/sentinel/server/functions/itemchecks/EntitySnapshotCheck.java create mode 100644 src/main/java/me/trouper/sentinel/server/functions/itemchecks/EquipmentCheck.java create mode 100644 src/main/java/me/trouper/sentinel/server/functions/itemchecks/InventoryCheck.java create mode 100644 src/main/java/me/trouper/sentinel/server/functions/itemchecks/ItemCheck.java create mode 100644 src/main/java/me/trouper/sentinel/server/functions/itemchecks/SpawnEggCheck.java create mode 100644 src/main/java/me/trouper/sentinel/server/functions/itemchecks/TrialSpawnerCheck.java rename src/main/java/me/trouper/sentinel/server/gui/{ => config}/ConfigGUI.java (82%) rename src/main/java/me/trouper/sentinel/server/gui/config/{ => chat}/ChatGUI.java (88%) delete mode 100644 src/main/java/me/trouper/sentinel/server/gui/config/nuke/CommandGUI.java delete mode 100644 src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBEditGUI.java delete mode 100644 src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBExecuteGUI.java delete mode 100644 src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBMCPlaceGUI.java delete mode 100644 src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBMCUseGUI.java delete mode 100644 src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBPlaceGUI.java delete mode 100644 src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBUseGUI.java delete mode 100644 src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/HotbarActionGUI.java delete mode 100644 src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/command/DangerousCMDGUI.java delete mode 100644 src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/command/LoggedCMDGUI.java delete mode 100644 src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/command/SpecificCMDGUI.java create mode 100644 src/main/java/me/trouper/sentinel/server/gui/whitelist/WhitelistGUI.java delete mode 100644 src/main/java/me/trouper/sentinel/startup/IndirectLaunch.java rename src/main/java/me/trouper/sentinel/startup/{ => drm}/Auth.java (75%) rename src/main/java/me/trouper/sentinel/startup/{Load.java => drm/Loader.java} (51%) delete mode 100644 src/main/java/me/trouper/sentinel/utils/CipherUtils.java create mode 100644 src/main/java/me/trouper/sentinel/utils/DisplayUtils.java create mode 100644 src/main/java/me/trouper/sentinel/utils/HashUtils.java create mode 100644 src/main/java/me/trouper/sentinel/utils/IPUtils.java create mode 100644 src/main/java/me/trouper/sentinel/utils/ImageUtils.java create mode 100644 src/main/java/me/trouper/sentinel/utils/InventoryUtils.java delete mode 100644 src/main/java/me/trouper/sentinel/utils/ItemUtils.java rename src/main/java/me/trouper/sentinel/utils/{Randomizer.java => Random.java} (81%) create mode 100644 src/main/java/me/trouper/sentinel/utils/display/BlockDisplayRaytracer.java create mode 100644 src/main/java/me/trouper/sentinel/utils/display/CustomDisplayRaytracer.java create mode 100644 src/main/java/me/trouper/sentinel/utils/display/RaycastUtils.java diff --git a/.gradle/8.5/checksums/checksums.lock b/.gradle/8.5/checksums/checksums.lock index d435398593c920b1deb203f7b9044e942032ef34..2c1b8cccadce4ffe23e20ce00e3bdd5b2b86f6ee 100644 GIT binary patch literal 17 VcmZSfyWC&;{v_{G1~A|W0suMG1n~d> literal 17 VcmZSfyWC&;{v_{G1~A~-0RTES1yuk5 diff --git a/.gradle/8.5/checksums/md5-checksums.bin b/.gradle/8.5/checksums/md5-checksums.bin index 1327cc1314a80b244d8345ec0dbb9f25275c9f0f..98a8e56fb7f0b1d2d2f7edc36000d9a2d80e1012 100644 GIT binary patch delta 4147 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 O$$Xy(ASt-w#D4*CWeDH^ delta 404 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 delta 1454 zcmXw3dr;I>6!yEAEYgU?jJqo(0t2FYgh0h8zI7;@#)gMFq@y$aktU9b1!j#P zuPYo8U67SWDkT1p%P7V&I7p-U0+z+pOgB=|L@CrMq_dL0KhB(U?>YB7-*?V#4tYPh z?sY7}fKzg@!KkGhbdXk~L0&LArDI~ewD`wsMgCzzI^J-~wn^36&Vcbk)&{1@=%9Gn z5M+`=K`B~J@DGq5hRl}@AxUz2=oZZpx)gFwSc+^2%ae&y4Kin{Q>ITd%eHAIE#%FE zLf)7juSL&bv_z9xR+^es0|_rs_g-Nj&teg;J}nkuf~rb{nI4u1Cq3CO@~HFxnb3@8-_+3@y5*%kRnZ9XW5A(yD4+|6hcv#q}=!i%mpQDU3e3T)oYD6*x)C#>?QY*?aW@_;$BSiBX{*}=mm`g|a_H$b8w|9|#>IU$E?#{I=9ujEpPT#=$BzJ52ZsOup8vXOf23{8 z+L80Z-(H>%PnlWJvMywJ1Z?JMdaDAp^+uW+^aih%WgMI;)CWc( z!i=Q9j1cgJC8oa~))V!lfC#qIU5(SmKrR|DHX zqp3UtM%9r4PtfB`rljq_BWn5v^OXNiCaV&@#aB>$-yv4e`&r1NzAUEOn$6S_cJmeW zalo6O3EZz#9JB zz^aEU%%d!Ye2QqqX4Tq=b)dMDFsZDQ;6IO}r_fLJP0TC%w0lW*G{d0kn-R&Pwy+z~ z;#MrXl_ltGWy;siU>Q_gn_Hc0XPJ0?J95>^pYRN{;T&hxpJU0w&vWS~=g}xA{38Ea zn=bO<>z7#UGnY^%6t4jf>g5VJ-P2E_H4YYo2j32Aa`J@M^zcO5e3`_nSJ^~eWrbZ= s-R;cv<8@enojd>h25yPa*>!1VD0m*3}lC& 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 diff --git a/.gradle/8.5/fileHashes/fileHashes.bin b/.gradle/8.5/fileHashes/fileHashes.bin index 90adcf03c66a27024c74837280b6a70206d2aa2d..61e328fd3aed3ed0ebcfc074329755a06a721950 100644 GIT binary patch delta 54673 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 diff --git a/.gradle/8.5/fileHashes/fileHashes.lock b/.gradle/8.5/fileHashes/fileHashes.lock index 37407a231e9a1ef2546b14298ad13fb49c407296..6ab458c59e0bdcf61d90034de27f39ac77508e3e 100644 GIT binary patch literal 17 VcmZR+zH2oTgY?$~1~8a*8vrse1tS0e literal 17 VcmZR+zH2oTgY?$~1~9M?1OPCq1N#5~ diff --git a/.gradle/8.5/fileHashes/resourceHashesCache.bin b/.gradle/8.5/fileHashes/resourceHashesCache.bin index 5db5d1b1b2c40c51bb26be879567c536959aa3bf..abff86486a9d5aac21b420675214f7500e3467b1 100644 GIT binary patch delta 69140 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 diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin index 440430721345330102a73ef0d5d18c03ec97b0f0..a1bfebd73e20f9a9da12114e82597b999c014052 100644 GIT binary patch delta 665 zcmcb)g7M-S#tkMCj3*|WN=)Q$j8iyZ?=8jv2BC(N_eun4^iRDUeLd^sC8*%{|4=YN zcS*tKb%lOV@%58gB^3lFo(W(`KE3!DL@-Qxv#g{8qrj?XYq^S>TOuK%A-^YwN(BgP zGT+YJzeaQ_RPg%bxl%I(cHg_9@G~ck4=NbBSyx(+k-zHHvzZGHoq!02@=i9D*&uLP zp!)RwCvOFzDqn8CE3=SMUGMtAptnxWP?ZN^Zhh)8Kh4@t`!`hl^5jyv83L~@VspMS zER==_hUrdbl@Abjzv)5ifr#{Fj9;ak2% zLCt(R*;ZkL09Qz(gvHha(7+0_pZrw8l3zopz?Hqp0IGZ8W>G~=Mt+4=OAAa}w4k~} zCz~prbsbPE^bu(A->VTgLSi`hXl{$dVk)@4MApbEZ`M2xxv!|$OmDS$q#(2 E0ko;)c>n+a delta 133 zcmV;00DAw?tO41m0kAX}0j!fX7?G3S7)O&;85pyL88`ux)fz~XH5-t#-5Uu3ll~m1 zv%MXe0h5LwkCQDRNRy2qsFO`0NRzE0sFQ6XF|)QJAOVx^Bfyi!BuBF)B~byBjwbJu nl_wap#V6qblfEezlkF)OlT9kJ0uV8=A@~=uFz5%fK}Z-34!boP diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe index c39cd17dcff7d50bd22c463d183d2cbc50e11ba7..a511867eab0c3bca648a7f24bbfa235a81659e2a 100644 GIT binary patch literal 8 PcmZQzV4OPr)2|}{3gH9Z literal 8 PcmZQzV4P|a>dFlO1zQ1d diff --git a/.idea/modules.xml b/.idea/modules.xml index 7377310..777feb1 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,9 @@ - + + + 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.gradle b/build.gradle index 04dbda4..e61ae50 100644 --- a/build.gradle +++ b/build.gradle @@ -3,18 +3,20 @@ import java.nio.file.Paths plugins { id 'java' - id("com.github.johnrengelman.shadow") version "8.1.1" + id 'com.gradleup.shadow' version '9.0.0-beta10' } group = project.group version = project.version -jar { - from { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - configurations.runtimeClasspath.collect { - it.isDirectory() ? it : zipTree(it) - } +def targetJavaVersion = 21 +java { + def javaVersion = JavaVersion.toVersion(targetJavaVersion) + sourceCompatibility = javaVersion + targetCompatibility = javaVersion + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) + if (JavaVersion.current() < javaVersion) { + toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) } } @@ -36,76 +38,91 @@ repositories { url = 'https://oss.sonatype.org/content/groups/public/' } maven { - url = uri("https://repo.codemc.io/repository/maven-releases/") - } - maven { - url = uri("https://repo.codemc.io/repository/maven-snapshots/") + name = "CodeMC" + url = uri("https://repo.codemc.io/repository/maven-public/") } } dependencies { + testImplementation("org.junit.jupiter:junit-jupiter-api:5.11.0") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.11.0") compileOnly "io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT" implementation 'com.google.code.gson:gson:2.10.1' implementation 'org.ow2.asm:asm-commons:9.5' implementation files("deps/PDK-1.4.0.jar") implementation "com.github.retrooper:packetevents-spigot:2.7.0" + implementation("de.tr7zw:item-nbt-api:2.14.1") } -def targetJavaVersion = 21 -java { - def javaVersion = JavaVersion.toVersion(targetJavaVersion) - sourceCompatibility = javaVersion - targetCompatibility = javaVersion - toolchain.languageVersion.set(JavaLanguageVersion.of(21)) - if (JavaVersion.current() < javaVersion) { - toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) - } -} - -tasks.withType(JavaCompile).configureEach { - if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { - options.release = targetJavaVersion - } -} - -tasks.register('copyDeps', Copy) { - from configurations.runtimeClasspath - into 'build/deps' - include '*.jar' -} - - processResources { - def props = [version: version] - inputs.properties props - filteringCharset 'UTF-8' filesMatching('plugin.yml') { - expand props + expand(version: project.version) } } -compileJava.options.encoding("UTF-8") +task cleanPluginYml { + doLast { + def jarFile = shadowJar.archiveFile.get().asFile + def tempDir = file("$buildDir/tmpCleanJar") + tempDir.mkdirs() -tasks.withType(JavaCompile) { - options.encoding = "UTF-8" + // Unpack the jar into a temporary directory + copy { + from zipTree(jarFile) + into tempDir + } + + // Delete any plugin.yml files from anywhere in the unpacked directory + fileTree(tempDir).matching { + include '**/plugin.yml' + }.each { file -> + file.delete() + } + + // Repackage the jar without the plugin.yml files + ant.zip(destfile: jarFile, basedir: tempDir) + delete tempDir + } } -tasks.shadowJar { +task injectPluginYml { + doLast { + def jarFile = shadowJar.archiveFile.get().asFile + def tempDir = file("$buildDir/tmpJar") + tempDir.mkdirs() + + // Unpack the jar that has been cleaned + copy { + from zipTree(jarFile) + into tempDir + } + + // Copy the already filtered plugin.yml from processed resources + copy { + from file("$buildDir/resources/main/plugin.yml") + into tempDir + } + + // Repackage the jar with the updated contents + ant.zip(destfile: jarFile, basedir: tempDir) + delete tempDir + } +} + +shadowJar { + archiveClassifier.set('') + minimize() + + mergeServiceFiles() + + exclude 'plugin.yml' + relocate("com.github.retrooper.packetevents", "me.trouper.sentinel.packetevents.api") relocate("io.github.retrooper.packetevents", "me.trouper.sentinel.packetevents.impl") + relocate("de.tr7zw.changeme.nbtapi", "me.trouper.sentinel.nbtapi.api") + } -task obfuscate(type: JavaExec) { - // Specify the main class in the obfuscator JAR (if required) - - // Path to the obfuscator JAR - classpath = files('obf/grunt-main.jar') - - // Arguments to pass to the obfuscator (e.g., input and output directories) - args = [ - '--config', '.\\obf\\config.json' - ] -} task copyLibs { doLast { @@ -143,6 +160,33 @@ task copyLibs { } } +task obfuscate(type: JavaExec) { + // Path to the obfuscator JAR + classpath = files('obf/grunt-main.jar') + + // Arguments to pass to the obfuscator (e.g., input and output directories) + args = [ + '--config', '.\\obf\\config.json' + ] +} + +tasks.withType(JavaCompile).configureEach { + if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { + options.release = targetJavaVersion + } +} + +compileJava.options.encoding("UTF-8") + +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} + test { useJUnitPlatform() +} + +build { + dependsOn(shadowJar) + dependsOn injectPluginYml } \ No newline at end of file diff --git a/build/classes/java/main/me/trouper/sentinel/Sentinel.class b/build/classes/java/main/me/trouper/sentinel/Sentinel.class index 3c6e291d525530a6a8c969fb5c801df6bf33e267..874a30059d7b416e64020d11c0233a822796ebf7 100644 GIT binary patch literal 5494 zcmbVQ31A#m8U9|b*=(jw8qRWVvCt$vrf9jEw$L0vXquKLq_m~NWcOt=&FsuNGn$WCxMVW#ax=W=9$=SYV*l#8Eg};P@xfEl}rp zqrSi~ZLxjVrrp6Pp!v@B;S`SL{OB6Hmu81e)ov#>Gb4vo)a4JtEIFF0ACtP6KBVN^|X;PzDo3R2fSXj_=x$<9i|Z zradKt(28-|y zU9mfrsoj2|;Cn{|;loTfK;l{b4SlCD6b8OeyJr^>1Oq~-^4}? zkOgy!FwfaNX=TUALz0nsBavNnEXoU{i-AJ~v%s3NuFRwzdP|~=GN|1m zCl&)mLhjUx%uFB!dnzM?zz;HhDO#a;a*K&UMUuv(9e8vuh0D0u)zS5ANMkE5H?U2` zwVkfclAROncEaKmdCMy1#;wgg8wAdni?lV_T^ z5?2YN94(TS0;lxPwceFUB)AktOk_dwY~rqrb*qUP?#RPVO=0v9XL_7KD$X`A_5}5; zB9TTOwaVvl6D|t$Iai+hL_Sw$vc9nZWqPD5bpp1?(8-l-H`C?%+3`#dlgkYrrtvJ$ zkI+PflE8vQUFgy^I3zxeYS{uRf*?dXDbgruQTy_+(0)Xsp3>Pne}|MKNVZIS> zGVo>-Z^4ZM$IS{?>81yxj?3H#oL4?vXb0xdm^4_WL)&_riMOjj8zNt=go^PUCf+P)udFA0nP5l%_cs8 z4|2su{h-&*691W`k(n@NCaLpZ9v?RG5k-6ruFceqsVey~6CYPyjZ8+t@tDIDZWZXR zY}yPlk;yrsJ>tsTqD9CUK8WQG+g?c|hWqmJ5vt%rt0WV=pQdLe5=z2|$XM$|Y(@<7oK zXhQImTi5O9*xlUEtII&CFd~B?1yZHY&)V*=9XP68?yQT(96HqCd+PW=Cw;Hii{@Wu2U$2RXAPK|GqVsY551)JE2=wmDU-*j;?kv^7b5$41uC%(BCYRjpyC z5}!91@$RzGE|#Nf^gWiMI2^^fmGTuc=3JGvB0*&)C(J;R_l~)4TS>2HWEE^VzjjBxf(Km9BCJ#S1No-}!-L~rv@&ux=)aUW?(e2t{ zC__Uq@uwe>lexk(L@SSChZiMY;#}ZNE^ZA^2mCohok96lqt11F3Vaja($8;~pWi7z zzgvEO58vl_ijw>th2KA5%i>e;S>Le-;#R)Y;D`KH!{Cn;kMP@!1@I$nPTX`NA3^dY?YP4Yu)3Fv^SjU^f`3YZ#W_FW`25^pNshGPahWX2Dbzouo45z z)+T1@QeKd@uy+WT@!wUvHaWPQz}bcpw&Qy2z-_n!cVQ>Jx)S$O;t^`?ppEPKd&N)j zGrlo<<;)f+|8x9;t6yU7f62ER{EFkp@)?i)dS>i5?E5XpT2Z_5F?Fxpt^cllls*gm zuChYZ6QgdC#==K+9&f3;3%6~n<@+7e*gH6lyP4QoXw2uL0+JI)k>M%t5w>EuE-E|P zsP0@eqp*GwpXyV08+P8pr$|737hAu_AIg%myMoD9qRCz1CCt>%AnlL%Q+e_Q%#_-q z<^kNdjTl+F2cJDyN1%16Y$=E1ou%IX%&jU~(u%yx+Rmkna(jNqK75Xq`b+!pl^UL9 zt9{y$@M%8&hQD*X7XQ#v|HQxeq%C2v?@|04kLkC^g%I^(KF87!qFF2uM`7Xr0RK2J A?*IS* literal 6168 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 diff --git a/build/classes/java/main/me/trouper/sentinel/data/WhitelistStorage.class b/build/classes/java/main/me/trouper/sentinel/data/WhitelistStorage.class deleted file mode 100644 index d72fab449621e306a037da2be97281eafffb85cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1152 zcmbu8TTj$L6vzLwyINSg%en&V1uuw~y&&V|v4li1Jec*C8VJ6aZab7=yX~f(Y9M|n zeGrT!egHp|@l3m-AQ}?WbUHmfmtX&9&bRMhz5;lN2W1pcG*B{e3a1%rTh3dD2Ttho z=e;fADu&aGGL&kGq1bG#8W>|}XHKb-0e7R&P2*UEiZ35*yE5zx?`0}dQHFuyIL^Q{ zP%%-(1Vd{exQe56C}N&aqzpyCJx4iwZBr@{$VAyHiXC4tOuqYR65jID$B%-@?I#TL z-IF6&2F@~=S%8fAiVTSA93~A+nV3d{Vdgj>dql=i)@&NFn`OWwpA=c-)gOD|0^c0YO0Da)C)&Tn3&8Y*FsbXqhjwvj!mhoxtD|C{>xLASd9q2WK2 z_ybRCP`0Bqc16xOb<|}+pH@=@rX_93LMcM(i_8wS! zAIAC~#y_F_G4pQGs;6A9CDhTTotgWigL!&mxP!(hmrXv}x3Q1f`e)SZ=l5{2+u6nC tU0hr1e4tMS=#*6|3RLK-Vv68&+UY!Wl(s?z7r-fTVFZ2`_pUsq diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/AdvancedConfig.class b/build/classes/java/main/me/trouper/sentinel/data/config/AdvancedConfig.class index eed028c7ec617b428894bc90c9a749e6e96e1ad6..3519414fb39dc81d29ffcc46030455bd174257d2 100644 GIT binary patch delta 622 zcmZ9ITWb?h6otPtGnq8wM98!jf;H_+=!E!C}HU z4VNZI6a(5RwbE4>Rq@!M{nNd$X(T1;85YB%Ox%=?Ab$=YS>ViBcqc&Tor9LTPjTyCbsV^ zm9p6co4M|CgPYM?YtXu_@T0Wtx6RmhRlFrZ3Eh(qQfy(X>~{nk+!a(@#V1$sgOva2 z?_mm#Vap!1hvS>n(tR=NmlC*GtYd*Oe&8#Q-8|@4m81^xkVnE4k6F~01s@Q-A?MI% zD*HY%-J$lL`3?)-$q$^)k9RoR;X-lzooEbn&RU6wl;nA2=+*x7Lb@h@1Nw1z!c&Rp w;o0(>awQ{NFWbA)=2}aT>Kf{IKvD)-{*ULp5N`02S8`g?-Op=5P8SJ)Ke};qB>(^b delta 456 zcmZ8dOG^S_6g}TKGw3u1O=S?FtAWx@i=d!YMA1SlXd&5Rs5B9^huZsL@At!)KcWa0 zMg4$QZQJ%MT196xTKFyQJ?B2oxqOw5S3`UMd42;hf`^Ls<&w0x{VDrJ*8Y}z)MXtC zR_tyi7|!X)BQ4-fCJIw?xx`XoJe5gkh+|F1IyMB6V?; z2L&PA*c4BTh;16w5#p5RHlG*b-PhWWkplMyY#B9{DE zRZhp*EgLR0m@T&RxfbADb$+kR{Y`yC{|*lz1~Z!@j!u9YSSf zD4DWE>zH9_wl*QGwh_zF6=bldq7eH|*4NzrN5!FM;VFBPCO@oOO9_wkS49rT1UWtW zTQe;}L1WV|KU_l83CPi0XJJ3lKW9;{37+r|N;u_3Y%DXL<(}k8nmXr;3iud{87rY6 zgaE2=!5x)ppcMY&l23#yWIZ9*1I(3p4__ej^AwJYnPV)tSgY7R!EU_8#es{XzLrDg pNboXBQNS2r_cGL>JntWZ)Zm$X1cW&J8aEsv;eFm)+~p1K-vClzg+%}W delta 313 zcmXAjyH3ME5Jm4ev5gjoyhKs}1zkdlNNK6kq)LHAfq-fyTgYIDFcux6ri|PqKzP5O z2}TJ5i4Wip_yB7D05Nuoy*sn_&YAtr{-nj%-}@(kDqeeHIX*2+>B(BdF)f#nDirIh za$A<|M!9A?*6axO{Y`aJJ2ar8U~qUOJlYgU6c&k)CE`R8^!$S>_y(E? zxuM7h@E3dr4Ps)NIrq+t?(V#rkuhJtpCjOeo1ytp{zMo*ML`%V)NLL^*<+KEVrtOo z_3OQEH;mik{c>?TFM>h60~Kkjr(xt9u?7{?~xPC?}p$6fJj z@|flTMUj$dHO|i{c^RurXH?`kl-(n7Z=$s@NbcSBf}65tosYU0C&P3-Fyv*QmE=F@fBV&^*p^+s+lWWu; DqQxz} delta 272 zcmXAkyGlb*5JlIVdjiQx5V6og5p1H~s4+%!eZ@zjFd*DOY3nErsl;-5=EZ-R@nlCCF_CM=8e^MIk$Q zc)aiQnw?gAlxEx4Ou<%iL~lcLBlbcF{bCUMuV6{ie$K8&yBciRL)f6GTK;kpMoc9X zQta!_fw~vfMp^Ywc6}j;Sqp8pUfvsjxy-yR-kN9C55p fLpaf&gqrG&raNh~ z>}Y0QC&W5BGvcGvv^}7p3)fU!#|?t3Ng1}DcPzWADd`Evwzf8PJ5kn6$1wF`;!H6R z#7*@2aZAN*+#xjmPfU;ui_o0;SG4n~ranE*L-b?7k3khl3=uBw>_&s>X+o_#koYFG zwB99z6D7?sujDOr)mXc_UbajH?HE@vaA(YX{rhM>mdx!m0B_)IPrkGt3UP{lN|!t}JzjCk)k-|$$)9I}MKR#~@A zt)%-ge{ke^NAen>uX^piI=jfd9NqTAASA2N26=-pT=l);*c+Bhg#NR2($^LZk81gi z70a?yy2H0_SSCRpD`<{*<4c8~z>lCi4Znk+JB0Y@>!k(Z^`~f2O!`~5mpG$>*!JKl zmYIZS7-GA?{xW1p%-C17FCL+@g2W+uD(H(0f5u3ryMj~&_vX9zSR-MPZ5=#p!)Qc= zA6Grx`i<2Zr}Z15;5l;KA%GX#@SV15XS!caWaft><7W#&55VHTNN Fpug^(tD^t_ delta 636 zcmXw$O>YuW6o%h3j0}u}1sXLhgqjp-MQLfp4=l8_Q2SkM?FY4rBiz=pGLyi#GwI%? z_a~U>whc)&HSq`dJKUJKaOui;rXRC7&%NiI`=0mC{T=-q)_?pv{tlpookP7!AC!?$ z481`FaLqs*2|2D5)a!&XNlIG&qDYU9zSD+&14-PFpJ+qAQ;Le-l%IX^#4sT;)G{sm zcEhoEtliA(j$`XcVZ^`%jLM(BoAWsXcab54c7)}QUMt%lPSY{C_74Nv8PzpOqZl-TA5~LE|A!t|IEr~JFbRvu zaGc@wDpV-UtKX2SeZ^pO=nJw7=_A}Z!o7|30hbiaa=Zi|#|T1*;xc;S$zC}$^bI#^D8|FE6)`>A+F#DJ$bDc9UnVZZ8BE$z{XmVnkDM0@L+XQTD diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/NBTConfig.class b/build/classes/java/main/me/trouper/sentinel/data/config/NBTConfig.class index f9c5cfdd9e5801ac4c275e4c8eca5e397d0e7c47..a6c710fb8dd838bc3040f34f68d7c67dbd20bc9b 100644 GIT binary patch literal 3313 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 diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/StrictConfig.class b/build/classes/java/main/me/trouper/sentinel/data/config/StrictConfig.class index fc1f0f2f789d8653bbc9261759082e5978e02c48..38035728e41a432be9f50cfd2f609a58029ca69c 100644 GIT binary patch delta 448 zcmX|-NlyYn5QSe22w`ZKfQ}K@lR*h;Ok5CmaY0eQeP4nTGcH3iGk1T)+`Q}2NHn9- z#G4my{vl%xiidvPsebjn>b{e2g#7va`VL?U$0hlLLcufDO6#U|q8C&`N3<(rm#C#Z zDWeaA0z()kxSo_|s0FKN{Cnz^N+}t@2$Ft`3XEagzNf~z$`wJYuAs}M^(Bd%G*J2b9jwkE@7P)G114mVJE!# z>rJkQ;AIW6h9RRKwP?T=R|LDjb8lmZcfu}GZXcib*(>qh@rL-$3wj+4JR#v=A~gGq x`Ap2gqJ!nF*aLecxEVF@lvQDR8|un@1yZLA`3q1ue2>xmb@(0k0EcD3(GMDyQXT*R delta 323 zcmXYr%P#{_6vlt|Rx7!t#dswWRu&jFiPZbmdep0EVqOL@B7ZVweF4i`g#2T3X~tsr_!bxzU#sMDTU=Wg1UbijGs5IYlbW{ z7D;BqZ#_COZ?Qm5kv{W+@=4vR1x5eTi$qHn%j6ZQtBa~%ZzvMOBgd_*Je3o7$}NyU zTx4XUq7yr2hcWrBO08Lwr({n`a6KGKR&I*Yw`mb<6YRp!O*=hoNXHTtQXEX=v);u#62Jo7f`IJl$bQ2q_Wi-&12Nw>nJN!7htE9{WhDD~!FWJR;DABLt1= zn$cnP1t|3pDHcaOj*%vCstIGg$rm6nYb(?|<8qu5@NM#>5R~NBQ8^-VSVV@00q1I+ zp}GB%$CVb<0)0VA5S5P8?A@H&>-NBV{u>s@fg)-5)>1 C-*ig= delta 324 zcmXYrJ4*vm5QV?Ho2+tKBW{d>ptTw>pufPz%0jRZZ6uII;v+&fVCj#@Z?IPac^HuR z*2MUXkEo@8!B(8RTHG@;cg{I;+KFylH-8R~fLT5dbSGlzOtBRm8#z(9sbI%X=Yy&B zU^}O&apn@`yl6X<)0Zw+G!%VneqkY<_j850?51x>O_vro3TJC0lg$STdwgQ0huhMs zv!_yRY(zy&E-D7_!djs!)1CAd_wtn7aReVqGriwx_fZxe5kW2J3C8JXkSFqCm}EjSnN$feHnu~5($IF) SEH$sZiL><|v&(yE@bL?KiAAvh diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockEdit.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandBlockEdit.class index 51427e169db8a93e3d8042dc26b06edc8b0cf064..b74f19aa266aec7ab6ab9fc9e9e60ed4cf4a502d 100644 GIT binary patch delta 33 ocmdnQxruW_2#aVj0}De511Cc%10O>fgD^umg9Jmxfgc5Ph4tb&{GiG^MnBw*=JYfT`45Q6(@%AccaU$^nVP#valw_HMP_Ah`7( zK;f5gsE~T#2k@g1vsO>2ms~vaJTq@*=gptLXTJeF#{Cv*sD}t5%wSfab0|N_IG1`H zzZxA<>I7yVDXrX&Kz)5<*hB-(5UmJvhy;>?VrR|dh-_Svc1lwoXVS?yHTr`Z$3tau z>6Fn)g?ou93aPUvxk*3nex`KdNFXFFM>%Bz;+=2Mjxg`pql`*s|59c9z~u-FKG2rV zQE}wnaNJn(p~VPSs+!!4_svsPrp7Yi*7{(|5w|yn0zqOj9&CQV589g)BeMH`LV<;W zNo78iR(X8>GjIp06xf}%)kWLb=7oE@-a~=SX$iTw^OD?w$zDq<3vy(6uGU*Ku_--M z{sD{sDe}MzF3{NHRX#VR3vpK@Zm$(f0$}PU3i4(N( x9bNpuN)@X}a1GZPF&%E;CXaB7{TTrqcTmS|1~uH_S-o9Dk8O*S8tmVL_y+JgD^uQg9Jm<dgD^ukg9Jm*b delta 33 ocmZ3_xt?=FAd9FA0}F#I11EzU10RDsgD`^!g9L-;oziXVI((W|y;T zyAfJx3)8OYpwoL+@ldy>hqza6RM+=uY;+M81DZj!LT_hMqknSZO3oO}$gCnP^BR*IipX=qD`&iM&N~-;Fe_^8FP1sxWs?FduqZW` gga;MW9!3eUEStd!s|8H5E@;b3iX>@qTf#O`zgk30@Bjb+ delta 396 zcmY+8xk^J}5QV@0-kZhnM?;E~B9<3L5UgBd;=Y@>#5JxlAVv&sm5QD1-`EHuHg<{x zMbHPZ5PX5OzJ)qBRyK3a%$YN1E~>x6q4)2XHy}>#A#|_8A=+pU(4pz1%am2Wzgv?a zX&S8hmR?O1T}6<7%>Y`Vw+p#+A(=0(753IWVMsIV3I5&voqTaC$f!BDZvEq$36hVE z_O((g=EF%i>xvHZ=`@);yT+t^%^3+nF)6=Tq=ZS5aOLGxP|j9R?)=+R_(+RqO4J|W zmf)lNgmRq{0XGseW#8G!Gp!}!-l)ebeXd<%&~;u3XYA-msv?g)h}B|RYApe&3pgxN z#S(@CE;;0iBW^fmM%36}6tiTclThZEm)91A`xKl;u*L43T&iQaLb9w1MrE`{mJQhs I3fn~e0%tWtX#fBK diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Logged.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Logged.class index 172f4cfc30ecec93edc707fce5606788832b0712..ad430f7f1751c08b77c9ec2402c572788766ebf9 100644 GIT binary patch delta 25 hcmcc2ahYSoS7zR83@i-S88{hkFvv07nasoz2mo~P2jTz# delta 25 gcmcc2ahYSoS7zQ?1{Q`o22O^0204c2$xJMP0A-~HegFUf diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Specific.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute$Specific.class index 4d029f670185becc8aa79d00ca5ebfc4e4bd7d89..3651003bba256d0a77d7b957a93194aa1493e59d 100644 GIT binary patch delta 294 zcmYL?$x6de6o$W>+|(q9NDCFjg*Dcp4t1zns|3VHaOJXAX)gvTG@+Y$XgM7MV_bld-Oq+DiNOp{^>zG_=AruZzk__jRE>Coe4K zQNMSB|6bv+AiG6L^h8_2m9(dW%aVQ|cWh@1M|Vuz5)6hNCBaEDg3oAP@OWj6H*&la x@WGO7@_#KXvm#FVSfwmq*F-xCuEw!h7h|!(=8!9F3uk4rBk^4ckGLXhs2>B4Fem^3 delta 200 zcmW-Z%SwV#6otQiJVG5PvY=_Gf0>${ywp(eWu&qA08PrvOwN=1ICJ30QSTx&FQmm` z!(NxY);@+!2;ZmO9+>b}hCkbm-P-(%5z~*0>9R3I6MMz}0oLpppa1{> diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CommandExecute.class index 93a845349297d408899f05720185c4e622607937..feb553dc9f72f1ec0e3dcd9e9a9ae60b953363c3 100644 GIT binary patch delta 24 gcmaFJ^^j}BXBOV|3@i*A7z7wLGRQD&n#7t20BTJKbpQYW delta 24 fcmaFJ^^j}BXBOTF1{Q`$1_6dB1{sFvNvw$gT(t%n diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CreativeHotbarAction.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig$CreativeHotbarAction.class index ac27fe8b230b36dea0c4299dd2c93b95384e44b4..8a24f383f25ea3546f96eded953346e34dd9326c 100644 GIT binary patch delta 65 zcmdnTxtn9cXKAVQ#N5=}{FGFM;?knB%(BEB1=WI_#LCnn)y)$b&ohZGWnf`g#=yz2 VoPm#F1%oidN(KprRg+6uJOKpP6vzMo delta 197 zcmWN|F-}536oug{QV0bNA+hr`bifW+01Y5ihFIM9IKafmotK#bLgf}ns9lAn30rV4 zx&fyAlkfj|y0tF6GxW>i@XiD7I zS#F_Nt8E$lAwtXKyp7PFY;ewR!OAi3Y3Cx6&zSktn^Td}c*Nh9d*~ g$rwi8orm8>Zz@fhNlWI^kyq);LY`%L{d=6;0nnj9tpET3 diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig.class b/build/classes/java/main/me/trouper/sentinel/data/config/ViolationConfig.class index 927e0f482708a8dbe27f9aebb0c78d4bcf9f3386..76b1f608d04ba6289f4dbd78ea2e9b891a12662c 100644 GIT binary patch literal 5312 zcmb_g`F9gl6#ibR1j2L$3T06clmgO-Qnyr~q!e1pW}qyhIGs$>X|vSHq^yd&xGUm@ z8}1t-AX5+&SJdMlvBb`t!Bb|!K=Y^4UGO4CwYZ7U#e{IaLf(Rh!%s540MMGzi=E94+ zCZhtC0aPidMvXwINDK5+8%9ZBO|d*ixD^gys=(SJ$>f>Y(Q8H3alJsWY_N3wXDUh z7;(w2Q?MTG0+n81z5jVFjWw>G1t!1-f!3mFa+6ucpuRqBb*pBJ=G=QhbhuK@7Y1qsOJ&r=TARr%)WGF_r>q_$(xwDvhj{4@sBo!wO7f^2vJ1P<*xL zBb&>F>=;fppBb43mP>WNf&)0nQ_UAwnFGb2YWXuTriTR-S=SrsP@9q9QTiy31n`)G z$MJ-~oI(dj)T`?Sro?rtJ(aQ4l*Tlj*RZ(Y=FjPe9pg_acpA^RLt71#ZA97}eP~W2 zJQ+vvTma82cmXet!YijNP-dhB<`yI)dy)3d_U!ux&jsSvz1Lu zTRIWbNw=Dw`lA+}gyRBrp}|~pv&`*w8v8QNfqw{ZIvveh0t*`Q$GK0kr{eZBQgJpg zj{Z9ePB^rjiMOaG1MV))A-t#HeSE;yE7>12OeQc}uiXKh6bN$(VhM z_}Towgroa()y%lfX?P|12BYb$sp&4|X~WvrMkfoH%$xL#wNZDXmI>gyu{RlBxv?-h zple2tp#^Yu>|JBdSZ6vO*JA;EFR*^pG%vhfxdt?6s+HZp&i0;bue7I9x*18RnT*cI z>d1($dAk4AwQiD4hW~wDhsGwV(0}r%wKv1|OJyt2Q_|4v%S)AB}EdRm@?ti~b* zswkEX`M0Mx$FR3KHu<`8?rPu3>kan5%CYfj$zhXniNFo-E+euegr6kz=!+QtB7%aI3d>^m#n`eTSIclu#T-mS1Ew>e zby&^7cQW)l`FSITSs1`<9Hz!m%*Anj=s3ycIK__!7rAVo(3au`2%q9JJ_&q|V{Y)U zhxW_(Zr)`a+I10+4B^T1cyAdX+k5p*7IvzGd~jSrB_>F zA&DNGr}hbI2RNTid$q(NVm*oN0 z6U=@A-#`flj3mZ*>BD&Km2uT6S7ws^t-b!W{xy4Me;@wcul)S;^&5Z*9G)mkjHSeT zM>3v1)U|_#uD}PS6Ds^10rWHUXjZM3Hw%+hORKbt7}Da{G2YeQj>)(xJ~%U{TaGki z6D>R8ak)GS!nRkG;|4~=g|sQf86LF?e9Lv36|?nQ9Jetl4A+`;hv6aVcZFM)$sl1D z+~;_JG4Z2gMH*+AqJo=ZUhY$qw)~jWVJ3wo?}Vlpcsbw9#|l!6GV*9PYp{PCt?Rr3wQGDkGaEE=U)xo(*XQh3JU!h+aPBcmxi z=U8evsU^L!nBONSdrS&1IF_-(&{3-tjQu);Gcuf$v37c{zGYI0A#-^!KA7FpHKS-~ zG7NF-nUyLGGnc9u@Wy(3QvZK;vF*JlK6{76FKkd0taYdYWPXrub^oAX@uG4@0 z2#F#<7Yn|EdP0!{Ia-I%DMCHhk2Wagb_F)EMLULI L#4-{R-hllDT7Z%0 diff --git a/build/classes/java/main/me/trouper/sentinel/data/config/lang/LanguageFile.class b/build/classes/java/main/me/trouper/sentinel/data/config/lang/LanguageFile.class index 634ef00a4938ae663790616cf2b3e0c0ab8874bf..d06244abb62ccb5738dc6c6d36fceef9ad605108 100644 GIT binary patch delta 928 zcmY+>Sx8h-7zgnG(c9da>vbG0P0Q3C5|d0SiD01A!kP*+BBU2dY!FFC8mC>;tZcC| zJ1yE~iF)}qoae9{XZeP82(IV&IZp*Hpqy(h>$%jsr*y*JP6JTK)!@s__&2NgXN|b% z+x5<7K;g_dpoxY=;^gVUc5A-8G;|rqx0+2)18~^|S delta 737 zcmYMw`%9Bi7zgn0k+;3>-Mg(VA`8(Vku}m02nA9!rN!ui5oG<4@xoTEmNPTEn62!l zcJG*(E?dj)TAN#G)9ik;{)VWZqCcTuqVGoF5AXANp68tByoWR5PP(+|zu74OUQDO7 zZ-p)`V6+&%NH`E|7MSf?yl%JAYIs5sk1rGsR+|w)*@H67P+0v+;~w#IZy$1I$dRTh^xo8D&W5aOErp|2O$ zWu>m?;N`*!t#+5uqT-go>Rc%&ZJKghz@INv zyG^+x;L8>E+{Us^O~zgocLmnwHze$fiXRB5p0Lrl(*nfE1v~QWYSS0|2UlDjMSa&qEmJ|heelMHm5e)%}3KA7zc0;=c&L2G%)z>TZoJK)!b|q6h@$U0->$r*fWZK863#qNET6G3@6hFrkL@t znM2Kd)l2LZ;4-e{y$VT}U@jWagI@HZA2AFfjs%8Kk2?5KgKGY+0_7-05$w3fi9(+H zkl%egWtGJ|_BABgDWWM_N~5Dp%9*5T1ps?q$Xi^~0L9=bqE&dCp5ufBgLN9Y6`sbR-z28?GHkVe8n9?4}#UUf}xn zK99K_zdv@Hc6sgP3qP#C+a@m!2GxXyB!gCdU0Pjdn5p(4P!tL&6KSE4+^TF=O4}lI z&4ejJ`s&)|PSvT1*mV>A$WUyeR;un)9EQT1o^lzma6?DN#2{``!F4|jT!z71zIwz@ zxb5@cz~0z9a_e!?z%Xv<7%`E>ZOZ)zPzhQMD&mCuEqCKB!(y(BUG{miS?mfpapVOD z#qJD3K2evL<-(Ci4S0dJ7L_4l9{ zGHY&p818TKh%m7mQFCg1=#fb~-a)|QRzy@Km#@+0EY zRCix_qV<99H7|&-zcNRIAEqBgT^SJ@50It3m6nzj$YjNW5<;VR4wHn>lQ&LQTt*vx zpD}O&>mzv#Q}j&AcM9RNm_|b47sv{RVX>)m3|)W;eBuiJF-6Qv6lPE$&!$@=+6Mup zwSpqW=Ey4aN}rk@J;UfHI{r^d$E@f=Dy0+D4xQzYB13$a{-HlOW^4*AVmqr diff --git a/build/classes/java/main/me/trouper/sentinel/data/types/Location.class b/build/classes/java/main/me/trouper/sentinel/data/types/Location.class deleted file mode 100644 index 4ec95478893b15ee69e766e43df5ddf4069f295d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1803 zcmb7FT~pIQ6g>-VQ%bP36fB@13epB7;bXOtN` z&hX$5@JBh`O#;LzcIv}sckk}G_uRW@H@|+L`~>hEFB9lNuY`zio%kXG2~ee#cWu{h&MY*6Ys2y zQH(LfUCZ~uu$(J($)%8Qry!hOk#Q9h4xxeO~w?iQ_c9Nre-({ zE8Tktv3gtHtY@l zBW_J6Liw)CL}GZ&-I`T>r`cMayWA#tCU-&gJZWq&42QXfa$wfq=N>G1up&cZ(`wok zzOD=I1E-Q%5IUhsp2~1=)v{d2wY7%N%8BDXL%%1h!a?D%aFk?_s0~m{QOhJ_C?v3i zWeF=Xp5ht97`l6UHK4$sW>s1ZnXQ&?qhLuh-M9^?JIm)W<=0A2(?CG|lShB?~aF`~Xwt zT9A}n3j<0v(n3nf##+c6({c}H=o=#_2ym0W_s9vsFpC`ZVkE+f^>Ht-ATur=45B1P zB6tjW8mA((6|@Po3|b%)y&ejI1*1jkg&RpZ!DZkZ4cnvpk)$Acir$I23*h6xm#*Mb zm=EAf6iiT$D|089{1<-2-*c9H9JjFG@$Ns%L-poBRQV36Qx?&{4Wp6>kSKym0Yni5 zc;IV?9L~W*7lKpWfk!R`XSxALaNFazh&$Ae(Q*6w^VR2T&sW}ET2E72Pe=%H63_!Y S#3MYWpB|7P`znf9$LJrBb7x2Z diff --git a/build/classes/java/main/me/trouper/sentinel/data/types/WhitelistedBlock.class b/build/classes/java/main/me/trouper/sentinel/data/types/WhitelistedBlock.class deleted file mode 100644 index 12d1d24594386bd83fe545c4582d647fe276ccc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2945 zcmbVOeOud96#rdlNhx8_@*>Q!RozA#HX=G-i?~6@hIVWY2Eu$x+dCSXHeHf}!+j+G zaL*&6?0NhEekdQ$y$Nlg70^F!Ztl%Fzw>@h{`&jyPXKrDGL9~ED~PCwqK9E{+t@L5 z%cyPXk9pB{N(?=-X3g~G7`oG$wO+)~t01nT4=RIR<+|tCjXHO9m)AVA#x1>Mc!uu1 zsB>3eFPk2>OxNS3dCM+V7^3!WjXMmMhg`7`kIcG2)m zyC!*)Du#qo5g}WEji?yK8)Sh|^voU3z&6G8xQdH{PAS^es!=P&aS0O&E~~hL6vN~> zWs)tswqucphSQmLYvOnt?8Cw$%l9( zT~=$jTpOta9RRT7@p}UWn6-iudGcFOEsv5;6aPVJzJ)D3{A+ z)&$W>0xuhGdBHAGhTQre;7bDQfr@23 zq+H|A8;0dFEdF<-PzpROZ1bX*&1^ChJEX>09ym&iYzQ)_vd#9Z%o-JqZ~s7^N_e?Z zEpTVmC|Kl?q`Qntx`{ji&@bXPNh`7F@(@WZWXisSeVA|9^6z#@7`n8+;NN6LG zeGF^k(S3})BAPBdre_cFi{}bG=X_qQ;t8DyMd~2Fr=%7_vy_;$=$3CnlZbp1xih zQ2eI|fEeOZ%Tb>>@C(g)Y+#e1d+`+C1S0OyN$5TC8kaT*@xwpx2?M>%(fXX$?E~Ct ziSV7!FX2_6k>I~2fCLSpQ~)|dy8uC_D_4+IOA`mjAT=v0YJ!QtX@<+FWf!#4 z($dJ?)ylH_y@+W7D$=qjv-`b#>Zx9O^u?9+KQmx!xq3Kf|1aPE_V@qxS!c(bqjS9n zkByB37jSsU+u&4Q9de$L@9T^vL-GD(B;FoXl+CLQ>;2B@I_C70)_ZASiN%F1RoJ*C zU47B^WTNjsTwtnHpTjajCQTO02`fr$mXoHs3P)E$af;0yChxBlV)``}5w6Y3 zT%1V6IuaY=y2erDYs#0AJ1nAHr++t=`md+Uq}yUGF~uxfrQYdq>lRm?J8qGnM-RHH zf_)bKBo*#tBAl=VIp22FZHu7_gAT3b=V&)-GdVQ{p88|ltd2p;*{8hTi#FW zJ^u2A&q&=X9hiYZ5zk6wW%f( zgSZC8;#3J?X0e7+dMOhh%f-bxyvnIeFqcCERPYC<@h6qaMU|St>8ge^Ge)k&Bfu@Z z#LE&*(9J87du;1ue+3dgBtZAn}YX+v3LQo>&l)_+figdsVHP%`bu_}rotp!X9X)9_q zDvBq-!|wr6@qmhFX&Y)=6~Pk@GwPi)I^KBamE)B+t?0K26fSoE?|;9;zVG|ZPQ>@j zaZjEaKMFMQ!=Sr7sKRDV@Dj7ZYK*SpN=>uJRjgLjnI@;s{AML1EnKZhdR#-RsSBm- zHjnFQH~T{g`+AQXSZ7X#nxpHvNz>tRGq)(JO_Md<>$m*!57ZZ<1c7Mt5#ZT_|^Dw{WM&86G2x$fTX{&ah~C%e73 zJJV%4baiAacWE*nS$fP)T_4#_PSfjANuN2cFSqVi)Sol!d^gN0?(x5JuW58vLTMJ8;_BVuIgqGWkd%H-TFQGh)JB@SWBd$0MWZ1&Qp1$QL_ zGq9-Q8Hv1NUpde6oZJ+K=lyyKf0vqwV3THH`4#Gz$&}B_FOG%AXi)4Y5ep5`SYTy= zHHiYQpID?A;o5Q5!YDV6ach9Vm}QMnF~Y{B&JgKJXOt~tYzuIV{$*~=Jwes!K|2cE zceblET(AevV6jll&5yG4Z24nnQiH-Wh_7zB=!co2c@{~kSw{`qxPX_K%|7OEkhvTo z&N%b>i zoU>%!C!*og9ETZP!I0b~9;VPCRxBBj5=Wphc|PMSxz);m7}I2*VOr!rIxd+3Ilq?k K8@}TNvHt<>sO*LS diff --git a/build/classes/java/main/me/trouper/sentinel/server/commands/MessageCommand.class b/build/classes/java/main/me/trouper/sentinel/server/commands/MessageCommand.class index e1bacc4bdaa1abd6192fdd8a6dd7c1e7a223c3ab..503aa73488ef6ae6a70a7d264f646d6dd52e5820 100644 GIT binary patch delta 1604 zcmaKtX>e0j6vuxzP4e28M`>YzKHAcXZ7JQ7;)1qNs6e4o7TGGhfyNSSQ%q76RN{gt z?&YcoD%KS@WGkV78>k?-D}HiD$B%w;#*dCVo|mMKj5AK&+73I3dHC9h| zx0g8buC=&{pdvQ|o)e68bcLiiQ`@vZyHKBT+Xb0DspNV(3_=z+BS$t40gMbl|KMIz z5mt;$(d&(eyZp`3XmCqgI2JeAK$b}>T^8L$^jgm(?v-?ezj#+cA9Xz1t8oP>jT0G>4 zWQcV0gWVw?kMO9$V-}C|g#O*ASG#mg_GI^y3cu(-{^-u^?QZp<&d(k*df9LzE6NwoIg?L0Z*allGd@4MVOXtlf8z++Kdd~<;5&Ufw>Hs-3x$@u z;g;EhSH`Dg<(FB>?5R4AIxL2Zr)6~9FpAwWT4?~!ut!#ky*w+UysV-UEL--DRey-l z;86`;+npq5FQv9;kl{&24PXyYGC-nyfa*c&PcYfV;S|c<|6iD(8vljLO45|%x*rGP;7h8QZRJZ8rx%gZ6nFL{gm0>bCeA6NDL9VgojYiN~POC zuGj^VwvE$%`4m!15p|4XG9@gQYr2H-tfP!gRIrT+qGcrmayuP~7Nup#NF;*r9M7j@ zJH|dKA2+}C*{T>_4z!a(2@8j8sORj6vlsN=Djd8Zv#SruZ)Tc$R%L#k~v-gHL1jFDv3djHXt@m1*2tUsL}n3 z$1Yl-b~C$JTpC6svoEz)tJU9N|3Y6YS9@kyu2$=P*ExHiz0Y&@^PGMDtQxBD|9W%k z8qms5!+x!N!z1;2T&)R|P--*V!DWvAOPvaam}@i7VLs)G_4=l2(!Y5kfrTuxsc@*I zO3(6kSk(?S)arz{IxoZ>4)v7l5$|lP(IHHep7urpOIc>K+~H1EXk}~p&TF|B_7>Fg3eTi7S;ZST~Ale)Ab1(PV zY;tI4vksWa0zX^XX0zR42lp$M=@E08uC%(Vb}5PuMvp`r4=4NLjjjFt(WC8s14%zS z@%mZG0}gxW)R(L!)`JdRbn8E?HGzkT+U#@K&m+3V-Wcqm*XDpj9|sl9`a)i_p0>L~ zaYbP7>dm>L)^_>nA(2UXOtDKJH;VP9ymf^=JmJvKfWk-~h$R$5x+PGd&)IYJjl8zG zhaHZvLSc&lTcU?z0iNQh&C?Fg@T}h8YcifwED=6s`n1ng$Hy=GE*Z67D~hYzv&7=j zL~rBPeFtOvldEcW=UZI%bBu2tK4PSRQGFt~C^+tCg70lo4wIzE$Aa}r{h)8;*BU=6 z=INIFQe9b4G#8bS; zOESu4wFNJEvNl|I9Xn#Gh~*k7d}k?j%`x&PD4w7!&4McoF7_~*4I1bF8?DPmb3t#4 zh7?QE+?Bx*pta(qXpPu)g&Wh{bB5bp>u2&^)3sbX&6bOlxz;a~{Qs~FoK)q_GKmlr zTX)V14ptD7{+>sJG=D4Q(vJ(-Mg`qe5ofX7PBm{!+h<7c$Pt?%Efjf~SF%D3^D3{2 zOqsRU1;v}S-r!A9){@9uf??v5{9&;yL0`%#$+Kf&zxTkq1 zCn{rE90*@!=blM+N6a)Gt~teCxWYpoTthxHMa(q{A5C#6#pCjK%uv?th4Tk94A#oo zf^v&Nn0IFwgl{nj-(nEvJ>Hj-eWJq|&I*GMWOwH3b1NTW%1e~Ko2hc6D3H-0SX+06 zq>{>>^pLGV8Yt6d{%knRZ8T*CEy<;4!6huWa!%xtm&1ASJY3*onM1PjiAehC0H5)B u3m3%~yNxe8xx|-}ktu0RGQQ#)zLR5Nj(8zTn3Bj+Irl0)AJbgpI_|%J_4ru; diff --git a/build/classes/java/main/me/trouper/sentinel/server/commands/ReopCommand.class b/build/classes/java/main/me/trouper/sentinel/server/commands/ReopCommand.class index 6b390f615c1ba996c428a9fc5da275c335e7c96c..34b97f86e693d104443dcf454c0444cb18c79c58 100644 GIT binary patch delta 1181 zcmaKrO>mP%7{`D6e)KiXT5Ktmn5Hcs#-vaRf?utaREt`u23xFB6^-RJMoUtY6veNA zD4?hiUOnJ=_vVa4rnJuB=t;&aSC5{&hzCzPj?X5vb_T}7KD+z;pXdLd{qOEi9e=KE z{`sHpz6XZ+qT2k8YOXH!wS}{Oc{E=tXY$j&q9>6Y3%08E>Myr+4>off9h%!&t1x9F zxuQQ^E)@SOi!7xEL)<~sqf-;3OVMdaT9;6Tl`AM>hB~r(G%eLVtn*l}*}z6c)a0$v zboJMvaGbk55}G8vL7&REY^Kj+i>9BgifvNZ8(dYf*xj1#>`*wlQhHWlCz4Z!^j^(< z+#lR(?P?id(BnbPPKFwdJ;7zGH8^99x1<>Mcu4awBZ{5EX`on;uoEqh@TkXQno;&B zx&&$uigv1LO!GKT1PgXNYFyc;d6IEONL(2!WV60_(wRsGf7qSz6#F$#8RpiRLU#9z zeZH^)N$%RFAMgg;h`T`O0#_DTHP4zFks94K?yRxtLWPSFcad$1 zFPSuT%N6(3c%a6v1$Hm8*P@Q3Z@?#<%UB0&!!fCtm}hF97bC8-h;Q*V#hXy(R#H~D zk#pw%IlFJdSy%`&EPoDmT*tM@s9BR#+I0Uyfkos_J`{+nYS-d~JIrS}x4 z8`r{wpstJ+_gQXSXuPhzqk%4~D?Yleu9nr+vby49&WO6rC&Fp*DWAz47Ok@~&XuTg SUJ`8rZ6;12$rJ;8j`|l(uj6t6 delta 972 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 Th38`!ITFvu@mz)H6U6@yMMT_f diff --git a/build/classes/java/main/me/trouper/sentinel/server/commands/TrapCommand.class b/build/classes/java/main/me/trouper/sentinel/server/commands/TrapCommand.class index 8ea7d500223932304dea34655c9612038c95fa3a..fb615f5ffb7f489380eacaa8bd4b75dd81416855 100644 GIT binary patch delta 653 zcmaKoOOp#x6vuzJ)2(ZgrWB&|^r#v(GVy4Sc#I%IJVIv1jA1-STGD2sNJ!dxEPR8z zwq&VlN-C66K7j9|$_l3wLN*rnoOAE_-T(jG`#SwLo&5Ua(-)w~WSK}F1z8tt$awb}#bY3N7o){AFh z+wX+UsL>3c_7AxlXE!AF~!9HV1?0WDRx#yq3MBSJ%|FoJSU1Ln6^3Tkmr=b z8-+($y)98pouOcJ){$qQ)78Y{rV z4y>ZCS|v5B-&i^Gn!*=+_xqogpIDf|Krtu(nUHL;LE^mZe920(mHmKPm%?B{V%&z! zT$kt!02a9+JH<_wB+l!XW7)NsP0AfevxDt@J}2_v{^sNA7PtEvE_b-wr(N!GUq*?$ z%RIo8ew85Bm9yv4i8Vaebj^?K>9N1dp)SX}obGYHM|o*5YW=VOIOBN#_eltkn*UU&CE^0RR91 delta 656 zcmZXQT~8BH5Qd+#yX}^`A;efi5tBxP3stBhet}qvU#Qh86)K|W0!t~@V!A80#y?;* zd*y}3EALTLjZtn*{0;sBe*|&1HHE~BGc#xAo%cLB9};g9@n2itHh~-~8}aYjaEyLM zZ@%LBm0CIFuehmY=dqi5T76co)V$GDpVvW}!zM>81{o4CJgG(nlWpdi$45Ny!RcGDFRGnHmQ}pZdl%ivKt^UOEeRoM=_s`|G z6@`UWchMgk*wJb&jm0_3xXC$-Eaw${;e)SVM#qb8eci23`<1GvFr{2r^IS!u|9^{t znK%<%Fu7=PiOY&?@ZLNSyo>D*ej4+!2_`MBaxEB)yi)mKGcsb_RAfTUr|xJ*pSq5Q z;)Z;oE;&L}VnJ>h$x3o0{ROolg~lz3;TXEOEzxQTn4%~*#T}+47J@e&>F1#~E<2E* zo4rl_h}c8&pFXT+C^bEj+-0_@PI8YqK?$e#d4M7Fc^-zo(t9Bep@+TOHh!SZ>K_^S zOs3f2#8<|WM$%|C8a}xOS4!L5olVakq1neFdUx>HZ63SLW4C$iHjm9C9O2Pe5CV-w OmL%^IGFQ&BNBJ+|Ja(J_ diff --git a/build/classes/java/main/me/trouper/sentinel/server/events/ChatEvent.class b/build/classes/java/main/me/trouper/sentinel/server/events/ChatEvent.class deleted file mode 100644 index d9fbde05c2e0e04d73651987887cba7b768b8f40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7553 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 literal 10283 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 diff --git a/build/classes/java/main/me/trouper/sentinel/server/functions/chatfilter/profanity/ProfanityResponse.class b/build/classes/java/main/me/trouper/sentinel/server/functions/chatfilter/profanity/ProfanityResponse.class index 9c33274d7e115f50a383959eaccb2dd51b7be392..b6a0c4273398df2dbe8f82cbad5a11eb16c09372 100644 GIT binary patch literal 11196 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 delta 870 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 diff --git a/build/classes/java/main/me/trouper/sentinel/server/functions/chatfilter/spam/SpamResponse.class b/build/classes/java/main/me/trouper/sentinel/server/functions/chatfilter/spam/SpamResponse.class index 3dc6682e9890fb52f28c657ac36dc99155925780..ed2f840f5e2b4286a81ee14f18ee79f4ccf6c4ca 100644 GIT binary patch literal 6896 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 L^D?gD8aDkG)AiHh diff --git a/build/classes/java/main/me/trouper/sentinel/server/gui/Items.class b/build/classes/java/main/me/trouper/sentinel/server/gui/Items.class index f8231835dbf04e95efab037c6cf80f31ce34c289..35cbc0103104f9e2eb6d456440910ed89bc91d46 100644 GIT binary patch delta 2630 zcmb`JdvMg%7018d%|5arGp3u65FTMe%o377K}4V;k;Fg}AShYP&=yDt0XEs}l8_LE z+AR+aK0xuOMG$RN9-;<>G$B6FQ3urz>d%fyhfidp_;WecijSI%)G zg$=1(fzOB}f3-iED+!OUVOFR;FuuB@xzSq|4y_EVO6F=}bj)(ZbUeR4RKKdxTOSO1 z*9F4C`u0FLwAS>}l*(q)OWLezf9103MRUt7SIKN4vVHYs;g**A&>S%G+SAS{@8%H#A2*wWhMo^yIFssPvc5tE}<&uFpJS z&wQF7uW@p33iolpiw9D1n$H@GMx3!fPvjdLN8~$Q>>+a0%`rMgdgwN`j+`7(c9isN z^ZTQ3S=ZYI!U3mju9fsnNz~g>cnmzEj*abVrB<894z;PV30%icNp9Z3J7bGEU-MiV z>-M-D$6;HXR9LgvUephFO+T(lO45)qY1FoeX>bCDsIA2aEJh)|BfsCp4OoJCSc)br z!$#C$3+nM8mSZ
    J+D(wZn!}`^XbMEd=~wzZhlW=HrZUqyZC)1$#N?r z`~mFTsG0is1(tFXlDJtz-+=8h{4U($!UPwpE@_3ETX?rZS4Q}Od?jnzMNeHUsNySw zGGp4Bb3PhVDbh5rNij9Uiy+3L1vept`3Or|gUx8gJ!r!Yti>L*;{ZBv44pWOJMd@R ziBGX18s|d!3tDlyq|M?zipfmuMRswk-s&YHzG!A)jzTuoX7EXJewO!|A?Ae7jPj_tcQ2PKLiEe@~W)$}0>2Enb`(rb*opH6{$t|=; z4bRBUiC8AL*m>YT6Fq&FSUmnE9J9gmiX|5n_v3i2=Z(1dqV~f5xPZUokhq)stqH@` zMv$SMm5WXAiN+M|uo<`;rRc(~*s9%kueA4RaZbe^-i#*_5PMLDy&~e&DhD6rL+UL-A9rZXfpVPT!!$$* zEaf8_)1%&_I+lcwBl$xX>=d#YbfVVW%kdp-jl?(bWVlBgYsO^E?t#;VG#A#puvz<= z5}@Xg^Pd>=jS*|f zv(W9#`(JcLp7#c!I~QjaL-&4MG|_FxKk%9kBhW#kf0V>Bq)ZjT? z7S9Xj3tD+dn#1VBixIqoBRV}tWvox;dht1aOdF1II1bW_mpKlva3WsibR1_1USkDb zPoV2dp!+<5u1;%W=qm7D0$r8R?T(_Wve1oVZX8_+hYUg&8;PTv1os#d9gi9xUpuVG zr1UuV{2!$c6OT~RtEN5M4-N|b&hN@qps*P`@W zQ93C~?~Bq0qV%CCeI!aBi_-7DA|>PNp_DZW$$Ua5C|WUXpQxvYf9PF3wuVXRTw>&o z^JXp^!1$wF-jB)YY#hM!ew3thRa6zYLKTdwLZ&LR#w@vv-dKOr-2`}KJzcdjLuG2H zlAEQIJPc!yjRM_h6EG6jYh*fdl<3h)bS~yfdmHlbZDFWGK31v@1vR@(NH(bSb)iTH zsTlnjk5^^koa)^lF%f^lBt$M?vI^<-_&27|fg6~HsT_rA^kO=FxRJ%WdalDwyb&{b z3rbY+Z|3d#A6Ji3hET><-8!9^&ATv1Ri#2jq>?*P#l4uz1DMAiRI?Wg_!4gAo4Adq su#o3aqay3)?@?W#JMKwEZR3yFt#`6=@r-^?@t~v>^`7GkJcR830y#lE_y7O^ delta 2668 zcmcJRdr(w$6vw}3VcCUf)J330TyH2Ft@y4)Ennb+P%P2RQW0^{m6yW$%E$WFOzm+M zmGGI0mKn&j;e#4St?@CYsbi_h;*Ts?zZ<-MmncB=O}|+1#V|a zX*ll6gh}5iZ6$tx!-k3^LJEIOPo1w*DR+pwAei< zr$kA2I}7zHx+7&KnglHeWR6PD&K#XFC_8mT=HT=pHSsYgLt}OeL|TMNWrXaNeJ1%Z z66fV3^{=>#azH)P#t~X2u&L+T*wol~il z>UhU%E1AP>UbIl_%eQV2M)C&=Z&?b!3tH4@l zg=j>IPmp!AHptto%scX~kF;TskF?k!G2tcxgM;dYBe^p33p%(4w70O*3sF!boVxXz&rQ}D(+%A_eGf)u~8ziNm^mE z#G+i{@qUoy4)nn;f(heOMtM(EN@KA@)>CK10DK`Eq>Qj2HrYrWKaydSP27vZH*A#6 zQciLy3^+#X>Oe31HGx*{f$%och*l;nf-h`4VI3hpXFPM0UTE~20k3u zHBrN{0zcyC2XJgd6l<(tCla|Qp205GYd7msi4oY#BVixr<3m2VALaO%cL#77pWq~_ z=&Od_sy(TtF`!b+sFlZYNLu5tSaC$!;iz;#T@c59OJOZk0T8yf;jBN zu|>yW_tQue@zdzT?HZub)psQNQ~?~_5N`V?96J#v>coU-oyb<%_7IU9K_dD_(Rb8Q zbzI_uL~cGvWPhTfo=laYc(sL$`bWvQrxidwdizECVJ5$N^)z_%Zq+d;?#xFQ8FbU zxB^0cKqw3d#Q|Z4dOb<;lvZmI^rPuEdQ*;K(AFG}(VR>%oFq^1n6mN^h(i+EaB#LI z4I8IQJM<%;L0rk;oOu}?I6@OSF6|tWNt`)PbG|q*j{|TelDVro!ON$Qa=cwdR}$)m z+Z?%`Kk4ocM@b`|;}OEp|LF$#{VWQeWwVev*d%GMxYTUd2FhB2}g$ zO^PsB+!(_D0Yf=&(q#=^lnuy`tr#Yi7%o-FltUOHC-9P7z{_$8BjqXv^9Wa|I` diff --git a/build/classes/java/main/me/trouper/sentinel/server/gui/MainGUI.class b/build/classes/java/main/me/trouper/sentinel/server/gui/MainGUI.class index afd91416fbf39fb17b0525db272ce7ae23f82741..250d33ddb658270e0eea737ce60807882388a90c 100644 GIT binary patch delta 1563 zcmZ{k`&U#|6vsbzUU%lsVFXkh2@wqTT zcX}wsT$5Tw9rFyX?lOn#8sE%N1M^K5C>GLaP^p&&EA<)QM9(6F3F1<^Zh0~tZ%cNh zqs{+na~8!cnsA%kXb{#7DqLvWHz}5Ivq7MBb-X1V7cYa_a$Tf^B z)lhcaiGy7g!cC zRw`DpT2J>!LTT2RtW~VzZi8aIz%16={e!~yDDJfjRXoPydPbl$KbVN8W2ugGOR6<)P*Og+uFD)Wsxh8QCsXk@ zNoq4&6kFLQiHmDpdm<5EZBQhRy3`7LrucbEZwhP+cA-t4QEcZ~U2IMDHS@e;hhAz8 zE_=ZsZ$-=6mgt&vvMpNE(y_cTp0?O&XB}gg;$>dZJFH`&oc5LRRJIR#Nw7U=*;{?3 z(r1Gg<{cd1<$b#{Cl&8-Fqb!EL*5Slc011H6B$pE_u*vVk9+p+eLXOE-krtEp~$@;2Tdqp=yF4U0$<(*CXc&k22UT2?!BlU5@>4au^PImmmBN^&X(6sm%H`wYanlmiUa7!;OE}H-2 zw;cBpiYaxl3;M42V+#;(@|K{x?7BNFFu$K9+((iSlH?IqWIF#3zD$xDbp?Dl3m?wH zOUwJ==LwmeLtey_;m-c(duYER>X1YL37M-}T zqDU_t274G7De7i@FZa3G=rZpK;qKY0K*n!t`podKvp2&YP!_yVL zJZIoOAm>-cFisH0Gl&VoI?;)tZwiVyDno`t*XhOx$j|Foc05Nq(LwM8JEco69_A%G zIm1cLo?#d$G(93+^m1Heg{1tM?495}`E2b2PRZA&eU#PCi1v;6j}ZR?Aw5qZ%(tR^ R?+fsQ$e%?1%&%Nv=syIVJWBuo delta 1260 zcmYk5SyYu(6vuz(P9NWQkC!n7x|d7}5ENIdft5=tL?A-ZEC;kfg)35YuL@B%-AcZP@Ms?};$`&?MW!*}-H=bZij@BKgfJ5+I@%scTGPfF zb1KvoxLHwYj%8gEYe*(`bayqcZ}C`*-<74wBa8K#4Qw#dimwU8wr)DeL6uLLw*RUpbH%(;v&AhaN@{j-l_I!eRef7)m~QrHdg(I<+$$sf+~;t=W-nce%cb&y zK@~DTyYq8g9^@g1hc%CoQbg^UiFuym?@7wt60_Tjs>jTLHxS;(eupPCPjbLigqKIR z^R(s}LW)R7PqH`OlIV{o`jUJ0Ivg^oaN)dzR*^=Y(>%`$=6HB4>?L=_6VsZRE}b;L z=%21XnjPdL+vCTYBOJ})HQ5-p8zY)GGtp@qozWbYD8!h@I1>(&nkhaV^&$&Y;GANa z`O#lvj`+0MnVlQ>N>M8twRs9x3ajtv>rN&dzP0WDo?U9TMjXGx_vW|kCAE$CWGsBR zwoVj-naGf9-(UQY>;*IHixLfruf(o2>vAendW5BEuFi>!aBbBj%OOo&nj6lraySF? zlB`vDvSt$?U%UeHxQL5o;x1!8iz#9m#ne$kgPgxCH}y0DukdOHL;98-OD;o}DYmFO z%}rCRO|_4)?liZgxpjg~J~sYO>SZp*grYP9S~#6J1B%eT&Km;jvm(#Hua?Maky{-| zbBEXUv2q*tY&$l7LpKwaPX>U}#&*auZQKe9*&PrLIi_QNcQ@A!e@{{Ty2>;3=$ diff --git a/build/classes/java/main/me/trouper/sentinel/server/gui/config/AntiNukeGUI.class b/build/classes/java/main/me/trouper/sentinel/server/gui/config/AntiNukeGUI.class index 02f92d60c97e37bfe1bd4296872e8b4789b6e2c6..a75f01335009672cd40bf72af62bca9756b4076f 100644 GIT binary patch literal 10037 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 diff --git a/build/classes/java/main/me/trouper/sentinel/server/gui/config/ChatGUI.class b/build/classes/java/main/me/trouper/sentinel/server/gui/config/ChatGUI.class deleted file mode 100644 index 7b8235310f29be75494665a1a179536eeb1fa8d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5969 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 U4`%u(->LM1u1~oNLH(lt25z;-PXGV_ delta 5742 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 ANB{r; diff --git a/build/classes/java/main/me/trouper/sentinel/server/gui/config/chat/SpamFilterGUI.class b/build/classes/java/main/me/trouper/sentinel/server/gui/config/chat/SpamFilterGUI.class index e3f79b4d46c115ae3321dec8b021857112f5f754..3cb9ef24b00c5bb10b4096e9e36bae20594be0d7 100644 GIT binary patch literal 14428 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 delta 5358 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 diff --git a/build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/CommandGUI.class b/build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/CommandGUI.class deleted file mode 100644 index 84212a7fdbb3d9de76c74e689aeab8c0d0c83a62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5495 zcmc&&X>=3U75<)WEL$GHh$USgGU-g8W~I% z+LDH}rF)z1X`41p_cjhO#-wSQ?rFO3dz-H5-u~?AY5TpAERTc0*qqb;kmlVt?|t{( z{l0nf-{X$~XvRO>C_}jm6$<8{QlR#zI-~{+HI)c-$Bt@oOQ5nzPwCbkfpULCuNzgU zcEPQn28zH&-3%mjYaka3=++56n;!1dGJ$m8U?6Se5_&3-$mxMlE^C>|j+{=cct%q# zO`yr&5Sh{5dhTz@=?3wGRhTER*0Rs+xf%=-)3)Uz%;X(SM|mRwD_PK zwOHVSN5MiY5?Gbg0#?S%rFq0`VPha?=|(otqaCNDbK<69W(0ix$fO#gRz^=Hf(;i> zxv>P-xUf{gGAtL^Ia_(i*wQV7TInjuc(%AfdBBwl>aa?nBCDSuhu!}0RVdy=pYbYK zgS7%xW-3%LX3iferP{2P8KSDC3&hP-zn%!Ba)VkRWG0hps;}cq9tijqG@wzyMdcWJd{7|sztT-OHRMpt1_c|j zNxA!qdtA6t!A(-5*H5F-(!s}|iSBO;h1Z3ns!Mm;PS+ojI-DTttxDJ&+Fv%r>_+8`(ycPrSB=hEr}W|A=+n3-0Y z3Zm$7;h=(E91_^%@EJ|IjMH-_e8`T^Hu+XS$!bP97{m)^K2Nj(M%k9tT~xOii}6=dptgM{1y<--U#N0kG(1$zUyo1Vo|3qyBP?rV_hY>}`{smH?-v$$F>jt#=XeRW?-f|#+vzi&zV89w4sWN~$DHG}2K4MiTYU!aQ}BM9yA%R7{ zSTQdYS5g%rvyZBnA7LNZl}pC7Oph9qZM7$2##N(NrHM%wCM%>s0y}2ey0RE1nU)J( z-nJ=*zk-?*Z0HpbJlkfce_hE&GLG?nHn?9+aQnI`j7ub%JR?>C!TM3o1()urCNd~H zpt*K))v2Zn%2Et98S7K)Q`)imZA`cha_dxb%T#hRP23bW3O7i1)XZh#TDvZ-GJoEGO)G0<)O4q24VZn|T0A;`4xVzeL-?wK3wX2^W1RSgocN}KbEGQqZApDc z!RKx2dy@LTg3sC14<+>@1)sI4pGfMb3ZAg3pS$r3{L+P|75oanrsq!H`KTt7H)Kku=D{RUieJ^h~hL)|5nC4(E9lZGR=$M(Zv1d4~IhSAg%RI}= z?{(f{3&o|Ch9IUTh zK7znFu9w8l+L{qGjq|IFP0SrZ%Q)Kd#QYJok0WdoqqzAIbUj2aWq6qXTcPk@HtP%F z#v+auV+C9DdR&7oSc*NQ_F)xnp;WhWR^__0+@a)8ibu#!VlkWwQgo7%(l1zFF^U80 zM{&#Jc-|OpKQzg79`6jG5Od(?SC$4Fa7lhs0g&5q9^-^8lc8IH9^yW^v#xyi%Jt{L zX8L|CT)%PzN6#Z!j=Rdlga3I(vMIM^lTra2c-D=SXaiOvV6*b_eHm@?DLiH~tt&FE z!>92$nSF-)s!{eYl!w2_isz@3NBA$u0qD01g9Tn2}f^6MC| zmWo{GP}x#jE)bJ>r8MSAV!re&UUo^$)x`9c#AN0wjd_ZgFF%XBFNwL5m~|yFnHx)E zo+joi&f=Aq#9T(q!)UT! ze^9>uNQu`i_Un(y*Pkr$x?MVju$`iX9tXO1C0)OUUlkTI5SKBAR$~W02kk^7Zs40; z95vDT_wY`8BSYsVhH*0vp#`^bt{h(^Mj5_@FY~#FSU=<3llTgMa`ZKPoxl9(TgB0L qi=!VDM?dE1H^f*=S6xEA{FZL_J3gPOti~Vs{1cyl#$WMwEc-8;<5`XX diff --git a/build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/CBEditGUI.class b/build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/CBEditGUI.class deleted file mode 100644 index 5236fa285378023fb467d1c7946bb618ad0289ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11387 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 IUts>90BXmVF8}}l diff --git a/build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/CBExecuteGUI.class b/build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/CBExecuteGUI.class deleted file mode 100644 index 22435abfb16acdfa2314f0d558f16907b847c8ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5775 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 diff --git a/build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/CBMCPlaceGUI.class b/build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/CBMCPlaceGUI.class deleted file mode 100644 index d39fc31902c81d6dbb5b731019eda76e6be2ad8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11445 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 UXB^$fy4)*#|EjzuzrdV70qupXP5=M^ diff --git a/build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/CBMCUseGUI.class b/build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/CBMCUseGUI.class deleted file mode 100644 index e6c60e778b628cdb16034f70b460807e0061a91f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11423 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 Pn0uM;Uy)bk*O>Ev4al-k diff --git a/build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/command/DangerousCMDGUI.class b/build/classes/java/main/me/trouper/sentinel/server/gui/config/nuke/checks/command/DangerousCMDGUI.class deleted file mode 100644 index 3fe52bb13aa8b351886e94f742aa1cb3c30b2d3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12097 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 diff --git a/build/classes/java/main/me/trouper/sentinel/startup/Telemetry.class b/build/classes/java/main/me/trouper/sentinel/startup/Telemetry.class index 5541fb67110f1d54d0073cab698ef74dfdc26ee1..03ebba82ec0b591dc79f553e94d3c8f976ea2861 100644 GIT binary patch delta 1864 zcmYk6d3>Bj8OOh~*Sq`fWJ%L4O=wfoBQ3d_(k;+7APGG~ufUeptwIdRCafg8akEi_ zO2n%uTANN4TWzga!J{IyVY6waK|!c^fC4IqS`<+678N{bd1eznAODzVp655u^UOTI zd1g*5IJ3}wxQ(SQK|&6$CX80hHdltD=6TPGO1luzoI|N1+>^|$PGysw zxlC43b$*R$3(u%{18E9T#&n0MW(Ma@9jy$~Bw@lGwZuZ4Yy>iQ9 zc)gqjml(@6D_CjPt4;GRQAFd(bS{-n_EyDn9ogJqf7QlJk9B*q<}#Yim};LM<8sX^ znic+BD%YC~vc}YSZW|5>k?S<;?U9_`RA(|hkhItpnhh57r!(nJLFTYYv)LlS!GUBp z-O*PB+cZ~N&}Ts#7uRgHfSc;Fhw4sE!rL_4E$m74Td#y>2XB?I8U2}TPS|O6xrS>U z+BI*d!#w9*5v-+C(?!zE^DVE4(WANE9^coM9O$&gy+d;Yy=K6-I+7;i(64zX(g2H# zP4bj)Q02@PfBB*T-lcgr0mV7}*;G0w9ChBXHQUjdbQ$7ChxcmU$NSAL|Bmp@e9+-T znh*04^RoZa$WCr?_?YJ7+$x=AM*QVwTVPvwC!e&N@3P%=HZa4q1p<*Km)(pw?6KL5 zIUQK5K5Z5`b>Z9itiwLdem-ZeaW0D-;Gn}7G>7=2Vy;;dikg$ohUk|RB`t%w>oeKZ zjUBmECLQF&jUwDH19lovk4iA`V?$Y1{-_<cr?naWcJsNq3$V$JSoyHBmcoqMe0che#XzmQar`eA|+ULGaSGt;2i~= zJSC${8)fDwmHY8~j~r$GLuiqCYWCsQ=4m{}{1e0yV_aBvQJ#y-n(|y)m#3v{b!qTE zJPEJl8D(ue;VUYw@r3^<8{>(|CI4`<=L(8}bZ#kNA!hshTb~J3v%A$j)ADy;q41GewqDE0o3LuUgJ*E}>E$ zbG^LE2D({7M*fl^mU0Vq+$yhlco)mq!^M)UNzyInp!_7avy#ISYROwBTx->~N5_pBI!+^aG`NU+!(q1a@mi26b;rB$j zslW%u`KX6+KB3rH;8SIL3w&mr&wD7lz3lKfcX*|+Nw=?Ax4V>xdlZ}M>I;nKIlj4J zoRf-aoH|5_=npFHr}pj>oJvft9On@j<3|&+t*^+~MU;p>+$T8h^v{QfDhQyugc6>2Hf% zs43)kf}9p4f;vOHa~(qtyB#L@y?{PRVQ=&$(LMZuKgt{w>@PC@#6QJaD#wlSFJ2|Y fPPxff10lJ#-NpG{$?|W(|0DDN1b>aQN>TATb=;d( delta 1626 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 TEBHJ9Lp7Z$|HX&+7{UJm;L%0c delta 2137 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 HWUBl>*;3&J diff --git a/build/classes/java/main/me/trouper/sentinel/utils/MathUtils.class b/build/classes/java/main/me/trouper/sentinel/utils/MathUtils.class index ced59e0382dee202bd49a5f716eb00ba664c697b..f924456dcb1ac5ff9a883e8433e2acf8fb3f17aa 100644 GIT binary patch literal 3311 zcma)8YgZfB72U&(G(s7S%*z%5OcFmp5EKJ(8n7LM!Lf*dmRCw~Y6fW#gT;u9M#OH@ zrVqF7J53*HlO}cABz3!1S5x-`X4P5lr+(?5>Gv$R={{EiW1MD%g>=t7bMHC(?z7Jw z{p-JfzXM$KZ+W}1l@i@=}0XMcg|JQRd1mIjRZA0F{+`d z9CFXrLD_r2!a>9}R2Ns=M8UMA4|mG-{&a-d?n@4Q%YeTP{(@VF4zqoJ{K4prdk zz<0gepcJ(RPgppPUJd4kAedYhlEyyec>NZhlyg)SRE3_h@HsrqV+5+qL%wfc(O`72 zv{4M=q={1&PUD&T6gGXO7&vPzjV;SFg>`O8>_%9Y;9#m;t6i!bOr^?N9I`Nsv&_3B z;+iSUQfVSF%4Dy~q8P5MT$9^ENg#4m~c_BZfc#L3y%Tpc;>1Nxd!P#(<{d8=tf*t9qHwWl;*y0tSk5vhs#<8B@359L4`9bwm-xkHrVuM1?pEVyev5NHP@s2Y$3}WwQ1g6 z6Q#8!$Dgs;h#KnB1;U!MeOKlcrxC2U1big@87{s-4K?MPZ$$Mr_kGgcH$n`uj6Cm^ zEJ;(kW}7Vx^C(N|R;00J7cceqZ5wRc(7Nph$1CIZoC|6yx52K?7lSLq|Ax9)*Cv`cZlZZYriq>DfYBYB z-9+2tn|Nd()GbfScDOrkh|dgvXwK{L&?XK~%^UHs8bfhY#=kOK8DTGR0d+)BPn=C? z!vXXljsY~|93JLX){0qrU%(+ad~La$%W0;{h;bF);Vi^w-^DearxpwN9vT_F4QYIz zmdF8|V_GIT`3YXFaB`AsOh$e=aVLBoHMh|*Uq^1c z|B=v-zq*BFI$@}BJP}gkM3=D9qM~L<)V(8@0h6K6jlGi6C(Zd&9 z5~pa5pbul{Cp%A)k*Bc16AN4C%V)t)@fv3bkyR{cjB!D+P{l|yiUpl}Y~q zZ`bWhs&10{HlCZOqA$qj7B1c+xSVs8G@WD4^AuugSI(n~2GAR;r8Rr4C|SmbDXSA zFv%o;K~B*(jbl{5RIr~_U}XFTZ*dgsZCV=Fx8b+A&O*Q5kyYcbA>6JC&)q7jF zy~g%h+c;q~8s!y}iB==KiH)vCLyhmi)NjBrPJ|e+(dgz=4n4-Ani^SJTlh-F&1qvu z=95Xq<4e*}_dZq?p>l!Llje67Wu3I%Q&yfW^*${T{jBmC)Zubv9Vy!>!Ocnu_LKHMpu`dg l-G>qV59&<>+DE9r3vGM2i>TIihO_>U7+b>)uC2l!{|7!f*5CjD literal 3514 zcma)9YggRX72S76gJ4iFn1>BvTsyJN%z&8~ySVn)m{$`f9l#CPsm*GOm=Pc&Bs$Vi zleS48Zt1&u^p!Mj(xj=|G>zjg1gC3d^-I5W^$+xOe?Y%>c{z3Wm1c}^SXqXJqSSM`jg+Y6b=xyweu6R0_D+NL)k5KX2o z#8Hbn6>$yq(5mib&$Kc_rINnp(%tn+hQ2J&o1DqtA1#{-O#X_%F*D`+XLl1%gN9vb z6i__FW#;|Ku={-3E_kM6XU>(3qFK;ABU_K%Xjajpp%rZcd;D}Ck4;eXrsWwWfs@G) zfE^-+6K=;RbM4j8jt++NoV+O!?%sw_+OlzUBB5fRhEHL?z@y7X#w$7H6{D1K4cjwq z!^%jAZf0Ef7SGEkQmd~n2sDSm`N2}7^>_r2syL|O5Dp78Y;7?>P-}8uy613jZQH=) zDt3~TL`p?kLpOQ|c`HKIML=ED-3j9gcf=xJk}G;OWbhbEVf*HmYV43SvMTxnnzm}3 zvpr+MD8+FU$2EK!{Q|Y7?Oo8Vvf)S0$=!TL!$~=%xaKoP99f)JaYn-c&Q`bQ=aB3O z#O5tW?oCOKrbbl^Rg3JqP&uZ(FkCjRqRcvos7yVo;j?(0sXZrfkj&(AmCA+|vQ%Tp zsd!Su=a8?8IpiQv=a%Ov17;CuP3|0H5EB|EagMUkS5^$WC~%}w$WW}R@Pk{65a}8ZJoIYAnNE@D?R+U(j$7mq_fgeuYfQJ?3f#FS3vLBW3s{4PVBr zKxaX>3T2BOlQ&ik+g7r%X3C)TA#CM*`0R?>w;b~<+x&0yhW4ka47!( zU6Z92wjD7Q`DIh1OiSG{P)qm-s?KxP^1HV&bQbvSMx|^=wYE}4cIZCe(;dl znC#;n=E$5|=BX$M?x879FD#AgE0tZ>O_>X}?v+cVC3*iDuyZ20#~6wJltwJwbq!a=J8b!nakA6) zRJ<#&?~`3A+kguk;29Y4PeT<{Hfngi%yEWy!Fw$IKEzEvM6krGoR$Zt#j6%LU>PZ{dKz#F1O*5_lOMd^jTTHyk~sbhRrR zIJRCGyMFiIUGzI4u%BDu%?v?Z8o6PtyI{cLfN%bM-In08k(1A3%(Bs2A z?t@F?C-^BdFb96dC-xP$^SsYs2iLv8rzqD%@gn(EeCQPU9kfV=@zWqcDxPCDiS(vK zs?18P%Ix2vN5-QA32A#1yLu9R(ZuA{DCztUt*7QJ)Oo`wl?| zH9f@^10F*Y`tUH7e1P{Z^i#|P0OR!Y#s*TpX|M-j_jIf(d2@?R8P+(z>q wi0bVQ;$q(*N5^k7b`-y)Zv?->uX&e8!JD+bjNjmQc$=0uEq}&ea0BiC2iGW2Qvd(} diff --git a/build/classes/java/main/me/trouper/sentinel/utils/PlayerUtils.class b/build/classes/java/main/me/trouper/sentinel/utils/PlayerUtils.class index 0286292424677781ca71fa66e8f0626e24d852d4..40741081c998d6ad15c6a59ed5969f25a21441cf 100644 GIT binary patch literal 7229 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 WO_;B$X~J&oR5egM;^}HC8vh4ze5win delta 1027 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 literal 10768 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 diff --git a/build/classes/java/main/me/trouper/sentinel/utils/Text.class b/build/classes/java/main/me/trouper/sentinel/utils/Text.class index 548d9f1312990cf0be208cad97e1a6e49a6b5154..4bde8137bbc009c8f218dee5d24d07fef3ef0c97 100644 GIT binary patch literal 6064 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 delta 2117 zcmZXUdvsLQ6~=$}&b@Qzaq?hfG7Jf1AYhU^iI^f$QV0Pe5=>%|*oY7UD&Zj@LVTdL zQ-S(MgySptti{H6BgPSeT5GMXTKjHot9^=E+xoz+)zz|Gr29-_V6}hDx%-^+&3C^2 zoxM-rl*5xFuN@tD0hrA{cSSzXyREiRj4_HNWA#VY9IHgv*u^Il6Npes%433JB4ri{ zy)cl_m)QlOQYI@-q+IW?CxoC<;gi;9Sta&@3Y}7uG)NsZzQ zOx2qMjYY*wQ`Ay!5$e8pQTL`b8&-Lorr!um4%R8Y$PE4OKv`axdS-bvC=okHzvv|N zEGMFC+!^_eobEACaR&1(eBI#s`XP6AbOC32G%H$Y)rZ}-n8lZjxWWjha<*a-ZMr=; zweZUp_Dj7H6z3`ybDrK4tSnus=wO*eaNWuctGZWP1S%?<3}(4v1?TIxgB3*=DpndV zyfvFoU%#v+daOn zxR&d5cW6rV25$7YNpUkf^pm05=uU3+*rnJ_nf^F5A+(d*6t{DS9uuAzyi3u;9^DX5 z7u;iEO)`@2Q{2x3`pR%w;z6-y>7)>QWp8toZG217%fq@qTw^_|-wUTxHjf)m+d@1k zn9dM=@<|z*eI~hb{jbQ~Prn~M+t#<|r+xk`Mp~8?;vHc&-*;tUiPcdMJwOGbud2Ow@Q_)~a&o&FPw5k$0CIMQ@j23% z2p&y7MI;y_t(KI2D3%%+mlHLA=-^>dB7ql0o=LEg?@DBBY2+n|0>V?z_jnmc*lLC6 z`yx9*HLvgk+54d&3$Txp@Q8VoW;}|=izU(w!)cx(9ELgK7oIqZ-YnCNSlfslHnr?H zqawUIY*fn#LyMq|qWC^$_A|GirslLiz*z{#!ee9+1IkcNL|ii0R5bK2Udu5?@#;7atW9MXP?_spu!V-5cVP72Y?2Co_{1J17(G_f!FjO;2-kt;}GnT2x z>Cco5n`(@4uoJd`fAslr)+{+LC_9(>uZSU8F6hDa`wr42SH8O04`f+0)2VlAow%un zB@Z&)k2|th>&D&vtS^z@1BAB+tX`7ok}R9s$2p>PD2%%^olO09$Lbw?`TsW4SuQ`8 zNGm6ZiqpiB>EtnkI5UUKqk&3!4{Mk!D~&SFqlx)qTa)C_OuJaPm6m8%$pY1yRhe__erNDY?HXYsS^B?3D=Nzlz^*O|G&t zTXGF#^IO?4@6^!d?<6u6nZi3t++$nL-JPFNDnWeI9FIqiP;x}BBg|W$+banSwXX7G zPsF}0PGouNSicRuh?g8!{ED|5a*6`{UJkSQ1Amn7XrXye1pFyC{v|ga;vcdT6U+iB OvkztWBR=6%lK%w}riirw diff --git a/build/classes/java/main/me/trouper/sentinel/utils/trees/EmbedFormatter.class b/build/classes/java/main/me/trouper/sentinel/utils/trees/EmbedFormatter.class index e81db2896efc73f7cff7e32680b1e4adc3f5e2d5..bc870e44fe555701a274f45098a86ca5fa2adaa1 100644 GIT binary patch literal 6860 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 UL=leRDKXz&Di(-EVhPIs3(5Dvg#Z8m delta 2846 zcmbVOdt6+_75>iM$KAa83BMC@slucM+Q^Kap5;4_o zABnHpS~6Ozv{7R#eMFOxbb-*=V6CmzzU@10wC|>^)>vDU(%_l9s--B2PxNyR1IKVtGg_lcg3X70M-+xl|;tv#iqR5};$gJYcDG zwDw z>?2?H*cN34P%ZEtEF)MtLosgW7jM*Y1z{XkbjSI`#Tg6cUt7DxNL`xkVSZSUzc zP=k=b4cI_%#=82pZSDz%yB+9~nam# zI#>H@&?^vA5Vlx2)*W(U2X55SFL0Auo6m=q+YyWf%Nw>uwuXDztap2GOUOVSJ}7WA zZjoo4b*XWJ=JzY*M+82KkIB`>QrB+Ws^d0=o+$^6V)q@mQ^&^z?!qSsdFnQtFzRRC z&HK$uo!E^5fjyA2!j*@>PKZM&z9$*ENQtI>rUQhGTMARCvFxm>nmiYz^)RmiNWN-Q~@pn2smq zq{xg<;3Vi2hjMdGj<)qP5XzdKZP;YOrlP|#^@h2UB7WfN0t8mYA1n^gZzhS}QcK556oOI3; znkC3ZVuqbE49X<0PFaGoDd%v+Q%0QKd(-^ZBEAmt;j}z?Dow<*pmEXycJN&@+rRUu zoJXAfu=z(ohgi_?(zFra#>*^=qyhecS9s*)a&nqAYH=l7tHo~rF!UoxIfArNc=xmB zQk0n{N<0bU#;f?}G)&$!%x)O4vaVtr`E8y8&t;xD7V9z0YqR*9M^M~2jzF7b1Z8WT zfs)drr~pQ>xHN&K7d?4QfLUDnX2ZvA00pL3e-fCT#+6t0f!C19elxhzWZ+*+)W=oy z-*_Ds4($xiPIKz#g%-B=kE5n-1hs3%QOA9KV~Iat8AU^hXJrDbT1x^6tWDtR1X>QV zbu#SPyuZL4z6f)XiuvrP7i29;prbYS0r>o$PTnW2&2oPG!_5hFClGBNLSbMSeYw_-1a^8p zl)#7MB}cGp6t}bJ`6M^3JpR-e?jclJz1A^2K)4^9l=L9sIJQ;Uyf!vho#zqH7ZMmU z#jkiCGevgq33zP^?SMiXLf%D(!#KpcOJ}DZRQ`{QVU#e2C;rFjqZgb`C>LY+Mv{PC z$xkO`-D_85>|R}UJUY+G&f+vDo;xl@4hYjga0S{_&cXx5HL^BYf}5cn?oqQG=v{1_UgFbVyxnC9hzFhf&Apn$DVT` zhTT;*zt2v+LjC|)hszPg5`NVxv5P+d z?nV_PmSP`2{KxnqJ&szgW_5_4#+7{0>&b=&61a*ou$&69f=aQHDzS>Hv6?oZk#?eq zZsCIX2(G3BXr^(jrziOxeFiP`JX+}_uAx(Cqt~#J&fr=t1Dmv3T(8w5sD-dui(rcu zMTePeJGpp885hWmb5fqh?L?gZb|!K&X)Ni??H;m_6*hLZCppV5Z%xiJiM`2LhBlb` zq)KhA*UVYo4wh6NGd@en>}84Uq{Bj-w_J_1X%BHHyGX(*$>7N*q%O>xgrQ?_5)C@G zokiAJXp`#WZtxs->6l<8|6WJYCHgxq(HEO~g>V*ysfKzHl9huWc+ISR`Tmjwem;bF z`r2XqhO6dp6Zk{GlEA4$0jp{idydM%Pm#qu=|WX;;ALGNOIchNvyp}z-mP5L=Cjs! zp|(1aN-5+vYg)cpPX+_mZxQ(K|qL@Mvi47SoJm1c6!jA=IOJe0=& gT-Fpa^K`gLJrFgRNBPWQ0%PQ}JE?#!qdD-t0ldt44gdfE diff --git a/build/resources/main/plugin.yml b/build/resources/main/plugin.yml index fc2faf1..5612acc 100644 --- a/build/resources/main/plugin.yml +++ b/build/resources/main/plugin.yml @@ -1,5 +1,5 @@ name: SentinelAntiNuke -version: '0.3.0' +version: '0.3.1' main: me.trouper.sentinel.Sentinel api-version: 1.21 authors: [ TheTrouper ] @@ -12,27 +12,20 @@ softdepend: - ViaBackwards - ViaRewind - Geyser-Spigot -load: STARTUP +load: POSTWORLD permissions: sentinel.admin: description: Allows access to all Sentinel admin commands. default: op children: - sentinel.reload: true - sentinel.config: true sentinel.debug: true + sentinel.extras: true sentinel.staff: description: Allows access to Sentinel staff commands. - default: false + default: op children: sentinel.socialspy: true sentinel.false-positive: true - sentinel.reload: - description: Allows the user to reload the Sentinel plugin. - default: false - sentinel.config: - description: Allows the user to modify the Sentinel configuration. - default: false sentinel.false-positive: description: Allows the user to manage false positives. default: false @@ -47,7 +40,10 @@ permissions: default: false sentinel.debug: description: Allows the user to toggle debug mode. - default: false + default: op + sentinel.extras: + description: Sentinel Extra Features + default: op sentinel.commandblock: description: Allows the user to manage command blocks. default: false @@ -130,6 +126,11 @@ commands: usage: /sentinel permission: sentinel.staff permission-message: You do not have permission to use this command. + sentinelextras: + usage: /sentinelextras [alfa|bravo|charlie|delta|echo|foxtrot|golf|hotel|india|juliett] + description: Extra features for sentinel. + permission: sentinel.extra + permission-message: You do not have permission to use this command. sentinelcallback: description: Callback command for Sentinel. usage: /callback diff --git a/build/tmp/.cache/expanded/expanded.lock b/build/tmp/.cache/expanded/expanded.lock index ea05fba4401b4afbf680a9bcdf398cbf6dcf6a3f..e1cd2a932b0bc74132c83a92b06730c4218adfc7 100644 GIT binary patch literal 17 TcmZSnZuw==9;=P53{U_7KY|4I literal 17 TcmZQJPk#NH+w+$!0~7!NFuDWZ diff --git a/build/tmp/compileJava/previous-compilation-data.bin b/build/tmp/compileJava/previous-compilation-data.bin index a89e01c5e52b2db3e141643fb455bf03e136a6d7..c447dbe5df42512551998edfb5c598fb3b801554 100644 GIT binary patch literal 90180 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 ShLN&__{=bRW(kq94C6n};nryY literal 83814 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 worlds = Bukkit.getWorlds(); + List entities = new ArrayList<>(); + for (World world : worlds) { + entities.addAll(world.getEntities().stream().filter(entity -> entity.getScoreboardTags().contains("./Sentinel/ Block Display")).toList()); + entities.forEach(Entity::remove); + } - // Save - mainConfig.save(); - advConfig.save(); - fpConfig.save(); - strictConfig.save(); - swearConfig.save(); - nbtConfig.save(); - violationConfig.save(); - - whitelist = JsonSerializable.load(cmdWhitelist, WhitelistStorage.class, new WhitelistStorage()); - whitelist.save(); - - log.info("Loading Dictionary (%s)...".formatted(Sentinel.mainConfig.plugin.lang)); - - lang = JsonSerializable.load(LanguageFile.PATH,LanguageFile.class,new LanguageFile()); - lang.save(); - - log.info("Setting License Key"); - license = Auth.getLicenseKey(); + director.launch(); } @Override public void onDisable() { // Plugin shutdown logic PacketEvents.getAPI().terminate(); - log.info("Sentinel has disabled! (%s) Your server is now no longer protected!".formatted(getDescription().getVersion())); + getLogger().info("Sentinel has disabled! (%s) Your server is now no longer protected!".formatted(getDescription().getVersion())); } public static Sentinel getInstance() { return instance; } + + public Director getDirector() { + return director; + } - public static File dataFolder() { - return dataFolder; + public NamespacedKey getNamespace(String key) { + return new NamespacedKey(Sentinel.getInstance(), key); } } diff --git a/src/main/java/me/trouper/sentinel/data/IO.java b/src/main/java/me/trouper/sentinel/data/IO.java new file mode 100644 index 0000000..ae63fda --- /dev/null +++ b/src/main/java/me/trouper/sentinel/data/IO.java @@ -0,0 +1,68 @@ +package me.trouper.sentinel.data; + +import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.data.config.*; +import me.trouper.sentinel.data.config.lang.LanguageFile; +import me.trouper.sentinel.data.storage.ExtraStorage; +import me.trouper.sentinel.data.storage.CommandBlockStorage; + +import java.io.File; + +public class IO { + private final File dataFolder = new File("plugins/SentinelAntiNuke"); + private final File violationcfg = new File(dataFolder,"/violation-config.json"); + private final File cfgfile = new File(dataFolder,"/main-config.json"); + private final File nbtcfg = new File(dataFolder, "/nbt-config.json"); + private final File strctcfg = new File(dataFolder, "/strict.json"); + private final File swrcfg = new File(dataFolder, "/swears.json"); + private final File fpcfg = new File(dataFolder, "/false-positives.json"); + private final File advcfg = new File(dataFolder, "/advanced-config.json"); + private final File cmdWhitelist = new File(dataFolder, "/storage/whitelist.json"); + private final File extraFile = new File(dataFolder, "/storage/extra.json"); + + public LanguageFile lang; + public ViolationConfig violationConfig = JsonSerializable.load(violationcfg, ViolationConfig.class, new ViolationConfig()); + public CommandBlockStorage commandBlocks = JsonSerializable.load(cmdWhitelist, CommandBlockStorage.class, new CommandBlockStorage()); + public ExtraStorage extraStorage = JsonSerializable.load(cmdWhitelist, ExtraStorage.class, new ExtraStorage()); + public MainConfig mainConfig = JsonSerializable.load(cfgfile, MainConfig.class, new MainConfig()); + public FPConfig fpConfig = JsonSerializable.load(fpcfg, FPConfig.class, new FPConfig()); + public SwearsConfig swearConfig = JsonSerializable.load(swrcfg, SwearsConfig.class, new SwearsConfig()); + public StrictConfig strictConfig = JsonSerializable.load(strctcfg, StrictConfig.class, new StrictConfig()); + public NBTConfig nbtConfig = JsonSerializable.load(nbtcfg, NBTConfig.class, new NBTConfig()); + public AdvancedConfig advConfig = JsonSerializable.load(advcfg, AdvancedConfig.class, new AdvancedConfig()); + + public void loadConfig() { + // Init + mainConfig = JsonSerializable.load(cfgfile,MainConfig.class,new MainConfig()); + advConfig = JsonSerializable.load(advcfg,AdvancedConfig.class,new AdvancedConfig()); + fpConfig = JsonSerializable.load(fpcfg,FPConfig.class,new FPConfig()); + strictConfig = JsonSerializable.load(strctcfg,StrictConfig.class,new StrictConfig()); + swearConfig = JsonSerializable.load(swrcfg,SwearsConfig.class,new SwearsConfig()); + nbtConfig = JsonSerializable.load(nbtcfg,NBTConfig.class,new NBTConfig()); + violationConfig = JsonSerializable.load(violationcfg,ViolationConfig.class,new ViolationConfig()); + + // Save + mainConfig.save(); + advConfig.save(); + fpConfig.save(); + strictConfig.save(); + swearConfig.save(); + nbtConfig.save(); + violationConfig.save(); + + commandBlocks = JsonSerializable.load(cmdWhitelist, CommandBlockStorage.class, new CommandBlockStorage()); + extraStorage = JsonSerializable.load(extraFile, ExtraStorage.class, new ExtraStorage()); + commandBlocks.save(); + extraStorage.save(); + + Sentinel.getInstance().getLogger().info("Loading Dictionary (%s)...".formatted(mainConfig.plugin.lang)); + + lang = JsonSerializable.load(LanguageFile.PATH,LanguageFile.class,new LanguageFile()); + lang.save(); + } + + public File getDataFolder() { + return dataFolder; + } +} diff --git a/src/main/java/me/trouper/sentinel/data/WhitelistStorage.java b/src/main/java/me/trouper/sentinel/data/WhitelistStorage.java deleted file mode 100644 index 7e18029..0000000 --- a/src/main/java/me/trouper/sentinel/data/WhitelistStorage.java +++ /dev/null @@ -1,20 +0,0 @@ -package me.trouper.sentinel.data; - -import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.types.WhitelistedBlock; - -import java.io.File; -import java.util.concurrent.ConcurrentLinkedQueue; - -public class WhitelistStorage implements JsonSerializable { - @Override - public File getFile() { - File file = new File(Sentinel.dataFolder(), "/storage/whitelist.json"); - file.getParentFile().mkdirs(); - return file; - } - - public ConcurrentLinkedQueue whitelistedCMDBlocks = new ConcurrentLinkedQueue<>(); - -} diff --git a/src/main/java/me/trouper/sentinel/data/config/AdvancedConfig.java b/src/main/java/me/trouper/sentinel/data/config/AdvancedConfig.java index dda05e5..303f1ac 100644 --- a/src/main/java/me/trouper/sentinel/data/config/AdvancedConfig.java +++ b/src/main/java/me/trouper/sentinel/data/config/AdvancedConfig.java @@ -10,11 +10,11 @@ import java.util.List; import java.util.Map; public class AdvancedConfig implements JsonSerializable { - public static String nonce = "%%__NONCE__%%"; + public transient String nonce = "%%__NONCE__%%"; @Override public File getFile() { - File file = new File(Sentinel.dataFolder(), "/advanced-config.json"); + File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/advanced-config.json"); file.getParentFile().mkdirs(); return file; } diff --git a/src/main/java/me/trouper/sentinel/data/config/FPConfig.java b/src/main/java/me/trouper/sentinel/data/config/FPConfig.java index ec6bb92..e2fd637 100644 --- a/src/main/java/me/trouper/sentinel/data/config/FPConfig.java +++ b/src/main/java/me/trouper/sentinel/data/config/FPConfig.java @@ -12,7 +12,7 @@ public class FPConfig implements JsonSerializable { @Override public File getFile() { - File file = new File(Sentinel.dataFolder(), "/false-positives.json"); + File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/false-positives.json"); file.getParentFile().mkdirs(); return file; } diff --git a/src/main/java/me/trouper/sentinel/data/config/MainConfig.java b/src/main/java/me/trouper/sentinel/data/config/MainConfig.java index 0245c35..f5106af 100644 --- a/src/main/java/me/trouper/sentinel/data/config/MainConfig.java +++ b/src/main/java/me/trouper/sentinel/data/config/MainConfig.java @@ -9,12 +9,12 @@ import java.util.List; public class MainConfig implements JsonSerializable { - public static String user = "%%__USER__%%"; - public static String username = "%%__USERNAME__%%"; + public transient String user = "%%__USER__%%"; + public transient String username = "%%__USERNAME__%%"; @Override public File getFile() { - File file = new File(Sentinel.dataFolder(), "/main-config.json"); + File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/main-config.json"); file.getParentFile().mkdirs(); return file; } @@ -23,8 +23,8 @@ public class MainConfig implements JsonSerializable { public boolean telemetry = true; public Plugin plugin = new Plugin(); - public Chat chat = new Chat(); public BackdoorDetection backdoorDetection = new BackdoorDetection(); + public Chat chat = new Chat(); public class Plugin { public String license = "null"; @@ -34,7 +34,7 @@ public class MainConfig implements JsonSerializable { public List trustedPlayers = Arrays.asList( "049460f7-21cb-42f5-8059-d42752bf406f" ); - + public boolean antiBan = true; public boolean reopCommand = false; public boolean pluginHider = true; public String identifier = "My Server (Edit in main-config.json)"; @@ -96,7 +96,7 @@ public class MainConfig implements JsonSerializable { public boolean enabled = true; public boolean silent = false; public boolean punished = false; - public String regex = "[^A-Za-z0-9\\[,./?><|\\]§()*&^%$#@!~`{}:;'\"-_]"; + public String regex = "[^A-Za-z0-9\\[,./?><|\\]§()*&^%$#@!~`{}:;'\"-_ ]"; public List punishCommands = Arrays.asList( "clearchat", "mute %player% 1m Please refrain from spamming!" diff --git a/src/main/java/me/trouper/sentinel/data/config/NBTConfig.java b/src/main/java/me/trouper/sentinel/data/config/NBTConfig.java index 2c3c2f1..368cc6f 100644 --- a/src/main/java/me/trouper/sentinel/data/config/NBTConfig.java +++ b/src/main/java/me/trouper/sentinel/data/config/NBTConfig.java @@ -8,7 +8,7 @@ import java.io.File; public class NBTConfig implements JsonSerializable { @Override public File getFile() { - File file = new File(Sentinel.dataFolder(), "/nbt-config.json"); + File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/nbt-config.json"); file.getParentFile().mkdirs(); return file; } @@ -17,6 +17,10 @@ public class NBTConfig implements JsonSerializable { public boolean allowLore = true; public boolean allowAttributes = false; public boolean allowPotions = false; + public boolean allowCustomConsumables = false; + public boolean allowCustomTools = false; + public boolean allowBooks = false; + public boolean allowRecursion = true; public int globalMaxEnchant = 5; public int maxMending = 1; public int maxUnbreaking = 3; diff --git a/src/main/java/me/trouper/sentinel/data/config/StrictConfig.java b/src/main/java/me/trouper/sentinel/data/config/StrictConfig.java index 7c00cab..3a1d57e 100644 --- a/src/main/java/me/trouper/sentinel/data/config/StrictConfig.java +++ b/src/main/java/me/trouper/sentinel/data/config/StrictConfig.java @@ -11,7 +11,7 @@ import java.util.List; public class StrictConfig implements JsonSerializable { @Override public File getFile() { - File file = new File(Sentinel.dataFolder(), "/strict.json"); + File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/strict.json"); file.getParentFile().mkdirs(); return file; } diff --git a/src/main/java/me/trouper/sentinel/data/config/SwearsConfig.java b/src/main/java/me/trouper/sentinel/data/config/SwearsConfig.java index 99ec70f..dfa66fb 100644 --- a/src/main/java/me/trouper/sentinel/data/config/SwearsConfig.java +++ b/src/main/java/me/trouper/sentinel/data/config/SwearsConfig.java @@ -10,7 +10,7 @@ import java.util.List; public class SwearsConfig implements JsonSerializable { @Override public File getFile() { - File file = new File(Sentinel.dataFolder(), "/swears.json"); + File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/swears.json"); file.getParentFile().mkdirs(); return file; } diff --git a/src/main/java/me/trouper/sentinel/data/config/ViolationConfig.java b/src/main/java/me/trouper/sentinel/data/config/ViolationConfig.java index 13cec4f..122ebb7 100644 --- a/src/main/java/me/trouper/sentinel/data/config/ViolationConfig.java +++ b/src/main/java/me/trouper/sentinel/data/config/ViolationConfig.java @@ -10,20 +10,107 @@ import java.util.List; public class ViolationConfig implements JsonSerializable { @Override public File getFile() { - File file = new File(Sentinel.dataFolder(), "/violation-config.json"); + File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/violation-config.json"); file.getParentFile().mkdirs(); return file; } public CommandBlockEdit commandBlockEdit = new CommandBlockEdit(); - public CommandBlockExecute commandBlockExecute = new CommandBlockExecute(); + public CommandBlockWhitelist commandBlockWhitelist = new CommandBlockWhitelist(); public CommandBlockMinecartPlace commandBlockMinecartPlace = new CommandBlockMinecartPlace(); public CommandBlockMinecartUse commandBlockMinecartUse = new CommandBlockMinecartUse(); + public CommandBlockMinecartBreak commandBlockMinecartBreak = new CommandBlockMinecartBreak(); public CommandBlockPlace commandBlockPlace = new CommandBlockPlace(); public CommandBlockUse commandBlockUse = new CommandBlockUse(); + public CommandBlockBreak commandBlockBreak = new CommandBlockBreak(); public CommandExecute commandExecute = new CommandExecute(); public CreativeHotbarAction creativeHotbarAction = new CreativeHotbarAction(); + public StructureBlockPlace structureBlockPlace = new StructureBlockPlace(); + public StructureBlockBreak structureBlockBreak = new StructureBlockBreak(); + public StructureBlockUse structureBlockUse = new StructureBlockUse(); + public JigsawBlockPlace jigsawBlockPlace = new JigsawBlockPlace(); + public JigsawBlockBreak jigsawBlockBreak = new JigsawBlockBreak(); + public JigsawBlockUse jigsawBlockUse = new JigsawBlockUse(); + public class JigsawBlockPlace { + public boolean enabled = true; + public boolean deop = true; + public boolean logToDiscord = true; + public boolean punish = false; + public List punishmentCommands = Arrays.asList( + "ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner." + ); + } + + public class JigsawBlockBreak { + public boolean enabled = true; + public boolean deop = true; + public boolean logToDiscord = true; + public boolean punish = false; + public List punishmentCommands = Arrays.asList( + "ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner." + ); + } + + public class JigsawBlockUse { + public boolean enabled = true; + public boolean deop = true; + public boolean logToDiscord = true; + public boolean punish = false; + public List punishmentCommands = Arrays.asList( + "ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner." + ); + } + + public class StructureBlockPlace { + public boolean enabled = true; + public boolean deop = true; + public boolean logToDiscord = true; + public boolean punish = false; + public List punishmentCommands = Arrays.asList( + "ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner." + ); + } + + public class StructureBlockBreak { + public boolean enabled = true; + public boolean deop = true; + public boolean logToDiscord = true; + public boolean punish = false; + public List punishmentCommands = Arrays.asList( + "ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner." + ); + } + + public class StructureBlockUse { + public boolean enabled = true; + public boolean deop = true; + public boolean logToDiscord = true; + public boolean punish = false; + public List punishmentCommands = Arrays.asList( + "ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner." + ); + } + + public class CommandBlockMinecartBreak { + public boolean enabled = true; + public boolean deop = true; + public boolean logToDiscord = true; + public boolean punish = false; + public List punishmentCommands = Arrays.asList( + "ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner." + ); + } + + public class CommandBlockBreak { + public boolean enabled = true; + public boolean deop = true; + public boolean logToDiscord = true; + public boolean punish = false; + public List punishmentCommands = Arrays.asList( + "ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner." + ); + } public class CommandBlockEdit { public boolean enabled = true; @@ -81,7 +168,7 @@ public class ViolationConfig implements JsonSerializable { public boolean logToDiscord = true; public boolean punish = false; public List punishmentCommands = Arrays.asList( - "ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner." + "gamemode survival %player%" ); } @@ -113,7 +200,9 @@ public class ViolationConfig implements JsonSerializable { "pluginmanager", "rl", "reload", - "plugman" + "plugman", + "spigot", + "paper" ); public boolean deop = true; public boolean logToDiscord = true; @@ -137,14 +226,24 @@ public class ViolationConfig implements JsonSerializable { public List punishmentCommands = Arrays.asList( "ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner." ); + public List whitelist = Arrays.asList( + "pluginwithstupidgui:callback" + ); } } - public class CommandBlockExecute { + public class CommandBlockWhitelist { public boolean enabled = true; public boolean destroyBlock = false; + public boolean destroyCart = false; public boolean attemptRestore = true; public boolean logToDiscord = false; + public List disabledCommands = Arrays.asList( + "op", + "deop", + "minecraft:op", + "minecraft:deop" + ); } } diff --git a/src/main/java/me/trouper/sentinel/data/config/lang/LanguageFile.java b/src/main/java/me/trouper/sentinel/data/config/lang/LanguageFile.java index c00f162..02472b6 100644 --- a/src/main/java/me/trouper/sentinel/data/config/lang/LanguageFile.java +++ b/src/main/java/me/trouper/sentinel/data/config/lang/LanguageFile.java @@ -6,7 +6,7 @@ import me.trouper.sentinel.Sentinel; import java.io.File; public class LanguageFile implements JsonSerializable { - public static final File PATH = new File(Sentinel.dataFolder(), "/lang/" + Sentinel.mainConfig.plugin.lang); + public static final File PATH = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/lang/" + Sentinel.getInstance().getDirector().io.mainConfig.plugin.lang); public LanguageFile() {} @Override @@ -180,13 +180,17 @@ public class LanguageFile implements JsonSerializable { public String use = "use"; public String edit = "edit"; public String place = "place"; + public String brake = "break"; public String run = "run"; public String grab = "grab"; // Types public String commandBlock = "Command Block"; - public String minecartCommandBlock = "Minecart Command Block"; + public String structureBlock = "Structure Block"; + public String jigsawBlock = "Jigsaw Block"; + public String commandMinecart = "Command Minecart"; public String commandBlockWhitelist = "Command Block Whitelist"; + public String commandBlockRestriction = "Command Block Restriction"; public String specificCommand = "Specific Command"; public String loggedCommand = "Logged Command"; public String dangerousCommand = "Dangerous Command"; diff --git a/src/main/java/me/trouper/sentinel/data/storage/CommandBlockStorage.java b/src/main/java/me/trouper/sentinel/data/storage/CommandBlockStorage.java new file mode 100644 index 0000000..8390fdb --- /dev/null +++ b/src/main/java/me/trouper/sentinel/data/storage/CommandBlockStorage.java @@ -0,0 +1,35 @@ +package me.trouper.sentinel.data.storage; + +import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.data.types.CommandBlockHolder; +import me.trouper.sentinel.data.types.SerialLocation; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CommandBlockStorage implements JsonSerializable { + @Override + public File getFile() { + File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/storage/whitelist.json"); + file.getParentFile().mkdirs(); + return file; + } + + public List holders = new ArrayList<>() { + @Override + public boolean add(CommandBlockHolder holder) { + for (CommandBlockHolder existing : holders) { + if (existing.loc().isSameLocation(holder.loc())) { + super.remove(existing); + } + } + + return super.add(holder); + } + }; + +} diff --git a/src/main/java/me/trouper/sentinel/data/storage/ExtraStorage.java b/src/main/java/me/trouper/sentinel/data/storage/ExtraStorage.java new file mode 100644 index 0000000..c419de4 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/data/storage/ExtraStorage.java @@ -0,0 +1,20 @@ +package me.trouper.sentinel.data.storage; + +import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.data.types.SerialLocation; + +import java.io.File; +import java.util.*; + +public class ExtraStorage implements JsonSerializable { + @Override + public File getFile() { + File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/storage/extra.json"); + file.getParentFile().mkdirs(); + return file; + } + + public Map shadowRealm = new HashMap<>(); + +} diff --git a/src/main/java/me/trouper/sentinel/data/types/CMDBlockType.java b/src/main/java/me/trouper/sentinel/data/types/CMDBlockType.java deleted file mode 100644 index 3fcd6ca..0000000 --- a/src/main/java/me/trouper/sentinel/data/types/CMDBlockType.java +++ /dev/null @@ -1,7 +0,0 @@ -package me.trouper.sentinel.data.types; - -public enum CMDBlockType { - CHAIN, - REPEAT, - IMPULSE, -} diff --git a/src/main/java/me/trouper/sentinel/data/types/CommandBlockHolder.java b/src/main/java/me/trouper/sentinel/data/types/CommandBlockHolder.java new file mode 100644 index 0000000..eb3cfad --- /dev/null +++ b/src/main/java/me/trouper/sentinel/data/types/CommandBlockHolder.java @@ -0,0 +1,199 @@ +package me.trouper.sentinel.data.types; + +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.CommandBlock; +import org.bukkit.command.Command; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.entity.minecart.CommandMinecart; +import org.bukkit.persistence.PersistentDataType; + +public class CommandBlockHolder { + + private final String owner; + private final SerialLocation loc; + private final String facing; + private final String type; + private final boolean auto; + private final boolean conditional; + private final String command; + private boolean whitelisted; + + public CommandBlockHolder(String owner, SerialLocation loc, String facing, String type, boolean auto, boolean conditional, String command) { + this.owner = owner; + this.loc = loc; + this.facing = facing; + this.type = type; + this.auto = auto; + this.conditional = conditional; + this.command = command; + this.whitelisted = false; + } + + public String owner() { + return owner; + } + + public SerialLocation loc() { + return loc; + } + + public String facing() { + return facing; + } + + public String type() { + return type; + } + + public boolean auto() { + return auto; + } + + public boolean conditional() { + return conditional; + } + + public String command() { + return command; + } + + public boolean present() { + if (this.loc.isUUID()) { + Entity cart = Bukkit.getEntity(this.loc.toUIID()); + if (!(cart instanceof CommandMinecart cm)) return false; + return this.command.equals(cm.getCommand()); + } else { + Location where = loc.translate(); + boolean preLoaded = where.isChunkLoaded(); + where.getChunk().load(false); + Block b = where.getBlock(); + if (!(b.getState() instanceof CommandBlock c) || !(b.getBlockData() instanceof org.bukkit.block.data.type.CommandBlock cb)) { + ServerUtils.verbose("Block is not present due to not being a command block."); + if (!this.whitelisted) this.delete(); + return false; + } + if (!this.command.equals(c.getCommand())) { + ServerUtils.verbose("Block is not present due to command mismatch. Should be '%s', is '%s'",this.command,c.getCommand()); + if (!this.whitelisted) this.delete(); + return false; + } + if (this.conditional != cb.isConditional()) { + ServerUtils.verbose("Block is not present due to conditional mismatch."); + if (!this.whitelisted) this.delete(); + return false; + } + if (!this.getType().equals(c.getType())) { + ServerUtils.verbose("Block is not present due to type mismatch. Should be '%s', is '%s'",this.type,c.getType()); + if (!this.whitelisted) this.delete(); + return false; + } + if (this.auto != (c.getPersistentDataContainer().getOrDefault(Sentinel.getInstance().getNamespace("auto"), PersistentDataType.BYTE,(byte) 0) == (byte) 1)) { + ServerUtils.verbose("Block is not present due to auto mismatch."); + if (!this.whitelisted) this.delete(); + return false; + } + if (!preLoaded) where.getChunk().unload(); + return true; + } + } + + public boolean whitelisted() { + return whitelisted; + } + + public CommandBlockHolder setWhitelisted(boolean whitelisted) { + this.whitelisted = whitelisted; + return this; + } + + public CommandBlockHolder addAndWhitelist() { + return setWhitelisted(true).add(); + } + + public BlockFace getDirection() { + try { + return BlockFace.valueOf(facing.toUpperCase()); + } catch (IllegalArgumentException e) { + return BlockFace.NORTH; + } + } + + public Material getType() { + return switch (this.type) { + case "COMMAND_BLOCK" -> Material.COMMAND_BLOCK; + case "REPEATING_COMMAND_BLOCK" -> Material.REPEATING_COMMAND_BLOCK; + case "CHAIN_COMMAND_BLOCK" -> Material.CHAIN_COMMAND_BLOCK; + case "COMMAND_BLOCK_MINECART" -> Material.COMMAND_BLOCK_MINECART; + default -> throw new IllegalArgumentException("Unknown command block type: " + type); + }; + } + + public void destroy() { + SerialLocation.translate(this.loc).getBlock().setType(Material.AIR); + if (!whitelisted) delete(); + } + + public boolean restore() { + if (Material.COMMAND_BLOCK_MINECART.equals(this.getType())) { + ServerUtils.verbose("Cannot restore minecarts yet."); + return false; + } + + if (this.present()) return false; + + Block block = SerialLocation.translate(this.loc).getBlock(); + block.setType(this.getType()); + if (!ServerUtils.isCommandBlock(block)) { + ServerUtils.verbose("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(this.command()); + block.setType(this.getType()); + block.getState().update(true, false); + + org.bukkit.block.data.type.CommandBlock conditional = (org.bukkit.block.data.type.CommandBlock) cb.getBlock().getBlockData(); + ServerUtils.verbose("Direction is " + this.getDirection()); + ServerUtils.verbose("Conditional is " + this.conditional); + + conditional.setFacing(this.getDirection()); + conditional.setConditional(this.conditional); + + cb.setBlockData(conditional); + + cb.getPersistentDataContainer().set( + Sentinel.getInstance().getNamespace("auto"), + PersistentDataType.BYTE, + this.auto ? (byte) 1 : (byte) 0 + ); + + cb.update(true,false); + ServerUtils.verbose("Command block at " + this.loc.toString() + " has been restored."); + return true; + } + + public boolean isCart() { + return loc.isUUID(); + } + + public CommandBlockHolder add() { + Sentinel.getInstance().getDirector().io.commandBlocks.holders.add(this); + Sentinel.getInstance().getDirector().io.commandBlocks.save(); + return this; + } + + public void delete() { + Sentinel.getInstance().getDirector().io.commandBlocks.holders.removeIf(h->h.loc.isSameLocation(this.loc)); + Sentinel.getInstance().getDirector().io.commandBlocks.save(); + } +} diff --git a/src/main/java/me/trouper/sentinel/data/Emojis.java b/src/main/java/me/trouper/sentinel/data/types/Emojis.java similarity index 98% rename from src/main/java/me/trouper/sentinel/data/Emojis.java rename to src/main/java/me/trouper/sentinel/data/types/Emojis.java index 13a1d0c..bab6afd 100644 --- a/src/main/java/me/trouper/sentinel/data/Emojis.java +++ b/src/main/java/me/trouper/sentinel/data/types/Emojis.java @@ -1,4 +1,4 @@ -package me.trouper.sentinel.data; +package me.trouper.sentinel.data.types; public class Emojis { public static String space = "<:space:1210008300515762238>"; diff --git a/src/main/java/me/trouper/sentinel/data/types/IPLocation.java b/src/main/java/me/trouper/sentinel/data/types/IPLocation.java new file mode 100644 index 0000000..c69f12e --- /dev/null +++ b/src/main/java/me/trouper/sentinel/data/types/IPLocation.java @@ -0,0 +1,180 @@ +package me.trouper.sentinel.data.types; + +public class IPLocation { + private String country; + private String countryCode; + private String region; + private String regionCode; + private String city; + private String district; + private String zip; + private String lat; + private String lon; + private String timezone; + private String isp; + private String org; + private String as; + private String reverse; + private boolean isMobile; + private boolean isProxied; + private boolean isHosted; + + + public IPLocation(String country, String countryCode, String region, String regionCode, String city, String district, String zip, String lat, String lon, String timezone, String isp, String org, String as, String reverse, boolean isMobile, boolean isProxied, boolean isHosted) { + this.country = country; + this.countryCode = countryCode; + this.region = region; + this.regionCode = regionCode; + this.city = city; + this.district = district; + this.zip = zip; + this.lat = lat; + this.lon = lon; + this.timezone = timezone; + this.isp = isp; + this.org = org; + this.as = as; + this.reverse = reverse; + this.isMobile = isMobile; + this.isProxied = isProxied; + this.isHosted = isHosted; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getCountryCode() { + return countryCode; + } + + public void setCountryCode(String countryCode) { + this.countryCode = countryCode; + } + + public String getRegion() { + return region; + } + + public void setRegion(String region) { + this.region = region; + } + + public String getRegionCode() { + return regionCode; + } + + public void setRegionCode(String regionCode) { + this.regionCode = regionCode; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getDistrict() { + return district; + } + + public void setDistrict(String district) { + this.district = district; + } + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } + + public String getLat() { + return lat; + } + + public void setLat(String lat) { + this.lat = lat; + } + + public String getLon() { + return lon; + } + + public void setLon(String lon) { + this.lon = lon; + } + + public String getTimezone() { + return timezone; + } + + public void setTimezone(String timezone) { + this.timezone = timezone; + } + + public String getIsp() { + return isp; + } + + public void setIsp(String isp) { + this.isp = isp; + } + + public String getOrg() { + return org; + } + + public void setOrg(String org) { + this.org = org; + } + + public String getAs() { + return as; + } + + public void setAs(String as) { + this.as = as; + } + + public String getReverse() { + return reverse; + } + + public void setReverse(String reverse) { + this.reverse = reverse; + } + + public boolean isMobile() { + return isMobile; + } + + public void setMobile(boolean mobile) { + isMobile = mobile; + } + + public boolean isProxied() { + return isProxied; + } + + public void setProxied(boolean proxied) { + isProxied = proxied; + } + + public boolean isHosted() { + return isHosted; + } + + public void setHosted(boolean hosted) { + isHosted = hosted; + } +} + + diff --git a/src/main/java/me/trouper/sentinel/data/types/Location.java b/src/main/java/me/trouper/sentinel/data/types/Location.java deleted file mode 100644 index cbf4c81..0000000 --- a/src/main/java/me/trouper/sentinel/data/types/Location.java +++ /dev/null @@ -1,4 +0,0 @@ -package me.trouper.sentinel.data.types; - -public record Location(String world, double x, double y,double z) { -} diff --git a/src/main/java/me/trouper/sentinel/data/types/Selection.java b/src/main/java/me/trouper/sentinel/data/types/Selection.java new file mode 100644 index 0000000..7d2f63c --- /dev/null +++ b/src/main/java/me/trouper/sentinel/data/types/Selection.java @@ -0,0 +1,68 @@ +package me.trouper.sentinel.data.types; + +import me.trouper.sentinel.utils.display.BlockDisplayRaytracer; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +public class Selection { + + private Location pos1; + private Location pos2; + + public void setPos1(Location loc) { this.pos1 = loc; } + public void setPos2(Location loc) { this.pos2 = loc; } + + public Location getPos1() { return pos1; } + public Location getPos2() { return pos2; } + + public boolean isComplete() { + return pos1 != null && pos2 != null; + } + + public void forEachBlock(Consumer action) { + for (Block block : getBlocks()) { + action.accept(block); + } + } + + public Set getBlocks() { + Location pos1 = this.getPos1(); + Location pos2 = this.getPos2(); + + World world = pos1.getWorld(); + int minX = Math.min(pos1.getBlockX(), pos2.getBlockX()); + int minY = Math.min(pos1.getBlockY(), pos2.getBlockY()); + int minZ = Math.min(pos1.getBlockZ(), pos2.getBlockZ()); + int maxX = Math.max(pos1.getBlockX(), pos2.getBlockX()); + int maxY = Math.max(pos1.getBlockY(), pos2.getBlockY()); + int maxZ = Math.max(pos1.getBlockZ(), pos2.getBlockZ()); + + Set blocks = new HashSet<>(); + + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + blocks.add(world.getBlockAt(x,y,z)); + } + } + } + + return blocks; + } + + public void display(Player beholder) { + if (!beholder.isOnline() + || this.pos1 == null + || this.pos2 == null + || (beholder.getLocation().distance(this.pos1) > 64 && beholder.getLocation().distance(this.pos2) > 64)) return; + BlockDisplayRaytracer.outline(Material.LIGHT_BLUE_STAINED_GLASS,this.getPos1(),this.getPos2(),0.1,2, List.of(beholder)); + } +} diff --git a/src/main/java/me/trouper/sentinel/data/types/SerialLocation.java b/src/main/java/me/trouper/sentinel/data/types/SerialLocation.java new file mode 100644 index 0000000..5122536 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/data/types/SerialLocation.java @@ -0,0 +1,65 @@ +package me.trouper.sentinel.data.types; + +import me.trouper.sentinel.utils.MathUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.UUID; + +public record SerialLocation(String world, double x, double y, double z) { + public static Location translate(SerialLocation loc) { + World w = Bukkit.getWorld(loc.world()); + return new Location(w,loc.x(),loc.y(),loc.z()); + } + + public static SerialLocation translate(Location loc) { + return new SerialLocation(loc.getWorld().getName(),loc.x(),loc.y(),loc.z()); + } + + public Location translate() { + return translate(this); + } + + public UUID toUIID() { + return toUUID(this); + } + + public boolean isUUID() { + return this.world.equals("./Sentinel/ UUID$"); + } + + public boolean isSameLocation(Location loc) { + if (this.isUUID()) return false; + Location thisLoc = this.translate(); + return thisLoc.getWorld().equals(loc.getWorld()) && + thisLoc.getBlockX() == loc.getBlockX() && + thisLoc.getBlockY() == loc.getBlockY() && + thisLoc.getBlockZ() == loc.getBlockZ(); + } + + public boolean isSameLocation(SerialLocation loc) { + if (this.isUUID() && loc.isUUID()) { + return loc.toUIID().equals(this.toUIID()); + } else if (this.isUUID() ^ loc.isUUID()) { + return false; + } + return this.world.equals(loc.world) && + (int) this.x == (int) loc.x && + (int) this.y == (int) loc.y && + (int) this.z == (int) loc.z; + } + + public static UUID toUUID(SerialLocation loc) { + if (!loc.world.equals("./Sentinel/ UUID$")) throw new IllegalArgumentException("You can only get UUIDs from locations which hold them."); + return MathUtils.doublesToUuid(new double[]{loc.x,loc.y,loc.z}); + } + + public static SerialLocation uuidToLocation(UUID uuid) { + double[] doubles = MathUtils.uuidToDoubles(uuid); + return new SerialLocation("./Sentinel/ UUID$",doubles[0],doubles[1],doubles[2]); + } +} diff --git a/src/main/java/me/trouper/sentinel/data/types/Test.java b/src/main/java/me/trouper/sentinel/data/types/Test.java new file mode 100644 index 0000000..f2e95f3 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/data/types/Test.java @@ -0,0 +1,12 @@ +package me.trouper.sentinel.data.types; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.UUID; + +public class Test { + + + +} diff --git a/src/main/java/me/trouper/sentinel/data/types/WhitelistedBlock.java b/src/main/java/me/trouper/sentinel/data/types/WhitelistedBlock.java deleted file mode 100644 index 46b272a..0000000 --- a/src/main/java/me/trouper/sentinel/data/types/WhitelistedBlock.java +++ /dev/null @@ -1,15 +0,0 @@ -package me.trouper.sentinel.data.types; - -import org.bukkit.Bukkit; -import org.bukkit.World; - -public record WhitelistedBlock(String owner, Location loc, String type, boolean active, String command) { - - public static org.bukkit.Location fromSerialized(Location loc) { - World w = Bukkit.getWorld(loc.world()); - return new org.bukkit.Location(w,loc.x(),loc.y(),loc.z()); - } - public static Location serialize(org.bukkit.Location loc) { - return new Location(loc.getWorld().getName(),loc.x(),loc.y(),loc.z()); - } -} diff --git a/src/main/java/me/trouper/sentinel/server/commands/CallbackCommand.java b/src/main/java/me/trouper/sentinel/server/commands/CallbackCommand.java index d2f822f..4d707e0 100644 --- a/src/main/java/me/trouper/sentinel/server/commands/CallbackCommand.java +++ b/src/main/java/me/trouper/sentinel/server/commands/CallbackCommand.java @@ -7,7 +7,7 @@ import io.github.itzispyder.pdk.commands.Permission; import io.github.itzispyder.pdk.commands.completions.CompletionBuilder; import io.github.itzispyder.pdk.utils.misc.Cooldown; import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.functions.helpers.FalsePositiveReporting; +import me.trouper.sentinel.server.functions.helpers.ReportHandler; import me.trouper.sentinel.server.functions.helpers.Report; import me.trouper.sentinel.utils.PlayerUtils; import me.trouper.sentinel.utils.Text; @@ -29,18 +29,18 @@ public class CallbackCommand implements CustomCommand { case "fpreport" -> { if (!PlayerUtils.checkPermission(sender,"sentinel.callbacks.fpreport")) return; if (fpReportCooldown.isOnCooldown(p.getUniqueId()) && !p.isOp()) { - p.sendMessage(Text.prefix(Sentinel.lang.cooldown.onCooldown + fpReportCooldown.getCooldown(p.getUniqueId()))); + p.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.cooldown.onCooldown + fpReportCooldown.getCooldown(p.getUniqueId()))); return; } long id = args.get(1).toLong(); - Report report = FalsePositiveReporting.reports.get(id); + Report report = Sentinel.getInstance().getDirector().reportHandler.reports.get(id); if (report == null) { - p.sendMessage(Text.prefix(Sentinel.lang.reports.noReport)); + p.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.reports.noReport)); return; } - p.sendMessage(Text.prefix(Sentinel.lang.reports.reportingFalsePositive)); - FalsePositiveReporting.sendReport(p,report); - p.sendMessage(Text.prefix(Sentinel.lang.reports.falsePositiveSuccess)); + p.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.reports.reportingFalsePositive)); + Sentinel.getInstance().getDirector().reportHandler.sendReport(p,report); + p.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.reports.falsePositiveSuccess)); } } } diff --git a/src/main/java/me/trouper/sentinel/server/commands/ExtraCommand.java b/src/main/java/me/trouper/sentinel/server/commands/ExtraCommand.java new file mode 100644 index 0000000..0eefffa --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/commands/ExtraCommand.java @@ -0,0 +1,237 @@ +package me.trouper.sentinel.server.commands; + +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; +import com.github.retrooper.packetevents.util.Vector3d; +import com.github.retrooper.packetevents.wrapper.play.server.*; +import io.github.itzispyder.pdk.commands.Args; +import io.github.itzispyder.pdk.commands.CommandRegistry; +import io.github.itzispyder.pdk.commands.CustomCommand; +import io.github.itzispyder.pdk.commands.Permission; +import io.github.itzispyder.pdk.commands.completions.CompletionBuilder; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.data.types.IPLocation; +import me.trouper.sentinel.data.types.SerialLocation; +import me.trouper.sentinel.server.events.extras.ShadowRealmEvents; +import me.trouper.sentinel.utils.IPUtils; +import me.trouper.sentinel.utils.ImageUtils; +import me.trouper.sentinel.utils.Random; +import me.trouper.sentinel.utils.Text; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerKickEvent; + +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +@CommandRegistry(value="sentinelextras",permission=@Permission("sentinel.extras")) +public class ExtraCommand implements CustomCommand { + @Override + public void dispatchCommand(CommandSender sender, Command command, String s, Args args) { + if (args.getSize() < 2) { + sender.sendMessage(Text.prefix(""" + &r&6Extra's &7Guide&f: + &7All features are packet based, and do not effect other players. + &bSyntax&f: &7/sentinelextras + &7Features&f: + &7 - &bfree&f: &7Release player from shadow realm. + &7 - &balfa&f: &7Reliable, crash player. + &7 - &bbravo&f: &7Reliable, send player to shadow realm. + &7 - &bcharlie&f: &7Reliable, delete player. + &7 - &bdelta&f: &7Reliable, Lock player's mouse. + &7 - &becho&f: &7Unreliable, Inflate player's log. + &7 - &bfoxtrot&f: &7Unreliable, Spam player with titles. + &7 - &bgolf&f: &7Reliable, corrupt player chunks. + &7 - &bhotel&f: &7Reliable, spam player with bogus entities. + &7 - &bindia&f: &7Reliable, kick with no back to server list button. + &7 - &bjuliett&f: &7Reliable, make player's screen dim rapidly. + """)); + return; + } + String target = args.get(1).toString(); + Player victim = Bukkit.getPlayer(target); + if (victim == null || !victim.isOnline()) { + sender.sendMessage("You must pick an online player."); + return; + } + switch (args.get(0).toString()) { + case "free" -> freePlayer(sender, victim, target); + case "alfa" -> crashPlayer(sender, victim, target); + case "bravo" -> sendToShadowRealm(sender, victim, target); + case "charlie" -> deletePlayer(sender, victim, target); + case "delta" -> freezePlayer(sender, victim, target); + case "echo" -> inflatePlayerLog(sender, victim, target); + case "foxtrot" -> spamPlayerWithTitles(sender, victim, target); + case "golf" -> corruptPlayerChunks(sender, victim, target); + case "hotel" -> spamPlayerWithEntities(sender, victim, target); + case "india" -> kickPlayerWithoutBackButton(sender, victim, target); + case "juliett" -> makePlayerDrowsy(sender,victim,target); + } + } + + @Override + public void dispatchCompletions(CommandSender commandSender, Command command, String s, CompletionBuilder b) { + b.then(b.arg("info")); + b.then(b.arg("free", "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel", "india", "juliett", "kilo", "lima").then( + b.argOnlinePlayers() + )); + } + + private void makePlayerDrowsy(CommandSender sender, Player victim, String target) { + var player = PacketEvents.getAPI().getPlayerManager().getUser(victim); + Bukkit.getScheduler().runTaskTimerAsynchronously(Sentinel.getInstance(), (t) -> { + if (!victim.isOnline()) t.cancel(); + player.sendPacket(new WrapperPlayServerEntityAnimation(victim.getEntityId(), WrapperPlayServerEntityAnimation.EntityAnimationType.WAKE_UP)); + }, 1, 1); + sender.sendMessage(Text.prefix("%s is getting very eepy.".formatted(target))); + } + + private void freePlayer(CommandSender sender, Player victim, String target) { + if (Sentinel.getInstance().getDirector().io.extraStorage.shadowRealm.containsKey(victim.getUniqueId())) { + Location to = SerialLocation.translate(Sentinel.getInstance().getDirector().io.extraStorage.shadowRealm.get(victim.getUniqueId())); + Sentinel.getInstance().getDirector().io.extraStorage.shadowRealm.remove(victim.getUniqueId()); + Sentinel.getInstance().getDirector().io.extraStorage.save(); + victim.teleport(to); + } + sender.sendMessage(Text.prefix("Released %s.".formatted(target))); + } + + private void crashPlayer(CommandSender sender, Player victim, String target) { + var player = PacketEvents.getAPI().getPlayerManager().getUser(victim); + player.sendPacket(new WrapperPlayServerUpdateViewDistance(4000)); + sender.sendMessage(Text.prefix("Crashing %s.".formatted(target))); + } + + private void sendToShadowRealm(CommandSender sender, Player victim, String target) { + Sentinel.getInstance().getDirector().io.extraStorage.shadowRealm.put(victim.getUniqueId(), SerialLocation.translate(victim.getLocation())); + Sentinel.getInstance().getDirector().io.extraStorage.save(); + ShadowRealmEvents.enforce(victim); + sender.sendMessage(Text.prefix("Sending %s to the shadow realm.".formatted(target))); + } + + private void deletePlayer(CommandSender sender, Player victim, String target) { + var player = PacketEvents.getAPI().getPlayerManager().getUser(victim); + player.sendPacket(new WrapperPlayServerDestroyEntities(victim.getEntityId())); + sender.sendMessage(Text.prefix("Deleting %s.".formatted(target))); + } + + private void freezePlayer(CommandSender sender, Player victim, String target) { + var player = PacketEvents.getAPI().getPlayerManager().getUser(victim); + Bukkit.getScheduler().runTaskTimerAsynchronously(Sentinel.getInstance(), (t) -> { + if (!victim.isOnline()) t.cancel(); + for (int i = 0; i < 35 * 9; i++) { + player.sendPacket(new WrapperPlayServerCloseWindow()); + player.sendPacket(new WrapperPlayServerChangeGameState(WrapperPlayServerChangeGameState.Reason.DEMO_EVENT, 0)); + } + }, 1, 1); + sender.sendMessage(Text.prefix("Freezing %s.".formatted(target))); + } + + private void inflatePlayerLog(CommandSender sender, Player victim, String target) { + Bukkit.getScheduler().runTaskTimerAsynchronously(Sentinel.getInstance(), (t) -> { + if (!victim.isOnline()) t.cancel(); + for (int i = 0; i < 4000; i++) { + victim.sendMessage(":3 Baiiiiii!!!!"); + } + }, 1, 1); + sender.sendMessage(Text.prefix("Filling the logs of %s.".formatted(target))); + } + + private void spamPlayerWithTitles(CommandSender sender, Player victim, String target) { + var player = PacketEvents.getAPI().getPlayerManager().getUser(victim); + Bukkit.getScheduler().runTaskTimerAsynchronously(Sentinel.getInstance(), (t) -> { + if (!victim.isOnline()) t.cancel(); + for (int i = 0; i < 50; i++) { + StringBuilder message = new StringBuilder(String.valueOf(Random.generateID())); + for (int j = 0; j < 256; j++) { + message.append(String.valueOf(Random.generateID())); + } + player.sendPacket(new WrapperPlayServerTitle( + WrapperPlayServerTitle.TitleAction.SET_TITLE, + Component.text(message.toString()).style(Style.style().color(NamedTextColor.DARK_GREEN).decorate(TextDecoration.OBFUSCATED).build()).asComponent(), + Component.text(message.toString()).style(Style.style().color(NamedTextColor.DARK_GREEN).decorate(TextDecoration.OBFUSCATED).build()).asComponent(), + Component.text(message.toString()).style(Style.style().color(NamedTextColor.DARK_GREEN).decorate(TextDecoration.OBFUSCATED).build()).asComponent(), + 0, 10000, 0 + )); + } + }, 1, 1); + sender.sendMessage(Text.prefix("Flooding %s's screen.".formatted(target))); + } + + private void corruptPlayerChunks(CommandSender sender, Player victim, String target) { + var player = PacketEvents.getAPI().getPlayerManager().getUser(victim); + Bukkit.getScheduler().runTaskTimerAsynchronously(Sentinel.getInstance(), (t) -> { + if (!victim.isOnline()) t.cancel(); + for (int i = 0; i < 50; i++) { + int chunkX = (victim.getLocation().getBlockX() >> 4) + i; + int chunkZ = (victim.getLocation().getBlockZ() >> 4) + i; + player.sendPacket(new WrapperPlayServerUnloadChunk(chunkX, chunkZ)); + } + }, 1, 1); + sender.sendMessage(Text.prefix("Corrupting %s's chunks.".formatted(target))); + } + + private void spamPlayerWithEntities(CommandSender sender, Player victim, String target) { + var player = PacketEvents.getAPI().getPlayerManager().getUser(victim); + AtomicInteger entityId = new AtomicInteger(999999); + Bukkit.getScheduler().runTaskTimerAsynchronously(Sentinel.getInstance(), (t) -> { + if (!victim.isOnline()) t.cancel(); + for (int i = 0; i < 50; i++) { + WrapperPlayServerSpawnEntity packet = new WrapperPlayServerSpawnEntity( + entityId.getAndIncrement(), + Optional.of(UUID.randomUUID()), + EntityTypes.ENDER_DRAGON, + new Vector3d(victim.getLocation().getX(), victim.getLocation().getY(), victim.getLocation().getZ()), + 0F, + 0F, + 0F, + 0, + Optional.of(new Vector3d(0, 0, 0)) + ); + player.sendPacket(packet); + } + }, 1, 1); + sender.sendMessage(Text.prefix("Summoning entities on %s.".formatted(target))); + } + + private void kickPlayerWithoutBackButton(CommandSender sender, Player victim, String target) { + String beforeLines = "\n".repeat(15 * 100 + 3); + String afterLines = "\n".repeat(15 * 100); + + Component image = Component.text("\n"); + for (Component component : ImageUtils.makeImage("https://r2.e-z.host/d440b58a-ba90-4839-8df6-8bba298cf817/x1ksxaas.png")) { + image = image.appendNewline().append(component); + } + String header = "Sorry %1$s!\nLooks like you're a griefer... \n...and this is a decoy server\nYour presence has been recorded. \n\nHow's the weather in %2$s, %3$s? \n"; + String footer = "\n\nWant to try again?\n Nope. No back to server list for you.\n\nCopyright © 2025 Sentinel Anti Nuke. All rights reserved.\n"; + String name = victim.getName(); + String ip = IPUtils.extractIp(victim.getAddress().getAddress()); + IPLocation location = IPUtils.getLocation(ip); + String region = location.getRegion(); + String city = location.getCity(); + victim.kick(Component.text(beforeLines) + .append(Component.text( + header.formatted( + name, + city, + region + ))) + .append(image) + .append(Component.text( + footer + afterLines + ) + ), + PlayerKickEvent.Cause.ILLEGAL_ACTION); + sender.sendMessage(Text.prefix("Kicked %1$s and removed the back to server list button. Their IP was %2$s (%3$s %4$s)".formatted(target, ip, city, region))); + } + + +} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/server/commands/MessageCommand.java b/src/main/java/me/trouper/sentinel/server/commands/MessageCommand.java index 9b79f42..51bdb46 100644 --- a/src/main/java/me/trouper/sentinel/server/commands/MessageCommand.java +++ b/src/main/java/me/trouper/sentinel/server/commands/MessageCommand.java @@ -6,7 +6,7 @@ import io.github.itzispyder.pdk.commands.CustomCommand; import io.github.itzispyder.pdk.commands.Permission; import io.github.itzispyder.pdk.commands.completions.CompletionBuilder; import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.functions.helpers.Message; +import me.trouper.sentinel.server.functions.helpers.MessageHandler; import me.trouper.sentinel.utils.PlayerUtils; import me.trouper.sentinel.utils.Text; import org.bukkit.Bukkit; @@ -24,11 +24,11 @@ public class MessageCommand implements CustomCommand { Player p = (Player) sender; Player r = null; if (args.getSize() == 0) { - p.sendMessage(Text.prefix(Sentinel.lang.playerInteraction.noOnlinePlayer)); + p.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.playerInteraction.noOnlinePlayer)); return; } if (args.getSize() == 1) { - p.sendMessage(Text.prefix(Sentinel.lang.playerInteraction.noMessageProvided)); + p.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.playerInteraction.noMessageProvided)); return; } r = Bukkit.getPlayer(args.get(0).toString()); @@ -36,8 +36,8 @@ public class MessageCommand implements CustomCommand { String msg = args.getAll(1).toString().trim(); if (PlayerUtils.checkPermission(sender,"sentinel.message") && r != null) { - Message.messagePlayer(p,r,msg); - } else if (r == null) p.sendMessage(Text.prefix((Sentinel.lang.playerInteraction.noOnlinePlayer))); + Sentinel.getInstance().getDirector().messageHandler.messagePlayer(p,r,msg); + } else if (r == null) p.sendMessage(Text.prefix((Sentinel.getInstance().getDirector().io.lang.playerInteraction.noOnlinePlayer))); } @Override diff --git a/src/main/java/me/trouper/sentinel/server/commands/ReopCommand.java b/src/main/java/me/trouper/sentinel/server/commands/ReopCommand.java index c8d2586..1061234 100644 --- a/src/main/java/me/trouper/sentinel/server/commands/ReopCommand.java +++ b/src/main/java/me/trouper/sentinel/server/commands/ReopCommand.java @@ -16,18 +16,18 @@ public class ReopCommand implements CustomCommand { @Override public void dispatchCommand(CommandSender sender, Command command, String s, Args args) { Player p = (Player) sender; - if (PlayerUtils.isTrusted(p) && Sentinel.mainConfig.plugin.reopCommand) { + if (PlayerUtils.isTrusted(p) && Sentinel.getInstance().getDirector().io.mainConfig.plugin.reopCommand) { if (!p.isOp()) { - p.sendMessage(Text.prefix(Sentinel.lang.permissions.elevatingPerms)); - Sentinel.log.info(Sentinel.lang.permissions.logElevatingPerms.formatted(p.getName())); + p.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.permissions.elevatingPerms)); + Sentinel.getInstance().getLogger().info(Sentinel.getInstance().getDirector().io.lang.permissions.logElevatingPerms.formatted(p.getName())); p.setOp(true); } else { - p.sendMessage(Text.prefix(Sentinel.lang.permissions.alreadyOp)); - Sentinel.log.info(Sentinel.lang.permissions.logAlreadyOp.formatted(p.getName())); + p.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.permissions.alreadyOp)); + Sentinel.getInstance().getLogger().info(Sentinel.getInstance().getDirector().io.lang.permissions.logAlreadyOp.formatted(p.getName())); p.setOp(true); } } else { - p.sendMessage(Text.prefix(Sentinel.lang.permissions.noTrust)); + p.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.permissions.noTrust)); } } diff --git a/src/main/java/me/trouper/sentinel/server/commands/ReplyCommand.java b/src/main/java/me/trouper/sentinel/server/commands/ReplyCommand.java index 8eeaf32..44f878d 100644 --- a/src/main/java/me/trouper/sentinel/server/commands/ReplyCommand.java +++ b/src/main/java/me/trouper/sentinel/server/commands/ReplyCommand.java @@ -6,7 +6,7 @@ import io.github.itzispyder.pdk.commands.CustomCommand; import io.github.itzispyder.pdk.commands.Permission; import io.github.itzispyder.pdk.commands.completions.CompletionBuilder; import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.functions.helpers.Message; +import me.trouper.sentinel.server.functions.helpers.MessageHandler; import me.trouper.sentinel.utils.PlayerUtils; import me.trouper.sentinel.utils.Text; import org.bukkit.command.Command; @@ -19,7 +19,7 @@ import java.util.UUID; @CommandRegistry(value = "reply", permission = @Permission("sentinel.reply"),printStackTrace = true) public class ReplyCommand implements CustomCommand { - public static Map replyMap = Message.replyMap; + public static Map replyMap = Sentinel.getInstance().getDirector().messageHandler.replyMap; @Override public void dispatchCommand(CommandSender sender, Command command, String s, Args args) { @@ -27,16 +27,16 @@ public class ReplyCommand implements CustomCommand { Player p = sender.getServer().getPlayer(name); UUID senderID = p.getUniqueId(); if (replyMap.get(senderID) == null) { - p.sendMessage(Text.prefix(Sentinel.lang.playerInteraction.noReply)); + p.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.playerInteraction.noReply)); } Player r = sender.getServer().getPlayer(replyMap.get(senderID)); UUID reciverID = r.getUniqueId(); if (args.get(0).toString() == null) { - p.sendMessage(Text.prefix(Sentinel.lang.playerInteraction.noMessageProvided)); + p.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.playerInteraction.noMessageProvided)); } String msg = args.getAll().toString(); if (PlayerUtils.checkPermission(sender,"sentinel.message")) { - Message.messagePlayer(p,r,msg); + Sentinel.getInstance().getDirector().messageHandler.messagePlayer(p,r,msg); replyMap.put(senderID,reciverID); } } diff --git a/src/main/java/me/trouper/sentinel/server/commands/SentinelCommand.java b/src/main/java/me/trouper/sentinel/server/commands/SentinelCommand.java index e31c13a..64a4245 100644 --- a/src/main/java/me/trouper/sentinel/server/commands/SentinelCommand.java +++ b/src/main/java/me/trouper/sentinel/server/commands/SentinelCommand.java @@ -8,15 +8,18 @@ import io.github.itzispyder.pdk.commands.completions.CompletionBuilder; import io.papermc.paper.chat.ChatRenderer; import io.papermc.paper.event.player.AsyncChatEvent; import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.types.WhitelistedBlock; -import me.trouper.sentinel.server.functions.helpers.CBWhitelistManager; +import me.trouper.sentinel.data.types.SerialLocation; +import me.trouper.sentinel.data.types.CommandBlockHolder; +import me.trouper.sentinel.server.events.admin.WandEvents; import me.trouper.sentinel.server.functions.chatfilter.profanity.ProfanityFilter; import me.trouper.sentinel.server.functions.chatfilter.spam.SpamFilter; import me.trouper.sentinel.server.functions.chatfilter.unicode.UnicodeFilter; import me.trouper.sentinel.server.functions.chatfilter.url.UrlFilter; +import me.trouper.sentinel.data.types.Selection; import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.startup.Load; +import me.trouper.sentinel.startup.drm.Loader; import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.Text; import me.trouper.sentinel.utils.trees.ConsoleFormatter; import me.trouper.sentinel.utils.trees.EmbedFormatter; @@ -30,290 +33,408 @@ import org.bukkit.block.CommandBlock; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.entity.minecart.CommandMinecart; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import java.util.*; -@CommandRegistry(value = "sentinel",permission = @Permission("sentinel.staff"),printStackTrace = true) +@CommandRegistry(value = "sentinel", permission = @Permission("sentinel.staff"), printStackTrace = true) public class SentinelCommand implements CustomCommand { + // Constants for usage messages + private static final String USAGE_SENTINEL = "Usage: /sentinel "; + private static final String USAGE_COMMANDBLOCK = "Usage: /sentinel commandblock "; + private static final String USAGE_SELECTION = "Usage: /sentinel commandblock selection "; + private static final String USAGE_RESTORE = "Usage: /sentinel commandblock restore "; + private static final String USAGE_CLEAR = "Usage: /sentinel commandblock clear "; + public static Map spyMap = new HashMap<>(); @Override public void dispatchCommand(CommandSender sender, Command command, String s, Args args) { try { - safety(sender,command,s,args); + processCommand(sender, command, s, args); } catch (IllegalArgumentException e) { - sender.sendMessage(Text.prefix(Sentinel.lang.plugin.invalidArgs)); + e.printStackTrace(); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.plugin.invalidArgs)); } } @Override - public void dispatchCompletions(CommandSender commandSender, Command command, String s, CompletionBuilder b) { + public void dispatchCompletions(CommandSender sender, Command command, String s, CompletionBuilder b) { b.then(b.arg("socialspy")); b.then(b.arg("config")); + b.then(b.arg("wand")); b.then(b.arg("reload")); - 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","cb").then(b.arg("add","remove","auto")) + 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", "cb").then( + b.arg("add", "remove", "auto")) + .then(b.arg("selection") + .then(b.arg("add", "remove", "delete", "deselect", "pos1", "pos2"))) .then(b.arg("restore") - .then(b.arg("","all"))) + .then(b.arg("", "all"))) .then(b.arg("clear") - .then(b.arg("","all")))); + .then(b.arg("", "all")))); } - - private void safety(CommandSender sender, Command command, String label, Args args) { - if (Load.lite) { + /* Main Command Processing */ + private void processCommand(CommandSender sender, Command command, String label, Args args) { + // Lite mode check + if (Sentinel.getInstance().getDirector().loader.isLite()) { handleLiteMessage(sender, args); return; } + if (args.isEmpty()) { - sender.sendMessage(Text.prefix("Usage: /sentinel ")); + sender.sendMessage(Text.prefix(USAGE_SENTINEL)); return; } String subCommand = args.get(0).toString().toLowerCase(); switch (subCommand) { case "reload" -> handleReload(sender); + case "wand" -> handleWand(sender); case "config" -> handleConfig(sender); case "commandblock", "cb" -> handleCommandBlock(sender, args); case "debug" -> handleDebugCommand(sender, args); case "false-positive" -> handleFalsePositive(sender, args); case "socialspy" -> handleSocialSpy(sender); - default -> sender.sendMessage(Text.prefix("Invalid sub-command. Usage: /sentinel ")); + default -> sender.sendMessage(Text.prefix("Invalid sub-command. " + USAGE_SENTINEL)); } } - - private void handleReload(CommandSender sender) { + /* Helper Method: Ensure Sender is a Player */ + private Player getPlayer(CommandSender sender) { if (sender instanceof Player p) { - if (!PlayerUtils.checkPermission(sender, "sentinel.reload") || !PlayerUtils.isTrusted(p)) { - p.sendMessage(Text.prefix(Sentinel.lang.permissions.noTrust)); - return; - } + return p; } - Sentinel.log.info("Sentinel is now reloading the config."); - sender.sendMessage(Text.prefix(Sentinel.lang.plugin.reloadingConfig)); - Sentinel.getInstance().loadConfig(); + sender.sendMessage(Text.prefix("Only players can execute this command.")); + return null; } - private void handleConfig(CommandSender sender) { - if (!PlayerUtils.playerCheck(sender)) + /* ======================= + Subcommand: RELOAD + ======================= */ + private void handleReload(CommandSender sender) { + if (!PlayerUtils.isTrusted(sender)) { + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.permissions.noTrust)); return; + } + Sentinel.getInstance().getLogger().info("Sentinel is now reloading the config."); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.plugin.reloadingConfig)); + Sentinel.getInstance().getDirector().io.loadConfig(); + } + + /* ======================= + Subcommand: WAND + ======================= */ + private void handleWand(CommandSender sender) { + if (!PlayerUtils.playerCheck(sender)) return; + if (!PlayerUtils.isTrusted(sender)) return; Player p = (Player) sender; - if (!PlayerUtils.checkPermission(sender, "sentinel.config") || !PlayerUtils.isTrusted(p)) - return; - if (!MainGUI.verify(p)) - return; + p.give(WandEvents.SELECTION_WAND); + sender.sendMessage(Text.prefix("Given you a selection wand.")); + } + + /* ======================= + Subcommand: CONFIG + ======================= */ + private void handleConfig(CommandSender sender) { + Player p = getPlayer(sender); + if (p == null) return; + if (!PlayerUtils.isTrusted(p)) return; + if (!MainGUI.verify(p)) return; + p.openInventory(new MainGUI().home.getInventory()); } + /* ======================= + Subcommand: COMMANDBLOCK + ======================= */ private void handleCommandBlock(CommandSender sender, Args args) { - if (!PlayerUtils.isTrusted(sender)) - return; + if (!PlayerUtils.isTrusted(sender)) return; if (args.getSize() < 2) { - sender.sendMessage(Text.prefix("Usage: /sentinel commandblock ")); + sender.sendMessage(Text.prefix(USAGE_COMMANDBLOCK)); return; } + String sub = args.get(1).toString().toLowerCase(); switch (sub) { - case "add" -> { - if (!PlayerUtils.playerCheck(sender)) - return; - Player p = (Player) sender; - Block target = p.getTargetBlock(Set.of(Material.AIR), 10); - if (target.getType() == Material.COMMAND_BLOCK || - target.getType() == Material.REPEATING_COMMAND_BLOCK || - target.getType() == Material.CHAIN_COMMAND_BLOCK) { - CommandBlock cb = (CommandBlock) target.getState(); - CBWhitelistManager.add(cb, p.getUniqueId()); - } else { - sender.sendMessage(Text.prefix(Sentinel.lang.commandBlock.notCommandBlock.formatted(Text.cleanName(target.getType().toString())))); - } + case "selection" -> handleCommandBlockSelection(sender, args); + case "add" -> handleCommandBlockAdd(sender); + case "remove" -> handleCommandBlockRemove(sender); + case "auto" -> handleCommandBlockAuto(sender); + case "restore" -> handleCommandBlockRestore(sender, args); + case "clear" -> handleCommandBlockClear(sender, args); + default -> sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.plugin.invalidSubCommand.formatted("commandblock"))); + } + } + + // --- CommandBlock -> SELECTION --- + private void handleCommandBlockSelection(CommandSender sender, Args args) { + if (args.getSize() < 3) { + sender.sendMessage(Text.prefix(USAGE_SELECTION)); + return; + } + Player p = getPlayer(sender); + if (p == null) return; + + String action = args.get(2).toString().toLowerCase(); + switch (action) { + case "add" -> Sentinel.getInstance().getDirector().whitelistManager.addSelectionToWhitelist(p); + case "remove" -> Sentinel.getInstance().getDirector().whitelistManager.removeSelectionFromWhitelist(p); + case "delete" -> Sentinel.getInstance().getDirector().whitelistManager.deleteSelection(p); + case "deselect", "desel" -> WandEvents.selections.remove(p.getUniqueId()); + case "pos1" -> { + Selection selection = WandEvents.selections.computeIfAbsent(p.getUniqueId(), k -> new Selection()); + selection.setPos1(p.getLocation()); + p.sendMessage(Text.prefix("Position 1 set at " + Text.formatLoc(p.getLocation()))); } - case "remove" -> { - if (!PlayerUtils.playerCheck(sender)) - return; - Player p = (Player) sender; - Block target = p.getTargetBlock(Set.of(Material.AIR), 10); - WhitelistedBlock wb = CBWhitelistManager.get(target.getLocation()); - if (wb != null) { - CBWhitelistManager.remove(target.getLocation()); - String cleanedType = Text.cleanName(WhitelistedBlock.fromSerialized(wb.loc()).getBlock().getType().toString()); - sender.sendMessage(Text.prefix(Sentinel.lang.commandBlock.removeSuccess.formatted(cleanedType, wb.command()))); - } else { - sender.sendMessage(Text.prefix(Sentinel.lang.commandBlock.notWhitelisted.formatted(Text.cleanName(target.getType().toString())))); - } + case "pos2" -> { + Selection selection = WandEvents.selections.computeIfAbsent(p.getUniqueId(), k -> new Selection()); + selection.setPos2(p.getLocation()); + p.sendMessage(Text.prefix("Position 2 set at " + Text.formatLoc(p.getLocation()))); } - case "auto" -> { - if (!PlayerUtils.playerCheck(sender)) - return; - Player p = (Player) sender; - if (CBWhitelistManager.autoWhitelist.contains(p.getUniqueId())) { - CBWhitelistManager.autoWhitelist.remove(p.getUniqueId()); - sender.sendMessage(Text.prefix(Sentinel.lang.commandBlock.autoWhitelistOn)); - } else { - CBWhitelistManager.autoWhitelist.add(p.getUniqueId()); - sender.sendMessage(Text.prefix(Sentinel.lang.commandBlock.autoWhitelistOff)); - } - } - case "restore" -> { - if (args.getSize() < 3) { - sender.sendMessage(Text.prefix("Usage: /sentinel commandblock restore ")); - return; - } - String targetPlayer = args.get(2).toString(); - if (targetPlayer.equalsIgnoreCase("all")) { - int result = CBWhitelistManager.restoreAll(); - sender.sendMessage(Text.prefix(Sentinel.lang.commandBlock.restoreSuccess.formatted(result))); - } else { - UUID id = Bukkit.getOfflinePlayer(targetPlayer).getUniqueId(); - int result = CBWhitelistManager.restoreAll(id); - sender.sendMessage(Text.prefix(Sentinel.lang.commandBlock.restorePlayerSuccess.formatted(result,targetPlayer))); - } - } - case "clear" -> { - if (args.getSize() < 3) { - sender.sendMessage(Text.prefix("Usage: /sentinel commandblock clear ")); - return; - } - String targetPlayer = args.get(2).toString(); - if (targetPlayer.equalsIgnoreCase("all")) { - int result = CBWhitelistManager.clearAll(); - sender.sendMessage(Text.prefix(Sentinel.lang.commandBlock.clearSuccess.formatted(result))); - } else { - UUID id = Bukkit.getOfflinePlayer(targetPlayer).getUniqueId(); - int result = CBWhitelistManager.clearAll(id); - sender.sendMessage(Text.prefix(Sentinel.lang.commandBlock.clearPlayerSuccess.formatted(result,targetPlayer))); - } - } - default -> sender.sendMessage(Text.prefix(Sentinel.lang.plugin.invalidSubCommand.formatted("commandblock"))); + default -> p.sendMessage(Text.prefix("Invalid selection action. " + USAGE_SELECTION)); } } - private void handleDebugCommand(CommandSender sender, Args args) { - if (!PlayerUtils.checkPermission(sender, "sentinel.debug")) + // --- CommandBlock -> ADD --- + private void handleCommandBlockAdd(CommandSender sender) { + Player p = getPlayer(sender); + if (p == null) return; + + if (p.getTargetEntity(10) instanceof CommandMinecart cm) { + Sentinel.getInstance().getDirector().whitelistManager + .generateHolder(p.getUniqueId(), cm).addToWhitelist(); return; + } + Block target = p.getTargetBlock(Set.of(Material.AIR), 10); + if (ServerUtils.isCommandBlock(target)) { + CommandBlock cb = (CommandBlock) target.getState(); + Sentinel.getInstance().getDirector().whitelistManager + .generateHolder(p.getUniqueId(), cb).addToWhitelist(); + } else { + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.commandBlock.notCommandBlock + .formatted(Text.cleanName(target.getType().toString())))); + } + } + + // --- CommandBlock -> REMOVE --- + private void handleCommandBlockRemove(CommandSender sender) { + Player p = getPlayer(sender); + if (p == null) return; + + if (p.getTargetEntity(10) instanceof CommandMinecart cm) { + CommandBlockHolder wb = Sentinel.getInstance().getDirector().whitelistManager + .generateHolder(p.getUniqueId(), cm); + if (wb.removeFromWhitelist()) { + String cleanedType = Text.cleanName(SerialLocation.translate(wb.loc()).getBlock().getType().toString()); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.commandBlock.removeSuccess + .formatted(cleanedType, wb.command()))); + } else { + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.commandBlock.notWhitelisted + .formatted(Text.cleanName(cm.getType().toString())))); + } + return; + } + + Block target = p.getTargetBlock(Set.of(Material.AIR), 10); + CommandBlockHolder wb = Sentinel.getInstance().getDirector().whitelistManager + .getFromWhitelist(target.getLocation()); + if (wb != null && wb.removeFromWhitelist()) { + String cleanedType = Text.cleanName(SerialLocation.translate(wb.loc()).getBlock().getType().toString()); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.commandBlock.removeSuccess + .formatted(cleanedType, wb.command()))); + } else { + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.commandBlock.notWhitelisted + .formatted(Text.cleanName(target.getType().toString())))); + } + } + + // --- CommandBlock -> AUTO --- + private void handleCommandBlockAuto(CommandSender sender) { + Player p = getPlayer(sender); + if (p == null) return; + + var whitelistManager = Sentinel.getInstance().getDirector().whitelistManager; + if (whitelistManager.autoWhitelist.contains(p.getUniqueId())) { + whitelistManager.autoWhitelist.remove(p.getUniqueId()); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.commandBlock.autoWhitelistOn)); + } else { + whitelistManager.autoWhitelist.add(p.getUniqueId()); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.commandBlock.autoWhitelistOff)); + } + } + + // --- CommandBlock -> RESTORE --- + private void handleCommandBlockRestore(CommandSender sender, Args args) { + if (args.getSize() < 3) { + sender.sendMessage(Text.prefix(USAGE_RESTORE)); + return; + } + String targetPlayer = args.get(2).toString(); + if (targetPlayer.equalsIgnoreCase("all")) { + int result = Sentinel.getInstance().getDirector().whitelistManager.restoreAll(); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.commandBlock.restoreSuccess + .formatted(result))); + } else { + UUID id = Bukkit.getOfflinePlayer(targetPlayer).getUniqueId(); + int result = Sentinel.getInstance().getDirector().whitelistManager.restoreAll(id); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.commandBlock.restorePlayerSuccess + .formatted(result, targetPlayer))); + } + } + + // --- CommandBlock -> CLEAR --- + private void handleCommandBlockClear(CommandSender sender, Args args) { + if (args.getSize() < 3) { + sender.sendMessage(Text.prefix(USAGE_CLEAR)); + return; + } + String targetPlayer = args.get(2).toString(); + if (targetPlayer.equalsIgnoreCase("all")) { + int result = Sentinel.getInstance().getDirector().whitelistManager.clearAll(); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.commandBlock.clearSuccess + .formatted(result))); + } else { + UUID id = Bukkit.getOfflinePlayer(targetPlayer).getUniqueId(); + int result = Sentinel.getInstance().getDirector().whitelistManager.clearAll(id); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.commandBlock.clearPlayerSuccess + .formatted(result, targetPlayer))); + } + } + + /* ======================= + Subcommand: DEBUG + ======================= */ + private void handleDebugCommand(CommandSender sender, Args args) { + if (!PlayerUtils.checkPermission(sender, "sentinel.debug")) return; if (args.getSize() < 2) { sender.sendMessage(Text.prefix("Usage: /sentinel debug ")); return; } String sub = args.get(1).toString().toLowerCase(); switch (sub) { - case "lang" -> sender.sendMessage(Sentinel.lang.brokenLang); + case "lang" -> sender.sendMessage(Sentinel.getInstance().getDirector().io.lang.brokenLang); case "toggle" -> { - Sentinel.mainConfig.debugMode = !Sentinel.mainConfig.debugMode; - String message = Sentinel.mainConfig.debugMode - ? Sentinel.lang.debug.debugEnabled - : Sentinel.lang.debug.debugDisabled; + Sentinel.getInstance().getDirector().io.mainConfig.debugMode = !Sentinel.getInstance().getDirector().io.mainConfig.debugMode; + String message = Sentinel.getInstance().getDirector().io.mainConfig.debugMode + ? Sentinel.getInstance().getDirector().io.lang.debug.debugEnabled + : Sentinel.getInstance().getDirector().io.lang.debug.debugDisabled; sender.sendMessage(Text.prefix(message)); - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.save(); } - case "chat" -> { - if (!PlayerUtils.playerCheck(sender)) - return; - if (args.getSize() < 3) { - sender.sendMessage(Text.prefix("Usage: /sentinel debug chat ")); - return; - } - Player p = (Player) sender; - String messageText = args.getAll(2).toString(); - AsyncChatEvent message = new AsyncChatEvent(true, - p, - Set.of(p), - ChatRenderer.defaultRenderer(), - Component.text(messageText), - Component.text(messageText), - SignedMessage.system(messageText, Component.text(messageText)) - ); - UnicodeFilter.handleUnicodeFilter(message); - UrlFilter.handleUrlFilter(message); - SpamFilter.handleSpamFilter(message); - ProfanityFilter.handleProfanityFilter(message); - if (!message.isCancelled()) { - sender.sendMessage(Text.prefix(Sentinel.lang.debug.notFlagged)); - } - } - default -> sender.sendMessage(Text.prefix(Sentinel.lang.plugin.invalidSubCommand.formatted("debug"))); + case "chat" -> handleDebugChat(sender, args); + default -> sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.plugin.invalidSubCommand.formatted("debug"))); } } + private void handleDebugChat(CommandSender sender, Args args) { + if (!PlayerUtils.playerCheck(sender)) return; + if (args.getSize() < 3) { + sender.sendMessage(Text.prefix("Usage: /sentinel debug chat ")); + return; + } + Player p = (Player) sender; + String messageText = args.getAll(2).toString(); + AsyncChatEvent chatEvent = new AsyncChatEvent(true, + p, + Set.of(p), + ChatRenderer.defaultRenderer(), + Component.text(messageText), + Component.text(messageText), + SignedMessage.system(messageText, Component.text(messageText)) + ); + UnicodeFilter.handleUnicodeFilter(chatEvent); + UrlFilter.handleUrlFilter(chatEvent); + SpamFilter.handleSpamFilter(chatEvent); + ProfanityFilter.handleProfanityFilter(chatEvent); + if (!chatEvent.isCancelled()) { + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.debug.notFlagged)); + } + } + + /* ======================= + Subcommand: FALSE-POSITIVE + ======================= */ private void handleFalsePositive(CommandSender sender, Args args) { if (args.getSize() < 2) { sender.sendMessage(Text.prefix("Usage: /sentinel false-positive ")); return; } - if (!PlayerUtils.checkPermission(sender, "sentinel.false-positive")) - return; + if (!PlayerUtils.checkPermission(sender, "sentinel.false-positive")) return; String sub = args.get(1).toString().toLowerCase(); String falsePositive = args.getAll(2).toString(); + Node root = new Node("Sentinel"); root.addTextLine("False Positive Management Log"); Node info = new Node("Info"); info.addKeyValue("User", sender.getName()); + switch (sub) { case "add" -> { - if (!PlayerUtils.checkPermission(sender,"sentinel.false-positive.add")) return; - Sentinel.fpConfig.swearWhitelist.add(falsePositive); - sender.sendMessage(Text.prefix(Sentinel.lang.falsePositive.addSuccess.formatted(falsePositive))); + if (!PlayerUtils.checkPermission(sender, "sentinel.false-positive.add")) return; + Sentinel.getInstance().getDirector().io.fpConfig.swearWhitelist.add(falsePositive); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.falsePositive.addSuccess.formatted(falsePositive))); info.addKeyValue("Action", "Add"); } case "remove" -> { - if (!PlayerUtils.checkPermission(sender,"sentinel.false-positive.remove")) return; - Sentinel.fpConfig.swearWhitelist.remove(falsePositive); - sender.sendMessage(Text.prefix(Sentinel.lang.falsePositive.removeSuccess.formatted(falsePositive))); + if (!PlayerUtils.checkPermission(sender, "sentinel.false-positive.remove")) return; + Sentinel.getInstance().getDirector().io.fpConfig.swearWhitelist.remove(falsePositive); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.falsePositive.removeSuccess.formatted(falsePositive))); info.addKeyValue("Action", "Remove"); } default -> { - sender.sendMessage(Text.prefix(Sentinel.lang.plugin.invalidSubCommand.formatted("false-positive"))); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.plugin.invalidSubCommand.formatted("false-positive"))); return; } } info.addKeyValue("False Positive Edited", falsePositive); root.addChild(info); - Sentinel.fpConfig.save(); - Sentinel.log.info(ConsoleFormatter.format(root)); + Sentinel.getInstance().getDirector().io.fpConfig.save(); + Sentinel.getInstance().getLogger().info(ConsoleFormatter.format(root)); EmbedFormatter.sendEmbed(EmbedFormatter.format(root)); } + /* ======================= + Subcommand: SOCIALSPY + ======================= */ private void handleSocialSpy(CommandSender sender) { - if (!PlayerUtils.playerCheck(sender)) - return; - if (!PlayerUtils.checkPermission(sender, "sentinel.socialspy")) - return; + if (!PlayerUtils.playerCheck(sender)) return; + if (!PlayerUtils.checkPermission(sender, "sentinel.socialspy")) return; Player p = (Player) sender; UUID senderID = p.getUniqueId(); boolean enabled = spyMap.getOrDefault(senderID, false); if (!enabled) { - sender.sendMessage(Text.prefix(Sentinel.lang.socialSpy.enabled)); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.socialSpy.enabled)); spyMap.put(senderID, true); } else { - sender.sendMessage(Text.prefix(Sentinel.lang.socialSpy.disabled)); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.socialSpy.disabled)); spyMap.put(senderID, false); } } + /* ======================= + Lite Mode Handler + ======================= */ private void handleLiteMessage(CommandSender sender, Args args) { if (!args.isEmpty() && args.get(0).toString().equalsIgnoreCase("reload")) { - if (sender instanceof Player p && !PlayerUtils.isTrusted(p)) { - sender.sendMessage(Text.prefix(Sentinel.lang.permissions.noTrust)); + if (!PlayerUtils.isTrusted(sender)) { + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.permissions.noTrust)); return; } - Sentinel.log.info("Sentinel is now reloading the config in lite mode."); - sender.sendMessage(Text.prefix(Sentinel.lang.plugin.reloadingConfigLite)); - Sentinel.getInstance().loadConfig(); + Sentinel.getInstance().getLogger().info("Sentinel is now reloading the config in lite mode."); + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.plugin.reloadingConfigLite)); + Sentinel.getInstance().getDirector().io.loadConfig(); - if (Load.load(Sentinel.getInstance().license, Sentinel.getInstance().identifier, false)) { + if (Sentinel.getInstance().getDirector().loader.load(Sentinel.getInstance().license, Sentinel.getInstance().identifier, false)) { return; } - Sentinel.log.info("Re-authentication Failed."); + Sentinel.getInstance().getLogger().info("Re-authentication Failed."); } else { - sender.sendMessage(Load.liteMode); + sender.sendMessage(Loader.LITE_MODE); } } -} +} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/server/commands/TrapCommand.java b/src/main/java/me/trouper/sentinel/server/commands/TrapCommand.java index 196c584..e4f59fa 100644 --- a/src/main/java/me/trouper/sentinel/server/commands/TrapCommand.java +++ b/src/main/java/me/trouper/sentinel/server/commands/TrapCommand.java @@ -23,7 +23,6 @@ public class TrapCommand implements CustomCommand { @Override public void dispatchCompletions(CommandSender commandSender, Command command, String s, CompletionBuilder b) { - ServerUtils.verbose("Listing the fake plugins: %s".formatted(Sentinel.advConfig.fakePlugins)); - b.then(b.arg(Sentinel.advConfig.fakePlugins)); + b.then(b.arg(Sentinel.getInstance().getDirector().io.advConfig.fakePlugins)); } } diff --git a/src/main/java/me/trouper/sentinel/server/events/CommandBlockEdit.java b/src/main/java/me/trouper/sentinel/server/events/CommandBlockEdit.java deleted file mode 100644 index d30bb1a..0000000 --- a/src/main/java/me/trouper/sentinel/server/events/CommandBlockEdit.java +++ /dev/null @@ -1,56 +0,0 @@ -package me.trouper.sentinel.server.events; - -import io.github.itzispyder.pdk.events.CustomListener; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.functions.helpers.AbstractViolation; -import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; -import me.trouper.sentinel.server.functions.helpers.CBWhitelistManager; -import me.trouper.sentinel.utils.PlayerUtils; -import me.trouper.sentinel.utils.ServerUtils; -import me.trouper.sentinel.utils.trees.Node; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockState; -import org.bukkit.block.CommandBlock; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.entity.EntityChangeBlockEvent; - -public class CommandBlockEdit extends AbstractViolation { - - @EventHandler - private void onCMDBlockChange(EntityChangeBlockEvent e) { - //ServerUtils.verbose("CommandBlockChange: Detected the event"); - if (!Sentinel.violationConfig.commandBlockEdit.enabled) return; - //ServerUtils.verbose("CommandBlockChange: Enabled"); - if (!(e.getEntity() instanceof Player p)) return; - //ServerUtils.verbose("CommandBlockChange: Changer is a player"); - Block b = e.getBlock(); - if (!(b.getType() == Material.COMMAND_BLOCK || b.getType() == Material.REPEATING_COMMAND_BLOCK || b.getType() == Material.CHAIN_COMMAND_BLOCK)) - return; - ServerUtils.verbose("CommandBlockChange: Block is a command block"); - CommandBlock cb = (CommandBlock) b.getState(); - if (PlayerUtils.isTrusted(p)) { - if (!CBWhitelistManager.autoWhitelist.contains(p.getUniqueId())) return; - CBWhitelistManager.add(cb, p.getUniqueId()); - return; - } - ServerUtils.verbose("CommandBlockChange: Not trusted, performing action"); - - ActionConfiguration.Builder config = new ActionConfiguration.Builder() - .setEvent(e) - .setPlayer(p) - .deop(Sentinel.violationConfig.commandBlockEdit.deop) - .cancel(true) - .punish(Sentinel.violationConfig.commandBlockEdit.punish) - .setPunishmentCommands(Sentinel.violationConfig.commandBlockEdit.punishmentCommands) - .logToDiscord(Sentinel.violationConfig.commandBlockEdit.logToDiscord); - - runActions( - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.edit, Sentinel.lang.violations.protections.rootName.commandBlock), - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.edit, Sentinel.lang.violations.protections.rootName.commandBlock), - generateCommandBlockInfo(cb), - config - ); - } -} diff --git a/src/main/java/me/trouper/sentinel/server/events/CommandBlockExecute.java b/src/main/java/me/trouper/sentinel/server/events/CommandBlockExecute.java deleted file mode 100644 index 5d4a391..0000000 --- a/src/main/java/me/trouper/sentinel/server/events/CommandBlockExecute.java +++ /dev/null @@ -1,46 +0,0 @@ -package me.trouper.sentinel.server.events; - -import io.github.itzispyder.pdk.events.CustomListener; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.functions.helpers.AbstractViolation; -import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; -import me.trouper.sentinel.server.functions.helpers.CBWhitelistManager; -import me.trouper.sentinel.utils.PlayerUtils; -import me.trouper.sentinel.utils.ServerUtils; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.CommandBlock; -import org.bukkit.command.BlockCommandSender; -import org.bukkit.event.EventHandler; -import org.bukkit.event.server.ServerCommandEvent; - -public class CommandBlockExecute extends AbstractViolation { - - @EventHandler - private void commandBlockExecute(ServerCommandEvent e) { - //ServerUtils.verbose("Handling command block event: " + e.getCommand()); - if (!Sentinel.violationConfig.commandBlockExecute.enabled) return; - //ServerUtils.verbose("Whitelist not disabled "); - if (!(e.getSender() instanceof BlockCommandSender s)) return; - //ServerUtils.verbose("Sender is command block"); - Block cmdBlock = s.getBlock(); - if (CBWhitelistManager.canRun(cmdBlock)) return; - ServerUtils.verbose("Command block can't run."); - - CommandBlock cb = (CommandBlock) cmdBlock.getState(); - - ActionConfiguration.Builder config = new ActionConfiguration.Builder() - .setBlock(cmdBlock) - .cancel(true) - .destroyBlock(Sentinel.violationConfig.commandBlockExecute.destroyBlock) - .restoreBlock(Sentinel.violationConfig.commandBlockExecute.attemptRestore) - .logToDiscord(Sentinel.violationConfig.commandBlockExecute.logToDiscord); - - runActions( - Sentinel.lang.violations.protections.rootName.rootNameFormat.formatted(Sentinel.lang.violations.protections.rootName.commandBlockWhitelist), - Sentinel.lang.violations.protections.rootName.rootNameFormat.formatted( Sentinel.lang.violations.protections.rootName.commandBlockWhitelist), - generateCommandBlockInfo(cb), - config - ); - } -} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/server/events/CommandBlockMinecartPlace.java b/src/main/java/me/trouper/sentinel/server/events/CommandBlockMinecartPlace.java deleted file mode 100644 index 35dc6f9..0000000 --- a/src/main/java/me/trouper/sentinel/server/events/CommandBlockMinecartPlace.java +++ /dev/null @@ -1,55 +0,0 @@ -package me.trouper.sentinel.server.events; - -import io.github.itzispyder.pdk.events.CustomListener; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.functions.helpers.AbstractViolation; -import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; -import me.trouper.sentinel.utils.PlayerUtils; -import me.trouper.sentinel.utils.ServerUtils; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.player.PlayerInteractEvent; - -public class CommandBlockMinecartPlace extends AbstractViolation { - - @EventHandler - private void onCMDMinecartPlace(PlayerInteractEvent e) { - //ServerUtils.verbose("MinecartCommandPlace: Detected interaction"); - if (!Sentinel.violationConfig.commandBlockMinecartPlace.enabled) return; - //ServerUtils.verbose("MinecartCommandPlace: Check is enabled"); - Player p = e.getPlayer(); - if (!p.isOp()) return; - //ServerUtils.verbose("MinecartCommandPlace: Player is op"); - if (e.getItem() == null) return; - ServerUtils.verbose("MinecartCommandPlace: Item isn't null"); - if (e.getClickedBlock() == null) return; - ServerUtils.verbose("MinecartCommandPlace: Clicked block isn't null"); - if (!e.getItem().getType().equals(Material.COMMAND_BLOCK_MINECART)) return; - ServerUtils.verbose("MinecartCommandPlace: Item is a minecart command"); - if (!(e.getClickedBlock().getType() == Material.RAIL || e.getClickedBlock().getType() == Material.POWERED_RAIL || e.getClickedBlock().getType() == Material.ACTIVATOR_RAIL || e.getClickedBlock().getType() == Material.DETECTOR_RAIL)) return; - ServerUtils.verbose("MinecartCommandPlace: Clicked block is a rail"); - if (PlayerUtils.isTrusted(p)) return; - ServerUtils.verbose("MinecartCommandPlace: Not trusted, performing action"); - - ActionConfiguration.Builder config = new ActionConfiguration.Builder() - .setEvent(e) - .setPlayer(p) - .cancel(true) - .punish(Sentinel.violationConfig.commandBlockMinecartPlace.punish) - .deop(Sentinel.violationConfig.commandBlockMinecartPlace.deop) - .setPunishmentCommands(Sentinel.violationConfig.commandBlockMinecartPlace.punishmentCommands) - .logToDiscord(Sentinel.violationConfig.commandBlockMinecartPlace.logToDiscord); - - // Remove the command block minecart from the player's inventory - p.getInventory().remove(Material.COMMAND_BLOCK_MINECART); - - runActions( - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.place, Sentinel.lang.violations.protections.rootName.minecartCommandBlock), - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.place, Sentinel.lang.violations.protections.rootName.minecartCommandBlock), - generateBlockInfo(e.getClickedBlock()), - config - ); - } -} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/server/events/CommandBlockMinecartUse.java b/src/main/java/me/trouper/sentinel/server/events/CommandBlockMinecartUse.java deleted file mode 100644 index 182f596..0000000 --- a/src/main/java/me/trouper/sentinel/server/events/CommandBlockMinecartUse.java +++ /dev/null @@ -1,46 +0,0 @@ -package me.trouper.sentinel.server.events; - -import io.github.itzispyder.pdk.events.CustomListener; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.functions.helpers.AbstractViolation; -import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; -import me.trouper.sentinel.utils.PlayerUtils; -import me.trouper.sentinel.utils.ServerUtils; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.player.PlayerInteractEntityEvent; - -public class CommandBlockMinecartUse extends AbstractViolation { - - @EventHandler - private void onCMDBlockMinecartUse(PlayerInteractEntityEvent e) { - //ServerUtils.verbose("MinecartCommandUse: Detected Interaction with entity"); - if (!Sentinel.violationConfig.commandBlockMinecartUse.enabled) return; - //ServerUtils.verbose("MinecartCommandUse: Enabled"); - Player p = e.getPlayer(); - if (!p.isOp()) return; - ServerUtils.verbose("MinecartCommandUse: Player op"); - if (e.getRightClicked().getType() != EntityType.COMMAND_BLOCK_MINECART) return; - ServerUtils.verbose("MinecartCommandUse: Entity is minecart command"); - if (PlayerUtils.isTrusted(p)) return; - ServerUtils.verbose("MinecartCommandUse: Not trusted, performing action"); - - ActionConfiguration.Builder config = new ActionConfiguration.Builder() - .setEvent(e) - .setPlayer(p) - .cancel(true) - .punish(Sentinel.violationConfig.commandBlockMinecartUse.punish) - .deop(Sentinel.violationConfig.commandBlockMinecartUse.deop) - .setPunishmentCommands(Sentinel.violationConfig.commandBlockMinecartUse.punishmentCommands) - .logToDiscord(Sentinel.violationConfig.commandBlockMinecartUse.logToDiscord); - - runActions( - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.use, Sentinel.lang.violations.protections.rootName.minecartCommandBlock), - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.use, Sentinel.lang.violations.protections.rootName.minecartCommandBlock), - generateMinecartInfo(e.getRightClicked()), - config - ); - } -} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/server/events/CommandBlockPlace.java b/src/main/java/me/trouper/sentinel/server/events/CommandBlockPlace.java deleted file mode 100644 index 12a2f1c..0000000 --- a/src/main/java/me/trouper/sentinel/server/events/CommandBlockPlace.java +++ /dev/null @@ -1,53 +0,0 @@ -package me.trouper.sentinel.server.events; - -import io.github.itzispyder.pdk.events.CustomListener; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.functions.helpers.AbstractViolation; -import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; -import me.trouper.sentinel.utils.PlayerUtils; -import me.trouper.sentinel.utils.ServerUtils; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.CommandBlock; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.block.BlockPlaceEvent; - -public class CommandBlockPlace extends AbstractViolation { - - @EventHandler - public void listen(BlockPlaceEvent e) { - //ServerUtils.verbose("CommandBlockPlace: Detected block place"); - if (!Sentinel.violationConfig.commandBlockPlace.enabled) return; - //ServerUtils.verbose("CommandBlockPlace: Enabled"); - Player p = e.getPlayer(); - if (!p.isOp()) return; - //ServerUtils.verbose("CommandBlockPlace: Player is operator"); - Block b = e.getBlockPlaced(); - if (!(b.getType().equals(Material.COMMAND_BLOCK) || - b.getType().equals(Material.REPEATING_COMMAND_BLOCK) || - b.getType().equals(Material.CHAIN_COMMAND_BLOCK))) return; - ServerUtils.verbose("CommandBlockPlace: Block is a command block"); - CommandBlock cb = (CommandBlock) b.getState(); - if (PlayerUtils.isTrusted(p)) return; - ServerUtils.verbose("CommandBlockPlace: Not trusted, performing action"); - - - ActionConfiguration.Builder config = new ActionConfiguration.Builder() - .setEvent(e) - .setPlayer(p) - .deop(Sentinel.violationConfig.commandBlockPlace.deop) - .cancel(true) - .setEvent(e) - .punish(true) - .setPunishmentCommands(Sentinel.violationConfig.commandBlockPlace.punishmentCommands) - .logToDiscord(Sentinel.violationConfig.commandBlockPlace.logToDiscord); - - runActions( - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.place, Sentinel.lang.violations.protections.rootName.commandBlock), - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.place, Sentinel.lang.violations.protections.rootName.commandBlock), - generateCommandBlockInfo(cb), - config - ); - } -} diff --git a/src/main/java/me/trouper/sentinel/server/events/CommandBlockUse.java b/src/main/java/me/trouper/sentinel/server/events/CommandBlockUse.java deleted file mode 100644 index 978e3fe..0000000 --- a/src/main/java/me/trouper/sentinel/server/events/CommandBlockUse.java +++ /dev/null @@ -1,58 +0,0 @@ -package me.trouper.sentinel.server.events; - -import io.github.itzispyder.pdk.events.CustomListener; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.functions.helpers.AbstractViolation; -import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; -import me.trouper.sentinel.server.functions.helpers.CBWhitelistManager; -import me.trouper.sentinel.utils.PlayerUtils; -import me.trouper.sentinel.utils.ServerUtils; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.CommandBlock; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.player.PlayerInteractEvent; - -public class CommandBlockUse extends AbstractViolation { - - @EventHandler - private void onCMDBlockUse(PlayerInteractEvent e) { - //ServerUtils.verbose("CommandBlockUse: Detected Interaction"); - if (!Sentinel.violationConfig.commandBlockUse.enabled) return; - //ServerUtils.verbose("CommandBlockUse: Enabled"); - Player p = e.getPlayer(); - if (!p.isOp()) return; - //ServerUtils.verbose("CommandBlockUse: Player is op"); - if (e.getClickedBlock() == null) return; - //ServerUtils.verbose("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.verbose("CommandBlockUse: Block is a command block"); - if (PlayerUtils.isTrusted(p)) { - if (!CBWhitelistManager.autoWhitelist.contains(p.getUniqueId())) return; - if (CBWhitelistManager.canRun(cb.getBlock())) return; - e.setCancelled(true); - CBWhitelistManager.add(cb, p.getUniqueId()); - return; - } - ServerUtils.verbose("CommandBlockUse: Not trusted, performing action"); - - ActionConfiguration.Builder config = new ActionConfiguration.Builder() - .setEvent(e) - .setPlayer(p) - .deop(Sentinel.violationConfig.commandBlockUse.deop) - .cancel(true) - .punish(Sentinel.violationConfig.commandBlockUse.punish) - .setPunishmentCommands(Sentinel.violationConfig.commandBlockUse.punishmentCommands) - .logToDiscord(Sentinel.violationConfig.commandBlockUse.logToDiscord); - - runActions( - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.use, Sentinel.lang.violations.protections.rootName.commandBlock), - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.use, Sentinel.lang.violations.protections.rootName.commandBlock), - generateCommandBlockInfo(cb), - config - ); - } -} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/server/events/CommandExecute.java b/src/main/java/me/trouper/sentinel/server/events/CommandExecute.java deleted file mode 100644 index c7ac7ad..0000000 --- a/src/main/java/me/trouper/sentinel/server/events/CommandExecute.java +++ /dev/null @@ -1,101 +0,0 @@ -package me.trouper.sentinel.server.events; - -import io.github.itzispyder.pdk.events.CustomListener; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.functions.helpers.AbstractViolation; -import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; -import me.trouper.sentinel.utils.PlayerUtils; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.player.PlayerCommandPreprocessEvent; - -import java.util.HashSet; -import java.util.Set; - -public class CommandExecute extends AbstractViolation { - - @EventHandler - private void onCommand(PlayerCommandPreprocessEvent e) { - Player p = e.getPlayer(); - if (PlayerUtils.isTrusted(p)) return; - String label = e.getMessage().substring(1).split(" ")[0]; - String args = e.getMessage(); - - Set status = getCommandStatus(label); - - if (status.contains("SPECIFIC") && Sentinel.violationConfig.commandExecute.specific.enabled) { - e.setCancelled(true); - ActionConfiguration.Builder config = new ActionConfiguration.Builder() - .setEvent(e) - .setPlayer(p) - .cancel(true) - .punish(Sentinel.violationConfig.commandExecute.specific.punish) - .setPunishmentCommands(Sentinel.violationConfig.commandExecute.specific.punishmentCommands) - .logToDiscord(Sentinel.violationConfig.commandExecute.specific.logToDiscord); - - runActions( - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.run, Sentinel.lang.violations.protections.rootName.specificCommand), - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.run, Sentinel.lang.violations.protections.rootName.specificCommand), - generateCommandInfo(args, p), - config - ); - return; - } - - if (status.contains("DANGEROUS") && Sentinel.violationConfig.commandExecute.dangerous.enabled) { - e.setCancelled(true); - ActionConfiguration.Builder config = new ActionConfiguration.Builder() - .setEvent(e) - .setPlayer(p) - .deop(Sentinel.violationConfig.commandExecute.dangerous.deop) - .cancel(true) - .punish(Sentinel.violationConfig.commandExecute.dangerous.punish) - .setPunishmentCommands(Sentinel.violationConfig.commandExecute.dangerous.punishmentCommands) - .logToDiscord(Sentinel.violationConfig.commandExecute.dangerous.logToDiscord); - - runActions( - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.run, Sentinel.lang.violations.protections.rootName.dangerousCommand), - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.run, Sentinel.lang.violations.protections.rootName.dangerousCommand), - generateCommandInfo(args, p), - config - ); - return; - } - - if (status.contains("LOGGED") && Sentinel.violationConfig.commandExecute.logged.enabled) { - ActionConfiguration.Builder config = new ActionConfiguration.Builder() - .setPlayer(p) - .logToDiscord(Sentinel.violationConfig.commandExecute.logged.logToDiscord); - - runActions( - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.run, Sentinel.lang.violations.protections.rootName.loggedCommand), - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.run, Sentinel.lang.violations.protections.rootName.loggedCommand), - generateCommandInfo(args, p), - config - ); - return; - } - } - - public static Set getCommandStatus(String label) { - Set commandTypes = new HashSet<>(); - - if (label.startsWith("/")) { - label = label.substring(1); - } - - if (label.contains(":")) { - commandTypes.add("SPECIFIC"); - } - - for (String loggedCommand : Sentinel.violationConfig.commandExecute.logged.commands) { - if (loggedCommand.equals(label)) commandTypes.add("LOGGED"); - } - - for (String dangerousCommand : Sentinel.violationConfig.commandExecute.dangerous.commands) { - if (dangerousCommand.equals(label)) commandTypes.add("DANGEROUS"); - } - - return commandTypes; - } -} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/server/events/CreativeHotbar.java b/src/main/java/me/trouper/sentinel/server/events/CreativeHotbar.java deleted file mode 100644 index edfd979..0000000 --- a/src/main/java/me/trouper/sentinel/server/events/CreativeHotbar.java +++ /dev/null @@ -1,52 +0,0 @@ -package me.trouper.sentinel.server.events; - -import io.github.itzispyder.pdk.events.CustomListener; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.functions.helpers.AbstractViolation; -import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; -import me.trouper.sentinel.utils.ItemUtils; -import me.trouper.sentinel.utils.PlayerUtils; -import me.trouper.sentinel.utils.ServerUtils; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.inventory.InventoryCreativeEvent; -import org.bukkit.inventory.ItemStack; - -public class CreativeHotbar extends AbstractViolation { - - @EventHandler - private void onNBTPull(InventoryCreativeEvent e) { - //ServerUtils.verbose("NBT: Detected creative mode action"); - if (!Sentinel.violationConfig.creativeHotbarAction.enabled) return; - ServerUtils.verbose("NBT: Enabled"); - if (!(e.getWhoClicked() instanceof Player p)) return; - ServerUtils.verbose("NBT: Clicker is a player"); - if (e.getCursor() == null) return; // Well it threw an exception during testing, so it isn't always false! - ServerUtils.verbose("NBT: Cursor isn't null"); - ItemStack i = e.getCursor(); - if (PlayerUtils.isTrusted(p)) return; - ServerUtils.verbose("NBT: Not trusted"); - if (e.getCursor().getItemMeta() == null) return; - ServerUtils.verbose("NBT: Cursor has meta"); - if (!(i.hasItemMeta() && i.getItemMeta() != null)) return; - ServerUtils.verbose("NBT: Item has meta"); - if (ItemUtils.itemPasses(i)) return; - ServerUtils.verbose("NBT: Item doesn't pass, performing action"); - - ActionConfiguration.Builder config = new ActionConfiguration.Builder() - .setEvent(e) - .setPlayer(p) - .cancel(true) - .punish(Sentinel.violationConfig.creativeHotbarAction.punish) - .deop(Sentinel.violationConfig.creativeHotbarAction.deop) - .setPunishmentCommands(Sentinel.violationConfig.creativeHotbarAction.punishmentCommands) - .logToDiscord(Sentinel.violationConfig.creativeHotbarAction.logToDiscord); - - runActions( - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.grab, Sentinel.lang.violations.protections.rootName.nbtItem), - Sentinel.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.lang.violations.protections.rootName.grab, Sentinel.lang.violations.protections.rootName.nbtItem), - generateItemInfo(i), - config - ); - } -} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/server/events/admin/AntiBanEvents.java b/src/main/java/me/trouper/sentinel/server/events/admin/AntiBanEvents.java new file mode 100644 index 0000000..1bdc40e --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/admin/AntiBanEvents.java @@ -0,0 +1,29 @@ +package me.trouper.sentinel.server.events.admin; + +import io.github.itzispyder.pdk.events.CustomListener; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.utils.PlayerUtils; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent; +import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerLoginEvent; + +public class AntiBanEvents implements CustomListener { + + // Well. I hope that no banning plugins use the highest priority as well, that would be embarrassing. + @EventHandler(priority = EventPriority.HIGHEST) + public void onKick(PlayerKickEvent e) { + if (PlayerUtils.isTrusted(e.getPlayer()) && Sentinel.getInstance().getDirector().io.mainConfig.plugin.antiBan) e.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onLogin(PlayerLoginEvent e) { + if (PlayerUtils.isTrusted(e.getPlayer()) && Sentinel.getInstance().getDirector().io.mainConfig.plugin.antiBan) e.allow(); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void beforeLogin(AsyncPlayerPreLoginEvent e) { + if (PlayerUtils.isTrusted(e.getUniqueId()) && Sentinel.getInstance().getDirector().io.mainConfig.plugin.antiBan) e.allow(); + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/admin/BlockDisplayHideEvent.java b/src/main/java/me/trouper/sentinel/server/events/admin/BlockDisplayHideEvent.java new file mode 100644 index 0000000..35192d1 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/admin/BlockDisplayHideEvent.java @@ -0,0 +1,23 @@ +package me.trouper.sentinel.server.events.admin; + +import io.github.itzispyder.pdk.events.CustomListener; +import me.trouper.sentinel.Sentinel; +import org.bukkit.entity.BlockDisplay; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerJoinEvent; + +public class BlockDisplayHideEvent implements CustomListener { + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + + for (Entity entity : player.getWorld().getEntities()) { + if (entity instanceof BlockDisplay && entity.getScoreboardTags().contains("./Sentinel/ Block Display")) { + player.hideEntity(Sentinel.getInstance(), entity); + } + } + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/admin/WandEvents.java b/src/main/java/me/trouper/sentinel/server/events/admin/WandEvents.java new file mode 100644 index 0000000..c82807a --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/admin/WandEvents.java @@ -0,0 +1,278 @@ +package me.trouper.sentinel.server.events.admin; + +import io.github.itzispyder.pdk.events.CustomListener; +import io.github.itzispyder.pdk.plugin.builders.ItemBuilder; +import io.github.itzispyder.pdk.utils.misc.SoundPlayer; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.data.types.CommandBlockHolder; +import me.trouper.sentinel.data.types.Selection; +import me.trouper.sentinel.utils.DisplayUtils; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import me.trouper.sentinel.utils.display.BlockDisplayRaytracer; +import org.bukkit.*; +import org.bukkit.block.CommandBlock; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.entity.minecart.CommandMinecart; +import org.bukkit.event.EventHandler; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.vehicle.VehicleDamageEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class WandEvents implements CustomListener { + public static final ItemStack SELECTION_WAND = ItemBuilder.create() + .material(Material.BLAZE_ROD) + .name(Text.color("&dCommand Block Wand")) + .lore(Text.color("&7Use this wand to manage command blocks.")) + .lore(Text.color("&7It can scan up to 10 blocks away.")) + .lore(Text.color("&7Selections are visible up to 64 blocks away.")) + .lore(Text.color("&8&l➥&r &7Left Click&8:&f Set Position 1")) + .lore(Text.color("&8&l➥&r &7Right Click&8:&f Set Position 2")) + .lore(Text.color("&8&l➥&r &7Break CMD Block&8:&f Remove from whitelist")) + .lore(Text.color("&8&l➥&r &7Click CMD Block&8:&f Add to whitelist")) + .lore(Text.color("&8&l➥&r &7Sneak&8:&f Force Position Setting")) + .lore(Text.color("&7Blocks close to you will get highlighted when holding.")) + .lore(Text.color(" &fHighlight Color Key&8:")) + .lore(Text.color(" &8- &cRed &7: &fNot whitelisted.")) + .lore(Text.color(" &8- &aGreen &7: &fWhitelisted.")) + .lore(Text.color(" &8- &9Blue &7: &fYour selection.")) + .lore(Text.color(" &8- &dPurple &7: &fMissing Command Block")) + .lore(Text.color(" &8- &fBlack &7: &fUnknown Command Block (Auto-Correcting)")) + .customModelData(1984) + .build(); + + public static final Map selections = new HashMap<>(); + private static final ConcurrentLinkedQueue blockHighlights = new ConcurrentLinkedQueue<>(); + private static final Map> playerBlockHighlights = new ConcurrentHashMap<>(); + private static final ConcurrentLinkedQueue entityHighlights = new ConcurrentLinkedQueue<>(); + private static final Map> playerEntityHighlights = new ConcurrentHashMap<>(); + + @EventHandler + public void onClickEntity(PlayerInteractEntityEvent e) { + Player p = e.getPlayer(); + ItemStack i = p.getInventory().getItemInMainHand(); + + if (!i.isSimilar(SELECTION_WAND)) return; + if (!PlayerUtils.isTrusted(p)) return; + + SoundPlayer add = new SoundPlayer(p.getLocation(),Sound.ENTITY_EXPERIENCE_ORB_PICKUP,100,1); + if (!(e.getRightClicked() instanceof CommandMinecart cm)) return; + + e.setCancelled(true); + + Sentinel.getInstance().getDirector().whitelistManager.generateHolder(p.getUniqueId(),cm).addToWhitelist(); + add.play(p); + } + + @EventHandler + public void onDamage(VehicleDamageEvent e) { + if (!(e.getAttacker() instanceof Player p)) return; + ItemStack i = p.getInventory().getItemInMainHand(); + + if (!i.isSimilar(SELECTION_WAND)) return; + if (!PlayerUtils.isTrusted(p)) return; + + SoundPlayer remove = new SoundPlayer(p.getLocation(),Sound.BLOCK_GLASS_BREAK,100,1); + + if (!(e.getVehicle() instanceof CommandMinecart cm)) return; + e.setCancelled(true); + + Sentinel.getInstance().getDirector().whitelistManager.generateHolder(p.getUniqueId(),cm).removeFromWhitelist(); + remove.play(p); + } + + @EventHandler + public void onClick(PlayerInteractEvent e) { + Player p = e.getPlayer(); + ItemStack i = p.getInventory().getItemInMainHand(); + + if (!i.isSimilar(SELECTION_WAND)) return; + if (!PlayerUtils.isTrusted(p)) return; + + SoundPlayer add = new SoundPlayer(p.getLocation(),Sound.ENTITY_EXPERIENCE_ORB_PICKUP,100,1); + SoundPlayer remove = new SoundPlayer(p.getLocation(),Sound.BLOCK_GLASS_BREAK,100,1); + SoundPlayer set1 = new SoundPlayer(p.getLocation(),Sound.UI_BUTTON_CLICK,100,1); + SoundPlayer set2 = new SoundPlayer(p.getLocation(),Sound.UI_BUTTON_CLICK,100,0.8F); + + Selection selection = selections.computeIfAbsent(p.getUniqueId(), k -> new Selection()); + if (p.getTargetBlockExact(10) == null) return; + Location loc = p.getTargetBlockExact(10).getLocation(); + + if (e.getAction() == Action.LEFT_CLICK_BLOCK) { + e.setCancelled(true); + if (p.isSneaking() && ServerUtils.isCommandBlock(loc.getBlock())) { + set1.play(p); + setPos1(p,selection,loc); + } else if (ServerUtils.isCommandBlock(loc.getBlock())) { + remove.play(p); + Sentinel.getInstance().getDirector().whitelistManager.generateHolder(p.getUniqueId(),(CommandBlock) loc.getBlock().getState()).removeFromWhitelist(); + } else { + set1.play(p); + setPos1(p,selection,loc); + } + } else if (e.getAction() == Action.RIGHT_CLICK_BLOCK) { + e.setCancelled(true); + if (p.isSneaking() && ServerUtils.isCommandBlock(loc.getBlock())) { + set2.play(p); + setPos2(p,selection,loc); + } else if (ServerUtils.isCommandBlock(loc.getBlock())) { + add.play(p); + Sentinel.getInstance().getDirector().whitelistManager.generateHolder(p.getUniqueId(),(CommandBlock) loc.getBlock().getState()).addToWhitelist(); + } else { + set2.play(p); + setPos2(p,selection,loc); + } + } + } + + private record EntityHighlight(Player beholder, Entity ent, Color color) { + public void display() { + for (int i = 0; i < 5; i++) { + DisplayUtils.ring(ent.getLocation().clone().add(0, (double) i /5,0),0.6, (location) -> { + DisplayUtils.PLAYER_DUST_PARTICLE_FACTORY.apply(color,1F).accept(beholder,location); + },((location, integer) -> { + return integer % 36 == 0; + })); + } + } + } + + private record BlockHighlight(Player beholder, Location loc, Material color) { + public void display() { + BlockDisplayRaytracer.outline(color, loc, 0.05, 2, List.of(beholder)); + } + } + + private static void sortNear(Player p) { + ItemStack i = p.getInventory().getItemInMainHand(); + + if (!i.isSimilar(SELECTION_WAND) || !PlayerUtils.isTrusted(p)) { + Set existingBlocks = playerBlockHighlights.remove(p.getUniqueId()); + Set existingEntities = playerEntityHighlights.remove(p.getUniqueId()); + if (existingBlocks != null) { + blockHighlights.removeAll(existingBlocks); + entityHighlights.removeAll(existingEntities); + } + return; + } + + Set currentBlocks = new HashSet<>(); + Set currentEntities = new HashSet<>(); + Selection around = new Selection(); + around.setPos1(p.getLocation().add(-10, -10, -10)); + around.setPos2(p.getLocation().add(10, 10, 10)); + around.getBlocks().stream() + .filter(block -> ServerUtils.isCommandBlock(block) && block.getLocation().distance(p.getLocation()) <= 10) + .forEach(block -> { + CommandBlock cb = (CommandBlock) block.getState(); + CommandBlockHolder holder = Sentinel.getInstance().getDirector().whitelistManager.generateHolder(p.getUniqueId(),cb); + Material color = holder.isWhitelisted() + ? Material.LIME_CONCRETE_POWDER + : Material.RED_CONCRETE_POWDER; + if (holder.isUnknown()) { + color = Material.BLACK_CONCRETE_POWDER; + holder.addToExisting(); + } + currentBlocks.add(new BlockHighlight(p, block.getLocation(), color)); + }); + + List carts = p.getNearbyEntities(10,10,10).stream().filter(entity -> entity instanceof CommandMinecart).toList(); + + for (Entity cart : carts) { + if (!(cart instanceof CommandMinecart cm)) continue; + CommandBlockHolder holder = Sentinel.getInstance().getDirector().whitelistManager.generateHolder(p.getUniqueId(),cm); + Color color = holder.isWhitelisted() + ? Color.fromRGB(0x00FF00) + : Color.fromRGB(0xFF0000); + if (holder.isUnknown()) { + color = Color.fromRGB(0); + holder.addToExisting(); + } + currentEntities.add(new EntityHighlight(p, cart, color)); + } + + for (CommandBlockHolder wl : Sentinel.getInstance().getDirector().io.commandBlocks.whitelistedCMDBlocks) { + if (!wl.isPresent() && !wl.isCart()) { + currentBlocks.add(new BlockHighlight(p, wl.loc().translate(), Material.PURPLE_CONCRETE_POWDER)); + } + } + + Set previousBlocks = playerBlockHighlights.getOrDefault(p.getUniqueId(), new HashSet<>()); + Set blocksToAdd = new HashSet<>(currentBlocks); + blocksToAdd.removeAll(previousBlocks); + Set blocksToRemove = new HashSet<>(previousBlocks); + blocksToRemove.removeAll(currentBlocks); + + Set previousEntities = playerEntityHighlights.getOrDefault(p.getUniqueId(), new HashSet<>()); + Set entitiesToAdd = new HashSet<>(currentEntities); + entitiesToAdd.removeAll(previousEntities); + Set entitiesToRemove = new HashSet<>(previousEntities); + entitiesToRemove.removeAll(currentEntities); + + blockHighlights.addAll(blocksToAdd); + blockHighlights.removeAll(blocksToRemove); + playerBlockHighlights.put(p.getUniqueId(), currentBlocks); + + entityHighlights.addAll(entitiesToAdd); + entityHighlights.removeAll(entitiesToRemove); + playerEntityHighlights.put(p.getUniqueId(), currentEntities); + } + + public static void handleDisplay() { + PlayerUtils.forEachTrusted(WandEvents::sortNear); + + Iterator blockIterator = blockHighlights.iterator(); + while (blockIterator.hasNext()) { + BlockHighlight bh = blockIterator.next(); + if (bh.beholder == null || !bh.beholder.isOnline() || bh.loc.distance(bh.beholder.getLocation()) > 10) { + blockIterator.remove(); + playerBlockHighlights.computeIfPresent(bh.beholder.getUniqueId(), (uuid, set) -> { + set.remove(bh); + return set.isEmpty() ? null : set; + }); + } else { + bh.display(); + } + } + + Iterator entityIterator = entityHighlights.iterator(); + while (entityIterator.hasNext()) { + EntityHighlight eh = entityIterator.next(); + if (eh.beholder == null || !eh.beholder.isOnline() || eh.ent.getLocation().distance(eh.beholder.getLocation()) > 10) { + entityIterator.remove(); + playerEntityHighlights.computeIfPresent(eh.beholder.getUniqueId(), (uuid,set) -> { + set.remove(eh); + return set.isEmpty() ? null : set; + }); + } else { + eh.display(); + } + } + + selections.forEach((uuid, selection) -> { + Player p = Bukkit.getPlayer(uuid); + if (p == null || !p.isOnline() || !p.getInventory().getItemInMainHand().isSimilar(SELECTION_WAND)) return; + selection.display(p); + }); + } + + private void setPos2(Player p, Selection selection, Location loc) { + if (selection.getPos2() != null && selection.getPos2().distance(loc) < 0.1) return; + selection.setPos2(loc); + p.sendMessage(Text.prefix("Position 2 set to " + Text.formatLoc(loc))); + } + + private void setPos1(Player p, Selection selection, Location loc) { + if (selection.getPos1() != null && selection.getPos1().distance(loc) < 0.1) return; + selection.setPos1(loc); + p.sendMessage(Text.prefix("Position 1 set to " + Text.formatLoc(loc))); + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/extras/ShadowRealmEvents.java b/src/main/java/me/trouper/sentinel/server/events/extras/ShadowRealmEvents.java new file mode 100644 index 0000000..c2e6a30 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/extras/ShadowRealmEvents.java @@ -0,0 +1,98 @@ +package me.trouper.sentinel.server.events.extras; + +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.event.PacketListenerAbstract; +import com.github.retrooper.packetevents.event.PacketReceiveEvent; +import com.github.retrooper.packetevents.event.PacketSendEvent; +import com.github.retrooper.packetevents.protocol.packettype.PacketType; +import com.github.retrooper.packetevents.protocol.player.GameMode; +import com.github.retrooper.packetevents.protocol.teleport.RelativeFlag; +import com.github.retrooper.packetevents.protocol.world.Difficulty; +import com.github.retrooper.packetevents.protocol.world.dimension.DimensionTypes; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerCloseWindow; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerPositionAndLook; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerRespawn; +import io.github.itzispyder.pdk.events.CustomListener; +import io.github.itzispyder.pdk.utils.SchedulerUtils; +import me.trouper.sentinel.Sentinel; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerJoinEvent; + +public class ShadowRealmEvents extends PacketListenerAbstract implements CustomListener { + + @EventHandler + public void onJoin(PlayerJoinEvent e) { + Player p = e.getPlayer(); + if (!Sentinel.getInstance().getDirector().io.extraStorage.shadowRealm.containsKey(p.getUniqueId())) return; + SchedulerUtils.later(20,()->{ + enforce(p); + }); + } + + @Override + public void onPacketReceive(PacketReceiveEvent e) { + if (e.getPacketType() == PacketType.Play.Client.KEEP_ALIVE) return; + if (e.getPacketType() == PacketType.Play.Client.TELEPORT_CONFIRM) return; + if (e.getPacketType() == PacketType.Play.Client.CLIENT_SETTINGS) return; + if (e.getPacketType() == PacketType.Play.Client.PLAYER_FLYING) return; + Player player = e.getPlayer(); + if (player == null) return; + if (!Sentinel.getInstance().getDirector().io.extraStorage.shadowRealm.containsKey(player.getUniqueId())) return; + e.setCancelled(true); + } + + @Override + public void onPacketSend(PacketSendEvent e) { + if (e.getPacketType() == PacketType.Play.Server.KEEP_ALIVE) return; + if (e.getPacketType() == PacketType.Play.Server.PLAYER_POSITION_AND_LOOK) return; + if (e.getPacketType() == PacketType.Play.Server.RESPAWN) return; + if (e.getPacketType() == PacketType.Play.Server.DISCONNECT) return; + if (e.getPacketType() == PacketType.Play.Server.CLOSE_WINDOW) return; + if (e.getPacketType() == PacketType.Play.Server.CHUNK_DATA) return; + if (e.getPacketType() == PacketType.Play.Server.CHUNK_BATCH_BEGIN) return; + if (e.getPacketType() == PacketType.Play.Server.CHUNK_BATCH_END) return; + if (e.getPacketType() == PacketType.Play.Server.CHUNK_BIOMES) return; + if (e.getPacketType() == PacketType.Play.Server.UNLOAD_CHUNK) return; + if (e.getPacketType() == PacketType.Play.Server.MAP_CHUNK_BULK) return; + + Player player = e.getPlayer(); + if (player == null) return; + if (!Sentinel.getInstance().getDirector().io.extraStorage.shadowRealm.containsKey(player.getUniqueId())) return; + e.setCancelled(true); + } + + public static void enforce(Player p) { + if (p == null || !Sentinel.getInstance().getDirector().io.extraStorage.shadowRealm.containsKey(p.getUniqueId())) return; + sendFakeRespawn(p); + Bukkit.getScheduler().runTaskTimerAsynchronously(Sentinel.getInstance(),(t)->{ + if (p == null || !p.isOnline() || !Sentinel.getInstance().getDirector().io.extraStorage.shadowRealm.containsKey(p.getUniqueId())) t.cancel(); + sendFakePosition(p,0,666,0); + sendCloseScreen(p); + },1,1); + } + + public static void sendFakeRespawn(Player victim) { + if (victim == null || !victim.isOnline()) return; + var player = PacketEvents.getAPI().getPlayerManager().getUser(victim); + WrapperPlayServerRespawn packet = new WrapperPlayServerRespawn(DimensionTypes.THE_END,"minecraft:the_end", Difficulty.PEACEFUL,1L, GameMode.SPECTATOR,null,false,false,false,null,null,null); + player.sendPacket(packet); + } + + public static void sendCloseScreen(Player victim) { + if (victim == null || !victim.isOnline()) return; + var player = PacketEvents.getAPI().getPlayerManager().getUser(victim); + if (player == null) return; + WrapperPlayServerCloseWindow packet = new WrapperPlayServerCloseWindow(); + player.sendPacket(packet); + } + + public static void sendFakePosition(Player victim, double x, double y, double z) { + if (victim == null || !victim.isOnline()) return; + var player = PacketEvents.getAPI().getPlayerManager().getUser(victim); + if (player == null) return; + WrapperPlayServerPlayerPositionAndLook packet = new WrapperPlayServerPlayerPositionAndLook(x,y,z,0,90, RelativeFlag.NONE.getMask(),0,false); + player.sendPacket(packet); + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/AbstractViolation.java b/src/main/java/me/trouper/sentinel/server/events/violations/AbstractViolation.java new file mode 100644 index 0000000..bde902e --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/AbstractViolation.java @@ -0,0 +1,169 @@ +package me.trouper.sentinel.server.events.violations; + +import io.github.itzispyder.pdk.commands.Args; +import io.github.itzispyder.pdk.events.CustomListener; +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater; +import io.papermc.paper.event.player.AsyncChatEvent; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.data.config.ViolationConfig; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.utils.FileUtils; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import me.trouper.sentinel.utils.trees.ConsoleFormatter; +import me.trouper.sentinel.utils.trees.EmbedFormatter; +import me.trouper.sentinel.utils.trees.HoverFormatter; +import me.trouper.sentinel.utils.trees.Node; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.Bukkit; +import org.bukkit.block.Block; +import org.bukkit.block.CommandBlock; +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.bukkit.entity.minecart.CommandMinecart; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.function.BiConsumer; + +public abstract class AbstractViolation implements CustomListener { + + public abstract CustomGui getConfigGui(); + public abstract void getMainPage(Inventory inv); + public abstract void onClick(InventoryClickEvent e); + + public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.getInstance().getDirector().io.violationConfig); + + protected void queuePlayer(Player player, BiConsumer action, String currentValue) { + MainGUI.awaitingCallback.add(player.getUniqueId()); + player.closeInventory(); + updater.queuePlayer(player, 20*60, (e)->{ + e.setCancelled(true); + return LegacyComponentSerializer.legacySection().serialize(e.message()); + }, (cfg, newValue) -> { + action.accept(cfg,new Args(newValue.split("\\s+"))); + cfg.save(); + player.sendMessage(Text.prefix("Value updated successfully")); + player.openInventory(getConfigGui().getInventory()); + }); + player.sendMessage(Component.text(Text.prefix("Enter the new value in chat. The value is currently set to &b%s&7. (Click to insert)".formatted(currentValue))).clickEvent(ClickEvent.suggestCommand(currentValue))); + } + + public void runActions(String rootName, String rootNamePlayer, Node violationInfo, ActionConfiguration.Builder configuration) { + ActionConfiguration config = configuration.build(); + + Node root = new Node("Sentinel"); + root.addTextLine(rootName); + + if (config.getPlayer() != null) root.addChild(generatePlayerInfo(config.getPlayer())); + + root.addChild(violationInfo); + + root.addChild(configuration.getActionNode()); + + notifyTrusted(root,(rootNamePlayer == null || rootNamePlayer.isBlank()) ? rootName : rootNamePlayer); + if (configuration.isLoggedToDiscord()) EmbedFormatter.sendEmbed(EmbedFormatter.format(root)); + Sentinel.getInstance().getLogger().info(ConsoleFormatter.format(root)); + } + + public void notifyTrusted(Node root, String rootNamePlayer) { + PlayerUtils.forEachTrusted(trusted -> { + trusted.sendMessage(Component.text(Text.prefix(rootNamePlayer)).hoverEvent(Component.text(HoverFormatter.format(root)).asHoverEvent())); + }); + } + + public Node generatePlayerInfo(Player p) { + Node playerInfo = new Node(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.playerInfo); + playerInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.name, p.getName()); + playerInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.uuid, p.getUniqueId().toString()); + playerInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.operator, p.isOp() ? Sentinel.getInstance().getDirector().io.lang.generic.yes : Sentinel.getInstance().getDirector().io.lang.generic.no); + playerInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.locationField, Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.locationFormat.formatted(Math.round(p.getX()), Math.round(p.getY()), Math.round(p.getZ()))); + + return playerInfo; + } + + public static Node generateBlockInfo(Block block) { + Node blockInfo = new Node(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.blockInfo); + blockInfo.addTextLine(Text.cleanName(block.getType().toString())); + blockInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.worldField,block.getWorld().getName()); + blockInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.blockLocationField,Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.locationFormat.formatted(block.getX(), block.getY(), block.getZ())); + + return blockInfo; + } + + public Node generateCommandBlockInfo(CommandBlock commandBlock) { + Node commandBlockInfo = new Node(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.blockInfo); + commandBlockInfo.addTextLine(Text.cleanName(commandBlock.getType().toString())); + commandBlockInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.worldField,commandBlock.getWorld().getName()); + commandBlockInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.blockLocationField,Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.locationFormat.formatted(commandBlock.getX(), commandBlock.getY(), commandBlock.getZ())); + + String command = commandBlock.getCommand(); + if (command == null || command.isBlank()) { + return commandBlockInfo; + } else if (command.length() <= 128) { + commandBlockInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.commandField, command); + } else { + commandBlockInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.commandTooLargeField, FileUtils.createCommandLog(command)); + } + + return commandBlockInfo; + } + + public Node generateMinecartInfo(CommandMinecart entity) { + Node minecartInfo = new Node(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.minecartInfo); + minecartInfo.addTextLine(Text.cleanName(entity.getType().toString())); + minecartInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.worldField,entity.getWorld().getName()); + minecartInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.cartLocationField,Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.locationFormat.formatted(Math.round(entity.getX()), Math.round(entity.getY()), Math.round(entity.getZ()))); + + String command = entity.getCommand(); + if (command == null || command.isBlank()) { + return minecartInfo; + } else if (command.length() <= 128) { + minecartInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.commandField, command); + } else { + minecartInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.commandTooLargeField, FileUtils.createCommandLog(command)); + } + + return minecartInfo; + } + + public Node generateItemInfo(ItemStack item) { + Node itemInfo = new Node(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.itemInfo); + itemInfo.addTextLine(Text.cleanName(item.getType().toString())); + itemInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.hasMeta,item.hasItemMeta() ? Sentinel.getInstance().getDirector().io.lang.generic.yes : Sentinel.getInstance().getDirector().io.lang.generic.no); + if (item.hasItemMeta()) { + itemInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.hasName,item.getItemMeta().hasCustomName() ? Sentinel.getInstance().getDirector().io.lang.generic.yes : Sentinel.getInstance().getDirector().io.lang.generic.no); + itemInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.hasLore,item.getItemMeta().hasLore() ? Sentinel.getInstance().getDirector().io.lang.generic.yes : Sentinel.getInstance().getDirector().io.lang.generic.no); + itemInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.hasAttributes,item.getItemMeta().hasAttributeModifiers() ? Sentinel.getInstance().getDirector().io.lang.generic.yes : Sentinel.getInstance().getDirector().io.lang.generic.no); + itemInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.hasEnchants,item.getItemMeta().hasEnchants() ? Sentinel.getInstance().getDirector().io.lang.generic.yes : Sentinel.getInstance().getDirector().io.lang.generic.no); + itemInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.nbtStored, FileUtils.createNBTLog(item)); + } + + return itemInfo; + } + + public Node generateCommandInfo(String command, Player executor) { + Node commandInfo = new Node(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.commandInfo); + String name = command.split(" ")[0].substring(1); + ServerUtils.verbose("Command Name: " + name); + Command executed = Bukkit.getServer().getCommandMap().getCommand(name); + + commandInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.name,name); + if (command.length() <= 128) { + commandInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.commandField, command); + } else { + commandInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.commandTooLargeField, FileUtils.createCommandLog(command)); + } + if (executed == null || executed.getPermission() == null) return commandInfo; + commandInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.permissionRequired,executed.getPermission()); + commandInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.permissionSatisfied,executor.hasPermission(executed.getPermission()) ? Sentinel.getInstance().getDirector().io.lang.generic.yes : Sentinel.getInstance().getDirector().io.lang.generic.no); + + return commandInfo; + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockBreak.java b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockBreak.java new file mode 100644 index 0000000..a709f23 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockBreak.java @@ -0,0 +1,146 @@ +package me.trouper.sentinel.server.events.violations.blocks.command; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.data.types.SerialLocation; +import me.trouper.sentinel.data.types.CommandBlockHolder; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.CommandBlock; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class CommandBlockBreak extends AbstractViolation{ + + @EventHandler + public void onBreak(BlockBreakEvent e) { + //ServerUtils.verbose("CommandBlockBreak: Detected the event"); + //ServerUtils.verbose("CommandBlockBreak: Changer is a player"); + Block b = e.getBlock(); + if (!(ServerUtils.isCommandBlock(b))) return; + ServerUtils.verbose("CommandBlockBreak: Block is a command block"); + Player p = e.getPlayer(); + CommandBlock cb = (CommandBlock) b.getState(); + CommandBlockHolder holder = Sentinel.getInstance().getDirector().whitelistManager.generateHolder(p.getUniqueId(),cb); + if (PlayerUtils.isTrusted(e.getPlayer())) { + if (!Sentinel.getInstance().getDirector().whitelistManager.autoWhitelist.contains(p.getUniqueId())) { + + return; + } + return; + } + + if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.enabled) return; + + ServerUtils.verbose("CommandBlockBreak: is enabled, performing action"); + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setPlayer(p) + .deop(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.deop) + .cancel(true) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.punish) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.brake, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlock), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.brake, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlock), + generateCommandBlockInfo(cb), + config + ); + } + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Command Block Break")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,22,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.enabled,Items.configItem("Check Toggle",Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(2,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(6,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 2 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.deop = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.deop; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 20 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 6 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.punish = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 24 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { + cfg.commandBlockBreak.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + } + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockEdit.java b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockEdit.java new file mode 100644 index 0000000..1ba0f16 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockEdit.java @@ -0,0 +1,141 @@ +package me.trouper.sentinel.server.events.violations.blocks.command; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.CommandBlock; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class CommandBlockEdit extends AbstractViolation { + + @EventHandler + private void onCMDBlockChange(EntityChangeBlockEvent e) { + //ServerUtils.verbose("CommandBlockChange: Detected the event"); + if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.enabled) return; + //ServerUtils.verbose("CommandBlockChange: Enabled"); + if (!(e.getEntity() instanceof Player p)) return; + //ServerUtils.verbose("CommandBlockChange: Changer is a player"); + Block b = e.getBlock(); + if (!(ServerUtils.isCommandBlock(b))) + return; + ServerUtils.verbose("CommandBlockChange: Block is a command block"); + CommandBlock cb = (CommandBlock) b.getState(); + if (PlayerUtils.isTrusted(p)) { + if (!Sentinel.getInstance().getDirector().whitelistManager.autoWhitelist.contains(p.getUniqueId())) return; + Sentinel.getInstance().getDirector().whitelistManager.generateHolder(p.getUniqueId(),cb).addToWhitelist(); + return; + } + ServerUtils.verbose("CommandBlockChange: Not trusted, performing action"); + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setPlayer(p) + .deop(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.deop) + .cancel(true) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.punish) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.edit, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlock), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.edit, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlock), + generateCommandBlockInfo(cb), + config + ); + } + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Command Block Edit")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,22,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.enabled,Items.configItem("Check Toggle", Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(2,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(6,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 2 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.deop = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.deop; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 20 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 6 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.punish = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 24 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { + cfg.commandBlockEdit.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockEdit.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + } + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockPlace.java b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockPlace.java new file mode 100644 index 0000000..8df9457 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockPlace.java @@ -0,0 +1,144 @@ +package me.trouper.sentinel.server.events.violations.blocks.command; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.data.types.CommandBlockHolder; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.CommandBlock; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class CommandBlockPlace extends AbstractViolation { + + @EventHandler + public void listen(BlockPlaceEvent e) { + Player p = e.getPlayer(); + Block b = e.getBlockPlaced(); + if (!ServerUtils.isCommandBlock(b)) return; + CommandBlock cb = (CommandBlock) b.getState(); + CommandBlockHolder holder = Sentinel.getInstance().getDirector().whitelistManager.generateHolder(p.getUniqueId(),cb); + if (PlayerUtils.isTrusted(p)) { + holder.addToExisting(); + if (Sentinel.getInstance().getDirector().whitelistManager.autoWhitelist.contains(p.getUniqueId())) holder.addToWhitelist(); + return; + } + + if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.enabled) { + holder.addToExisting(); + return; + } + + ServerUtils.verbose("CommandBlockPlace: Enabled, performing action"); + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setPlayer(p) + .deop(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.deop) + .cancel(true) + .setEvent(e) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.punish) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.place, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlock), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.place, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlock), + generateCommandBlockInfo(cb), + config + ); + } + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Command Block Place")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,22,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.enabled,Items.configItem("Check Toggle", Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(2,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(6,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 2 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.deop = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.deop; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 20 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 6 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.punish = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 24 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { + cfg.commandBlockPlace.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + } + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockUse.java b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockUse.java new file mode 100644 index 0000000..8dba11f --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/command/CommandBlockUse.java @@ -0,0 +1,142 @@ +package me.trouper.sentinel.server.events.violations.blocks.command; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.CommandBlock; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class CommandBlockUse extends AbstractViolation { + + @EventHandler + private void onCMDBlockUse(PlayerInteractEvent e) { + //ServerUtils.verbose("CommandBlockUse: Detected Interaction"); + if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.enabled) return; + //ServerUtils.verbose("CommandBlockUse: Enabled"); + Player p = e.getPlayer(); + if (e.getClickedBlock() == null) return; + //ServerUtils.verbose("CommandBlockUse: Block isn't null"); + Block b = e.getClickedBlock(); + if (!(ServerUtils.isCommandBlock(b))) return; + CommandBlock cb = (CommandBlock) b.getState(); + if (PlayerUtils.isTrusted(p)) { + if (!Sentinel.getInstance().getDirector().whitelistManager.autoWhitelist.contains(p.getUniqueId())) return; + if (Sentinel.getInstance().getDirector().whitelistManager.isWhitelisted(cb)) return; + e.setCancelled(true); + Sentinel.getInstance().getDirector().whitelistManager.generateHolder(p.getUniqueId(), cb).addToWhitelist(); + return; + } + ServerUtils.verbose("CommandBlockUse: Not trusted, performing action"); + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setPlayer(p) + .deop(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.deop) + .cancel(true) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.punish) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.use, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlock), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.use, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlock), + generateCommandBlockInfo(cb), + config + ); + } + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Command Block Use")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,22,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.enabled,Items.configItem("Check Toggle", Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(2,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(6,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 2 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.deop = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.deop; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 20 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 6 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.punish = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 24 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { + cfg.commandBlockUse.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockUse.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + } + } +} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/blocks/jigsaw/JigsawBlockBreak.java b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/jigsaw/JigsawBlockBreak.java new file mode 100644 index 0000000..90f67ed --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/jigsaw/JigsawBlockBreak.java @@ -0,0 +1,133 @@ +package me.trouper.sentinel.server.events.violations.blocks.jigsaw; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class JigsawBlockBreak extends AbstractViolation { + + @EventHandler + public void onPlace(BlockBreakEvent e) { + if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.enabled) return; + Player p = e.getPlayer(); + Block b = e.getBlock(); + if (b == null) return; + if (!Material.JIGSAW.equals(b.getType())) return; + ServerUtils.verbose("StructureBlockBreak: Block is a Structure block"); + if (PlayerUtils.isTrusted(p)) return; + ServerUtils.verbose("StructureBlockBreak: Not trusted, performing action"); + + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setPlayer(p) + .deop(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.deop) + .cancel(true) + .setEvent(e) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.punish) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.brake, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.jigsawBlock), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.brake, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.jigsawBlock), + generateBlockInfo(b), + config + ); + } + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Jigsaw Block Break")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,22,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.enabled,Items.configItem("Check Toggle",Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(2,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(6,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 2 -> { + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.deop = !Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.deop; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 20 -> { + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 6 -> { + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.punish = !Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 24 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { + cfg.jigsawBlockBreak.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockBreak.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + } + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/blocks/jigsaw/JigsawBlockPlace.java b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/jigsaw/JigsawBlockPlace.java new file mode 100644 index 0000000..8a2db17 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/jigsaw/JigsawBlockPlace.java @@ -0,0 +1,134 @@ +package me.trouper.sentinel.server.events.violations.blocks.jigsaw; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class JigsawBlockPlace extends AbstractViolation { + + @EventHandler + public void onPlace(BlockPlaceEvent e) { + if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.enabled) return; + Player p = e.getPlayer(); + Block b = e.getBlockPlaced(); + if (b == null) return; + if (!b.getType().equals(Material.JIGSAW)) return; + ServerUtils.verbose("StructureBlockPlace: Block is a Structure block"); + if (PlayerUtils.isTrusted(p)) return; + ServerUtils.verbose("StructureBlockPlace: Not trusted, performing action"); + + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setPlayer(p) + .deop(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.deop) + .cancel(true) + .setEvent(e) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.punish) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.place, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.jigsawBlock), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.place, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.jigsawBlock), + generateBlockInfo(b), + config + ); + } + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Jigsaw Block Place")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,22,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.enabled,Items.configItem("Check Toggle",Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(2,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(6,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 2 -> { + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.deop = !Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.deop; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 20 -> { + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 6 -> { + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.punish = !Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 24 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { + cfg.jigsawBlockPlace.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockPlace.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + } + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/blocks/jigsaw/JigsawBlockUse.java b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/jigsaw/JigsawBlockUse.java new file mode 100644 index 0000000..f3318b5 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/jigsaw/JigsawBlockUse.java @@ -0,0 +1,133 @@ +package me.trouper.sentinel.server.events.violations.blocks.jigsaw; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class JigsawBlockUse extends AbstractViolation { + @EventHandler + public void onPlace(PlayerInteractEvent e) { + if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.enabled) return; + Player p = e.getPlayer(); + Block b = e.getClickedBlock(); + if (b == null) return; + if (!Material.JIGSAW.equals(b.getType())) return; + ServerUtils.verbose("StructureBlockUse: Block is a Structure block"); + if (PlayerUtils.isTrusted(p)) return; + ServerUtils.verbose("StructureBlockUse: Not trusted, performing action"); + + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setPlayer(p) + .deop(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.deop) + .cancel(true) + .setEvent(e) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.punish) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.use, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.jigsawBlock), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.use, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.jigsawBlock), + generateBlockInfo(b), + config + ); + } + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Jigsaw Block Use")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,22,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.enabled,Items.configItem("Check Toggle",Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(2,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(6,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 2 -> { + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.deop = !Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.deop; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 20 -> { + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 6 -> { + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.punish = !Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 24 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { + cfg.jigsawBlockUse.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.jigsawBlockUse.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + } + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/blocks/structure/StructureBlockBreak.java b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/structure/StructureBlockBreak.java new file mode 100644 index 0000000..8dd9aef --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/structure/StructureBlockBreak.java @@ -0,0 +1,134 @@ +package me.trouper.sentinel.server.events.violations.blocks.structure; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class StructureBlockBreak extends AbstractViolation { + + @EventHandler + public void onPlace(BlockBreakEvent e) { + if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.enabled) return; + Player p = e.getPlayer(); + Block b = e.getBlock(); + if (b == null) return; + if (!Material.STRUCTURE_BLOCK.equals(b.getType())) return; + ServerUtils.verbose("StructureBlockBreak: Block is a Structure block"); + if (PlayerUtils.isTrusted(p)) return; + ServerUtils.verbose("StructureBlockBreak: Not trusted, performing action"); + + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setPlayer(p) + .deop(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.deop) + .cancel(true) + .setEvent(e) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.punish) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.brake, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.structureBlock), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.brake, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.structureBlock), + generateBlockInfo(b), + config + ); + } + + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Structure Block Break")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,22,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.enabled,Items.configItem("Check Toggle",Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(2,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(6,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 2 -> { + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.deop = !Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.deop; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 20 -> { + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 6 -> { + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.punish = !Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 24 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { + cfg.structureBlockBreak.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockBreak.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + } + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/blocks/structure/StructureBlockPlace.java b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/structure/StructureBlockPlace.java new file mode 100644 index 0000000..9b90594 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/structure/StructureBlockPlace.java @@ -0,0 +1,132 @@ +package me.trouper.sentinel.server.events.violations.blocks.structure; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class StructureBlockPlace extends AbstractViolation { + @EventHandler + public void onPlace(BlockPlaceEvent e) { + if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.enabled) return; + Player p = e.getPlayer(); + Block b = e.getBlockPlaced(); + if (b == null) return; + if (!b.getType().equals(Material.STRUCTURE_BLOCK)) return; + ServerUtils.verbose("StructureBlockPlace: Block is a Structure block"); + if (PlayerUtils.isTrusted(p)) return; + ServerUtils.verbose("StructureBlockPlace: Not trusted, performing action"); + + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setPlayer(p) + .deop(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.deop) + .cancel(true) + .setEvent(e) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.punish) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.place, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.structureBlock), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.place, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.structureBlock), + generateBlockInfo(b), + config + ); + } + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Structure Block Place")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,22,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.enabled,Items.configItem("Check Toggle",Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(2,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(6,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 2 -> { + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.deop = !Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.deop; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 20 -> { + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 6 -> { + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.punish = !Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 24 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { + cfg.structureBlockPlace.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockPlace.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + } + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/blocks/structure/StructureBlockUse.java b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/structure/StructureBlockUse.java new file mode 100644 index 0000000..876f4fe --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/blocks/structure/StructureBlockUse.java @@ -0,0 +1,133 @@ +package me.trouper.sentinel.server.events.violations.blocks.structure; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class StructureBlockUse extends AbstractViolation { + @EventHandler + public void onPlace(PlayerInteractEvent e) { + if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockPlace.enabled) return; + Player p = e.getPlayer(); + Block b = e.getClickedBlock(); + if (b == null) return; + if (!Material.STRUCTURE_BLOCK.equals(b.getType())) return; + ServerUtils.verbose("StructureBlockUse: Block is a Structure block"); + if (PlayerUtils.isTrusted(p)) return; + + ServerUtils.verbose("StructureBlockUse: Not trusted, performing action"); + + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setPlayer(p) + .deop(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.deop) + .cancel(true) + .setEvent(e) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.punish) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.use, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.structureBlock), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.use, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.structureBlock), + generateBlockInfo(b), + config + ); + } + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Structure Block Use")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,22,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.enabled,Items.configItem("Check Toggle",Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(2,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(6,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 2 -> { + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.deop = !Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.deop; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 20 -> { + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 6 -> { + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.punish = !Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 24 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { + cfg.structureBlockUse.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.structureBlockUse.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + } + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/command/DangerousCommand.java b/src/main/java/me/trouper/sentinel/server/events/violations/command/DangerousCommand.java new file mode 100644 index 0000000..48f15da --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/command/DangerousCommand.java @@ -0,0 +1,140 @@ +package me.trouper.sentinel.server.events.violations.command; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class DangerousCommand extends AbstractViolation { + @EventHandler + private void onCommand(PlayerCommandPreprocessEvent e) { + Player p = e.getPlayer(); + if (PlayerUtils.isTrusted(p)) return; + String label = e.getMessage().substring(1).split(" ")[0]; + String args = e.getMessage(); + + if (Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.commands.contains(label) && Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.enabled) { + e.setCancelled(true); + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setPlayer(p) + .deop(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.deop) + .cancel(true) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.punish) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.run, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.dangerousCommand), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.run, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.dangerousCommand), + generateCommandInfo(args, p), + config + ); + } + } + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Dangerous Command Check")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i, Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.enabled,Items.configItem("Check Toggle", Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(2,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(6,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); + inv.setItem(22,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.commands,Material.CRIMSON_HANGING_SIGN,"Commands","Commands that will flag this check.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 2 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.deop = !Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.deop; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 20 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 6 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.punish = !Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 24 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> { + cfg.commandExecute.dangerous.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 22 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> { + cfg.commandExecute.dangerous.commands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.commands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.dangerous.commands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + } + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/command/LoggedCommand.java b/src/main/java/me/trouper/sentinel/server/events/violations/command/LoggedCommand.java new file mode 100644 index 0000000..e6b839e --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/command/LoggedCommand.java @@ -0,0 +1,110 @@ +package me.trouper.sentinel.server.events.violations.command; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class LoggedCommand extends AbstractViolation { + + @EventHandler + public void onCommand(PlayerCommandPreprocessEvent e) { + Player p = e.getPlayer(); + if (PlayerUtils.isTrusted(p)) return; + String label = e.getMessage().substring(1).split(" ")[0]; + String args = e.getMessage(); + + if (Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.logged.commands.contains(label) && Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.logged.enabled) { + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setPlayer(p) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.logged.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.run, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.loggedCommand), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.run, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.loggedCommand), + generateCommandInfo(args, p), + config + ); + } + } + + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Logged Command Check")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.logged.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,22,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.logged.enabled,Items.configItem("Check Toggle", Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(11,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.logged.logToDiscord,Items.configItem("Log to Discord",Material.OAK_LOG,"If this check will log to discord"))); + inv.setItem(15,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.logged.commands,Material.CRIMSON_HANGING_SIGN,"Commands","Commands that will flag this check")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.logged.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.logged.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 11 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.logged.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.logged.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 15 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> { + cfg.commandExecute.logged.commands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.logged.commands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.logged.commands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + } + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/command/SpecificCommand.java b/src/main/java/me/trouper/sentinel/server/events/violations/command/SpecificCommand.java new file mode 100644 index 0000000..3adc88a --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/command/SpecificCommand.java @@ -0,0 +1,117 @@ +package me.trouper.sentinel.server.events.violations.command; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public class SpecificCommand extends AbstractViolation { + + @EventHandler + private void onCommand(PlayerCommandPreprocessEvent e) { + Player p = e.getPlayer(); + if (PlayerUtils.isTrusted(p)) return; + String label = e.getMessage().substring(1).split(" ")[0]; + String args = e.getMessage(); + + if (label.contains(":") && Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.enabled) { + e.setCancelled(true); + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setPlayer(p) + .cancel(true) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.punish) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.run, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.specificCommand), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.run, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.specificCommand), + generateCommandInfo(args, p), + config + ); + } + } + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Specific Command Check")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack top = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.enabled) { + top = Items.GREEN; + } + + for (int i = 0; i < 9; i++) { + inv.setItem(i,top); + } + + inv.setItem(26,Items.BACK); + inv.setItem(4,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.enabled,Items.configItem("Check Toggle", Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(11,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"If this check will run the punishment commands"))); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(15,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.punishmentCommands,Material.DIAMOND_AXE,"Commands","Commands that will flag this check")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 4 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 11 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.punish = !Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 15 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { + cfg.commandExecute.specific.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.commandExecute.specific.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + } + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartBreak.java b/src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartBreak.java new file mode 100644 index 0000000..2cfe79d --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartBreak.java @@ -0,0 +1,145 @@ +package me.trouper.sentinel.server.events.violations.entities; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.entity.minecart.CommandMinecart; +import org.bukkit.event.EventHandler; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class CommandMinecartBreak extends AbstractViolation { + @EventHandler + public void onBreak(EntityDamageEvent e) { + //ServerUtils.verbose("CommandBlockBreak: Detected the event"); + if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.enabled) return; + //ServerUtils.verbose("CommandBlockBreak: Changer is a player"); + if (!(e.getEntity() instanceof CommandMinecart s)) return; + if (e.getDamageSource() == null) { + e.setCancelled(true); + return; + } + if (e.getDamageSource().getCausingEntity() == null) { + e.setCancelled(true); + return; + } + if (!(e.getDamageSource().getCausingEntity() instanceof Player p)) { + e.setCancelled(true); + return; + } + if (PlayerUtils.isTrusted(p)) { + if (Sentinel.getInstance().getDirector().whitelistManager.getFromExisting(s.getLocation()).isWhitelisted()) return; + Sentinel.getInstance().getDirector().whitelistManager.getFromExisting(s.getLocation()).removeFromExisting(); + return; + } + ServerUtils.verbose("Not trusted, performing action"); + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setEntity(s) + .setPlayer(p) + .deop(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.deop) + .cancel(true) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.punish) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockBreak.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.brake, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandMinecart), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.brake, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandMinecart), + generateMinecartInfo(s), + config + ); + } + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Command Cart Break")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,22,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.enabled,Items.configItem("Check Toggle", Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(2,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(6,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 2 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.deop = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.deop; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 20 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 6 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.punish = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 24 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { + cfg.commandBlockMinecartBreak.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartBreak.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + } + } +} diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartPlace.java b/src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartPlace.java new file mode 100644 index 0000000..6fea190 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartPlace.java @@ -0,0 +1,196 @@ +package me.trouper.sentinel.server.events.violations.entities; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.entity.minecart.CommandMinecart; +import org.bukkit.event.EventHandler; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.vehicle.VehicleCreateEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; + +public class CommandMinecartPlace extends AbstractViolation { + + private final ConcurrentHashMap queuedInteractions = new ConcurrentHashMap<>(); + + private UUID getPlayer(Location loc) { + ServerUtils.verbose("Getting responsible player for a location"); + AtomicReference player = new AtomicReference<>(); + + queuedInteractions.forEach((location,uuid)->{ + if (player.get() == null) { + ServerUtils.verbose("Loop is running"); + if (loc.distance(location) < 1) { + ServerUtils.verbose("Found a matching minecart"); + player.set(uuid); + queuedInteractions.remove(location); + } + } + }); + return player.get(); + } + + @EventHandler + private void onVehicleCreate(VehicleCreateEvent e) { + //ServerUtils.verbose("Vehicle Creation Event"); + if (!(e.getVehicle() instanceof CommandMinecart commandMinecart)) return; + if (queuedInteractions.isEmpty()) { + ServerUtils.verbose("Queue is empty, preventing"); + e.setCancelled(true); + return; + } + UUID uuid = getPlayer(e.getVehicle().getLocation()); + if (uuid == null) { + ServerUtils.verbose("UUID is null, preventing"); + e.setCancelled(true); + return; + } + Player p = Bukkit.getPlayer(uuid); + if (p == null) { + ServerUtils.verbose("Player is null, preventing"); + e.setCancelled(true); + return; + } + + if (PlayerUtils.isTrusted(p)) { + ServerUtils.verbose("Player is trusted, allowing."); + Sentinel.getInstance().getDirector().whitelistManager.generateHolder(p.getUniqueId(),commandMinecart) + .addToExisting(); + return; + } + + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setPlayer(p) + .cancel(true) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.punish) + .deop(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.deop) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.logToDiscord); + + // Remove the command block minecart from the player's inventory + p.getInventory().remove(Material.COMMAND_BLOCK_MINECART); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.place, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandMinecart), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.place, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandMinecart), + generateMinecartInfo(commandMinecart), + config + ); + } + + @EventHandler + private void onIneteract(PlayerInteractEvent e) { + //ServerUtils.verbose("Player Interaction Event"); + if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.enabled) return; + //ServerUtils.verbose("MinecartCommandPlace: Check is enabled"); + Player p = e.getPlayer(); + if (e.getItem() == null) return; + //ServerUtils.verbose("MinecartCommandPlace: Item isn't null"); + if (e.getClickedBlock() == null) return; + //ServerUtils.verbose("MinecartCommandPlace: Clicked block isn't null"); + if (!e.getItem().getType().equals(Material.COMMAND_BLOCK_MINECART)) return; + ServerUtils.verbose("Item is a minecart command"); + if (!(e.getClickedBlock().getType() == Material.RAIL || e.getClickedBlock().getType() == Material.POWERED_RAIL || e.getClickedBlock().getType() == Material.ACTIVATOR_RAIL || e.getClickedBlock().getType() == Material.DETECTOR_RAIL)) return; + ServerUtils.verbose("Clicked block is a rail, adding to list"); + + queuedInteractions.put(e.getClickedBlock().getLocation(),p.getUniqueId()); + } + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Command Cart Place")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,22,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.enabled,Items.configItem("Check Toggle", Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(2,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(6,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 2 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.deop = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.deop; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 20 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 6 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.punish = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 24 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { + cfg.commandBlockMinecartPlace.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartPlace.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartUse.java b/src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartUse.java new file mode 100644 index 0000000..9264f9c --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/entities/CommandMinecartUse.java @@ -0,0 +1,132 @@ +package me.trouper.sentinel.server.events.violations.entities; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.entity.minecart.CommandMinecart; +import org.bukkit.event.EventHandler; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class CommandMinecartUse extends AbstractViolation { + + @EventHandler + private void onCMDBlockMinecartUse(PlayerInteractEntityEvent e) { + //ServerUtils.verbose("MinecartCommandUse: Detected Interaction with entity"); + if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.enabled) return; + //ServerUtils.verbose("MinecartCommandUse: Enabled"); + Player p = e.getPlayer(); + if (!(e.getRightClicked() instanceof CommandMinecart s)) return; + ServerUtils.verbose("MinecartCommandUse: Entity is minecart command"); + if (PlayerUtils.isTrusted(p)) return; + ServerUtils.verbose("MinecartCommandUse: Not trusted, performing action"); + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setPlayer(p) + .setEntity(s) + .cancel(true) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.punish) + .deop(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.deop) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.use, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandMinecart), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.use, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandMinecart), + generateMinecartInfo(s), + config + ); + } + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Command Cart Use")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,22,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.enabled,Items.configItem("Check Toggle", Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(2,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(6,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 2 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.deop = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.deop; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 20 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 6 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.punish = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 24 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { + cfg.commandBlockMinecartUse.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockMinecartUse.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/server/events/ChatEvent.java b/src/main/java/me/trouper/sentinel/server/events/violations/players/ChatEvent.java similarity index 54% rename from src/main/java/me/trouper/sentinel/server/events/ChatEvent.java rename to src/main/java/me/trouper/sentinel/server/events/violations/players/ChatEvent.java index 9cb6b7a..a007717 100644 --- a/src/main/java/me/trouper/sentinel/server/events/ChatEvent.java +++ b/src/main/java/me/trouper/sentinel/server/events/violations/players/ChatEvent.java @@ -1,9 +1,27 @@ -package me.trouper.sentinel.server.events; +package me.trouper.sentinel.server.events.violations.players; import io.github.itzispyder.pdk.events.CustomListener; import io.github.itzispyder.pdk.utils.SchedulerUtils; import io.papermc.paper.event.player.AsyncChatEvent; import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.blocks.command.CommandBlockBreak; +import me.trouper.sentinel.server.events.violations.blocks.command.CommandBlockEdit; +import me.trouper.sentinel.server.events.violations.blocks.command.CommandBlockPlace; +import me.trouper.sentinel.server.events.violations.blocks.command.CommandBlockUse; +import me.trouper.sentinel.server.events.violations.blocks.jigsaw.JigsawBlockBreak; +import me.trouper.sentinel.server.events.violations.blocks.jigsaw.JigsawBlockPlace; +import me.trouper.sentinel.server.events.violations.blocks.jigsaw.JigsawBlockUse; +import me.trouper.sentinel.server.events.violations.blocks.structure.StructureBlockBreak; +import me.trouper.sentinel.server.events.violations.blocks.structure.StructureBlockPlace; +import me.trouper.sentinel.server.events.violations.blocks.structure.StructureBlockUse; +import me.trouper.sentinel.server.events.violations.command.DangerousCommand; +import me.trouper.sentinel.server.events.violations.command.LoggedCommand; +import me.trouper.sentinel.server.events.violations.command.SpecificCommand; +import me.trouper.sentinel.server.events.violations.entities.CommandMinecartBreak; +import me.trouper.sentinel.server.events.violations.entities.CommandMinecartPlace; +import me.trouper.sentinel.server.events.violations.entities.CommandMinecartUse; +import me.trouper.sentinel.server.events.violations.whitelist.CommandBlockExecute; +import me.trouper.sentinel.server.events.violations.whitelist.CommandMinecartExecute; import me.trouper.sentinel.server.functions.chatfilter.profanity.ProfanityFilter; import me.trouper.sentinel.server.functions.chatfilter.spam.SpamFilter; import me.trouper.sentinel.server.functions.chatfilter.unicode.UnicodeFilter; @@ -13,10 +31,6 @@ import me.trouper.sentinel.server.gui.config.chat.ProfanityFilterGUI; import me.trouper.sentinel.server.gui.config.chat.SpamFilterGUI; import me.trouper.sentinel.server.gui.config.chat.UnicodeFilterGUI; import me.trouper.sentinel.server.gui.config.chat.UrlFilterGUI; -import me.trouper.sentinel.server.gui.config.nuke.checks.*; -import me.trouper.sentinel.server.gui.config.nuke.checks.command.DangerousCMDGUI; -import me.trouper.sentinel.server.gui.config.nuke.checks.command.LoggedCMDGUI; -import me.trouper.sentinel.server.gui.config.nuke.checks.command.SpecificCMDGUI; import me.trouper.sentinel.utils.PlayerUtils; import me.trouper.sentinel.utils.ServerUtils; import org.bukkit.entity.Player; @@ -45,15 +59,25 @@ public class ChatEvent implements CustomListener { UrlFilterGUI.updater.invokeCallbacks(e); ProfanityFilterGUI.updater.invokeCallbacks(e); SpamFilterGUI.updater.invokeCallbacks(e); - DangerousCMDGUI.updater.invokeCallbacks(e); - LoggedCMDGUI.updater.invokeCallbacks(e); - SpecificCMDGUI.updater.invokeCallbacks(e); - CBEditGUI.updater.invokeCallbacks(e); - CBMCPlaceGUI.updater.invokeCallbacks(e); - CBMCUseGUI.updater.invokeCallbacks(e); - CBPlaceGUI.updater.invokeCallbacks(e); - CBUseGUI.updater.invokeCallbacks(e); - HotbarActionGUI.updater.invokeCallbacks(e); + DangerousCommand.updater.invokeCallbacks(e); + LoggedCommand.updater.invokeCallbacks(e); + SpecificCommand.updater.invokeCallbacks(e); + CommandBlockBreak.updater.invokeCallbacks(e); + CommandBlockEdit.updater.invokeCallbacks(e); + CommandBlockPlace.updater.invokeCallbacks(e); + CommandBlockUse.updater.invokeCallbacks(e); + JigsawBlockBreak.updater.invokeCallbacks(e); + JigsawBlockPlace.updater.invokeCallbacks(e); + JigsawBlockUse.updater.invokeCallbacks(e); + StructureBlockBreak.updater.invokeCallbacks(e); + StructureBlockPlace.updater.invokeCallbacks(e); + StructureBlockUse.updater.invokeCallbacks(e); + CommandMinecartBreak.updater.invokeCallbacks(e); + CommandMinecartPlace.updater.invokeCallbacks(e); + CommandMinecartUse.updater.invokeCallbacks(e); + CommandBlockExecute.updater.invokeCallbacks(e); + CommandMinecartExecute.updater.invokeCallbacks(e); + CreativeHotbar.updater.invokeCallbacks(e); }); } return; @@ -65,7 +89,7 @@ public class ChatEvent implements CustomListener { handle(p, "sentinel.chatfilter.unicode.bypass", - Sentinel.mainConfig.chat.unicodeFilter.enabled, "unicode", + Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.enabled, "unicode", e, UnicodeFilter::handleUnicodeFilter); @@ -73,7 +97,7 @@ public class ChatEvent implements CustomListener { handle(p, "sentinel.chatfilter.url.bypass", - Sentinel.mainConfig.chat.urlFilter.enabled, "url", + Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.enabled, "url", e, UrlFilter::handleUrlFilter); @@ -81,7 +105,7 @@ public class ChatEvent implements CustomListener { handle(p, "sentinel.chatfilter.spam.bypass", - Sentinel.mainConfig.chat.spamFilter.enabled, + Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.enabled, "spam", e, SpamFilter::handleSpamFilter); @@ -90,7 +114,7 @@ public class ChatEvent implements CustomListener { handle(p, "sentinel.chatfilter.swear.bypass", - Sentinel.mainConfig.chat.profanityFilter.enabled, + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.enabled, "swear", e, ProfanityFilter::handleProfanityFilter); diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/players/CreativeHotbar.java b/src/main/java/me/trouper/sentinel/server/events/violations/players/CreativeHotbar.java new file mode 100644 index 0000000..cd7bcb7 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/players/CreativeHotbar.java @@ -0,0 +1,141 @@ +package me.trouper.sentinel.server.events.violations.players; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.functions.itemchecks.ItemCheck; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.PlayerUtils; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCreativeEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class CreativeHotbar extends AbstractViolation { + + @EventHandler + private void onNBTPull(InventoryCreativeEvent e) { + //ServerUtils.verbose("NBT: Detected creative mode action"); + if (!Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.enabled) return; + ServerUtils.verbose("NBT: Enabled"); + if (!(e.getWhoClicked() instanceof Player p)) return; + ServerUtils.verbose("NBT: Clicker is a player"); + if (e.getCursor() == null) return; // Well it threw an exception during testing, so it isn't always false! + ServerUtils.verbose("NBT: Cursor isn't null"); + ItemStack i = e.getCursor(); + if (PlayerUtils.isTrusted(p)) return; + ServerUtils.verbose("NBT: Not trusted"); + if (e.getCursor().getItemMeta() == null) return; + ServerUtils.verbose("NBT: Cursor has meta"); + if (!(i.hasItemMeta() && i.getItemMeta() != null)) return; + ServerUtils.verbose("NBT: Item has meta"); + if (new ItemCheck().passes(i)) return; + ServerUtils.verbose("NBT: Item doesn't pass, performing action"); + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setPlayer(p) + .cancel(true) + .punish(Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.punish) + .deop(Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.deop) + .setPunishmentCommands(Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.punishmentCommands) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.logToDiscord); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.grab, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.nbtItem), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.grab, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.nbtItem), + generateItemInfo(i), + config + ); + } + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Creative Hotbar Check")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i, Items.BLANK); + } + + ItemStack ring = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.enabled) { + ring = Items.GREEN; + } + + List ringList = List.of(3,4,5,12,14,21,22,23); + + for (Integer i : ringList) { + inv.setItem(i,ring); + } + + inv.setItem(26,Items.BACK); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.enabled,Items.configItem("Check Toggle", Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(2,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(6,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + + switch (e.getSlot()) { + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 2 -> { + Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.deop = !Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.deop; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 20 -> { + Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 6 -> { + Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.punish = !Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.punish; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + case 24 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { + cfg.creativeHotbarAction.punishmentCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.punishmentCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.punishmentCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + + } + } +} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/server/events/PluginCloakingEvent.java b/src/main/java/me/trouper/sentinel/server/events/violations/players/PluginCloakingEvents.java similarity index 59% rename from src/main/java/me/trouper/sentinel/server/events/PluginCloakingEvent.java rename to src/main/java/me/trouper/sentinel/server/events/violations/players/PluginCloakingEvents.java index 549ed74..e29d763 100644 --- a/src/main/java/me/trouper/sentinel/server/events/PluginCloakingEvent.java +++ b/src/main/java/me/trouper/sentinel/server/events/violations/players/PluginCloakingEvents.java @@ -1,9 +1,8 @@ -package me.trouper.sentinel.server.events; +package me.trouper.sentinel.server.events.violations.players; import io.github.itzispyder.pdk.events.CustomListener; import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.utils.PlayerUtils; -import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.Text; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -13,7 +12,7 @@ import org.bukkit.event.player.PlayerQuitEvent; import java.util.List; -public class PluginCloakingEvent implements CustomListener { +public class PluginCloakingEvents implements CustomListener { @EventHandler public void onQuit(PlayerQuitEvent e) { @@ -23,7 +22,7 @@ public class PluginCloakingEvent implements CustomListener { @EventHandler public void onCommand(PlayerCommandPreprocessEvent e) { - if (!Sentinel.mainConfig.plugin.pluginHider) return; + if (!Sentinel.getInstance().getDirector().io.mainConfig.plugin.pluginHider) return; Player p = e.getPlayer(); if (PlayerUtils.isTrusted(p)) return; @@ -33,16 +32,16 @@ public class PluginCloakingEvent implements CustomListener { message = message.substring(1); } - for (String alias : Sentinel.advConfig.commandsWithPluginAccess) { + for (String alias : Sentinel.getInstance().getDirector().io.advConfig.commandsWithPluginAccess) { if (!message.startsWith(alias)) continue; e.setCancelled(true); - p.sendMessage(Text.color(Sentinel.lang.permissions.noPlugins)); + p.sendMessage(Text.color(Sentinel.getInstance().getDirector().io.lang.permissions.noPlugins)); } } @EventHandler public void onTabComplete(PlayerCommandSendEvent e) { - if (!Sentinel.mainConfig.plugin.pluginHider) return; + if (!Sentinel.getInstance().getDirector().io.mainConfig.plugin.pluginHider) return; Player p = e.getPlayer(); if (PlayerUtils.isTrusted(p)) return; @@ -52,15 +51,15 @@ public class PluginCloakingEvent implements CustomListener { e.getCommands().remove(command); continue; } - if (Sentinel.advConfig.commandsWithPluginAccess.contains(command)) { + if (Sentinel.getInstance().getDirector().io.advConfig.commandsWithPluginAccess.contains(command)) { e.getCommands().remove(command); continue; } } - ServerUtils.verbose("Removed all the plugin specific commands form the listing. It now contains %s".formatted(e.getCommands().stream().toList().toString())); - for (String fakePlugin : Sentinel.advConfig.fakePlugins) { + //ServerUtils.verbose("Removed all the plugin specific commands form the listing. It now contains %s".formatted(e.getCommands().stream().toList().toString())); + for (String fakePlugin : Sentinel.getInstance().getDirector().io.advConfig.fakePlugins) { e.getCommands().add(fakePlugin + ":" + fakePlugin); } - ServerUtils.verbose("Added the fake plugins, now it contains this: %s".formatted(e.getCommands().stream().toList().toString())); + //ServerUtils.verbose("Added the fake plugins, now it contains this: %s".formatted(e.getCommands().stream().toList().toString())); } } diff --git a/src/main/java/me/trouper/sentinel/server/events/PluginCloakingPacket.java b/src/main/java/me/trouper/sentinel/server/events/violations/players/PluginCloakingPacket.java similarity index 87% rename from src/main/java/me/trouper/sentinel/server/events/PluginCloakingPacket.java rename to src/main/java/me/trouper/sentinel/server/events/violations/players/PluginCloakingPacket.java index 3b6a440..d791205 100644 --- a/src/main/java/me/trouper/sentinel/server/events/PluginCloakingPacket.java +++ b/src/main/java/me/trouper/sentinel/server/events/violations/players/PluginCloakingPacket.java @@ -1,4 +1,4 @@ -package me.trouper.sentinel.server.events; +package me.trouper.sentinel.server.events.violations.players; import com.github.retrooper.packetevents.event.PacketListenerAbstract; import com.github.retrooper.packetevents.event.PacketListenerPriority; @@ -29,7 +29,7 @@ public class PluginCloakingPacket extends PacketListenerAbstract { @Override public void onPacketReceive(PacketReceiveEvent event) { - if (!Sentinel.mainConfig.plugin.pluginHider) return; + if (!Sentinel.getInstance().getDirector().io.mainConfig.plugin.pluginHider) return; if (event.getPacketType() != PacketType.Play.Client.TAB_COMPLETE) return; WrapperPlayClientTabComplete wrapper = new WrapperPlayClientTabComplete(event); @@ -38,7 +38,7 @@ public class PluginCloakingPacket extends PacketListenerAbstract { if (PlayerUtils.isTrusted(player)) return; String text = wrapper.getText(); - for (String versionAlias : Sentinel.advConfig.pluginTabCompletions) { + for (String versionAlias : Sentinel.getInstance().getDirector().io.advConfig.pluginTabCompletions) { if (!text.contains(versionAlias)) continue; ServerUtils.verbose("Caught a version command tab completion. (%s -> %s)".formatted(text,versionAlias)); tabReplaceQueue.add(player.getUniqueId()); @@ -50,7 +50,7 @@ public class PluginCloakingPacket extends PacketListenerAbstract { @Override public void onPacketSend(PacketSendEvent event) { - if (!Sentinel.mainConfig.plugin.pluginHider) return; + if (!Sentinel.getInstance().getDirector().io.mainConfig.plugin.pluginHider) return; Player player = (Player) event.getPlayer(); if (player == null) return; @@ -63,7 +63,7 @@ public class PluginCloakingPacket extends PacketListenerAbstract { ServerUtils.verbose("Player was queued for replacement, setting tab completions."); WrapperPlayServerTabComplete wrapper = new WrapperPlayServerTabComplete(event); List matches = new ArrayList<>(); - for (String fakePlugin : Sentinel.advConfig.fakePlugins) { + for (String fakePlugin : Sentinel.getInstance().getDirector().io.advConfig.fakePlugins) { matches.add(new WrapperPlayServerTabComplete.CommandMatch(fakePlugin)); } wrapper.setCommandMatches(matches); diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/whitelist/CommandBlockExecute.java b/src/main/java/me/trouper/sentinel/server/events/violations/whitelist/CommandBlockExecute.java new file mode 100644 index 0000000..f4acc19 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/whitelist/CommandBlockExecute.java @@ -0,0 +1,155 @@ +package me.trouper.sentinel.server.events.violations.whitelist; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.AntiNukeGUI; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.CommandBlock; +import org.bukkit.command.BlockCommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.server.ServerCommandEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public class CommandBlockExecute extends AbstractViolation { + + @EventHandler + private void commandBlockExecute(ServerCommandEvent e) { + //ServerUtils.verbose("Handling command block event: " + e.getCommand()); + if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.enabled) return; + //ServerUtils.verbose("Whitelist not disabled"); + if (!(e.getSender() instanceof BlockCommandSender s)) return; + //ServerUtils.verbose("Sender is command block"); + + Block block = s.getBlock(); + CommandBlock cb = (CommandBlock) block.getState(); + + String label = cb.getCommand(); + ServerUtils.verbose("Command block is set to %s.".formatted(label)); + label = label.split(" ")[0]; + if (label.startsWith("/")) label = label.substring(1); + ServerUtils.verbose("It's label is %s.".formatted(label)); + + boolean isRestricted = Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.disabledCommands.contains(label); + boolean canRun = Sentinel.getInstance().getDirector().whitelistManager.isWhitelisted(cb); + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setBlock(block) + .cancel(true) + .destroyBlock(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.destroyBlock) + .restoreBlock(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.attemptRestore) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.logToDiscord); + + if (isRestricted) { + ServerUtils.verbose("Command block is using a restricted command."); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormat.formatted(Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlockRestriction), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormat.formatted( Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlockRestriction), + generateCommandBlockInfo(cb), + config + ); + } else if (!canRun) { + ServerUtils.verbose("Command block can't run."); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormat.formatted(Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlockWhitelist), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormat.formatted(Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlockWhitelist), + generateCommandBlockInfo(cb), + config + ); + } + } + + + @Override + public CustomGui getConfigGui() { + return CustomGui.create() + .title(Text.color("&6&lSentinel &8»&0 Command Block Whitelist")) + .size(27) + .onDefine(this::getMainPage) + .defineMain(this::onClick) + .define(26, Items.BACK, e->{ + e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); + }) + .build(); + } + + @Override + public void getMainPage(Inventory inv) { + for (int i = 0; i < inv.getSize(); i++) { + inv.setItem(i,Items.BLANK); + } + + ItemStack top = Items.RED; + if (Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.enabled) { + top = Items.GREEN; + } + + for (int i = 0; i < 9; i++) { + inv.setItem(i,top); + } + + inv.setItem(26,Items.BACK); + inv.setItem(4,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.enabled,Items.configItem("Check Toggle", Material.CLOCK,"Enable/Disable this check entirely"))); + inv.setItem(11,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.destroyBlock,Items.configItem("Destroy",Material.NETHERITE_PICKAXE,"Destroy the offending command-block"))); + inv.setItem(12,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.destroyCart,Items.configItem("Destroy",Material.TNT_MINECART,"Destroy the offending command-cart"))); + inv.setItem(13,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.attemptRestore,Items.configItem("Restore",Material.COMMAND_BLOCK,"Attempt to restore the block if a \nwhitelisted one exists at the location"))); + inv.setItem(14,Items.booleanItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); + inv.setItem(15,Items.stringListItem(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.disabledCommands,Material.CRIMSON_HANGING_SIGN,"Commands","Commands no command blocks can run. \nWorks even if whitelist is disabled. \nYou must add plugin specific versions yourself.")); + } + + @Override + public void onClick(InventoryClickEvent e) { + e.setCancelled(true); + if (!MainGUI.verify((Player) e.getWhoClicked())) return; + switch (e.getSlot()) { + case 4 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.enabled = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.enabled; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 11 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.destroyBlock = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.destroyBlock; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 12 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.destroyCart = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.destroyCart; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 13 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.attemptRestore = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.attemptRestore; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 14 -> { + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.logToDiscord = !Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.logToDiscord; + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + case 15 -> { + if (e.isLeftClick()) { + queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> { + cfg.commandBlockWhitelist.disabledCommands.add(args.getAll().toString()); + },"" + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.disabledCommands); + return; + } + Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.disabledCommands.clear(); + getMainPage(e.getInventory()); + Sentinel.getInstance().getDirector().io.violationConfig.save(); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/server/events/violations/whitelist/CommandMinecartExecute.java b/src/main/java/me/trouper/sentinel/server/events/violations/whitelist/CommandMinecartExecute.java new file mode 100644 index 0000000..fdb3b77 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/events/violations/whitelist/CommandMinecartExecute.java @@ -0,0 +1,79 @@ +package me.trouper.sentinel.server.events.violations.whitelist; + +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.events.violations.AbstractViolation; +import me.trouper.sentinel.server.functions.helpers.ActionConfiguration; +import me.trouper.sentinel.utils.ServerUtils; +import org.bukkit.entity.minecart.CommandMinecart; +import org.bukkit.event.EventHandler; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.server.ServerCommandEvent; +import org.bukkit.inventory.Inventory; + +public class CommandMinecartExecute extends AbstractViolation { + + @EventHandler + public void onExecute(ServerCommandEvent e) { + //ServerUtils.verbose("Handling command block event: " + e.getCommand()); + if (!Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.enabled) return; + //ServerUtils.verbose("Whitelist not disabled"); + if (!(e.getSender() instanceof CommandMinecart s)) return; + //ServerUtils.verbose("Sender is command block"); + + + String label = s.getCommand(); + ServerUtils.verbose("Command block is set to %s.".formatted(label)); + label = label.split(" ")[0]; + if (label.startsWith("/")) label = label.substring(1); + ServerUtils.verbose("It's label is %s.".formatted(label)); + + boolean isRestricted = Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.disabledCommands.contains(label); + boolean canRun = Sentinel.getInstance().getDirector().whitelistManager.isWhitelisted(s); + + ActionConfiguration.Builder config = new ActionConfiguration.Builder() + .setEvent(e) + .setEntity(s) + .cancel(true) + .removeEntity(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.destroyCart) + .logToDiscord(Sentinel.getInstance().getDirector().io.violationConfig.commandBlockWhitelist.logToDiscord); + + if (isRestricted) { + ServerUtils.verbose("Command cart is using a restricted command."); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormat.formatted(Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlockRestriction), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormat.formatted( Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlockRestriction), + generateMinecartInfo(s), + config + ); + return; + } + + if (!canRun) { + ServerUtils.verbose("Command cart can't run."); + + runActions( + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormat.formatted(Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlockWhitelist), + Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormat.formatted(Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.commandBlockWhitelist), + generateMinecartInfo(s), + config + ); + } + } + + @Override + public CustomGui getConfigGui() { + return new CommandBlockExecute().getConfigGui(); + } + + @Override + public void getMainPage(Inventory inv) { + new CommandBlockExecute().getMainPage(inv); + } + + @Override + public void onClick(InventoryClickEvent e) { + new CommandBlockExecute().onClick(e); + } +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/AbstractActionHandler.java b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/AbstractActionHandler.java index 8c70761..1e9ff7f 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/AbstractActionHandler.java +++ b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/AbstractActionHandler.java @@ -1,9 +1,8 @@ package me.trouper.sentinel.server.functions.chatfilter; import io.github.itzispyder.pdk.utils.discord.DiscordEmbed; +import io.papermc.paper.event.player.AsyncChatEvent; import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.functions.helpers.FalsePositiveReporting; -import me.trouper.sentinel.server.functions.helpers.FilterHelpers; import me.trouper.sentinel.utils.trees.ConsoleFormatter; import me.trouper.sentinel.utils.trees.EmbedFormatter; import me.trouper.sentinel.utils.trees.Node; @@ -11,11 +10,11 @@ import me.trouper.sentinel.utils.trees.Node; public abstract class AbstractActionHandler { public void run(T response) { - FalsePositiveReporting.reports.put(response.getReport().getId(), response.getReport()); + Sentinel.getInstance().getDirector().reportHandler.reports.put(response.getReport().getId(), response.getReport()); Node tree = buildTree(response); if (response.isBlocked()) { - FilterHelpers.restrictMessage(response.getEvent(),!shouldWarnPlayer(response)); + restrictMessage(response.getEvent(),!shouldWarnPlayer(response)); } if (response.isPunished()) { punish(response); @@ -36,11 +35,20 @@ public abstract class AbstractActionHandler { protected abstract boolean shouldWarnPlayer(T response); protected void consoleLog(Node tree) { - Sentinel.log.info(ConsoleFormatter.format(tree)); + Sentinel.getInstance().getLogger().info(ConsoleFormatter.format(tree)); } protected void discordNotification(Node tree) { DiscordEmbed embed = EmbedFormatter.format(tree); EmbedFormatter.sendEmbed(embed); } + + protected void restrictMessage(AsyncChatEvent event, boolean silent) { + if (silent) { + event.viewers().clear(); + event.viewers().add(event.getPlayer()); + } else { + event.setCancelled(true); + } + } } diff --git a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/ProfanityAction.java b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/ProfanityAction.java index 1c803de..7b74979 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/ProfanityAction.java +++ b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/ProfanityAction.java @@ -2,6 +2,7 @@ package me.trouper.sentinel.server.functions.chatfilter.profanity; import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.server.functions.chatfilter.AbstractActionHandler; +import me.trouper.sentinel.utils.PlayerUtils; import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.Text; import me.trouper.sentinel.utils.trees.HoverFormatter; @@ -14,11 +15,11 @@ public class ProfanityAction extends AbstractActionHandler { @Override public void punish(ProfanityResponse response) { if (response.getSeverity().equals(Severity.SLUR)) { - for (String slurCommand : Sentinel.mainConfig.chat.profanityFilter.strictPunishCommands) { + for (String slurCommand : Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.strictPunishCommands) { ServerUtils.sendCommand(slurCommand.replaceAll("%player%", response.getPlayer().getName())); } } - for (String swearCommand : Sentinel.mainConfig.chat.profanityFilter.profanityPunishCommands) { + for (String swearCommand : Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.profanityPunishCommands) { ServerUtils.sendCommand(swearCommand.replaceAll("%player%", response.getPlayer().getName())); } } @@ -27,21 +28,21 @@ public class ProfanityAction extends AbstractActionHandler { public void staffWarning(ProfanityResponse response, Node tree) { String messageText = Text.prefix("&b&n%s&r &7%s &8(&4%s&7/&c%s&8)".formatted( response.getPlayer().getName(), - response.isPunished() ? Sentinel.lang.violations.chat.profanity.autoPunishNotification : Sentinel.lang.violations.chat.profanity.preventNotification, + response.isPunished() ? Sentinel.getInstance().getDirector().io.lang.violations.chat.profanity.autoPunishNotification : Sentinel.getInstance().getDirector().io.lang.violations.chat.profanity.preventNotification, ProfanityFilter.scoreMap.getOrDefault(response.getPlayer().getUniqueId(), 0), - Sentinel.mainConfig.chat.profanityFilter.punishScore + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.punishScore )); String hoverText = HoverFormatter.format(tree); - ServerUtils.forEachPlayer(player -> { + PlayerUtils.forEachPlayer(player -> { if (player.hasPermission("sentinel.chatfilter.profanity.view")) player.sendMessage(Component.text(messageText).hoverEvent(Component.text(hoverText).asHoverEvent())); }); } @Override public void playerWarning(ProfanityResponse response) { - String message = Text.prefix(response.isPunished() ? Sentinel.lang.violations.chat.profanity.autoPunishWarning : Sentinel.lang.violations.chat.profanity.preventWarning); - String hoverText = Sentinel.lang.automatedActions.reportable; + String message = Text.prefix(response.isPunished() ? Sentinel.getInstance().getDirector().io.lang.violations.chat.profanity.autoPunishWarning : Sentinel.getInstance().getDirector().io.lang.violations.chat.profanity.preventWarning); + String hoverText = Sentinel.getInstance().getDirector().io.lang.automatedActions.reportable; String command = "/sentinelcallback fpreport %s".formatted(response.getReport().getId()); response.getPlayer().sendMessage(Component.text(message) .hoverEvent(Component.text(hoverText).asHoverEvent()) @@ -51,22 +52,22 @@ public class ProfanityAction extends AbstractActionHandler { @Override public Node buildTree(ProfanityResponse response) { Node root = new Node("Sentinel"); - root.addTextLine(Sentinel.lang.violations.chat.profanity.treeTitle); + root.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.chat.profanity.treeTitle); - Node playerInfo = new Node(Sentinel.lang.violations.protections.infoNode.playerInfo.formatted(response.getPlayer().getName())); - playerInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.uuid, response.getPlayer().getUniqueId().toString()); - playerInfo.addKeyValue(Sentinel.lang.violations.chat.profanity.score, "%s/%s".formatted(ProfanityFilter.scoreMap.getOrDefault(response.getPlayer().getUniqueId(),0),Sentinel.mainConfig.chat.profanityFilter.punishScore)); + Node playerInfo = new Node(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.playerInfo.formatted(response.getPlayer().getName())); + playerInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.uuid, response.getPlayer().getUniqueId().toString()); + playerInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.chat.profanity.score, "%s/%s".formatted(ProfanityFilter.scoreMap.getOrDefault(response.getPlayer().getUniqueId(),0),Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.punishScore)); root.addChild(playerInfo); - Node reportInfo = new Node(Sentinel.lang.violations.chat.profanity.reportInfoTitle); - reportInfo.addField(Sentinel.lang.violations.chat.originalMessage, response.getOriginalMessage()); - reportInfo.addField(Sentinel.lang.violations.chat.profanity.processedMessage, response.getProcessedMessage()); - reportInfo.addKeyValue(Sentinel.lang.violations.chat.profanity.severity, response.getSeverity().toString()); + Node reportInfo = new Node(Sentinel.getInstance().getDirector().io.lang.violations.chat.profanity.reportInfoTitle); + reportInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.chat.originalMessage, response.getOriginalMessage()); + reportInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.chat.profanity.processedMessage, response.getProcessedMessage()); + reportInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.chat.profanity.severity, response.getSeverity().toString()); root.addChild(reportInfo); - Node actions = new Node(Sentinel.lang.violations.protections.actionNode.actionNodeTitle); - actions.addTextLine(Sentinel.lang.violations.chat.denyMessage); - if (response.isPunished()) actions.addTextLine(Sentinel.lang.violations.protections.actionNode.punishmentCommandsExecuted); + Node actions = new Node(Sentinel.getInstance().getDirector().io.lang.violations.protections.actionNode.actionNodeTitle); + actions.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.chat.denyMessage); + if (response.isPunished()) actions.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.protections.actionNode.punishmentCommandsExecuted); root.addChild(actions); return root; @@ -74,6 +75,6 @@ public class ProfanityAction extends AbstractActionHandler { @Override protected boolean shouldWarnPlayer(ProfanityResponse response) { - return !Sentinel.mainConfig.chat.profanityFilter.silent; + return !Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.silent; } } diff --git a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/ProfanityFilter.java b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/ProfanityFilter.java index c07ddb1..516e8e4 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/ProfanityFilter.java +++ b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/ProfanityFilter.java @@ -18,7 +18,7 @@ public class ProfanityFilter { ServerUtils.verbose("Anti Profanity Opening: Event is canceled."); } Player player = event.getPlayer(); - ProfanityResponse response = ProfanityResponse.generate(event); + ProfanityResponse response = new ProfanityResponse(null,null,null,null,null,false,false).generate(event); Severity severity = response.getSeverity(); ServerUtils.verbose("Response came back."); if (severity == null) return; @@ -33,7 +33,7 @@ public class ProfanityFilter { int newScore = previousScore + severity.getScore(); scoreMap.put(player.getUniqueId(), newScore); - if (newScore > Sentinel.mainConfig.chat.profanityFilter.punishScore || Severity.SLUR.equals(severity)) { + if (newScore > Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.punishScore || Severity.SLUR.equals(severity)) { response.setPunished(true); new ProfanityAction().run(response); return; @@ -46,7 +46,7 @@ public class ProfanityFilter { for (UUID uuid : scoreMap.keySet()) { int score = scoreMap.get(uuid); if (score > 0) { - score = score - Sentinel.mainConfig.chat.profanityFilter.scoreDecay; + score = score - Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.scoreDecay; scoreMap.put(uuid, Math.max(0, score)); } } diff --git a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/ProfanityResponse.java b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/ProfanityResponse.java index b40737e..ac9e812 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/ProfanityResponse.java +++ b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/ProfanityResponse.java @@ -1,9 +1,8 @@ package me.trouper.sentinel.server.functions.chatfilter.profanity; import io.papermc.paper.event.player.AsyncChatEvent; -import me.trouper.sentinel.data.Emojis; -import me.trouper.sentinel.server.functions.helpers.FalsePositiveReporting; -import me.trouper.sentinel.server.functions.helpers.FilterHelpers; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.data.types.Emojis; import me.trouper.sentinel.server.functions.chatfilter.FilterResponse; import me.trouper.sentinel.server.functions.helpers.Report; import me.trouper.sentinel.utils.ServerUtils; @@ -11,6 +10,9 @@ import me.trouper.sentinel.utils.Text; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.bukkit.entity.Player; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + public class ProfanityResponse implements FilterResponse { private AsyncChatEvent event; @@ -98,7 +100,7 @@ public class ProfanityResponse implements FilterResponse { } String message = LegacyComponentSerializer.legacySection().serialize(e.message()); - Report report = FalsePositiveReporting.initializeReport(message); + Report report = Sentinel.getInstance().getDirector().reportHandler.initializeReport(message); Severity severity = Severity.SAFE; ProfanityResponse response = new ProfanityResponse(e,message,null,report,severity,false,false); @@ -109,77 +111,78 @@ public class ProfanityResponse implements FilterResponse { // 1: String lowercasedText = text.toLowerCase(); response.getReport().getStepsTaken().put("Lowercased", lowercasedText); - response.setProcessedMessage(FilterHelpers.highlightProfanity(lowercasedText,"", "")); + response.setProcessedMessage(highlightProfanity(lowercasedText,"", "")); ServerUtils.verbose("ProfanityFilter: Lowercased: " + lowercasedText); // 2: - String cleanedText = FilterHelpers.removeFalsePositives(lowercasedText); + String cleanedText = removeFalsePositives(lowercasedText); response.getReport().getStepsTaken().put("Remove False Positives", cleanedText); - response.setProcessedMessage(FilterHelpers.highlightProfanity(cleanedText,"", "")); + response.setProcessedMessage(highlightProfanity(cleanedText,"", "")); ServerUtils.verbose(("ProfanityFilter: Removed False positives: " + cleanedText)); - response.setSeverity(FilterHelpers.checkSlur(cleanedText, Severity.LOW)); + response.setSeverity(checkProfanity(cleanedText, Severity.LOW)); if (response.getSeverity() != Severity.SAFE) { response.getReport().getStepsTaken().replace("Remove False Positives", "%s %s".formatted( - FilterHelpers.highlightProfanity(cleanedText,"||","||"), + highlightProfanity(cleanedText,"||","||"), Emojis.alarm)); return response; } // 4: - String convertedText = FilterHelpers.convertLeetSpeakCharacters(cleanedText); + String convertedText = convertLeetSpeakCharacters(cleanedText); response.getReport().getStepsTaken().put("Convert LeetSpeak", convertedText); - response.setProcessedMessage(FilterHelpers.highlightProfanity(convertedText,"", "")); + response.setProcessedMessage(highlightProfanity(convertedText,"", "")); ServerUtils.verbose(("ProfanityFilter: Leet Converted: " + convertedText)); - response.setSeverity(FilterHelpers.checkSlur(convertedText, Severity.MEDIUM_LOW)); + response.setSeverity(checkProfanity(convertedText, Severity.MEDIUM_LOW)); if (response.getSeverity() != Severity.SAFE) { response.getReport().getStepsTaken().replace("Convert LeetSpeak", "%s %s".formatted( - FilterHelpers.highlightProfanity(cleanedText,"||","||"), + highlightProfanity(cleanedText,"||","||"), Emojis.alarm)); return response; } // 6: - String strippedText = FilterHelpers.stripSpecialCharacters(convertedText); + String strippedText = stripSpecialCharacters(convertedText); response.getReport().getStepsTaken().put("Remove Special Characters", strippedText); - response.setProcessedMessage(FilterHelpers.highlightProfanity(strippedText,"", "")); + response.setProcessedMessage(highlightProfanity(strippedText,"", "")); ServerUtils.verbose(("ProfanityFilter: Specials Removed: " + strippedText)); - response.setSeverity(FilterHelpers.checkSlur(strippedText, Severity.MEDIUM)); + response.setSeverity(checkProfanity(strippedText, Severity.MEDIUM)); if (response.getSeverity() != Severity.SAFE) { response.getReport().getStepsTaken().replace("Remove Special Characters", "%s %s".formatted( - FilterHelpers.highlightProfanity(cleanedText,"||","||"), + highlightProfanity(cleanedText,"||","||"), Emojis.alarm)); return response; } // 8: - String simplifiedText = FilterHelpers.simplifyRepeatingLetters(strippedText); + String simplifiedText = simplifyRepeatingLetters(strippedText); response.getReport().getStepsTaken().put("Remove Repeats", simplifiedText); - response.setProcessedMessage(FilterHelpers.highlightProfanity(simplifiedText,"", "")); + response.setProcessedMessage(highlightProfanity(simplifiedText,"", "")); ServerUtils.verbose(("ProfanityFilter: Removed Repeating: " + simplifiedText)); - response.setSeverity(FilterHelpers.checkSlur(simplifiedText, Severity.MEDIUM_HIGH)); + response.setSeverity(checkProfanity(simplifiedText, Severity.MEDIUM_HIGH)); if (response.getSeverity() != Severity.SAFE) { response.getReport().getStepsTaken().replace("Remove Repeats", "%s %s".formatted( - FilterHelpers.highlightProfanity(cleanedText,"||","||"), + highlightProfanity(cleanedText,"||","||"), Emojis.alarm)); return response; } // 10: - String finalText = FilterHelpers.removePeriodsAndSpaces(simplifiedText); + String finalText = removePeriodsAndSpaces(simplifiedText); response.getReport().getStepsTaken().put("Remove Punctuation", finalText); - response.setProcessedMessage(FilterHelpers.highlightProfanity(finalText,"", "")); + response.setProcessedMessage(highlightProfanity(finalText,"", "")); ServerUtils.verbose(("ProfanityFilter: Remove Punctuation: " + finalText)); - response.setSeverity(FilterHelpers.checkSlur(finalText, Severity.HIGH)); + response.setSeverity(checkProfanity(finalText, Severity.HIGH)); if (response.getSeverity() != Severity.SAFE) { response.getReport().getStepsTaken().replace("Remove Punctuation", "%s %s".formatted( - FilterHelpers.highlightProfanity(cleanedText,"||","||"), + highlightProfanity(cleanedText,"||","||"), Emojis.alarm)); + return response; } ServerUtils.verbose(("ProfanityFilter: Finished " + finalText)); @@ -188,4 +191,91 @@ public class ProfanityResponse implements FilterResponse { } return response; } + + private static Severity checkProfanity(String text, Severity backup) { + if (containsSlurs(text)) return Severity.SLUR; + if (containsSwears(text)) return backup; + return Severity.SAFE; + } + + private static boolean containsSwears(String text) { + ServerUtils.verbose("ProfanityFilter: Checking for swears"); + for (String swear : Sentinel.getInstance().getDirector().io.swearConfig.swears) { + if (text.contains(swear)) return true; + } + + Pattern pattern = Pattern.compile(Sentinel.getInstance().getDirector().io.swearConfig.regexSwears, Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(text); + + return matcher.find() && Sentinel.getInstance().getDirector().io.swearConfig.useRegex; + } + + private static boolean containsSlurs(String text) { + ServerUtils.verbose("ProfanityFilter: Checking for slurs"); + for (String slur : Sentinel.getInstance().getDirector().io.strictConfig.strict) { + if (text.contains(slur)) return true; + } + + Pattern pattern = Pattern.compile(Sentinel.getInstance().getDirector().io.strictConfig.regexStrict, Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(text); + + return matcher.find() && Sentinel.getInstance().getDirector().io.strictConfig.useRegex; + } + + private static String removeFalsePositives(String text) { + for (String falsePositive : Sentinel.getInstance().getDirector().io.fpConfig.swearWhitelist) { + text = text.replace(falsePositive, ""); + } + if (Sentinel.getInstance().getDirector().io.fpConfig.useRegex) text = text.replaceAll(Sentinel.getInstance().getDirector().io.fpConfig.regexWhitelist,""); + return text; + } + + private static String convertLeetSpeakCharacters(String text) { + text = Text.fromLeetString(text); + return text; + } + + private static String stripSpecialCharacters(String text) { + text = text.replaceAll("[^A-Za-z0-9.,!?;:'\"()\\[\\]{}]", "").trim(); + return text; + } + + private static String simplifyRepeatingLetters(String text) { + text = Text.replaceRepeatingLetters(text); + return text; + } + + private static String removePeriodsAndSpaces(String text) { + return text.replaceAll("[^A-Za-z0-9]", "").replace(" ", ""); + } + + private static String highlightProfanity(String text, String start, String end) { + String highlightedSwears = highlightSwears(fullSimplify(text), start, end); + return Text.color(highlightSlurs(highlightedSwears, start, end)); + } + + private static String highlightSwears(String text, String start, String end) { + for (String swear : Sentinel.getInstance().getDirector().io.swearConfig.swears) { + text = text.replace(swear, start + swear + end); + } + return text; + } + + private static String highlightSlurs(String text, String start, String end) { + for (String slur : Sentinel.getInstance().getDirector().io.strictConfig.strict) { + text = text.replace(slur, start + slur + end); + } + return text; + } + + private static String fullSimplify(String text) { + String lowercasedText = text.toLowerCase(); + String cleanedText = removeFalsePositives(lowercasedText); + String convertedText =convertLeetSpeakCharacters(cleanedText); + String strippedText = stripSpecialCharacters(convertedText); + String simplifiedText = simplifyRepeatingLetters(strippedText); + return removePeriodsAndSpaces(simplifiedText); + } + + } diff --git a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/Severity.java b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/Severity.java index dbe2b2b..8cf7dc2 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/Severity.java +++ b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/profanity/Severity.java @@ -3,13 +3,13 @@ package me.trouper.sentinel.server.functions.chatfilter.profanity; import me.trouper.sentinel.Sentinel; public enum Severity { - LOW(Sentinel.mainConfig.chat.profanityFilter.lowScore), - MEDIUM_LOW(Sentinel.mainConfig.chat.profanityFilter.mediumLowScore), - MEDIUM(Sentinel.mainConfig.chat.profanityFilter.mediumScore), - MEDIUM_HIGH(Sentinel.mainConfig.chat.profanityFilter.mediumHighScore), - HIGH(Sentinel.mainConfig.chat.profanityFilter.highScore), - REGEX(Sentinel.mainConfig.chat.profanityFilter.regexScore), - SLUR(Sentinel.mainConfig.chat.profanityFilter.highScore), + LOW(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.lowScore), + MEDIUM_LOW(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.mediumLowScore), + MEDIUM(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.mediumScore), + MEDIUM_HIGH(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.mediumHighScore), + HIGH(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.highScore), + REGEX(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.regexScore), + SLUR(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.highScore), SAFE(0); private final int score; diff --git a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/spam/SpamAction.java b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/spam/SpamAction.java index c6a4792..0548fc2 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/spam/SpamAction.java +++ b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/spam/SpamAction.java @@ -2,6 +2,7 @@ package me.trouper.sentinel.server.functions.chatfilter.spam; import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.server.functions.chatfilter.AbstractActionHandler; +import me.trouper.sentinel.utils.PlayerUtils; import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.Text; import me.trouper.sentinel.utils.trees.HoverFormatter; @@ -13,7 +14,7 @@ public class SpamAction extends AbstractActionHandler { @Override public void punish(SpamResponse response) { - for (String spamPunishCommand : Sentinel.mainConfig.chat.spamFilter.punishCommands) { + for (String spamPunishCommand : Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.punishCommands) { ServerUtils.sendCommand(spamPunishCommand.replaceAll("%player%", response.getEvent().getPlayer().getName())); } } @@ -22,21 +23,21 @@ public class SpamAction extends AbstractActionHandler { public void staffWarning(SpamResponse report, Node tree) { String messageText = Text.prefix("&b&n%s&r &7%s &8(&4%s&7/&c%s&8)".formatted( report.getEvent().getPlayer().getName(), - report.isPunished() ? Sentinel.lang.violations.chat.spam.autoPunishNotification : Sentinel.lang.violations.chat.spam.preventNotification, + report.isPunished() ? Sentinel.getInstance().getDirector().io.lang.violations.chat.spam.autoPunishNotification : Sentinel.getInstance().getDirector().io.lang.violations.chat.spam.preventNotification, SpamFilter.heatMap.get(report.getEvent().getPlayer().getUniqueId()), - Sentinel.mainConfig.chat.spamFilter.punishHeat + Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.punishHeat )); String hoverText = HoverFormatter.format(tree); - ServerUtils.forEachPlayer(player -> { + PlayerUtils.forEachPlayer(player -> { if (player.hasPermission("sentinel.chatfilter.spam.view")) player.sendMessage(Component.text(messageText).hoverEvent(Component.text(hoverText).asHoverEvent())); }); } @Override public void playerWarning(SpamResponse response) { - String message = Text.prefix(response.isPunished() ? Sentinel.lang.violations.chat.spam.autoPunishWarning : Sentinel.lang.violations.chat.spam.preventWarning) ; - String hoverText = Sentinel.lang.automatedActions.reportable; + String message = Text.prefix(response.isPunished() ? Sentinel.getInstance().getDirector().io.lang.violations.chat.spam.autoPunishWarning : Sentinel.getInstance().getDirector().io.lang.violations.chat.spam.preventWarning) ; + String hoverText = Sentinel.getInstance().getDirector().io.lang.automatedActions.reportable; String command = "/sentinelcallback fpreport %s".formatted(response.getReport().getId()); response.getEvent().getPlayer().sendMessage(Component.text(message) .hoverEvent(Component.text(hoverText).asHoverEvent()) @@ -46,22 +47,22 @@ public class SpamAction extends AbstractActionHandler { @Override public Node buildTree(SpamResponse response) { Node root = new Node("Sentinel"); - root.addTextLine(Sentinel.lang.violations.chat.spam.treeTitle); + root.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.chat.spam.treeTitle); - Node playerInfo = new Node(Sentinel.lang.violations.protections.infoNode.playerInfo.formatted(response.getEvent().getPlayer().getName())); - playerInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.uuid, response.getEvent().getPlayer().getUniqueId().toString()); - playerInfo.addKeyValue(Sentinel.lang.violations.chat.spam.heat, "%s/%s".formatted(SpamFilter.heatMap.get(response.getEvent().getPlayer().getUniqueId()),Sentinel.mainConfig.chat.spamFilter.punishHeat)); + Node playerInfo = new Node(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.playerInfo.formatted(response.getEvent().getPlayer().getName())); + playerInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.uuid, response.getEvent().getPlayer().getUniqueId().toString()); + playerInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.chat.spam.heat, "%s/%s".formatted(SpamFilter.heatMap.get(response.getEvent().getPlayer().getUniqueId()),Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.punishHeat)); root.addChild(playerInfo); - Node reportInfo = new Node(Sentinel.lang.violations.chat.spam.reportInfoTitle); - reportInfo.addField(Sentinel.lang.violations.chat.spam.previousMessage, response.getPreviousMessage()); - reportInfo.addField(Sentinel.lang.violations.chat.spam.currentMessage, response.getCurrentMessage()); - reportInfo.addKeyValue(Sentinel.lang.violations.chat.spam.similarity, "%s/%s".formatted((int) Math.round(response.getSimilarity()),Sentinel.mainConfig.chat.spamFilter.blockSimilarity)); + Node reportInfo = new Node(Sentinel.getInstance().getDirector().io.lang.violations.chat.spam.reportInfoTitle); + reportInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.chat.spam.previousMessage, response.getPreviousMessage()); + reportInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.chat.spam.currentMessage, response.getCurrentMessage()); + reportInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.chat.spam.similarity, "%s/%s".formatted((int) Math.round(response.getSimilarity()),Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.blockSimilarity)); root.addChild(reportInfo); - Node actions = new Node(Sentinel.lang.violations.protections.actionNode.actionNodeTitle); - actions.addTextLine(Sentinel.lang.violations.chat.denyMessage); - if (response.isPunished()) actions.addTextLine(Sentinel.lang.violations.protections.actionNode.punishmentCommandsExecuted); + Node actions = new Node(Sentinel.getInstance().getDirector().io.lang.violations.protections.actionNode.actionNodeTitle); + actions.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.chat.denyMessage); + if (response.isPunished()) actions.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.protections.actionNode.punishmentCommandsExecuted); root.addChild(actions); return root; @@ -69,6 +70,6 @@ public class SpamAction extends AbstractActionHandler { @Override protected boolean shouldWarnPlayer(SpamResponse response) { - return !Sentinel.mainConfig.chat.spamFilter.silent; + return !Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.silent; } } diff --git a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/spam/SpamFilter.java b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/spam/SpamFilter.java index 7967bc0..82e02ed 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/spam/SpamFilter.java +++ b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/spam/SpamFilter.java @@ -21,7 +21,7 @@ public class SpamFilter { } Player p = e.getPlayer(); String message = Text.removeFirstColor(LegacyComponentSerializer.legacySection().serialize(e.message())); - for (String whitelistedMessage : Sentinel.mainConfig.chat.spamFilter.whitelist) { + for (String whitelistedMessage : Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.whitelist) { if (whitelistedMessage.equalsIgnoreCase(message)) return; } int currentHeat = heatMap.getOrDefault(p.getUniqueId(),0); @@ -34,7 +34,7 @@ public class SpamFilter { ServerUtils.verbose("AntiSpam responded"); response.getReport().getStepsTaken().put("Response came back", "Heat to add: %s".formatted(addHeat)); - if (currentHeat > Sentinel.mainConfig.chat.spamFilter.punishHeat) { + if (currentHeat > Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.punishHeat) { response.setBlocked(true); response.getReport().getStepsTaken().put("Punished user", "Their final heat was %s".formatted(currentHeat)); response.setPunished(true); @@ -43,7 +43,7 @@ public class SpamFilter { return; } - if (currentHeat > Sentinel.mainConfig.chat.spamFilter.blockHeat) { + if (currentHeat > Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.blockHeat) { response.setBlocked(true); response.getReport().getStepsTaken().put("Blocked message", "Their heat is %s".formatted(currentHeat)); new SpamAction().run(response); @@ -51,7 +51,7 @@ public class SpamFilter { return; } - if (response.getSimilarity() > Sentinel.mainConfig.chat.spamFilter.blockSimilarity) { + if (response.getSimilarity() > Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.blockSimilarity) { response.setBlocked(true); response.getReport().getStepsTaken().put("Blocked message", "The similarity was too high! %s".formatted(response.getSimilarity())); new SpamAction().run(response); @@ -69,7 +69,7 @@ public class SpamFilter { for (UUID p : heatMap.keySet()) { int heat = heatMap.getOrDefault(p,0); if (heat > 0) { - heat = heat - Sentinel.mainConfig.chat.spamFilter.heatDecay; + heat = heat - Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.heatDecay; heatMap.put(p, Math.max(0, heat)); } } diff --git a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/spam/SpamResponse.java b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/spam/SpamResponse.java index 96098e3..37d1156 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/spam/SpamResponse.java +++ b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/spam/SpamResponse.java @@ -3,7 +3,7 @@ package me.trouper.sentinel.server.functions.chatfilter.spam; import io.github.retrooper.packetevents.adventure.serializer.legacy.LegacyComponentSerializer; import io.papermc.paper.event.player.AsyncChatEvent; import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.functions.helpers.FalsePositiveReporting; +import me.trouper.sentinel.server.functions.helpers.ReportHandler; import me.trouper.sentinel.server.functions.chatfilter.FilterResponse; import me.trouper.sentinel.server.functions.helpers.Report; import me.trouper.sentinel.utils.MathUtils; @@ -40,7 +40,7 @@ public class SpamResponse implements FilterResponse { } String message = LegacyComponentSerializer.legacySection().serialize(e.message()); - Report report = FalsePositiveReporting.initializeReport(message); + Report report = Sentinel.getInstance().getDirector().reportHandler.initializeReport(message); message = Text.removeFirstColor(message); String previousMessage = lastMessageMap.getOrDefault(e.getPlayer().getUniqueId(),"/* Placeholder Message from Sentinel */"); @@ -52,25 +52,25 @@ public class SpamResponse implements FilterResponse { response.setSimilarity(similarity); report.getStepsTaken().put("Calculated Similarity: ","%s".formatted(similarity)); - int addHeat = Sentinel.mainConfig.chat.spamFilter.defaultGain; - if (similarity > Sentinel.mainConfig.chat.spamFilter.blockSimilarity) { - addHeat = Sentinel.mainConfig.chat.spamFilter.highGain; - response.getReport().getStepsTaken().put("Similarity is greater than %s%%".formatted(Sentinel.mainConfig.chat.spamFilter.blockSimilarity), "That is %s heat. (Auto-Block due to configured value)".formatted(addHeat)); + int addHeat = Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.defaultGain; + if (similarity > Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.blockSimilarity) { + addHeat = Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.highGain; + response.getReport().getStepsTaken().put("Similarity is greater than %s%%".formatted(Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.blockSimilarity), "That is %s heat. (Auto-Block due to configured value)".formatted(addHeat)); response.setHeatAdded(addHeat); return response; } else if (similarity > 90) { - addHeat = Sentinel.mainConfig.chat.spamFilter.highGain; + addHeat = Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.highGain; response.getReport().getStepsTaken().put("Similarity is greater than 90%", "That is %s heat.".formatted(addHeat)); response.setHeatAdded(addHeat); return response; } else if (similarity > 50) { - addHeat = Sentinel.mainConfig.chat.spamFilter.mediumGain; + addHeat = Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.mediumGain; response.getReport().getStepsTaken().put("Similarity is greater than 50%", "That is %s heat.".formatted(addHeat)); response.setHeatAdded(addHeat); return response; } else if (similarity > 25) { response.getReport().getStepsTaken().put("Similarity is greater than 25%", "That is %s heat.".formatted(addHeat)); - addHeat = Sentinel.mainConfig.chat.spamFilter.lowGain; + addHeat = Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.lowGain; response.setHeatAdded(addHeat); return response; } diff --git a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/unicode/UnicodeAction.java b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/unicode/UnicodeAction.java index a5c5243..2ad38e0 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/unicode/UnicodeAction.java +++ b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/unicode/UnicodeAction.java @@ -2,6 +2,7 @@ package me.trouper.sentinel.server.functions.chatfilter.unicode; import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.server.functions.chatfilter.AbstractActionHandler; +import me.trouper.sentinel.utils.PlayerUtils; import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.Text; import me.trouper.sentinel.utils.trees.HoverFormatter; @@ -12,7 +13,7 @@ import net.kyori.adventure.text.event.ClickEvent; public class UnicodeAction extends AbstractActionHandler { @Override protected void punish(UnicodeResponse response) { - for (String punishCommand : Sentinel.mainConfig.chat.unicodeFilter.punishCommands) { + for (String punishCommand : Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.punishCommands) { ServerUtils.sendCommand(punishCommand.replaceAll("%player%",response.getPlayer().getName())); } } @@ -21,19 +22,19 @@ public class UnicodeAction extends AbstractActionHandler { protected void staffWarning(UnicodeResponse response, Node tree) { String messageText = Text.prefix("&b&n%s&r &7%s".formatted( response.getPlayer().getName(), - response.isPunished() ? Sentinel.lang.violations.chat.unicode.autoPunishNotification : Sentinel.lang.violations.chat.unicode.preventNotification + response.isPunished() ? Sentinel.getInstance().getDirector().io.lang.violations.chat.unicode.autoPunishNotification : Sentinel.getInstance().getDirector().io.lang.violations.chat.unicode.preventNotification )); String hoverText = HoverFormatter.format(tree); - ServerUtils.forEachPlayer(player -> { + PlayerUtils.forEachPlayer(player -> { if (player.hasPermission("sentinel.chatfilter.unicode.view")) player.sendMessage(Component.text(messageText).hoverEvent(Component.text(hoverText).asHoverEvent())); }); } @Override protected void playerWarning(UnicodeResponse response) { - String message = Text.prefix(response.isPunished() ? Sentinel.lang.violations.chat.unicode.autoPunishWarning : Sentinel.lang.violations.chat.unicode.preventWarning); - String hoverText = Sentinel.lang.automatedActions.reportable; + String message = Text.prefix(response.isPunished() ? Sentinel.getInstance().getDirector().io.lang.violations.chat.unicode.autoPunishWarning : Sentinel.getInstance().getDirector().io.lang.violations.chat.unicode.preventWarning); + String hoverText = Sentinel.getInstance().getDirector().io.lang.automatedActions.reportable; String command = "/sentinelcallback fpreport %s".formatted(response.getReport().getId()); response.getPlayer().sendMessage(Component.text(message) .hoverEvent(Component.text(hoverText).asHoverEvent()) @@ -43,20 +44,20 @@ public class UnicodeAction extends AbstractActionHandler { @Override protected Node buildTree(UnicodeResponse response) { Node root = new Node("Sentinel"); - root.addTextLine(Sentinel.lang.violations.chat.unicode.treeTitle); + root.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.chat.unicode.treeTitle); - Node playerInfo = new Node(Sentinel.lang.violations.protections.infoNode.playerInfo.formatted(response.getPlayer().getName())); - playerInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.uuid, response.getPlayer().getUniqueId().toString()); + Node playerInfo = new Node(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.playerInfo.formatted(response.getPlayer().getName())); + playerInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.uuid, response.getPlayer().getUniqueId().toString()); root.addChild(playerInfo); - Node reportInfo = new Node(Sentinel.lang.violations.chat.unicode.reportInfoTitle); - reportInfo.addField(Sentinel.lang.violations.chat.originalMessage, response.getOriginalMessage()); - reportInfo.addField(Sentinel.lang.violations.chat.highlightedMessage, response.getHighlightedMessage()); + Node reportInfo = new Node(Sentinel.getInstance().getDirector().io.lang.violations.chat.unicode.reportInfoTitle); + reportInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.chat.originalMessage, response.getOriginalMessage()); + reportInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.chat.highlightedMessage, response.getHighlightedMessage()); root.addChild(reportInfo); - Node actions = new Node(Sentinel.lang.violations.protections.actionNode.actionNodeTitle); - actions.addTextLine(Sentinel.lang.violations.chat.denyMessage); - if (response.isPunished()) actions.addTextLine(Sentinel.lang.violations.protections.actionNode.punishmentCommandsExecuted); + Node actions = new Node(Sentinel.getInstance().getDirector().io.lang.violations.protections.actionNode.actionNodeTitle); + actions.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.chat.denyMessage); + if (response.isPunished()) actions.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.protections.actionNode.punishmentCommandsExecuted); root.addChild(actions); return root; @@ -64,6 +65,6 @@ public class UnicodeAction extends AbstractActionHandler { @Override protected boolean shouldWarnPlayer(UnicodeResponse response) { - return !Sentinel.mainConfig.chat.unicodeFilter.silent; + return !Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.silent; } } diff --git a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/unicode/UnicodeResponse.java b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/unicode/UnicodeResponse.java index 419350d..4fd216c 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/unicode/UnicodeResponse.java +++ b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/unicode/UnicodeResponse.java @@ -3,8 +3,7 @@ package me.trouper.sentinel.server.functions.chatfilter.unicode; import io.github.retrooper.packetevents.adventure.serializer.legacy.LegacyComponentSerializer; import io.papermc.paper.event.player.AsyncChatEvent; import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.Emojis; -import me.trouper.sentinel.server.functions.helpers.FalsePositiveReporting; +import me.trouper.sentinel.data.types.Emojis; import me.trouper.sentinel.server.functions.chatfilter.FilterResponse; import me.trouper.sentinel.server.functions.helpers.Report; import me.trouper.sentinel.utils.ServerUtils; @@ -39,11 +38,11 @@ public class UnicodeResponse implements FilterResponse { String message = LegacyComponentSerializer.legacySection().serialize(e.message()); message = Text.removeFirstColor(message); - Report report = FalsePositiveReporting.initializeReport(message); + Report report = Sentinel.getInstance().getDirector().reportHandler.initializeReport(message); UnicodeResponse response = new UnicodeResponse(e,message,message,report,false,false); - String disallowedRegex = Sentinel.mainConfig.chat.unicodeFilter.regex; + String disallowedRegex = Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.regex; ServerUtils.verbose("Regex: %s\nMessage: %s".formatted(disallowedRegex,message)); Pattern pattern = Pattern.compile(disallowedRegex, Pattern.CASE_INSENSITIVE); @@ -59,8 +58,8 @@ public class UnicodeResponse implements FilterResponse { response.getReport().getStepsTaken().replace("Anti-Unicode", "`%s` %s".formatted(message, Emojis.alarm)); response.setBlocked(true); - response.setPunished(Sentinel.mainConfig.chat.unicodeFilter.punished); - response.setHighlightedMessage(Text.regexHighlighter(message,Sentinel.mainConfig.chat.unicodeFilter.regex," > ", " < ")); + response.setPunished(Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.punished); + response.setHighlightedMessage(Text.regexHighlighter(message,Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.regex," > ", " < ")); } return response; diff --git a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/url/UrlAction.java b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/url/UrlAction.java index 9d71d10..6c27539 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/url/UrlAction.java +++ b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/url/UrlAction.java @@ -2,6 +2,7 @@ package me.trouper.sentinel.server.functions.chatfilter.url; import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.server.functions.chatfilter.AbstractActionHandler; +import me.trouper.sentinel.utils.PlayerUtils; import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.Text; import me.trouper.sentinel.utils.trees.HoverFormatter; @@ -12,7 +13,7 @@ import net.kyori.adventure.text.event.ClickEvent; public class UrlAction extends AbstractActionHandler { @Override protected void punish(UrlResponse response) { - for (String punishCommand : Sentinel.mainConfig.chat.urlFilter.punishCommands) { + for (String punishCommand : Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.punishCommands) { ServerUtils.sendCommand(punishCommand.replaceAll("%player%",response.getPlayer().getName())); } } @@ -21,19 +22,19 @@ public class UrlAction extends AbstractActionHandler { protected void staffWarning(UrlResponse response, Node tree) { String messageText = Text.prefix("&b&n%s&r &7%s".formatted( response.getPlayer().getName(), - response.isPunished() ? Sentinel.lang.violations.chat.url.autoPunishNotification : Sentinel.lang.violations.chat.url.preventNotification + response.isPunished() ? Sentinel.getInstance().getDirector().io.lang.violations.chat.url.autoPunishNotification : Sentinel.getInstance().getDirector().io.lang.violations.chat.url.preventNotification )); String hoverText = HoverFormatter.format(tree); - ServerUtils.forEachPlayer(player -> { + PlayerUtils.forEachPlayer(player -> { if (player.hasPermission("sentinel.chatfilter.url.view")) player.sendMessage(Component.text(messageText).hoverEvent(Component.text(hoverText).asHoverEvent())); }); } @Override protected void playerWarning(UrlResponse response) { - String message = Text.prefix(response.isPunished() ? Sentinel.lang.violations.chat.url.autoPunishWarning : Sentinel.lang.violations.chat.url.preventWarning); - String hoverText = Sentinel.lang.automatedActions.reportable; + String message = Text.prefix(response.isPunished() ? Sentinel.getInstance().getDirector().io.lang.violations.chat.url.autoPunishWarning : Sentinel.getInstance().getDirector().io.lang.violations.chat.url.preventWarning); + String hoverText = Sentinel.getInstance().getDirector().io.lang.automatedActions.reportable; String command = "/sentinelcallback fpreport %s".formatted(response.getReport().getId()); response.getPlayer().sendMessage(Component.text(message) .hoverEvent(Component.text(hoverText).asHoverEvent()) @@ -43,20 +44,20 @@ public class UrlAction extends AbstractActionHandler { @Override protected Node buildTree(UrlResponse response) { Node root = new Node("Sentinel"); - root.addTextLine(Sentinel.lang.violations.chat.url.treeTitle); + root.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.chat.url.treeTitle); - Node playerInfo = new Node(Sentinel.lang.violations.protections.infoNode.playerInfo.formatted(response.getPlayer().getName())); - playerInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.uuid, response.getPlayer().getUniqueId().toString()); + Node playerInfo = new Node(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.playerInfo.formatted(response.getPlayer().getName())); + playerInfo.addKeyValue(Sentinel.getInstance().getDirector().io.lang.violations.protections.infoNode.uuid, response.getPlayer().getUniqueId().toString()); root.addChild(playerInfo); - Node reportInfo = new Node(Sentinel.lang.violations.chat.url.reportInfoTitle); - reportInfo.addField(Sentinel.lang.violations.chat.originalMessage, response.getOriginalMessage()); - reportInfo.addField(Sentinel.lang.violations.chat.highlightedMessage, response.getHighlightedMessage()); + Node reportInfo = new Node(Sentinel.getInstance().getDirector().io.lang.violations.chat.url.reportInfoTitle); + reportInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.chat.originalMessage, response.getOriginalMessage()); + reportInfo.addField(Sentinel.getInstance().getDirector().io.lang.violations.chat.highlightedMessage, response.getHighlightedMessage()); root.addChild(reportInfo); - Node actions = new Node(Sentinel.lang.violations.protections.actionNode.actionNodeTitle); - actions.addTextLine(Sentinel.lang.violations.chat.denyMessage); - if (response.isPunished()) actions.addTextLine(Sentinel.lang.violations.protections.actionNode.punishmentCommandsExecuted); + Node actions = new Node(Sentinel.getInstance().getDirector().io.lang.violations.protections.actionNode.actionNodeTitle); + actions.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.chat.denyMessage); + if (response.isPunished()) actions.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.protections.actionNode.punishmentCommandsExecuted); root.addChild(actions); return root; @@ -64,6 +65,6 @@ public class UrlAction extends AbstractActionHandler { @Override protected boolean shouldWarnPlayer(UrlResponse response) { - return !Sentinel.mainConfig.chat.urlFilter.silent; + return !Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.silent; } } diff --git a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/url/UrlResponse.java b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/url/UrlResponse.java index 10c4c64..8f7f75e 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/chatfilter/url/UrlResponse.java +++ b/src/main/java/me/trouper/sentinel/server/functions/chatfilter/url/UrlResponse.java @@ -3,8 +3,7 @@ package me.trouper.sentinel.server.functions.chatfilter.url; import io.github.retrooper.packetevents.adventure.serializer.legacy.LegacyComponentSerializer; import io.papermc.paper.event.player.AsyncChatEvent; import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.Emojis; -import me.trouper.sentinel.server.functions.helpers.FalsePositiveReporting; +import me.trouper.sentinel.data.types.Emojis; import me.trouper.sentinel.server.functions.chatfilter.FilterResponse; import me.trouper.sentinel.server.functions.helpers.Report; import me.trouper.sentinel.utils.ServerUtils; @@ -36,9 +35,9 @@ public class UrlResponse implements FilterResponse { } String message = LegacyComponentSerializer.legacySection().serialize(e.message()); - Report report = FalsePositiveReporting.initializeReport(message); + Report report = Sentinel.getInstance().getDirector().reportHandler.initializeReport(message); UrlResponse response = new UrlResponse(e,message,message,report,false,false); - for (String allowed : Sentinel.mainConfig.chat.urlFilter.whitelist) { + for (String allowed : Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.whitelist) { message = message.replaceAll(allowed,""); } @@ -46,7 +45,7 @@ public class UrlResponse implements FilterResponse { message )); - String urlRegex = Sentinel.mainConfig.chat.urlFilter.regex; + String urlRegex = Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.regex; Pattern pattern = Pattern.compile(urlRegex, Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(message); @@ -56,12 +55,12 @@ public class UrlResponse implements FilterResponse { )); if (matcher.find()) { - String highlighted = Text.regexHighlighter(message,Sentinel.mainConfig.chat.urlFilter.regex," > "," < "); + String highlighted = Text.regexHighlighter(message,Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.regex," > "," < "); ServerUtils.verbose("Caught URL: " + highlighted); response.getReport().getStepsTaken().replace("Anti-URL", "`%s` %s".formatted(highlighted, Emojis.alarm)); response.setBlocked(true); - response.setPunished(Sentinel.mainConfig.chat.urlFilter.punished); + response.setPunished(Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.punished); response.setHighlightedMessage(highlighted); } diff --git a/src/main/java/me/trouper/sentinel/server/functions/helpers/AbstractViolation.java b/src/main/java/me/trouper/sentinel/server/functions/helpers/AbstractViolation.java deleted file mode 100644 index 65979a4..0000000 --- a/src/main/java/me/trouper/sentinel/server/functions/helpers/AbstractViolation.java +++ /dev/null @@ -1,133 +0,0 @@ -package me.trouper.sentinel.server.functions.helpers; - -import io.github.itzispyder.pdk.events.CustomListener; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.utils.FileUtils; -import me.trouper.sentinel.utils.PlayerUtils; -import me.trouper.sentinel.utils.ServerUtils; -import me.trouper.sentinel.utils.Text; -import me.trouper.sentinel.utils.trees.ConsoleFormatter; -import me.trouper.sentinel.utils.trees.EmbedFormatter; -import me.trouper.sentinel.utils.trees.HoverFormatter; -import me.trouper.sentinel.utils.trees.Node; -import net.kyori.adventure.text.Component; -import org.bukkit.Bukkit; -import org.bukkit.block.Block; -import org.bukkit.block.CommandBlock; -import org.bukkit.command.Command; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.Plugin; -import org.jetbrains.annotations.NotNull; - -import java.util.Arrays; -import java.util.concurrent.atomic.AtomicReference; - -public abstract class AbstractViolation implements CustomListener { - - public void runActions(String rootName, String rootNamePlayer, Node violationInfo, ActionConfiguration.Builder configuration) { - ActionConfiguration config = configuration.build(); - - Node root = new Node("Sentinel"); - root.addTextLine(rootName); - - if (config.getPlayer() != null) root.addChild(generatePlayerInfo(config.getPlayer())); - - root.addChild(violationInfo); - - root.addChild(configuration.getActionNode()); - - notifyTrusted(root,(rootNamePlayer == null || rootNamePlayer.isBlank()) ? rootName : rootNamePlayer); - if (configuration.isLoggedToDiscord()) EmbedFormatter.sendEmbed(EmbedFormatter.format(root)); - Sentinel.log.info(ConsoleFormatter.format(root)); - } - - public void notifyTrusted(Node root, String rootNamePlayer) { - ServerUtils.forEachPlayer(trusted -> { - if (PlayerUtils.isTrusted(trusted)) { - trusted.sendMessage(Component.text(Text.prefix(rootNamePlayer)).hoverEvent(Component.text(HoverFormatter.format(root)).asHoverEvent())); - } - }); - } - - public Node generatePlayerInfo(Player p) { - Node playerInfo = new Node(Sentinel.lang.violations.protections.infoNode.playerInfo); - playerInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.name, p.getName()); - playerInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.uuid, p.getUniqueId().toString()); - playerInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.operator, p.isOp() ? Sentinel.lang.generic.yes : Sentinel.lang.generic.no); - playerInfo.addField(Sentinel.lang.violations.protections.infoNode.locationField, Sentinel.lang.violations.protections.infoNode.locationFormat.formatted(Math.round(p.getX()), Math.round(p.getY()), Math.round(p.getZ()))); - - return playerInfo; - } - - public static Node generateBlockInfo(Block block) { - Node blockInfo = new Node(Sentinel.lang.violations.protections.infoNode.blockInfo); - blockInfo.addTextLine(Text.cleanName(block.getType().toString())); - blockInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.worldField,block.getWorld().getName()); - blockInfo.addField(Sentinel.lang.violations.protections.infoNode.blockLocationField,Sentinel.lang.violations.protections.infoNode.locationFormat.formatted(block.getX(), block.getY(), block.getZ())); - - return blockInfo; - } - - public Node generateCommandBlockInfo(CommandBlock commandBlock) { - Node commandBlockInfo = new Node(Sentinel.lang.violations.protections.infoNode.blockInfo); - commandBlockInfo.addTextLine(Text.cleanName(commandBlock.getType().toString())); - commandBlockInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.worldField,commandBlock.getWorld().getName()); - commandBlockInfo.addField(Sentinel.lang.violations.protections.infoNode.blockLocationField,Sentinel.lang.violations.protections.infoNode.locationFormat.formatted(commandBlock.getX(), commandBlock.getY(), commandBlock.getZ())); - - String command = commandBlock.getCommand(); - if (command == null || command.isBlank()) { - return commandBlockInfo; - } else if (command.length() <= 128) { - commandBlockInfo.addField(Sentinel.lang.violations.protections.infoNode.commandField, command); - } else { - commandBlockInfo.addField(Sentinel.lang.violations.protections.infoNode.commandTooLargeField, FileUtils.createCommandLog(command)); - } - - return commandBlockInfo; - } - - public Node generateMinecartInfo(Entity entity) { - Node minecartInfo = new Node(Sentinel.lang.violations.protections.infoNode.minecartInfo); - minecartInfo.addTextLine(Text.cleanName(entity.getType().toString())); - minecartInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.worldField,entity.getWorld().getName()); - minecartInfo.addField(Sentinel.lang.violations.protections.infoNode.cartLocationField,Sentinel.lang.violations.protections.infoNode.locationFormat.formatted(Math.round(entity.getX()), Math.round(entity.getY()), Math.round(entity.getZ()))); - - return minecartInfo; - } - - public Node generateItemInfo(ItemStack item) { - Node itemInfo = new Node(Sentinel.lang.violations.protections.infoNode.itemInfo); - itemInfo.addTextLine(Text.cleanName(item.getType().toString())); - itemInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.hasMeta,item.hasItemMeta() ? Sentinel.lang.generic.yes : Sentinel.lang.generic.no); - if (item.hasItemMeta()) { - itemInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.hasName,item.getItemMeta().hasCustomName() ? Sentinel.lang.generic.yes : Sentinel.lang.generic.no); - itemInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.hasLore,item.getItemMeta().hasLore() ? Sentinel.lang.generic.yes : Sentinel.lang.generic.no); - itemInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.hasAttributes,item.getItemMeta().hasAttributeModifiers() ? Sentinel.lang.generic.yes : Sentinel.lang.generic.no); - itemInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.hasEnchants,item.getItemMeta().hasEnchants() ? Sentinel.lang.generic.yes : Sentinel.lang.generic.no); - itemInfo.addField(Sentinel.lang.violations.protections.infoNode.nbtStored, FileUtils.createNBTLog(item)); - } - - return itemInfo; - } - - public Node generateCommandInfo(String command, Player executor) { - Node commandInfo = new Node(Sentinel.lang.violations.protections.infoNode.commandInfo); - String name = command.split(" ")[0].substring(1); - ServerUtils.verbose("Command Name: " + name); - Command executed = Bukkit.getServer().getCommandMap().getCommand(name); - - commandInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.name,name); - if (command.length() <= 128) { - commandInfo.addField(Sentinel.lang.violations.protections.infoNode.commandField, command); - } else { - commandInfo.addField(Sentinel.lang.violations.protections.infoNode.commandTooLargeField, FileUtils.createCommandLog(command)); - } - if (executed == null || executed.getPermission() == null) return commandInfo; - commandInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.permissionRequired,executed.getPermission()); - commandInfo.addKeyValue(Sentinel.lang.violations.protections.infoNode.permissionSatisfied,executor.hasPermission(executed.getPermission()) ? Sentinel.lang.generic.yes : Sentinel.lang.generic.no); - - return commandInfo; - } -} diff --git a/src/main/java/me/trouper/sentinel/server/functions/helpers/ActionConfiguration.java b/src/main/java/me/trouper/sentinel/server/functions/helpers/ActionConfiguration.java index 5408505..fc2eb15 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/helpers/ActionConfiguration.java +++ b/src/main/java/me/trouper/sentinel/server/functions/helpers/ActionConfiguration.java @@ -1,10 +1,12 @@ package me.trouper.sentinel.server.functions.helpers; import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.data.types.SerialLocation; import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.trees.Node; import org.bukkit.Material; import org.bukkit.block.Block; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; @@ -18,7 +20,9 @@ public class ActionConfiguration { private Cancellable event; private boolean cancel; private Block block; + private Entity entity; private boolean destroyBlock; + private boolean removeEntity; private boolean restoreBlock; private boolean punish; private List punishmentCommands; @@ -31,7 +35,9 @@ public class ActionConfiguration { this.event = builder.event; this.cancel = builder.cancel; this.block = builder.block; + this.entity = builder.entity; this.destroyBlock = builder.destroyBlock; + this.removeEntity = builder.removeEntity; this.restoreBlock = builder.restoreBlock; this.punish = builder.punish; this.punishmentCommands = builder.punishmentCommands; @@ -80,6 +86,14 @@ public class ActionConfiguration { this.block = block; } + public Entity getEntity() { + return entity; + } + + public void setEntity(Entity entity) { + this.entity = entity; + } + public boolean isDestroyBlock() { return destroyBlock; } @@ -88,6 +102,14 @@ public class ActionConfiguration { this.destroyBlock = destroyBlock; } + public void setRemoveEntity(boolean removeEntity) { + this.removeEntity = removeEntity; + } + + public boolean getRemoveEntity() { + return removeEntity; + } + public boolean isRestoreBlock() { return restoreBlock; } @@ -134,12 +156,14 @@ public class ActionConfiguration { private Cancellable event; private boolean cancel; private Block block; + private Entity entity; private boolean destroyBlock; + private boolean removeEntity; private boolean restoreBlock; private boolean punish; private List punishmentCommands = new ArrayList<>(); private boolean logToDiscord; - private Node actionNode = new Node(Sentinel.lang.violations.protections.actionNode.actionNodeTitle); + private Node actionNode = new Node(Sentinel.getInstance().getDirector().io.lang.violations.protections.actionNode.actionNodeTitle); private List> actions = new ArrayList<>(); @@ -156,7 +180,7 @@ public class ActionConfiguration { if (config.player != null) { config.player.setOp(false); } - config.actionNode.addTextLine(Sentinel.lang.violations.protections.actionNode.userDeoped); + config.actionNode.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.protections.actionNode.userDeoped); }); return this; } @@ -174,7 +198,7 @@ public class ActionConfiguration { if (config.event != null) { config.event.setCancelled(true); } - config.actionNode.addTextLine(Sentinel.lang.violations.protections.actionNode.eventCancelled); + config.actionNode.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.protections.actionNode.eventCancelled); }); return this; } @@ -191,7 +215,7 @@ public class ActionConfiguration { config.destroyBlock = destroyBlock; if (config.block != null) { config.block.setType(Material.AIR); - config.actionNode.addTextLine(Sentinel.lang.violations.protections.actionNode.destroyedBlock); + config.actionNode.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.protections.actionNode.destroyedBlock); } }); return this; @@ -202,16 +226,33 @@ public class ActionConfiguration { actions.add(config -> { config.restoreBlock = restoreBlock; if (config.block != null) { - if (CBWhitelistManager.restore(config.block.getLocation())) { - config.actionNode.addTextLine(Sentinel.lang.violations.protections.actionNode.restore); + if (Sentinel.getInstance().getDirector().whitelistManager.getFromWhitelist(config.block.getLocation()) != null && Sentinel.getInstance().getDirector().whitelistManager.getFromWhitelist(config.block.getLocation()).restore()) { + config.actionNode.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.protections.actionNode.restore); } else { - config.actionNode.addTextLine(Sentinel.lang.violations.protections.actionNode.restoreFailed); + config.actionNode.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.protections.actionNode.restoreFailed); } } }); return this; } + public Builder setEntity(Entity entity) { + this.entity = entity; + actions.add(config -> config.entity = entity); + return this; + } + + public Builder removeEntity(boolean removeEntity) { + this.removeEntity = removeEntity; + actions.add(config -> { + config.removeEntity = removeEntity; + if (config.entity != null) { + entity.remove(); + } + }); + return this; + } + public Builder punish(boolean punish) { this.punish = punish; actions.add(config -> config.punish = punish); @@ -226,7 +267,7 @@ public class ActionConfiguration { for (String cmd : punishmentCommands) { ServerUtils.sendCommand(cmd.replaceAll("%player%", config.player.getName())); } - config.actionNode.addTextLine(Sentinel.lang.violations.protections.actionNode.punishmentCommandsExecuted); + config.actionNode.addTextLine(Sentinel.getInstance().getDirector().io.lang.violations.protections.actionNode.punishmentCommandsExecuted); } }); return this; diff --git a/src/main/java/me/trouper/sentinel/server/functions/helpers/CBWhitelistManager.java b/src/main/java/me/trouper/sentinel/server/functions/helpers/CBWhitelistManager.java index 3a7d125..b8edf1a 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/helpers/CBWhitelistManager.java +++ b/src/main/java/me/trouper/sentinel/server/functions/helpers/CBWhitelistManager.java @@ -1,185 +1,166 @@ package me.trouper.sentinel.server.functions.helpers; import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.types.WhitelistedBlock; +import me.trouper.sentinel.data.types.SerialLocation; +import me.trouper.sentinel.data.types.CommandBlockHolder; +import me.trouper.sentinel.server.events.admin.WandEvents; +import me.trouper.sentinel.data.types.Selection; import me.trouper.sentinel.utils.PlayerUtils; import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.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; +import org.bukkit.block.data.Directional; +import org.bukkit.entity.Player; +import org.bukkit.entity.minecart.CommandMinecart; import org.bukkit.persistence.PersistentDataType; import java.util.HashSet; import java.util.Set; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; public class CBWhitelistManager { - public static Set autoWhitelist = new HashSet<>(); + public Set autoWhitelist = new HashSet<>(); - public static void add(CommandBlock cb, UUID owner) { - ServerUtils.verbose("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 wbloc = WhitelistedBlock.fromSerialized(wb.loc()); - - 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) { - for (WhitelistedBlock cb : Sentinel.whitelist.whitelistedCMDBlocks) { - Location cbl = WhitelistedBlock.fromSerialized(cb.loc()); - if (cbl.distance(where) < 0.5) { - Sentinel.whitelist.whitelistedCMDBlocks.remove(cb); - } - } - - Sentinel.whitelist.save(); - } - - public static boolean canRun(Block b) { - CommandBlock test = (CommandBlock) b.getState(); - String command = test.getCommand(); - boolean alwaysActive = getNBTBoolean(test, "auto"); - for (WhitelistedBlock cb : Sentinel.whitelist.whitelistedCMDBlocks) { - if (!(b.getLocation().distance(WhitelistedBlock.fromSerialized(cb.loc())) < 0.5)) continue; - if (cb.active() != alwaysActive) return false; - if (!cb.command().equals(command)) return false; - if (!cb.type().equals(getType(test))) return false; - return PlayerUtils.isTrusted(cb.owner()); - } - return false; - } - - public static WhitelistedBlock get(Location where) { - for (WhitelistedBlock cb : Sentinel.whitelist.whitelistedCMDBlocks) { - Location cbl = WhitelistedBlock.fromSerialized(cb.loc()); - if (cbl.distance(where) < 0.5) { - return cb; - } - } - 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.verbose("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.verbose("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.verbose("Command block at " + where.toString() + " has been restored."); - return true; - } - - public static String getType(CommandBlock cb) { - switch (cb.getType()) { - case COMMAND_BLOCK -> { - return "impulse"; - } - case REPEATING_COMMAND_BLOCK -> { - return "repeat"; - } - case CHAIN_COMMAND_BLOCK -> { - return "chain"; - } - } - 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 + public CommandBlockHolder generateHolder(UUID owner, CommandMinecart cm) { + return new CommandBlockHolder(owner.toString(), + SerialLocation.uuidToLocation(cm.getUniqueId()), + "minecart", + cm.getType().name(), + false, + false, + cm.getCommand() ); } - - private static boolean getNBTBoolean(CommandBlock cmdBlock, String key) { - return cmdBlock.getPersistentDataContainer().has( - getKey(key), - PersistentDataType.BYTE - ) && cmdBlock.getPersistentDataContainer().get( - getKey(key), - PersistentDataType.BYTE - ) == 1; + public CommandBlockHolder generateHolder(UUID owner, CommandBlock cb) { + return new CommandBlockHolder(owner.toString(), + SerialLocation.translate(cb.getLocation()), + serializeFacing(cb.getBlock()), + serializeType(cb), + isAuto(cb), + isConditional(cb), + cb.getCommand() + ); } - private static NamespacedKey getKey(String key) { - return new NamespacedKey(Sentinel.getInstance(), key); + public void removeSelectionFromWhitelist(Player player) { + Selection selection = WandEvents.selections.get(player.getUniqueId()); + if (selection == null || !selection.isComplete()) { + player.sendMessage(Text.prefix("You must set 2 points first.")); + return; + } + AtomicInteger number = new AtomicInteger(); + selection.forEachBlock(block -> { + if (block.getType().equals(Material.COMMAND_BLOCK) || block.getType().equals(Material.REPEATING_COMMAND_BLOCK) || block.getType().equals(Material.CHAIN_COMMAND_BLOCK)) { + generateHolder(player.getUniqueId(),(CommandBlock) block.getState()).removeFromWhitelist(); + number.getAndIncrement(); + } + }); + + player.sendMessage(Text.prefix("Removed all &b%s&7 command blocks from the whitelist in your selection.".formatted(number.get()))); + } + + public void deleteSelection(Player player) { + Selection selection = WandEvents.selections.get(player.getUniqueId()); + if (selection == null || !selection.isComplete()) { + player.sendMessage(Text.prefix("You must set 2 points first.")); + return; + } + AtomicInteger number = new AtomicInteger(); + selection.forEachBlock(block -> { + if (block.getType().equals(Material.COMMAND_BLOCK) || block.getType().equals(Material.REPEATING_COMMAND_BLOCK) || block.getType().equals(Material.CHAIN_COMMAND_BLOCK)) { + generateHolder(player.getUniqueId(),(CommandBlock) block.getState()).destroy(); + number.getAndIncrement(); + } + }); + + player.sendMessage(Text.prefix("Deleted all &b%s&7 command blocks from the whitelist in your selection.".formatted(number.get()))); + } + + public void addSelectionToWhitelist(Player player) { + Selection selection = WandEvents.selections.get(player.getUniqueId()); + if (selection == null || !selection.isComplete()) { + player.sendMessage(Text.prefix("You must set 2 points first.")); + return; + } + + AtomicInteger number = new AtomicInteger(); + selection.forEachBlock(block -> { + if (ServerUtils.isCommandBlock(block)) { + CommandBlock cb = (CommandBlock) block.getState(); + generateHolder(player.getUniqueId(),cb).addToWhitelist(); + number.getAndIncrement(); + } + }); + + player.sendMessage(Text.prefix("Whitelisted all &b%s&7 command blocks in your selection.".formatted(number.get()))); + } + + public int clearAll() { + int total = 0; + for (CommandBlockHolder cb : Sentinel.getInstance().getDirector().io.commandBlocks.holders) { + cb.removeFromWhitelist(); + total++; + + if (cb.loc().isUUID()) continue; + Location remove = SerialLocation.translate(cb.loc()); + remove.getBlock().setType(Material.AIR); + } + return total; + } + + public int clearAll(UUID who) { + int total = 0; + for (CommandBlockHolder cb : Sentinel.getInstance().getDirector().io.commandBlocks.holders) { + if (!cb.owner().equals(who.toString())) continue; + cb.removeFromWhitelist(); + total++; + + if (cb.loc().isUUID()) continue; + Location remove = SerialLocation.translate(cb.loc()); + remove.getBlock().setType(Material.AIR); + } + return total; + } + + public int restoreAll() { + int total = 0; + for (CommandBlockHolder cb : Sentinel.getInstance().getDirector().io.commandBlocks.holders) { + if (cb.isWhitelisted() && cb.restore()) total++; + } + return total; + } + + public int restoreAll(UUID who) { + int total = 0; + for (CommandBlockHolder cb : Sentinel.getInstance().getDirector().io.commandBlocks.holders) { + if (!cb.owner().equals(who.toString())) continue; + if (cb.isWhitelisted() && cb.restore()) total++; + } + return total; + } + + public String serializeFacing(Block block) { + if (block.getBlockData() instanceof Directional directional) { + return directional.getFacing().name(); + } + return "UNKNOWN"; + } + + public String serializeType(CommandBlock cb) { + return cb.getType().name(); + } + + public boolean isAuto(CommandBlock cb) { + return cb.getPersistentDataContainer().getOrDefault(Sentinel.getInstance().getNamespace("auto"), PersistentDataType.BYTE,(byte) 0) == (byte) 1; + } + + public boolean isConditional(CommandBlock cb) { + return cb.getBlock().getBlockData() instanceof org.bukkit.block.data.type.CommandBlock cbs && cbs.isConditional(); } } diff --git a/src/main/java/me/trouper/sentinel/server/functions/helpers/FilterHelpers.java b/src/main/java/me/trouper/sentinel/server/functions/helpers/FilterHelpers.java deleted file mode 100644 index f5ae2ff..0000000 --- a/src/main/java/me/trouper/sentinel/server/functions/helpers/FilterHelpers.java +++ /dev/null @@ -1,107 +0,0 @@ -package me.trouper.sentinel.server.functions.helpers; - -import io.papermc.paper.event.player.AsyncChatEvent; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.functions.chatfilter.profanity.Severity; -import me.trouper.sentinel.utils.ServerUtils; -import me.trouper.sentinel.utils.Text; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class FilterHelpers { - - public static Severity checkSlur(String text, Severity backup) { - if (containsSlurs(text)) return Severity.SLUR; - if (containsSwears(text)) return backup; - return Severity.SAFE; - } - - public static boolean containsSwears(String text) { - ServerUtils.verbose("ProfanityFilter: Checking for swears"); - for (String swear : Sentinel.swearConfig.swears) { - if (text.contains(swear)) return true; - } - - Pattern pattern = Pattern.compile(Sentinel.swearConfig.regexSwears, Pattern.CASE_INSENSITIVE); - Matcher matcher = pattern.matcher(text); - - return matcher.find() && Sentinel.swearConfig.useRegex; - } - - public static boolean containsSlurs(String text) { - ServerUtils.verbose("ProfanityFilter: Checking for slurs"); - for (String slur : Sentinel.strictConfig.strict) { - if (text.contains(slur)) return true; - } - - Pattern pattern = Pattern.compile(Sentinel.strictConfig.regexStrict, Pattern.CASE_INSENSITIVE); - Matcher matcher = pattern.matcher(text); - - return matcher.find() && Sentinel.strictConfig.useRegex; - } - - public static String removeFalsePositives(String text) { - for (String falsePositive : Sentinel.fpConfig.swearWhitelist) { - text = text.replace(falsePositive, ""); - } - text = text.replaceAll(Sentinel.fpConfig.regexWhitelist,""); - return text; - } - - public static String convertLeetSpeakCharacters(String text) { - text = Text.fromLeetString(text); - return text; - } - - public static String stripSpecialCharacters(String text) { - text = text.replaceAll("[^A-Za-z0-9.,!?;:'\"()\\[\\]{}]", "").trim(); - return text; - } - - public static String simplifyRepeatingLetters(String text) { - text = Text.replaceRepeatingLetters(text); - return text; - } - - public static String removePeriodsAndSpaces(String text) { - return text.replaceAll("[^A-Za-z0-9]", "").replace(" ", ""); - } - - public static String highlightProfanity(String text, String start, String end) { - String highlightedSwears = highlightSwears(fullSimplify(text), start, end); - return Text.color(highlightSlurs(highlightedSwears, start, end)); - } - - private static String highlightSwears(String text, String start, String end) { - for (String swear : Sentinel.swearConfig.swears) { - text = text.replace(swear, start + swear + end); - } - return text; - } - - private static String highlightSlurs(String text, String start, String end) { - for (String slur : Sentinel.strictConfig.strict) { - text = text.replace(slur, start + slur + end); - } - return text; - } - - public static String fullSimplify(String text) { - String lowercasedText = text.toLowerCase(); - String cleanedText = FilterHelpers.removeFalsePositives(lowercasedText); - String convertedText = FilterHelpers.convertLeetSpeakCharacters(cleanedText); - String strippedText = FilterHelpers.stripSpecialCharacters(convertedText); - String simplifiedText = FilterHelpers.simplifyRepeatingLetters(strippedText); - return FilterHelpers.removePeriodsAndSpaces(simplifiedText); - } - - public static void restrictMessage(AsyncChatEvent event, boolean silent) { - if (silent) { - event.viewers().clear(); - event.viewers().add(event.getPlayer()); - } else { - event.setCancelled(true); - } - } -} diff --git a/src/main/java/me/trouper/sentinel/server/functions/helpers/Message.java b/src/main/java/me/trouper/sentinel/server/functions/helpers/MessageHandler.java similarity index 57% rename from src/main/java/me/trouper/sentinel/server/functions/helpers/Message.java rename to src/main/java/me/trouper/sentinel/server/functions/helpers/MessageHandler.java index a4896f4..82a2242 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/helpers/Message.java +++ b/src/main/java/me/trouper/sentinel/server/functions/helpers/MessageHandler.java @@ -5,7 +5,7 @@ import io.papermc.paper.chat.ChatRenderer; import io.papermc.paper.event.player.AsyncChatEvent; import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.server.commands.SentinelCommand; -import me.trouper.sentinel.server.events.ChatEvent; +import me.trouper.sentinel.server.events.violations.players.ChatEvent; import net.kyori.adventure.chat.SignedMessage; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; @@ -13,27 +13,27 @@ import org.bukkit.entity.Player; import java.util.*; -public class Message { - public static final Map replyMap = new HashMap<>(); - public static void messagePlayer(Player sender, Player receiver, String message) { +public class MessageHandler { + public final Map replyMap = new HashMap<>(); + public void messagePlayer(Player sender, Player receiver, String message) { AsyncChatEvent checkEvent = new AsyncChatEvent(true,sender, new HashSet<>(Arrays.asList(receiver, sender)), ChatRenderer.defaultRenderer(),Component.text(message),Component.text(message), SignedMessage.system(message,Component.text(message))); if (checkEvent.isCancelled()) return; new ChatEvent().handleEvent(checkEvent); if (checkEvent.isCancelled()) return; - sender.sendMessage(Sentinel.lang.playerInteraction.messageSent.formatted(receiver.getName(),message)); - receiver.sendMessage(Sentinel.lang.playerInteraction.messageReceived.formatted(sender.getName(),message)); + sender.sendMessage(Sentinel.getInstance().getDirector().io.lang.playerInteraction.messageSent.formatted(receiver.getName(),message)); + receiver.sendMessage(Sentinel.getInstance().getDirector().io.lang.playerInteraction.messageReceived.formatted(sender.getName(),message)); replyMap.put(receiver.getUniqueId(),sender.getUniqueId()); sendSpy(sender,receiver,message); } - public static void sendSpy(Player sender, Player receiver, String message) { + public void sendSpy(Player sender, Player receiver, String message) { ServerUtils.forEachPlayer(player -> { if (SentinelCommand.spyMap.getOrDefault(player.getUniqueId(),false)) { TextComponent notification = Component - .text(Sentinel.lang.socialSpy.spyMessage.formatted(sender.getName(),receiver.getName())) - .hoverEvent(Component.text(Sentinel.lang.socialSpy.spyMessageHover.formatted(sender.getName(),receiver.getName(),message))); + .text(Sentinel.getInstance().getDirector().io.lang.socialSpy.spyMessage.formatted(sender.getName(),receiver.getName())) + .hoverEvent(Component.text(Sentinel.getInstance().getDirector().io.lang.socialSpy.spyMessageHover.formatted(sender.getName(),receiver.getName(),message))); player.sendMessage(notification); } }); diff --git a/src/main/java/me/trouper/sentinel/server/functions/helpers/FalsePositiveReporting.java b/src/main/java/me/trouper/sentinel/server/functions/helpers/ReportHandler.java similarity index 81% rename from src/main/java/me/trouper/sentinel/server/functions/helpers/FalsePositiveReporting.java rename to src/main/java/me/trouper/sentinel/server/functions/helpers/ReportHandler.java index 329874f..c3da9d2 100644 --- a/src/main/java/me/trouper/sentinel/server/functions/helpers/FalsePositiveReporting.java +++ b/src/main/java/me/trouper/sentinel/server/functions/helpers/ReportHandler.java @@ -2,8 +2,8 @@ package me.trouper.sentinel.server.functions.helpers; import io.github.itzispyder.pdk.utils.SchedulerUtils; import io.github.itzispyder.pdk.utils.discord.DiscordEmbed; -import me.trouper.sentinel.data.Emojis; -import me.trouper.sentinel.utils.Randomizer; +import me.trouper.sentinel.data.types.Emojis; +import me.trouper.sentinel.utils.Random; import me.trouper.sentinel.utils.trees.EmbedFormatter; import org.bukkit.entity.Player; @@ -11,11 +11,11 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -public class FalsePositiveReporting { - public static Map reports = new HashMap<>(); +public class ReportHandler { + public Map reports = new HashMap<>(); - public static Report initializeReport(String message) { - final long reportID = Randomizer.generateID(); + public Report initializeReport(String message) { + final long reportID = Random.generateID(); LinkedHashMap steps = new LinkedHashMap<>(); steps.put("Original Message", "`%s`".formatted(message)); @@ -25,7 +25,7 @@ public class FalsePositiveReporting { return new Report(reportID,message,steps); } - public static void sendReport(Player sender, Report report) { + public void sendReport(Player sender, Report report) { DiscordEmbed.Builder embed = DiscordEmbed.create() .author(new DiscordEmbed.Author("Anti-Swear False Positive","",null)) .title("A player has reported a false positive") diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/AbstractCheck.java b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/AbstractCheck.java new file mode 100644 index 0000000..97f49cc --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/AbstractCheck.java @@ -0,0 +1,10 @@ +package me.trouper.sentinel.server.functions.itemchecks; + +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.utils.ServerUtils; + +import java.util.Arrays; + +public abstract class AbstractCheck { + public abstract boolean passes(T input); +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EnchantmentCheck.java b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EnchantmentCheck.java new file mode 100644 index 0000000..6ad5d4d --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EnchantmentCheck.java @@ -0,0 +1,122 @@ +package me.trouper.sentinel.server.functions.itemchecks; + +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.utils.ServerUtils; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.Map; + +import static org.bukkit.enchantments.Enchantment.MENDING; + +public class EnchantmentCheck { + + public boolean hasIllegalEnchants(ItemStack item) { + ServerUtils.verbose("Checking item for illegal enchants: ", item.getType().name()); + if (item.hasItemMeta() && item.getItemMeta().hasEnchants()) { + ItemMeta meta = item.getItemMeta(); + Map enchantments = meta.getEnchants(); + for (Map.Entry entry : enchantments.entrySet()) { + Enchantment enchantment = entry.getKey(); + int level = entry.getValue(); + if (level > Sentinel.getInstance().getDirector().io.nbtConfig.globalMaxEnchant || isOverLimit(enchantment, level)) { + return true; + } + } + } + return false; + } + + public static boolean isOverLimit(Enchantment enchantment, int level) { + int maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.globalMaxEnchant; + + if (enchantment.equals(MENDING)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxMending; + } else if (enchantment.equals(Enchantment.UNBREAKING)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxUnbreaking; + } else if (enchantment.equals(Enchantment.VANISHING_CURSE)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxCurseOfVanishing; + } else if (enchantment.equals(Enchantment.BINDING_CURSE)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxCurseOfBinding; + } else if (enchantment.equals(Enchantment.AQUA_AFFINITY)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxAquaAffinity; + } else if (enchantment.equals(Enchantment.PROTECTION)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxProtection; + } else if (enchantment.equals(Enchantment.BLAST_PROTECTION)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxBlastProtection; + } else if (enchantment.equals(Enchantment.DEPTH_STRIDER)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxDepthStrider; + } else if (enchantment.equals(Enchantment.FEATHER_FALLING)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxFeatherFalling; + } else if (enchantment.equals(Enchantment.FIRE_PROTECTION)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxFireProtection; + } else if (enchantment.equals(Enchantment.FROST_WALKER)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxFrostWalker; + } else if (enchantment.equals(Enchantment.PROJECTILE_PROTECTION)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxProjectileProtection; + } else if (enchantment.equals(Enchantment.RESPIRATION)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxRespiration; + } else if (enchantment.equals(Enchantment.SOUL_SPEED)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxSoulSpeed; + } else if (enchantment.equals(Enchantment.THORNS)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxThorns; + } else if (enchantment.equals(Enchantment.SWEEPING_EDGE)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxSweepingEdge; + } else if (enchantment.equals(Enchantment.SWIFT_SNEAK)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxSwiftSneak; + } else if (enchantment.equals(Enchantment.BANE_OF_ARTHROPODS)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxBaneOfArthropods; + } else if (enchantment.equals(Enchantment.FIRE_ASPECT)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxFireAspect; + } else if (enchantment.equals(Enchantment.LOOTING)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxLooting; + } else if (enchantment.equals(Enchantment.IMPALING)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxImpaling; + } else if (enchantment.equals(Enchantment.KNOCKBACK)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxKnockback; + } else if (enchantment.equals(Enchantment.SHARPNESS)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxSharpness; + } else if (enchantment.equals(Enchantment.SMITE)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxSmite; + } else if (enchantment.equals(Enchantment.CHANNELING)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxChanneling; + } else if (enchantment.equals(Enchantment.FLAME)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxFlame; + } else if (enchantment.equals(Enchantment.INFINITY)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxInfinity; + } else if (enchantment.equals(Enchantment.LOYALTY)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxLoyalty; + } else if (enchantment.equals(Enchantment.RIPTIDE)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxRiptide; + } else if (enchantment.equals(Enchantment.MULTISHOT)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxMultishot; + } else if (enchantment.equals(Enchantment.PIERCING)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxPiercing; + } else if (enchantment.equals(Enchantment.POWER)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxPower; + } else if (enchantment.equals(Enchantment.PUNCH)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxPunch; + } else if (enchantment.equals(Enchantment.QUICK_CHARGE)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxQuickCharge; + } else if (enchantment.equals(Enchantment.EFFICIENCY)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxEfficiency; + } else if (enchantment.equals(Enchantment.FORTUNE)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxFortune; + } else if (enchantment.equals(Enchantment.LUCK_OF_THE_SEA)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxLuckOfTheSea; + } else if (enchantment.equals(Enchantment.LURE)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxLure; + } else if (enchantment.equals(Enchantment.SILK_TOUCH)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxSilkTouch; + } else if (enchantment.equals(Enchantment.BREACH)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxBreach; + } else if (enchantment.equals(Enchantment.DENSITY)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxDensity; + } else if (enchantment.equals(Enchantment.WIND_BURST)) { + maxLevel = Sentinel.getInstance().getDirector().io.nbtConfig.maxWindBurst; + } + + return level > maxLevel; + } +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EntityCheck.java b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EntityCheck.java new file mode 100644 index 0000000..37ade6a --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EntityCheck.java @@ -0,0 +1,74 @@ +package me.trouper.sentinel.server.functions.itemchecks; + +import de.tr7zw.changeme.nbtapi.NBT; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.utils.InventoryUtils; +import me.trouper.sentinel.utils.ServerUtils; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Item; +import org.bukkit.entity.Mob; +import org.bukkit.entity.Villager; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.MerchantRecipe; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class EntityCheck extends AbstractCheck { + + @Override + public boolean passes(Entity entity) { + if (entity instanceof Item itemEntity) { + if (!new ItemCheck().passes(itemEntity.getItemStack())) { + ServerUtils.verbose("Entity failed check: Item not allowed."); + return false; + } + } + Inventory inv = InventoryUtils.getInventory(entity); + if (inv != null && !new InventoryCheck().passes(inv)) { + ServerUtils.verbose("Entity inventory failed check."); + return false; + } + if (entity instanceof Villager villager) { + for (MerchantRecipe recipe : villager.getRecipes()) { + if (!new ItemCheck().passes(recipe.getResult())) { + ServerUtils.verbose("Villager recipe failed check."); + return false; + } + } + } + if (entity instanceof Mob mob) { + if (!new EquipmentCheck().passes(mob)) { + ServerUtils.verbose("Mob equipment failed check."); + return false; + } + } + if (!entity.getPassengers().isEmpty()) { + if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowRecursion) { + ServerUtils.verbose("Entity recursion not allowed."); + return false; + } + for (Entity passenger : entity.getPassengers()) { + if (!passes(passenger)) { + ServerUtils.verbose("Entity passenger failed check."); + return false; + } + } + } + AtomicBoolean failsTiming = new AtomicBoolean(false); + NBT.get(entity, nbt -> { + if (nbt.hasTag("DeathTime") && nbt.getInteger("DeathTime") < 1) { + ServerUtils.verbose("Entity death time check failed."); + failsTiming.set(true); + } + if (nbt.hasTag("Hurttime") && nbt.getInteger("Hurttime") < 1) { + ServerUtils.verbose("Entity hurt time check failed."); + failsTiming.set(true); + } + }); + if (failsTiming.get()) { + ServerUtils.verbose("Entity timing check failed."); + return false; + } + return true; + } +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EntitySnapshotCheck.java b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EntitySnapshotCheck.java new file mode 100644 index 0000000..b9e1ce6 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EntitySnapshotCheck.java @@ -0,0 +1,20 @@ +package me.trouper.sentinel.server.functions.itemchecks; + +import me.trouper.sentinel.utils.ServerUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntitySnapshot; + +public class EntitySnapshotCheck extends AbstractCheck { + + @Override + public boolean passes(EntitySnapshot input) { + Location loc = new Location(Bukkit.getWorlds().getFirst(), 0, 1000000, 0); + Entity temp = input.createEntity(loc); + boolean result = new EntityCheck().passes(temp); + ServerUtils.verbose("Temp Entity %s Entity Check", result ? "failed" : "passed"); + temp.remove(); + return result; + } +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EquipmentCheck.java b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EquipmentCheck.java new file mode 100644 index 0000000..af83534 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/EquipmentCheck.java @@ -0,0 +1,21 @@ +package me.trouper.sentinel.server.functions.itemchecks; + +import me.trouper.sentinel.utils.ServerUtils; +import org.bukkit.entity.Mob; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.EquipmentSlot; + +public class EquipmentCheck extends AbstractCheck { + + @Override + public boolean passes(Mob mob) { + ServerUtils.verbose("Running mob check."); + for (EquipmentSlot slot : EquipmentSlot.values()) { + ItemStack item = mob.getEquipment().getItem(slot); + if (item != null && !new ItemCheck().passes(item)) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/InventoryCheck.java b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/InventoryCheck.java new file mode 100644 index 0000000..0d442f2 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/InventoryCheck.java @@ -0,0 +1,32 @@ +package me.trouper.sentinel.server.functions.itemchecks; + +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.functions.itemchecks.AbstractCheck; +import me.trouper.sentinel.server.functions.itemchecks.ItemCheck; +import me.trouper.sentinel.utils.InventoryUtils; +import me.trouper.sentinel.utils.ServerUtils; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public class InventoryCheck extends AbstractCheck { + + @Override + public boolean passes(Inventory inventory) { + ServerUtils.verbose("Running Inventory Check"); + for (ItemStack item : inventory.getContents()) { + if (item == null || item.getType().isAir()) continue; + if (!new ItemCheck().passes(item)) { + ServerUtils.verbose("Inventory item failed check."); + return false; + } + Inventory subInventory = InventoryUtils.getInventory(item); + if (subInventory != null && !Sentinel.getInstance().getDirector().io.nbtConfig.allowRecursion) return false; + if (subInventory != null && !passes(subInventory)) { + ServerUtils.verbose("Sub-inventory failed check."); + return false; + } + } + ServerUtils.verbose("Inventory passed all checks."); + return true; + } +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/ItemCheck.java b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/ItemCheck.java new file mode 100644 index 0000000..3860338 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/ItemCheck.java @@ -0,0 +1,193 @@ +package me.trouper.sentinel.server.functions.itemchecks; + +import de.tr7zw.changeme.nbtapi.NBT; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.utils.InventoryUtils; +import me.trouper.sentinel.utils.ServerUtils; +import org.bukkit.Material; +import org.bukkit.block.*; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BlockStateMeta; +import org.bukkit.inventory.meta.BundleMeta; +import org.bukkit.inventory.meta.ItemMeta; + +public class ItemCheck extends AbstractCheck { + @Override + public boolean passes(ItemStack item) { + ServerUtils.verbose("Checking item: " + item.getType().name()); + + // No metadata? Nothing to check. + if (item.getItemMeta() == null) { + ServerUtils.verbose("Item passes because it has no metadata."); + return true; + } + ItemMeta meta = item.getItemMeta(); + + // Check for an inventory inside the item. + Inventory inv = InventoryUtils.getInventory(item); + if (inv != null) { + ServerUtils.verbose("Item contains an inventory: " + inv); + if (!new InventoryCheck().passes(inv)) { + ServerUtils.verbose("Item failed inventory check."); + return false; + } + } + + // NBT-based checks (e.g. custom consumables/tools). + var nbt = NBT.itemStackToNBT(item); + var components = nbt.getCompound("components"); + if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowCustomConsumables && components.getCompound("minecraft:consumable") != null) { + ServerUtils.verbose("Item is consumable and not allowed."); + return false; + } + if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowCustomTools && components.getCompound("minecraft:tool") != null) { + ServerUtils.verbose("Item is custom tool and not allowed."); + return false; + } + var entityData = components.getCompound("minecraft:entity_data"); + if (entityData != null) { + if (item.getType().name().contains("ITEM_FRAME")) { + var itemData = entityData.getCompound("Item"); + ItemStack heldItem = NBT.itemStackFromNBT(itemData); + if (heldItem != null && !new ItemCheck().passes(heldItem)) { + ServerUtils.verbose("Item frame contents failed check."); + return false; + } + } + if (isSpawnEgg(item)) { + if (entityData.hasTag("DeathTime") && entityData.getInteger("DeathTime") < 1) { + ServerUtils.verbose("Egg death time check failed."); + return false; + } + if (entityData.hasTag("Hurttime") && entityData.getInteger("HurtTime") < 1) { + ServerUtils.verbose("Egg hurt time check failed."); + return false; + } + } + } + + // Bundle check – recursively check the contained items. + if (item.getType().name().contains("_BUNDLE") && meta instanceof BundleMeta bm) { + for (ItemStack bundleItem : bm.getItems()) { + if (!passes(bundleItem)) return false; + } + } + + // Campfire check. + if (item.getType().name().contains("CAMPFIRE") && meta instanceof BlockStateMeta blockStateMeta) { + BlockState bs = blockStateMeta.getBlockState(); + if (bs instanceof Campfire campfire) { + for (int slot = 0; slot < 4; slot++) { + ItemStack campfireItem = campfire.getItem(slot); + if (campfireItem != null && !passes(campfireItem)) { + ServerUtils.verbose("Campfire item failed check."); + return false; + } + } + } + } + + // Lectern and Chiseled Bookshelf check (by validating their inventories). + if (item.getType().equals(Material.LECTERN) && meta instanceof BlockStateMeta blockStateMeta) { + BlockState bs = blockStateMeta.getBlockState(); + if (bs instanceof Lectern lectern) { + if (!new InventoryCheck().passes(lectern.getInventory())) { + ServerUtils.verbose("Lectern inventory failed check."); + return false; + } + } + } + if (item.getType().equals(Material.CHISELED_BOOKSHELF) && meta instanceof BlockStateMeta blockStateMeta) { + BlockState bs = blockStateMeta.getBlockState(); + if (bs instanceof ChiseledBookshelf bookshelf) { + if (!new InventoryCheck().passes(bookshelf.getInventory())) { + ServerUtils.verbose("Chiseled bookshelf inventory failed check."); + return false; + } + } + } + + // Spawner check. + if (item.getType().equals(Material.SPAWNER) && meta instanceof BlockStateMeta blockStateMeta) { + BlockState bs = blockStateMeta.getBlockState(); + if (bs instanceof CreatureSpawner spawner) { + if (spawner.getSpawnedEntity() != null) { + if (spawner.getSpawnedEntity().getEntityType().equals(EntityType.FALLING_BLOCK) || + spawner.getSpawnedEntity().getEntityType().equals(EntityType.COMMAND_BLOCK_MINECART)) { + ServerUtils.verbose("Spawner contains disallowed entity type."); + return false; + } + if (!new EntitySnapshotCheck().passes(spawner.getSpawnedEntity())) { + ServerUtils.verbose("Spawner entity snapshot check failed."); + return false; + } + } + } + } + + // Trial Spawner check. + if (item.getType() == Material.TRIAL_SPAWNER && meta instanceof BlockStateMeta blockStateMeta) { + BlockState bs = blockStateMeta.getBlockState(); + if (bs instanceof TrialSpawner trialSpawner) { + if (!new TrialSpawnerCheck().passes(trialSpawner)) return false; + } + } + + // Spawn egg checks. + if (isSpawnEgg(item)) { + if (!SpawnEggCheck.matches(item)) { + ServerUtils.verbose("Spawn egg match check failed."); + return false; + } + if (!new SpawnEggCheck().passes(item)) { + ServerUtils.verbose("Spawn egg check failed."); + return false; + } + } + + // Name, lore, potion, attribute and enchantment checks. + if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowName && meta.hasDisplayName()) { + ServerUtils.verbose("Custom names not allowed."); + return false; + } + if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowLore && meta.hasLore()) { + ServerUtils.verbose("Custom lore not allowed."); + return false; + } + if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowPotions && + (item.getType().equals(Material.POTION) || + item.getType().equals(Material.SPLASH_POTION) || + item.getType().equals(Material.LINGERING_POTION))) { + ServerUtils.verbose("Potions not allowed."); + return false; + } + if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowAttributes && meta.hasAttributeModifiers()) { + ServerUtils.verbose("Attribute modifiers not allowed."); + return false; + } + if (Sentinel.getInstance().getDirector().io.nbtConfig.globalMaxEnchant != 0 && new EnchantmentCheck().hasIllegalEnchants(item)) { + ServerUtils.verbose("Illegal enchantments found."); + return false; + } + // Recursion check for use-remainder items. + if (meta.hasUseRemainder()) { + if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowRecursion) { + ServerUtils.verbose("Recursion not allowed."); + return false; + } + if (meta.getUseRemainder() != null && !passes(meta.getUseRemainder())) { + ServerUtils.verbose("Use remainder item failed check."); + return false; + } + } + + ServerUtils.verbose("Item passed all checks."); + return true; + } + + public static boolean isSpawnEgg(ItemStack item) { + return item.getType().name().toLowerCase().contains("spawn_egg"); + } +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/SpawnEggCheck.java b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/SpawnEggCheck.java new file mode 100644 index 0000000..79d0ad5 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/SpawnEggCheck.java @@ -0,0 +1,31 @@ +package me.trouper.sentinel.server.functions.itemchecks; + +import me.trouper.sentinel.utils.ServerUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SpawnEggMeta; + +public class SpawnEggCheck extends AbstractCheck { + + @Override + public boolean passes(ItemStack item) { + ServerUtils.verbose("Running spawn egg checks on item: ",item.getType().name()); + if (item.hasItemMeta() && item.getItemMeta() instanceof SpawnEggMeta sem) { + if (sem.getSpawnedEntity() != null && !new EntitySnapshotCheck().passes(sem.getSpawnedEntity())) { + return false; + } + } + return true; + } + + public static boolean matches(ItemStack item) { + if (item.hasItemMeta() && item.getItemMeta() instanceof SpawnEggMeta sem) { + String eggEntityName = item.getType().name().replace("_SPAWN_EGG", ""); + return sem.getSpawnedEntity() != null && + sem.getSpawnedEntity().getEntityType().name().equals(eggEntityName); + } + return false; + } +} diff --git a/src/main/java/me/trouper/sentinel/server/functions/itemchecks/TrialSpawnerCheck.java b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/TrialSpawnerCheck.java new file mode 100644 index 0000000..4b06e98 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/functions/itemchecks/TrialSpawnerCheck.java @@ -0,0 +1,33 @@ +package me.trouper.sentinel.server.functions.itemchecks; + +import me.trouper.sentinel.utils.ServerUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.block.TrialSpawner; +import org.bukkit.block.spawner.SpawnerEntry; +import org.bukkit.entity.Entity; +import org.bukkit.spawner.TrialSpawnerConfiguration; + +public class TrialSpawnerCheck extends AbstractCheck { + + @Override + public boolean passes(TrialSpawner spawner) { + ServerUtils.verbose("Running trial spawner check."); + if (spawner.getNormalConfiguration() != null) { + TrialSpawnerConfiguration config = spawner.getNormalConfiguration(); + if (config.getSpawnedEntity() != null && !new EntitySnapshotCheck().passes(config.getSpawnedEntity())) { + ServerUtils.verbose("Trial Spawner failed check: Normal entity snapshot not allowed."); + return false; + } + } + if (spawner.getOminousConfiguration() != null) { + TrialSpawnerConfiguration config = spawner.getOminousConfiguration(); + if (config.getSpawnedEntity() != null && !new EntitySnapshotCheck().passes(config.getSpawnedEntity())) { + ServerUtils.verbose("Trial Spawner failed check: Ominous entity snapshot not allowed."); + return false; + } + } + return true; + } +} + diff --git a/src/main/java/me/trouper/sentinel/server/gui/Items.java b/src/main/java/me/trouper/sentinel/server/gui/Items.java index ad5b265..0b2019e 100644 --- a/src/main/java/me/trouper/sentinel/server/gui/Items.java +++ b/src/main/java/me/trouper/sentinel/server/gui/Items.java @@ -2,7 +2,6 @@ package me.trouper.sentinel.server.gui; import io.github.itzispyder.pdk.plugin.builders.ItemBuilder; import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.startup.Auth; import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.Text; import org.bukkit.Material; @@ -62,7 +61,8 @@ public class Items { .name(Text.color("&bChat Config")) .lore(Text.color("&8&l➥&7 Spam Filter")) .lore(Text.color("&8&l➥&7 Profanity Filter")) - .lore(Text.color("&8&l➥&7 Regex Filters")) + .lore(Text.color("&8&l➥&7 Unicode Filter")) + .lore(Text.color("&8&l➥&7 URL Filter")) .enchant(Enchantment.PROTECTION,64) .flag(ItemFlag.HIDE_ENCHANTS) .build(); @@ -70,16 +70,18 @@ public class Items { public static final ItemStack ANTI_NUKE_CONFIG = ItemBuilder.create() .material(Material.TNT) .name(Text.color("&cAnti-Nuke Config")) - .lore(Text.color("&8&l➥&7 Command Block Whitelist")) - .lore(Text.color("&8&l➥&7 Command Block editing")) - .lore(Text.color("&8&l➥&7 Command Block placing")) - .lore(Text.color("&8&l➥&7 Command Block using")) - .lore(Text.color("&8&l➥&7 Command Block Minecart placing")) - .lore(Text.color("&8&l➥&7 Command Block Minecart using")) - .lore(Text.color("&8&l➥&7 Creative Hotbar Items")) + .lore(Text.color("&8&l➥&7 Manage all violations")) .enchant(Enchantment.PROTECTION,64) .flag(ItemFlag.HIDE_ENCHANTS) .build(); + + public static final ItemStack WHITELIST = ItemBuilder.create() + .material(Material.TNT) + .name(Text.color("&aCommand Block Whitelist")) + .lore(Text.color("&8&l➥&7 Manage running command blocks")) + .enchant(Enchantment.PROTECTION, 64) + .flag(ItemFlag.HIDE_ENCHANTS) + .build(); public static ItemStack configItem(String valueName, Material material, String description) { ServerUtils.verbose("Items#configItem: Creating a config item:\n Value Name -> %s\nMaterial in use -> %s".formatted(valueName,material.toString())); diff --git a/src/main/java/me/trouper/sentinel/server/gui/MainGUI.java b/src/main/java/me/trouper/sentinel/server/gui/MainGUI.java index 1bafef7..5d7bd5c 100644 --- a/src/main/java/me/trouper/sentinel/server/gui/MainGUI.java +++ b/src/main/java/me/trouper/sentinel/server/gui/MainGUI.java @@ -2,6 +2,8 @@ package me.trouper.sentinel.server.gui; import io.github.itzispyder.pdk.plugin.gui.CustomGui; import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.server.gui.config.ConfigGUI; +import me.trouper.sentinel.server.gui.whitelist.WhitelistGUI; import me.trouper.sentinel.utils.PlayerUtils; import me.trouper.sentinel.utils.Text; import org.bukkit.entity.Player; @@ -21,10 +23,15 @@ public class MainGUI { .size(27) .onDefine(this::blankPage) .defineMain(this::mainClick) - .define(12,Items.CREDITS) - .define(14,Items.CONFIG,this::openConfig) + .define(11,Items.CREDITS) + .define(13,Items.WHITELIST,this::openWhitelist) + .define(15,Items.CONFIG,this::openConfig) .build(); + private void openWhitelist(InventoryClickEvent e) { + e.getWhoClicked().openInventory(new WhitelistGUI().createGUI((Player) e.getWhoClicked()).getInventory()); + } + private void openConfig(InventoryClickEvent e) { e.getWhoClicked().openInventory(new ConfigGUI().home.getInventory()); } @@ -42,7 +49,7 @@ public class MainGUI { public static boolean verify(Player p) { if (PlayerUtils.isTrusted(p)) return true; - Sentinel.log.info("WARNING: %s has just attempted to use the GUI without authorization. This has been prevented by Sentinel, as we are NOT Vulcan AntiCheat."); + Sentinel.getInstance().getLogger().info("WARNING: %s has just attempted to use the GUI without authorization. This has been prevented by Sentinel, as we are NOT Vulcan AntiCheat."); p.closeInventory(); return false; } diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/AntiNukeGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/AntiNukeGUI.java index 9182050..9190dd3 100644 --- a/src/main/java/me/trouper/sentinel/server/gui/config/AntiNukeGUI.java +++ b/src/main/java/me/trouper/sentinel/server/gui/config/AntiNukeGUI.java @@ -2,11 +2,27 @@ package me.trouper.sentinel.server.gui.config; import io.github.itzispyder.pdk.plugin.builders.ItemBuilder; import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import me.trouper.sentinel.server.gui.ConfigGUI; +import me.trouper.sentinel.server.events.violations.blocks.command.CommandBlockBreak; +import me.trouper.sentinel.server.events.violations.blocks.command.CommandBlockEdit; +import me.trouper.sentinel.server.events.violations.blocks.command.CommandBlockPlace; +import me.trouper.sentinel.server.events.violations.blocks.command.CommandBlockUse; +import me.trouper.sentinel.server.events.violations.blocks.jigsaw.JigsawBlockBreak; +import me.trouper.sentinel.server.events.violations.blocks.jigsaw.JigsawBlockPlace; +import me.trouper.sentinel.server.events.violations.blocks.jigsaw.JigsawBlockUse; +import me.trouper.sentinel.server.events.violations.blocks.structure.StructureBlockBreak; +import me.trouper.sentinel.server.events.violations.blocks.structure.StructureBlockPlace; +import me.trouper.sentinel.server.events.violations.blocks.structure.StructureBlockUse; +import me.trouper.sentinel.server.events.violations.command.DangerousCommand; +import me.trouper.sentinel.server.events.violations.command.LoggedCommand; +import me.trouper.sentinel.server.events.violations.command.SpecificCommand; +import me.trouper.sentinel.server.events.violations.entities.CommandMinecartBreak; +import me.trouper.sentinel.server.events.violations.entities.CommandMinecartPlace; +import me.trouper.sentinel.server.events.violations.entities.CommandMinecartUse; +import me.trouper.sentinel.server.events.violations.players.CreativeHotbar; +import me.trouper.sentinel.server.events.violations.whitelist.CommandBlockExecute; import me.trouper.sentinel.server.gui.Items; import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.nuke.CommandGUI; -import me.trouper.sentinel.server.gui.config.nuke.checks.*; +import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.Text; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -17,36 +33,48 @@ import org.bukkit.inventory.ItemStack; public class AntiNukeGUI { public final CustomGui home = CustomGui.create() .title(Text.color("&6&lSentinel &8»&0 Choose a check")) - .size(54) + .size(9*5) .onDefine(this::blankPage) .defineMain(this::mainClick) - .define(53, Items.BACK, e->{ + .define((9*5)-1, Items.BACK, e->{ e.getWhoClicked().openInventory(new ConfigGUI().home.getInventory()); }) - .define(10,COMMAND_BLOCK_WHITELIST, e->{ - e.getWhoClicked().openInventory(new CBExecuteGUI().home.getInventory()); - }) - .define(12,COMMAND_BLOCK_PLACE, e->{ - e.getWhoClicked().openInventory(new CBPlaceGUI().home.getInventory()); - }) - .define(14,COMMAND_BLOCK_USE, e->{ - e.getWhoClicked().openInventory(new CBUseGUI().home.getInventory()); - }) - .define(16,COMMAND_BLOCK_EDITING, e->{ - e.getWhoClicked().openInventory(new CBEditGUI().home.getInventory()); - }) - .define(37,COMMAND_BLOCK_MINECART_USE, e->{ - e.getWhoClicked().openInventory(new CBMCUseGUI().home.getInventory()); - }) - .define(39,COMMAND_BLOCK_MINECART_PLACE, e->{ - e.getWhoClicked().openInventory(new CBMCPlaceGUI().home.getInventory()); - }) - .define(41,COMMAND_EXECUTE, e->{ - e.getWhoClicked().openInventory(new CommandGUI().home.getInventory()); - }) - .define(43,HOTBAR_ACTION, e->{ - e.getWhoClicked().openInventory(new HotbarActionGUI().home.getInventory()); - }) + .define(10,getCheckItem(Material.COMMAND_BLOCK,"Command Block Break"), + e->e.getWhoClicked().openInventory(new CommandBlockBreak().getConfigGui().getInventory())) + .define(11,getCheckItem(Material.REPEATING_COMMAND_BLOCK,"Command Block Edit"), + e->e.getWhoClicked().openInventory(new CommandBlockEdit().getConfigGui().getInventory())) + .define(12,getCheckItem(Material.CHAIN_COMMAND_BLOCK,"Command Block Place"), + e->e.getWhoClicked().openInventory(new CommandBlockPlace().getConfigGui().getInventory())) + .define(13,getCheckItem(Material.CHAIN_COMMAND_BLOCK,"Command Block Use"), + e->e.getWhoClicked().openInventory(new CommandBlockUse().getConfigGui().getInventory())) + .define(14,getCheckItem(Material.JIGSAW,"Jigsaw Block Break"), + e->e.getWhoClicked().openInventory(new JigsawBlockBreak().getConfigGui().getInventory())) + .define(15,getCheckItem(Material.JIGSAW,"Jigsaw Block Place"), + e->e.getWhoClicked().openInventory(new JigsawBlockPlace().getConfigGui().getInventory())) + .define(16,getCheckItem(Material.JIGSAW,"Jigsaw Block Use"), + e->e.getWhoClicked().openInventory(new JigsawBlockUse().getConfigGui().getInventory())) + .define(19,getCheckItem(Material.STRUCTURE_BLOCK,"Structure Block Break"), + e->e.getWhoClicked().openInventory(new StructureBlockBreak().getConfigGui().getInventory())) + .define(20,getCheckItem(Material.STRUCTURE_BLOCK,"Structure Block Place"), + e->e.getWhoClicked().openInventory(new StructureBlockPlace().getConfigGui().getInventory())) + .define(21,getCheckItem(Material.STRUCTURE_BLOCK,"Structure Block Use"), + e->e.getWhoClicked().openInventory(new StructureBlockUse().getConfigGui().getInventory())) + .define(22,getCheckItem(Material.TNT,"Dangerous Commands"), + e->e.getWhoClicked().openInventory(new DangerousCommand().getConfigGui().getInventory())) + .define(23,getCheckItem(Material.ENDER_PEARL,"Specific Commands"), + e->e.getWhoClicked().openInventory(new SpecificCommand().getConfigGui().getInventory())) + .define(24,getCheckItem(Material.SPYGLASS,"Logged Commands"), + e->e.getWhoClicked().openInventory(new LoggedCommand().getConfigGui().getInventory())) + .define(25,getCheckItem(Material.TNT_MINECART,"Command Minecart Break"), + e->e.getWhoClicked().openInventory(new CommandMinecartBreak().getConfigGui().getInventory())) + .define(29,getCheckItem(Material.COMMAND_BLOCK_MINECART,"Command Minecart Place"), + e->e.getWhoClicked().openInventory(new CommandMinecartPlace().getConfigGui().getInventory())) + .define(30,getCheckItem(Material.COMMAND_BLOCK_MINECART,"Command Minecart Use"), + e->e.getWhoClicked().openInventory(new CommandMinecartUse().getConfigGui().getInventory())) + .define(32,getCheckItem(Material.DIAMOND_SWORD,"NBT Item Pull"), + e->e.getWhoClicked().openInventory(new CreativeHotbar().getConfigGui().getInventory())) + .define(33,getCheckItem(Material.EMERALD,"Command Block Whitelist"), + e->e.getWhoClicked().openInventory(new CommandBlockExecute().getConfigGui().getInventory())) .build(); private void mainClick(InventoryClickEvent e) { @@ -55,59 +83,17 @@ public class AntiNukeGUI { } private void blankPage(Inventory inv) { + ServerUtils.verbose("Making anti-nuke page"); for (int i = 0; i < inv.getSize(); i++) { inv.setItem(i,Items.BLANK); } } - private static final ItemStack COMMAND_BLOCK_EDITING = ItemBuilder.create() - .material(Material.DEBUG_STICK) - .name(Text.color("&bCommand Block Editing")) - .lore(Text.color("&8&l➥&7 Modify this check")) - .build(); - - private static final ItemStack COMMAND_BLOCK_WHITELIST = ItemBuilder.create() - .material(Material.EMERALD) - .name(Text.color("&bCommand Block Whitelist")) - .lore(Text.color("&8&l➥&7 Modify this check")) - .build(); - - private static final ItemStack COMMAND_BLOCK_MINECART_PLACE = ItemBuilder.create() - .material(Material.RAIL) - .name(Text.color("&bCommand Block Minecart Placing")) - .lore(Text.color("&8&l➥&7 Modify this check")) - .build(); - - private static final ItemStack COMMAND_BLOCK_MINECART_USE = ItemBuilder.create() - .material(Material.COMMAND_BLOCK_MINECART) - .name(Text.color("&bCommand Block Minecart Using")) - .lore(Text.color("&8&l➥&7 Modify this check")) - .build(); - - private static final ItemStack COMMAND_BLOCK_PLACE = ItemBuilder.create() - .material(Material.CHAIN_COMMAND_BLOCK) - .name(Text.color("&bCommand Block Placing")) - .lore(Text.color("&8&l➥&7 Modify this check")) - .build(); - - private static final ItemStack COMMAND_BLOCK_USE = ItemBuilder.create() - .material(Material.REPEATING_COMMAND_BLOCK) - .name(Text.color("&bCommand Block Using")) - .lore(Text.color("&8&l➥&7 Modify this check")) - .build(); - - private static final ItemStack COMMAND_EXECUTE = ItemBuilder.create() - .material(Material.SPYGLASS) - .name(Text.color("&bCommand Execution")) - .lore(Text.color("&8&l➥&7 Dangerous Commands")) - .lore(Text.color("&8&l➥&7 Logged Commands")) - .lore(Text.color("&8&l➥&7 Specific Commands")) - .build(); - - private static final ItemStack HOTBAR_ACTION = ItemBuilder.create() - .material(Material.DIAMOND_SWORD) - .name(Text.color("&bNBT Items")) - .lore(Text.color("&8&l➥&7 Modify this check")) - .build(); - + private static ItemStack getCheckItem(Material item, String name) { + return ItemBuilder.create() + .material(item) + .name(Text.color("&b" + name)) + .lore(Text.color("&8&l➥&7 Modify this check")) + .build(); + } } diff --git a/src/main/java/me/trouper/sentinel/server/gui/ConfigGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/ConfigGUI.java similarity index 82% rename from src/main/java/me/trouper/sentinel/server/gui/ConfigGUI.java rename to src/main/java/me/trouper/sentinel/server/gui/config/ConfigGUI.java index e2ea3ba..476ff43 100644 --- a/src/main/java/me/trouper/sentinel/server/gui/ConfigGUI.java +++ b/src/main/java/me/trouper/sentinel/server/gui/config/ConfigGUI.java @@ -1,8 +1,9 @@ -package me.trouper.sentinel.server.gui; +package me.trouper.sentinel.server.gui.config; import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import me.trouper.sentinel.server.gui.config.AntiNukeGUI; -import me.trouper.sentinel.server.gui.config.ChatGUI; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.server.gui.MainGUI; +import me.trouper.sentinel.server.gui.config.chat.ChatGUI; import me.trouper.sentinel.utils.Text; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; @@ -14,7 +15,7 @@ public class ConfigGUI { .size(27) .onDefine(this::blankPage) .defineMain(this::mainClick) - .define(12, Items.ANTI_NUKE_CONFIG,e->{ + .define(12, Items.ANTI_NUKE_CONFIG, e->{ e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); }) .define(14,Items.CHAT_CONFIG,e->{ diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/ChatGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/chat/ChatGUI.java similarity index 88% rename from src/main/java/me/trouper/sentinel/server/gui/config/ChatGUI.java rename to src/main/java/me/trouper/sentinel/server/gui/config/chat/ChatGUI.java index e745281..36dee5a 100644 --- a/src/main/java/me/trouper/sentinel/server/gui/config/ChatGUI.java +++ b/src/main/java/me/trouper/sentinel/server/gui/config/chat/ChatGUI.java @@ -1,14 +1,10 @@ -package me.trouper.sentinel.server.gui.config; +package me.trouper.sentinel.server.gui.config.chat; import io.github.itzispyder.pdk.plugin.builders.ItemBuilder; import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import me.trouper.sentinel.server.gui.ConfigGUI; import me.trouper.sentinel.server.gui.Items; import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.chat.ProfanityFilterGUI; -import me.trouper.sentinel.server.gui.config.chat.SpamFilterGUI; -import me.trouper.sentinel.server.gui.config.chat.UnicodeFilterGUI; -import me.trouper.sentinel.server.gui.config.chat.UrlFilterGUI; +import me.trouper.sentinel.server.gui.config.ConfigGUI; import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.Text; import org.bukkit.Material; diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/chat/ProfanityFilterGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/chat/ProfanityFilterGUI.java index e82f10f..d3c8e8a 100644 --- a/src/main/java/me/trouper/sentinel/server/gui/config/chat/ProfanityFilterGUI.java +++ b/src/main/java/me/trouper/sentinel/server/gui/config/chat/ProfanityFilterGUI.java @@ -8,7 +8,6 @@ import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.data.config.MainConfig; import me.trouper.sentinel.server.gui.Items; import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.ChatGUI; import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.Text; import net.kyori.adventure.text.Component; @@ -17,7 +16,6 @@ import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; @@ -44,7 +42,7 @@ public class ProfanityFilterGUI { } ServerUtils.verbose("ProfanityFilterGUI#blankPage Page now blank"); ItemStack top = Items.RED; - if (Sentinel.mainConfig.chat.profanityFilter.enabled) { + if (Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.enabled) { top = Items.GREEN; } @@ -54,18 +52,18 @@ public class ProfanityFilterGUI { ServerUtils.verbose("ProfanityFilterGUI#blankPage Adding GUI Items"); inv.setItem(53,Items.BACK); - inv.setItem(3,Items.booleanItem(Sentinel.mainConfig.chat.profanityFilter.enabled, Items.configItem("Profanity Filter Toggle",Material.CLOCK,"Enable or Disable the whole Profanity filter"))); - inv.setItem(5,Items.booleanItem(Sentinel.mainConfig.chat.profanityFilter.silent, Items.configItem("Silent Mode",Material.FEATHER,"Whether to notify players that their messages \nwere blocked. Enabling could help deter bypassing."))); - inv.setItem(10,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.lowScore, Items.configItem("Low Score Gain", Material.WHITE_WOOL, "How much score will be added if the player \ndid not attempt to bypass the filter."))); - inv.setItem(19,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.mediumLowScore, Items.configItem("Medium-Low Score Gain", Material.LIME_WOOL, "How much score will be added if the player \nused l33t speak to attempt a bypass"))); - inv.setItem(28,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.mediumScore, Items.configItem("Medium Score Gain", Material.YELLOW_WOOL, "How much score will be added if the player \nused sp/ecia|l characters to attempt a bypass"))); - inv.setItem(37,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.mediumHighScore, Items.configItem("Medium-High Score Gain", Material.ORANGE_WOOL, "How much score will be added if the player \nused reeeeeeepeating letters to attempt a bypass"))); - inv.setItem(46,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.highScore, Items.configItem("High Score Gain", Material.RED_WOOL, "How much score will be added if the player \nused pun. ctua, tion or spaces to attempt a bypass"))); - inv.setItem(29,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.regexScore, Items.configItem("Regex Score Gain", Material.DISPENSER, "How much score will be added if the player \nmatched the regex setting throughout \nthe processing of the message"))); - inv.setItem(22,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.punishScore, Items.configItem("Punish Score", Material.IRON_BARS, "If the player's score is above this \nthe punishment commands will be ran."))); - inv.setItem(33,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.scoreDecay, Items.configItem("Score Decay", Material.DEAD_BUBBLE_CORAL_BLOCK, "How much score players will loose each minute."))); - inv.setItem(31,Items.stringListItem(Sentinel.mainConfig.chat.profanityFilter.profanityPunishCommands,Material.WOODEN_AXE, "Default Punishment Commands", "%player% will be replaced with the offender's name")); - inv.setItem(40,Items.stringListItem(Sentinel.mainConfig.chat.profanityFilter.strictPunishCommands,Material.DIAMOND_AXE, "Strict Punishment Commands", "If words from the strict words list are flagged, \nthis list will be ran instead \n%player% will be replaced with the offender's name")); + inv.setItem(3,Items.booleanItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.enabled, Items.configItem("Profanity Filter Toggle",Material.CLOCK,"Enable or Disable the whole Profanity filter"))); + inv.setItem(5,Items.booleanItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.silent, Items.configItem("Silent Mode",Material.FEATHER,"Whether to notify players that their messages \nwere blocked. Enabling could help deter bypassing."))); + inv.setItem(10,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.lowScore, Items.configItem("Low Score Gain", Material.WHITE_WOOL, "How much score will be added if the player \ndid not attempt to bypass the filter."))); + inv.setItem(19,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.mediumLowScore, Items.configItem("Medium-Low Score Gain", Material.LIME_WOOL, "How much score will be added if the player \nused l33t speak to attempt a bypass"))); + inv.setItem(28,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.mediumScore, Items.configItem("Medium Score Gain", Material.YELLOW_WOOL, "How much score will be added if the player \nused sp/ecia|l characters to attempt a bypass"))); + inv.setItem(37,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.mediumHighScore, Items.configItem("Medium-High Score Gain", Material.ORANGE_WOOL, "How much score will be added if the player \nused reeeeeeepeating letters to attempt a bypass"))); + inv.setItem(46,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.highScore, Items.configItem("High Score Gain", Material.RED_WOOL, "How much score will be added if the player \nused pun. ctua, tion or spaces to attempt a bypass"))); + inv.setItem(29,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.regexScore, Items.configItem("Regex Score Gain", Material.DISPENSER, "How much score will be added if the player \nmatched the regex setting throughout \nthe processing of the message"))); + inv.setItem(22,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.punishScore, Items.configItem("Punish Score", Material.IRON_BARS, "If the player's score is above this \nthe punishment commands will be ran."))); + inv.setItem(33,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.scoreDecay, Items.configItem("Score Decay", Material.DEAD_BUBBLE_CORAL_BLOCK, "How much score players will loose each minute."))); + inv.setItem(31,Items.stringListItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.profanityPunishCommands,Material.WOODEN_AXE, "Default Punishment Commands", "%player% will be replaced with the offender's name")); + inv.setItem(40,Items.stringListItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.strictPunishCommands,Material.DIAMOND_AXE, "Strict Punishment Commands", "If words from the strict words list are flagged, \nthis list will be ran instead \n%player% will be replaced with the offender's name")); } catch (Exception e) { e.printStackTrace(); } @@ -76,35 +74,35 @@ public class ProfanityFilterGUI { if (!MainGUI.verify((Player) e.getWhoClicked())) return; switch (e.getSlot()) { case 3 -> { - Sentinel.mainConfig.chat.profanityFilter.enabled = !Sentinel.mainConfig.chat.profanityFilter.enabled; - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.enabled = !Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.enabled; + Sentinel.getInstance().getDirector().io.mainConfig.save(); blankPage(e.getInventory()); } case 5 -> { - Sentinel.mainConfig.chat.profanityFilter.silent = !Sentinel.mainConfig.chat.profanityFilter.silent; - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.silent = !Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.silent; + Sentinel.getInstance().getDirector().io.mainConfig.save(); blankPage(e.getInventory()); } - case 10 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.lowScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.lowScore); - case 19 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.mediumLowScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.mediumLowScore); - case 28 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.mediumScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.mediumScore); - case 37 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.mediumHighScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.mediumHighScore); - case 46 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.highScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.highScore); - case 29 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.regexScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.regexScore); - case 22 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.punishScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.punishScore); - case 33 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.scoreDecay = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.scoreDecay); + case 10 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.lowScore = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.lowScore); + case 19 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.mediumLowScore = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.mediumLowScore); + case 28 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.mediumScore = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.mediumScore); + case 37 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.mediumHighScore = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.mediumHighScore); + case 46 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.highScore = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.highScore); + case 29 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.regexScore = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.regexScore); + case 22 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.punishScore = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.punishScore); + case 33 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.scoreDecay = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.scoreDecay); case 31 -> { if (e.isLeftClick()) { queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> { cfg.chat.profanityFilter.profanityPunishCommands.add(args.getAll().toString()); - },"" + Sentinel.mainConfig.chat.profanityFilter.profanityPunishCommands); + },"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.profanityPunishCommands); return; } - Sentinel.mainConfig.chat.profanityFilter.profanityPunishCommands.clear(); - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.profanityPunishCommands.clear(); + Sentinel.getInstance().getDirector().io.mainConfig.save(); blankPage(e.getInventory()); } @@ -112,17 +110,17 @@ public class ProfanityFilterGUI { if (e.isLeftClick()) { queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> { cfg.chat.profanityFilter.strictPunishCommands.add(args.getAll().toString()); - },"" + Sentinel.mainConfig.chat.profanityFilter.strictPunishCommands); + },"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.strictPunishCommands); return; } - Sentinel.mainConfig.chat.profanityFilter.strictPunishCommands.clear(); - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.profanityFilter.strictPunishCommands.clear(); + Sentinel.getInstance().getDirector().io.mainConfig.save(); blankPage(e.getInventory()); } } } - public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.mainConfig); + public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.getInstance().getDirector().io.mainConfig); private void queuePlayer(Player player, BiConsumer action, String currentValue) { MainGUI.awaitingCallback.add(player.getUniqueId()); diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/chat/SpamFilterGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/chat/SpamFilterGUI.java index bcccd27..30ef5f4 100644 --- a/src/main/java/me/trouper/sentinel/server/gui/config/chat/SpamFilterGUI.java +++ b/src/main/java/me/trouper/sentinel/server/gui/config/chat/SpamFilterGUI.java @@ -8,8 +8,6 @@ import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.data.config.MainConfig; import me.trouper.sentinel.server.gui.Items; import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.ChatGUI; -import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.Text; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; @@ -17,7 +15,6 @@ import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; @@ -43,7 +40,7 @@ public class SpamFilterGUI { } ItemStack top = Items.RED; - if (Sentinel.mainConfig.chat.spamFilter.enabled) { + if (Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.enabled) { top = Items.GREEN; } @@ -52,18 +49,18 @@ public class SpamFilterGUI { } inv.setItem(53,Items.BACK); - inv.setItem(3,Items.booleanItem(Sentinel.mainConfig.chat.spamFilter.enabled, Items.configItem("Spam Filter Toggle", Material.CLOCK, "Enable or disable the whole Spam Filter"))); - inv.setItem(5,Items.booleanItem(Sentinel.mainConfig.chat.spamFilter.silent, Items.configItem("Silent Toggle", Material.FEATHER, "Whether to notify players that their messages \nwere blocked. Enabling could help deter bypassing."))); - inv.setItem(10,Items.intItem(Sentinel.mainConfig.chat.spamFilter.defaultGain, Items.configItem("Default Heat Gain", Material.BUCKET, "How much heat will be added to each message."))); - inv.setItem(19,Items.intItem(Sentinel.mainConfig.chat.spamFilter.lowGain, Items.configItem("Low Heat Gain", Material.WATER_BUCKET, "Extra heat to be added if the \nmessage is greater than 25% similar \nto their previous message."))); - inv.setItem(28,Items.intItem(Sentinel.mainConfig.chat.spamFilter.mediumGain, Items.configItem("Medium Heat Gain", Material.COD_BUCKET, "Extra heat to be added if the \nmessage is greater than 50% similar \nto their previous message."))); - inv.setItem(37,Items.intItem(Sentinel.mainConfig.chat.spamFilter.highGain, Items.configItem("High Heat Gain", Material.PUFFERFISH_BUCKET, "Extra heat to be added if the \nmessage is greater than 90% similar \nto their previous message."))); - inv.setItem(46,Items.intItem(Sentinel.mainConfig.chat.spamFilter.blockHeat, Items.configItem("Block Heat", Material.BARRIER, "If the player's heat is above this \nthen their message will be blocked and \nflagged as spam."))); - inv.setItem(21,Items.intItem(Sentinel.mainConfig.chat.spamFilter.blockSimilarity, Items.configItem("Block Similarity", Material.BARRIER, "If the message's similarity is above \nthis, it will get automatically blocked \nand flagged as spam."))); - inv.setItem(23,Items.intItem(Sentinel.mainConfig.chat.spamFilter.punishHeat, Items.configItem("Punish Heat", Material.IRON_BARS, "If the player's heat is above this \nthe punishment commands will be ran."))); - inv.setItem(25,Items.intItem(Sentinel.mainConfig.chat.spamFilter.heatDecay, Items.configItem("Heat Decay", Material.DEAD_BUBBLE_CORAL_BLOCK, "How much heat players will loose each second."))); - inv.setItem(32,Items.stringListItem(Sentinel.mainConfig.chat.spamFilter.punishCommands,Material.DIAMOND_AXE, "Punishment Commands", "%player% will be replaced with the offender's name")); - inv.setItem(34,Items.stringListItem(Sentinel.mainConfig.chat.spamFilter.whitelist,Material.PAPER, "Message Whitelist", "Messages which will be ignored by the spam filter")); + inv.setItem(3,Items.booleanItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.enabled, Items.configItem("Spam Filter Toggle", Material.CLOCK, "Enable or disable the whole Spam Filter"))); + inv.setItem(5,Items.booleanItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.silent, Items.configItem("Silent Toggle", Material.FEATHER, "Whether to notify players that their messages \nwere blocked. Enabling could help deter bypassing."))); + inv.setItem(10,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.defaultGain, Items.configItem("Default Heat Gain", Material.BUCKET, "How much heat will be added to each message."))); + inv.setItem(19,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.lowGain, Items.configItem("Low Heat Gain", Material.WATER_BUCKET, "Extra heat to be added if the \nmessage is greater than 25% similar \nto their previous message."))); + inv.setItem(28,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.mediumGain, Items.configItem("Medium Heat Gain", Material.COD_BUCKET, "Extra heat to be added if the \nmessage is greater than 50% similar \nto their previous message."))); + inv.setItem(37,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.highGain, Items.configItem("High Heat Gain", Material.PUFFERFISH_BUCKET, "Extra heat to be added if the \nmessage is greater than 90% similar \nto their previous message."))); + inv.setItem(46,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.blockHeat, Items.configItem("Block Heat", Material.BARRIER, "If the player's heat is above this \nthen their message will be blocked and \nflagged as spam."))); + inv.setItem(21,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.blockSimilarity, Items.configItem("Block Similarity", Material.BARRIER, "If the message's similarity is above \nthis, it will get automatically blocked \nand flagged as spam."))); + inv.setItem(23,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.punishHeat, Items.configItem("Punish Heat", Material.IRON_BARS, "If the player's heat is above this \nthe punishment commands will be ran."))); + inv.setItem(25,Items.intItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.heatDecay, Items.configItem("Heat Decay", Material.DEAD_BUBBLE_CORAL_BLOCK, "How much heat players will loose each second."))); + inv.setItem(32,Items.stringListItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.punishCommands,Material.DIAMOND_AXE, "Punishment Commands", "%player% will be replaced with the offender's name")); + inv.setItem(34,Items.stringListItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.whitelist,Material.PAPER, "Message Whitelist", "Messages which will be ignored by the spam filter")); } private void mainClick(InventoryClickEvent e) { @@ -73,40 +70,40 @@ public class SpamFilterGUI { switch (e.getSlot()) { case 3 -> { - Sentinel.mainConfig.chat.spamFilter.enabled = !Sentinel.mainConfig.chat.spamFilter.enabled; - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.enabled = !Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.enabled; + Sentinel.getInstance().getDirector().io.mainConfig.save(); blankPage(e.getInventory()); } case 5 -> { - Sentinel.mainConfig.chat.spamFilter.silent = !Sentinel.mainConfig.chat.spamFilter.silent; - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.silent = !Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.silent; + Sentinel.getInstance().getDirector().io.mainConfig.save(); blankPage(e.getInventory()); } - case 10 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.defaultGain = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.spamFilter.defaultGain); - case 19 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.lowGain = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.spamFilter.lowGain); - case 28 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.mediumGain = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.spamFilter.mediumGain); - case 37 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.highGain = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.spamFilter.highGain); - case 46 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.blockHeat = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.spamFilter.blockHeat); - case 21 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.blockSimilarity = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.spamFilter.blockSimilarity); - case 23 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.punishHeat = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.spamFilter.punishHeat); - case 25 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.heatDecay = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.spamFilter.heatDecay); + case 10 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.defaultGain = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.defaultGain); + case 19 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.lowGain = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.lowGain); + case 28 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.mediumGain = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.mediumGain); + case 37 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.highGain = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.highGain); + case 46 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.blockHeat = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.blockHeat); + case 21 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.blockSimilarity = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.blockSimilarity); + case 23 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.punishHeat = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.punishHeat); + case 25 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.heatDecay = args.getAll().toInt(),"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.heatDecay); case 32 -> { if (e.isLeftClick()) { queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> { cfg.chat.spamFilter.punishCommands.add(args.getAll().toString()); - },"" + Sentinel.mainConfig.chat.spamFilter.punishCommands); + },"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.punishCommands); return; } - Sentinel.mainConfig.chat.spamFilter.punishCommands.clear(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.spamFilter.punishCommands.clear(); blankPage(e.getInventory()); - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.save(); } } } - public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.mainConfig); + public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.getInstance().getDirector().io.mainConfig); private void queuePlayer(Player player, BiConsumer action, String currentValue) { MainGUI.awaitingCallback.add(player.getUniqueId()); diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/chat/UnicodeFilterGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/chat/UnicodeFilterGUI.java index 75c56b1..601d0b9 100644 --- a/src/main/java/me/trouper/sentinel/server/gui/config/chat/UnicodeFilterGUI.java +++ b/src/main/java/me/trouper/sentinel/server/gui/config/chat/UnicodeFilterGUI.java @@ -8,7 +8,6 @@ import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.data.config.MainConfig; import me.trouper.sentinel.server.gui.Items; import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.ChatGUI; import me.trouper.sentinel.utils.ServerUtils; import me.trouper.sentinel.utils.Text; import net.kyori.adventure.text.Component; @@ -41,7 +40,7 @@ public class UnicodeFilterGUI { } ServerUtils.verbose("Unicode Filter GUI blank!"); ItemStack top = Items.RED; - if (Sentinel.mainConfig.chat.unicodeFilter.enabled) { + if (Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.enabled) { top = Items.GREEN; } @@ -49,11 +48,11 @@ public class UnicodeFilterGUI { inv.setItem(i,top); } - inv.setItem(3,Items.booleanItem(Sentinel.mainConfig.chat.unicodeFilter.enabled, Items.configItem("Unicode Filter Toggle", Material.CLOCK,"Enable or Disable the whole Unicode filter"))); - inv.setItem(5,Items.booleanItem(Sentinel.mainConfig.chat.unicodeFilter.silent, Items.configItem("Silent Mode",Material.FEATHER,"Whether to notify players that their messages \nwere blocked. Enabling could help deter bypassing."))); - inv.setItem(20,Items.booleanItem(Sentinel.mainConfig.chat.unicodeFilter.punished,Items.configItem("Punished",Material.IRON_BARS,"Toggles execution of punishment commands."))); - inv.setItem(22,Items.stringItem(Sentinel.mainConfig.chat.unicodeFilter.regex,Items.configItem("Allowed Char Regex",Material.DISPENSER,"Toggles execution of punishment commands."))); - inv.setItem(24,Items.stringListItem(Sentinel.mainConfig.chat.unicodeFilter.punishCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands which will be executed if punishment is enabled.")); + inv.setItem(3,Items.booleanItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.enabled, Items.configItem("Unicode Filter Toggle", Material.CLOCK,"Enable or Disable the whole Unicode filter"))); + inv.setItem(5,Items.booleanItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.silent, Items.configItem("Silent Mode",Material.FEATHER,"Whether to notify players that their messages \nwere blocked. Enabling could help deter bypassing."))); + inv.setItem(20,Items.booleanItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.punished,Items.configItem("Punished",Material.IRON_BARS,"Toggles execution of punishment commands."))); + inv.setItem(22,Items.stringItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.regex,Items.configItem("Allowed Char Regex",Material.DISPENSER,"Toggles execution of punishment commands."))); + inv.setItem(24,Items.stringListItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.punishCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands which will be executed if punishment is enabled.")); } @@ -63,41 +62,41 @@ public class UnicodeFilterGUI { switch (e.getSlot()) { case 3 -> { - Sentinel.mainConfig.chat.unicodeFilter.enabled = !Sentinel.mainConfig.chat.unicodeFilter.enabled; - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.enabled = !Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.enabled; + Sentinel.getInstance().getDirector().io.mainConfig.save(); blankPage(e.getInventory()); } case 5 -> { - Sentinel.mainConfig.chat.unicodeFilter.silent = !Sentinel.mainConfig.chat.unicodeFilter.silent; - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.silent = !Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.silent; + Sentinel.getInstance().getDirector().io.mainConfig.save(); blankPage(e.getInventory()); } case 20 -> { - Sentinel.mainConfig.chat.unicodeFilter.punished = !Sentinel.mainConfig.chat.unicodeFilter.punished; - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.punished = !Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.punished; + Sentinel.getInstance().getDirector().io.mainConfig.save(); blankPage(e.getInventory()); } - case 22 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.unicodeFilter.regex = args.getAll().toString(),Sentinel.mainConfig.chat.unicodeFilter.regex); + case 22 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.unicodeFilter.regex = args.getAll().toString(),Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.regex); case 24 -> { if (e.isLeftClick()) { queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> { cfg.chat.unicodeFilter.punishCommands.add(args.getAll().toString()); - },"" + Sentinel.mainConfig.chat.unicodeFilter.punishCommands); + },"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.punishCommands); return; } - Sentinel.mainConfig.chat.unicodeFilter.punishCommands.clear(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.unicodeFilter.punishCommands.clear(); blankPage(e.getInventory()); - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.save(); } } } - public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.mainConfig); + public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.getInstance().getDirector().io.mainConfig); private void queuePlayer(Player player, BiConsumer action, String currentValue) { MainGUI.awaitingCallback.add(player.getUniqueId()); diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/chat/UrlFilterGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/chat/UrlFilterGUI.java index aa8989e..ec00b9b 100644 --- a/src/main/java/me/trouper/sentinel/server/gui/config/chat/UrlFilterGUI.java +++ b/src/main/java/me/trouper/sentinel/server/gui/config/chat/UrlFilterGUI.java @@ -9,7 +9,6 @@ import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.data.config.MainConfig; import me.trouper.sentinel.server.gui.Items; import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.ChatGUI; import me.trouper.sentinel.utils.Text; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; @@ -40,7 +39,7 @@ public class UrlFilterGUI implements CustomListener { } ItemStack top = Items.RED; - if (Sentinel.mainConfig.chat.urlFilter.enabled) { + if (Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.enabled) { top = Items.GREEN; } @@ -48,12 +47,12 @@ public class UrlFilterGUI implements CustomListener { inv.setItem(i,top); } - inv.setItem(3,Items.booleanItem(Sentinel.mainConfig.chat.urlFilter.enabled, Items.configItem("Unicode Filter Toggle", Material.CLOCK,"Enable or Disable the whole Unicode filter"))); - inv.setItem(5,Items.booleanItem(Sentinel.mainConfig.chat.urlFilter.silent, Items.configItem("Silent Mode",Material.FEATHER,"Whether to notify players that their messages \nwere blocked. Enabling could help deter bypassing."))); - inv.setItem(19,Items.booleanItem(Sentinel.mainConfig.chat.urlFilter.punished,Items.configItem("Punished",Material.IRON_BARS,"Toggles execution of punishment commands."))); - inv.setItem(21,Items.stringItem(Sentinel.mainConfig.chat.urlFilter.regex,Items.configItem("Allowed Char Regex",Material.DISPENSER,"Toggles execution of punishment commands."))); - inv.setItem(23,Items.stringListItem(Sentinel.mainConfig.chat.urlFilter.punishCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands which will be executed if punishment is enabled.")); - inv.setItem(25,Items.stringListItem(Sentinel.mainConfig.chat.urlFilter.whitelist,Material.PAPER,"Whitelist","URLs which will not flag the filter.")); + inv.setItem(3,Items.booleanItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.enabled, Items.configItem("Unicode Filter Toggle", Material.CLOCK,"Enable or Disable the whole Unicode filter"))); + inv.setItem(5,Items.booleanItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.silent, Items.configItem("Silent Mode",Material.FEATHER,"Whether to notify players that their messages \nwere blocked. Enabling could help deter bypassing."))); + inv.setItem(19,Items.booleanItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.punished,Items.configItem("Punished",Material.IRON_BARS,"Toggles execution of punishment commands."))); + inv.setItem(21,Items.stringItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.regex,Items.configItem("Allowed Char Regex",Material.DISPENSER,"Toggles execution of punishment commands."))); + inv.setItem(23,Items.stringListItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.punishCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands which will be executed if punishment is enabled.")); + inv.setItem(25,Items.stringListItem(Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.whitelist,Material.PAPER,"Whitelist","URLs which will not flag the filter.")); } @@ -63,53 +62,53 @@ public class UrlFilterGUI implements CustomListener { switch (e.getSlot()) { case 3 -> { - Sentinel.mainConfig.chat.urlFilter.enabled = !Sentinel.mainConfig.chat.urlFilter.enabled; - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.enabled = !Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.enabled; + Sentinel.getInstance().getDirector().io.mainConfig.save(); blankPage(e.getInventory()); } case 5 -> { - Sentinel.mainConfig.chat.urlFilter.silent = !Sentinel.mainConfig.chat.urlFilter.silent; - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.silent = !Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.silent; + Sentinel.getInstance().getDirector().io.mainConfig.save(); blankPage(e.getInventory()); } case 19 -> { - Sentinel.mainConfig.chat.urlFilter.punished = !Sentinel.mainConfig.chat.urlFilter.punished; - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.punished = !Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.punished; + Sentinel.getInstance().getDirector().io.mainConfig.save(); blankPage(e.getInventory()); } - case 21 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.urlFilter.regex = args.getAll().toString(),Sentinel.mainConfig.chat.urlFilter.regex); + case 21 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.urlFilter.regex = args.getAll().toString(),Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.regex); case 23 -> { if (e.isLeftClick()) { queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> { cfg.chat.urlFilter.punishCommands.add(args.getAll().toString()); - },"" + Sentinel.mainConfig.chat.urlFilter.punishCommands); + },"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.punishCommands); return; } - Sentinel.mainConfig.chat.urlFilter.punishCommands.clear(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.punishCommands.clear(); blankPage(e.getInventory()); - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.save(); } case 25 -> { if (e.isLeftClick()) { queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> { cfg.chat.urlFilter.whitelist.add(args.getAll().toString()); - },"" + Sentinel.mainConfig.chat.urlFilter.whitelist); + },"" + Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.whitelist); return; } - Sentinel.mainConfig.chat.urlFilter.whitelist.clear(); + Sentinel.getInstance().getDirector().io.mainConfig.chat.urlFilter.whitelist.clear(); blankPage(e.getInventory()); - Sentinel.mainConfig.save(); + Sentinel.getInstance().getDirector().io.mainConfig.save(); } } } - public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.mainConfig); + public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.getInstance().getDirector().io.mainConfig); private void queuePlayer(Player player, BiConsumer action, String currentValue) { MainGUI.awaitingCallback.add(player.getUniqueId()); diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/CommandGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/nuke/CommandGUI.java deleted file mode 100644 index 20a51f6..0000000 --- a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/CommandGUI.java +++ /dev/null @@ -1,67 +0,0 @@ -package me.trouper.sentinel.server.gui.config.nuke; - -import io.github.itzispyder.pdk.plugin.builders.ItemBuilder; -import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import me.trouper.sentinel.server.gui.Items; -import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.AntiNukeGUI; -import me.trouper.sentinel.server.gui.config.nuke.checks.command.DangerousCMDGUI; -import me.trouper.sentinel.server.gui.config.nuke.checks.command.LoggedCMDGUI; -import me.trouper.sentinel.server.gui.config.nuke.checks.command.SpecificCMDGUI; -import me.trouper.sentinel.utils.Text; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -public class CommandGUI { - public final CustomGui home = CustomGui.create() - .title(Text.color("&6&lSentinel &8»&0 Choose a check")) - .size(27) - .onDefine(this::blankPage) - .defineMain(this::mainClick) - .define(26, Items.BACK, e->{ - e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); - }) - .define(11,SPECIFIC, e->{ - e.getWhoClicked().openInventory(new SpecificCMDGUI().home.getInventory()); - }) - .define(13,LOGGED, e->{ - e.getWhoClicked().openInventory(new LoggedCMDGUI().home.getInventory()); - }) - .define(15,DANGEROUS, e->{ - e.getWhoClicked().openInventory(new DangerousCMDGUI().home.getInventory()); - }) - .build(); - - private void mainClick(InventoryClickEvent e) { - e.setCancelled(true); - MainGUI.verify((Player) e.getWhoClicked()); - } - - private void blankPage(Inventory inv) { - for (int i = 0; i < inv.getSize(); i++) { - inv.setItem(i,Items.BLANK); - } - } - - private static final ItemStack SPECIFIC = ItemBuilder.create() - .material(Material.SPECTRAL_ARROW) - .name(Text.color("&bSpecific Commands")) - .lore(Text.color("&8&l➥&7 Modify this check")) - .build(); - - private static final ItemStack LOGGED = ItemBuilder.create() - .material(Material.SPYGLASS) - .name(Text.color("&bLogged Commands")) - .lore(Text.color("&8&l➥&7 Modify this check")) - - .build(); - - private static final ItemStack DANGEROUS = ItemBuilder.create() - .material(Material.TNT) - .name(Text.color("&bDangerous Commands")) - .lore(Text.color("&8&l➥&7 Modify this check")) - .build(); -} diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBEditGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBEditGUI.java deleted file mode 100644 index 2d6efa6..0000000 --- a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBEditGUI.java +++ /dev/null @@ -1,120 +0,0 @@ -package me.trouper.sentinel.server.gui.config.nuke.checks; - -import io.github.itzispyder.pdk.commands.Args; -import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater; -import io.papermc.paper.event.player.AsyncChatEvent; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.config.ViolationConfig; -import me.trouper.sentinel.server.gui.Items; -import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.AntiNukeGUI; -import me.trouper.sentinel.utils.ServerUtils; -import me.trouper.sentinel.utils.Text; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import java.util.List; -import java.util.function.BiConsumer; - -public class CBEditGUI { - - public final CustomGui home = CustomGui.create() - .title(Text.color("&6&lSentinel &8»&0 CB Edit Check")) - .size(27) - .onDefine(this::blankPage) - .defineMain(this::mainClick) - .define(26, Items.BACK, e->{ - e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); - }) - .build(); - - private void blankPage(Inventory inv) { - for (int i = 0; i < inv.getSize(); i++) { - inv.setItem(i,Items.BLANK); - } - - ItemStack ring = Items.RED; - if (Sentinel.violationConfig.commandBlockEdit.enabled) { - ring = Items.GREEN; - } - - List ringList = List.of(3,4,5,12,14,21,22,23); - - for (Integer i : ringList) { - inv.setItem(i,ring); - } - - inv.setItem(26,Items.BACK); - inv.setItem(13,Items.booleanItem(Sentinel.violationConfig.commandBlockEdit.enabled,Items.configItem("Check Toggle",Material.CLOCK,"Enable/Disable this check entirely"))); - inv.setItem(2,Items.booleanItem(Sentinel.violationConfig.commandBlockEdit.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); - inv.setItem(20,Items.booleanItem(Sentinel.violationConfig.commandBlockEdit.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); - inv.setItem(6,Items.booleanItem(Sentinel.violationConfig.commandBlockEdit.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); - inv.setItem(24,Items.stringListItem(Sentinel.violationConfig.commandBlockEdit.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); - } - - private void mainClick(InventoryClickEvent e) { - e.setCancelled(true); - if (!MainGUI.verify((Player) e.getWhoClicked())) return; - switch (e.getSlot()) { - case 13 -> { - Sentinel.violationConfig.commandBlockEdit.enabled = !Sentinel.violationConfig.commandBlockEdit.enabled; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 2 -> { - Sentinel.violationConfig.commandBlockEdit.deop = !Sentinel.violationConfig.commandBlockEdit.deop; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 20 -> { - Sentinel.violationConfig.commandBlockEdit.logToDiscord = !Sentinel.violationConfig.commandBlockEdit.logToDiscord; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 6 -> { - Sentinel.violationConfig.commandBlockEdit.punish = !Sentinel.violationConfig.commandBlockEdit.punish; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - - case 24 -> { - if (e.isLeftClick()) { - queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { - cfg.commandBlockEdit.punishmentCommands.add(args.getAll().toString()); - },"" + Sentinel.violationConfig.commandBlockEdit.punishmentCommands); - return; - } - Sentinel.violationConfig.commandBlockEdit.punishmentCommands.clear(); - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - - } - } - - - public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.violationConfig); - - private void queuePlayer(Player player, BiConsumer action, String currentValue) { - MainGUI.awaitingCallback.add(player.getUniqueId()); - player.closeInventory(); - updater.queuePlayer(player, 20*60, (e)->{ - e.setCancelled(true); - return LegacyComponentSerializer.legacySection().serialize(e.message()); - }, (cfg, newValue) -> { - action.accept(cfg,new Args(newValue.split("\\s+"))); - cfg.save(); - player.sendMessage(Text.prefix("Value updated successfully")); - player.openInventory(home.getInventory()); - }); - player.sendMessage(Component.text(Text.prefix("Enter the new value in chat. The value is currently set to &b%s&7. (Click to insert)".formatted(currentValue))).clickEvent(ClickEvent.suggestCommand(currentValue))); - } -} diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBExecuteGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBExecuteGUI.java deleted file mode 100644 index b02be97..0000000 --- a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBExecuteGUI.java +++ /dev/null @@ -1,74 +0,0 @@ -package me.trouper.sentinel.server.gui.config.nuke.checks; - -import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.server.gui.Items; -import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.AntiNukeGUI; -import me.trouper.sentinel.utils.Text; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -public class CBExecuteGUI { - public final CustomGui home = CustomGui.create() - .title(Text.color("&6&lSentinel &8»&0 CB Whitelist")) - .size(27) - .onDefine(this::blankPage) - .defineMain(this::mainClick) - .define(26, Items.BACK, e->{ - e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); - }) - .build(); - - private void blankPage(Inventory inv) { - for (int i = 0; i < inv.getSize(); i++) { - inv.setItem(i,Items.BLANK); - } - - ItemStack top = Items.RED; - if (Sentinel.violationConfig.commandBlockExecute.enabled) { - top = Items.GREEN; - } - - for (int i = 0; i < 9; i++) { - inv.setItem(i,top); - } - - inv.setItem(26,Items.BACK); - inv.setItem(4,Items.booleanItem(Sentinel.violationConfig.commandBlockExecute.enabled,Items.configItem("Check Toggle", Material.CLOCK,"Enable/Disable this check entirely"))); - inv.setItem(11,Items.booleanItem(Sentinel.violationConfig.commandBlockExecute.destroyBlock,Items.configItem("Destroy",Material.NETHERITE_PICKAXE,"Destroy the offending command-block"))); - inv.setItem(13,Items.booleanItem(Sentinel.violationConfig.commandBlockExecute.attemptRestore,Items.configItem("Restore",Material.COMMAND_BLOCK,"Attempt to restore the block if a \nwhitelisted one exists at the location"))); - inv.setItem(15,Items.booleanItem(Sentinel.violationConfig.commandBlockExecute.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); - } - - private void mainClick(InventoryClickEvent e) { - e.setCancelled(true); - if (!MainGUI.verify((Player) e.getWhoClicked())) return; - switch (e.getSlot()) { - case 4 -> { - Sentinel.violationConfig.commandBlockExecute.enabled = !Sentinel.violationConfig.commandBlockExecute.enabled; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 11 -> { - Sentinel.violationConfig.commandBlockExecute.destroyBlock = !Sentinel.violationConfig.commandBlockExecute.destroyBlock; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 13 -> { - Sentinel.violationConfig.commandBlockExecute.attemptRestore = !Sentinel.violationConfig.commandBlockExecute.attemptRestore; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 15 -> { - Sentinel.violationConfig.commandBlockExecute.logToDiscord = !Sentinel.violationConfig.commandBlockExecute.logToDiscord; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - } - } -} - diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBMCPlaceGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBMCPlaceGUI.java deleted file mode 100644 index 0d70a52..0000000 --- a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBMCPlaceGUI.java +++ /dev/null @@ -1,119 +0,0 @@ -package me.trouper.sentinel.server.gui.config.nuke.checks; - -import io.github.itzispyder.pdk.commands.Args; -import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater; -import io.papermc.paper.event.player.AsyncChatEvent; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.config.ViolationConfig; -import me.trouper.sentinel.server.gui.Items; -import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.AntiNukeGUI; -import me.trouper.sentinel.utils.ServerUtils; -import me.trouper.sentinel.utils.Text; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import java.util.List; -import java.util.function.BiConsumer; - -public class CBMCPlaceGUI { - - public final CustomGui home = CustomGui.create() - .title(Text.color("&6&lSentinel &8»&0 CB MC Place Check")) - .size(27) - .onDefine(this::blankPage) - .defineMain(this::mainClick) - .define(26, Items.BACK, e->{ - e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); - }) - .build(); - - private void blankPage(Inventory inv) { - for (int i = 0; i < inv.getSize(); i++) { - inv.setItem(i,Items.BLANK); - } - - ItemStack ring = Items.RED; - if (Sentinel.violationConfig.commandBlockMinecartPlace.enabled) { - ring = Items.GREEN; - } - - List ringList = List.of(3,4,5,12,14,21,22,23); - - for (Integer i : ringList) { - inv.setItem(i,ring); - } - - inv.setItem(26,Items.BACK); - inv.setItem(13,Items.booleanItem(Sentinel.violationConfig.commandBlockMinecartPlace.enabled,Items.configItem("Check Toggle",Material.CLOCK,"Enable/Disable this check entirely"))); - inv.setItem(2,Items.booleanItem(Sentinel.violationConfig.commandBlockMinecartPlace.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); - inv.setItem(20,Items.booleanItem(Sentinel.violationConfig.commandBlockMinecartPlace.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); - inv.setItem(6,Items.booleanItem(Sentinel.violationConfig.commandBlockMinecartPlace.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); - inv.setItem(24,Items.stringListItem(Sentinel.violationConfig.commandBlockMinecartPlace.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); - } - - private void mainClick(InventoryClickEvent e) { - e.setCancelled(true); - if (!MainGUI.verify((Player) e.getWhoClicked())) return; - switch (e.getSlot()) { - case 13 -> { - Sentinel.violationConfig.commandBlockMinecartPlace.enabled = !Sentinel.violationConfig.commandBlockMinecartPlace.enabled; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 2 -> { - Sentinel.violationConfig.commandBlockMinecartPlace.deop = !Sentinel.violationConfig.commandBlockMinecartPlace.deop; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 20 -> { - Sentinel.violationConfig.commandBlockMinecartPlace.logToDiscord = !Sentinel.violationConfig.commandBlockMinecartPlace.logToDiscord; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 6 -> { - Sentinel.violationConfig.commandBlockMinecartPlace.punish = !Sentinel.violationConfig.commandBlockMinecartPlace.punish; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - - case 24 -> { - if (e.isLeftClick()) { - queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { - cfg.commandBlockMinecartPlace.punishmentCommands.add(args.getAll().toString()); - },"" + Sentinel.violationConfig.commandBlockMinecartPlace.punishmentCommands); - return; - } - Sentinel.violationConfig.commandBlockMinecartPlace.punishmentCommands.clear(); - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - - } - } - - - public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.violationConfig); - private void queuePlayer(Player player, BiConsumer action, String currentValue) { - MainGUI.awaitingCallback.add(player.getUniqueId()); - player.closeInventory(); - updater.queuePlayer(player, 20*60, (e)->{ - e.setCancelled(true); - return LegacyComponentSerializer.legacySection().serialize(e.message()); - }, (cfg, newValue) -> { - action.accept(cfg,new Args(newValue.split("\\s+"))); - cfg.save(); - player.sendMessage(Text.prefix("Value updated successfully")); - player.openInventory(home.getInventory()); - }); - player.sendMessage(Component.text(Text.prefix("Enter the new value in chat. The value is currently set to &b%s&7. (Click to insert)".formatted(currentValue))).clickEvent(ClickEvent.suggestCommand(currentValue))); - } -} diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBMCUseGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBMCUseGUI.java deleted file mode 100644 index f47e415..0000000 --- a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBMCUseGUI.java +++ /dev/null @@ -1,120 +0,0 @@ -package me.trouper.sentinel.server.gui.config.nuke.checks; - -import io.github.itzispyder.pdk.commands.Args; -import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater; -import io.papermc.paper.event.player.AsyncChatEvent; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.config.ViolationConfig; -import me.trouper.sentinel.server.gui.Items; -import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.AntiNukeGUI; -import me.trouper.sentinel.utils.ServerUtils; -import me.trouper.sentinel.utils.Text; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import java.util.List; -import java.util.function.BiConsumer; - -public class CBMCUseGUI { - - public final CustomGui home = CustomGui.create() - .title(Text.color("&6&lSentinel &8»&0 CB MC Use Check")) - .size(27) - .onDefine(this::blankPage) - .defineMain(this::mainClick) - .define(26, Items.BACK, e->{ - e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); - }) - .build(); - - private void blankPage(Inventory inv) { - for (int i = 0; i < inv.getSize(); i++) { - inv.setItem(i,Items.BLANK); - } - - ItemStack ring = Items.RED; - if (Sentinel.violationConfig.commandBlockMinecartUse.enabled) { - ring = Items.GREEN; - } - - List ringList = List.of(3,4,5,12,14,21,22,23); - - for (Integer i : ringList) { - inv.setItem(i,ring); - } - - inv.setItem(26,Items.BACK); - inv.setItem(13,Items.booleanItem(Sentinel.violationConfig.commandBlockMinecartUse.enabled,Items.configItem("Check Toggle",Material.CLOCK,"Enable/Disable this check entirely"))); - inv.setItem(2,Items.booleanItem(Sentinel.violationConfig.commandBlockMinecartUse.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); - inv.setItem(20,Items.booleanItem(Sentinel.violationConfig.commandBlockMinecartUse.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); - inv.setItem(6,Items.booleanItem(Sentinel.violationConfig.commandBlockMinecartUse.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); - inv.setItem(24,Items.stringListItem(Sentinel.violationConfig.commandBlockMinecartUse.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); - } - - private void mainClick(InventoryClickEvent e) { - e.setCancelled(true); - if (!MainGUI.verify((Player) e.getWhoClicked())) return; - switch (e.getSlot()) { - case 13 -> { - Sentinel.violationConfig.commandBlockMinecartUse.enabled = !Sentinel.violationConfig.commandBlockMinecartUse.enabled; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 2 -> { - Sentinel.violationConfig.commandBlockMinecartUse.deop = !Sentinel.violationConfig.commandBlockMinecartUse.deop; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 20 -> { - Sentinel.violationConfig.commandBlockMinecartUse.logToDiscord = !Sentinel.violationConfig.commandBlockMinecartUse.logToDiscord; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 6 -> { - Sentinel.violationConfig.commandBlockMinecartUse.punish = !Sentinel.violationConfig.commandBlockMinecartUse.punish; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - - case 24 -> { - if (e.isLeftClick()) { - queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { - cfg.commandBlockMinecartUse.punishmentCommands.add(args.getAll().toString()); - },"" + Sentinel.violationConfig.commandBlockMinecartUse.punishmentCommands); - return; - } - Sentinel.violationConfig.commandBlockMinecartUse.punishmentCommands.clear(); - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - - } - } - - - public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.violationConfig); - - private void queuePlayer(Player player, BiConsumer action, String currentValue) { - MainGUI.awaitingCallback.add(player.getUniqueId()); - player.closeInventory(); - updater.queuePlayer(player, 20*60,(e)->{ - e.setCancelled(true); - return LegacyComponentSerializer.legacySection().serialize(e.message()); - }, (cfg, newValue) -> { - action.accept(cfg,new Args(newValue.split("\\s+"))); - cfg.save(); - player.sendMessage(Text.prefix("Value updated successfully")); - player.openInventory(home.getInventory()); - }); - player.sendMessage(Component.text(Text.prefix("Enter the new value in chat. The value is currently set to &b%s&7. (Click to insert)".formatted(currentValue))).clickEvent(ClickEvent.suggestCommand(currentValue))); - } -} diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBPlaceGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBPlaceGUI.java deleted file mode 100644 index 9a665ce..0000000 --- a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBPlaceGUI.java +++ /dev/null @@ -1,120 +0,0 @@ -package me.trouper.sentinel.server.gui.config.nuke.checks; - -import io.github.itzispyder.pdk.commands.Args; -import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater; -import io.papermc.paper.event.player.AsyncChatEvent; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.config.ViolationConfig; -import me.trouper.sentinel.server.gui.Items; -import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.AntiNukeGUI; -import me.trouper.sentinel.utils.ServerUtils; -import me.trouper.sentinel.utils.Text; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import java.util.List; -import java.util.function.BiConsumer; - -public class CBPlaceGUI { - - public final CustomGui home = CustomGui.create() - .title(Text.color("&6&lSentinel &8»&0 CB Place Check")) - .size(27) - .onDefine(this::blankPage) - .defineMain(this::mainClick) - .define(26, Items.BACK, e->{ - e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); - }) - .build(); - - private void blankPage(Inventory inv) { - for (int i = 0; i < inv.getSize(); i++) { - inv.setItem(i,Items.BLANK); - } - - ItemStack ring = Items.RED; - if (Sentinel.violationConfig.commandBlockPlace.enabled) { - ring = Items.GREEN; - } - - List ringList = List.of(3,4,5,12,14,21,22,23); - - for (Integer i : ringList) { - inv.setItem(i,ring); - } - - inv.setItem(26,Items.BACK); - inv.setItem(13,Items.booleanItem(Sentinel.violationConfig.commandBlockPlace.enabled,Items.configItem("Check Toggle",Material.CLOCK,"Enable/Disable this check entirely"))); - inv.setItem(2,Items.booleanItem(Sentinel.violationConfig.commandBlockPlace.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); - inv.setItem(20,Items.booleanItem(Sentinel.violationConfig.commandBlockPlace.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); - inv.setItem(6,Items.booleanItem(Sentinel.violationConfig.commandBlockPlace.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); - inv.setItem(24,Items.stringListItem(Sentinel.violationConfig.commandBlockPlace.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); - } - - private void mainClick(InventoryClickEvent e) { - e.setCancelled(true); - if (!MainGUI.verify((Player) e.getWhoClicked())) return; - switch (e.getSlot()) { - case 13 -> { - Sentinel.violationConfig.commandBlockPlace.enabled = !Sentinel.violationConfig.commandBlockPlace.enabled; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 2 -> { - Sentinel.violationConfig.commandBlockPlace.deop = !Sentinel.violationConfig.commandBlockPlace.deop; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 20 -> { - Sentinel.violationConfig.commandBlockPlace.logToDiscord = !Sentinel.violationConfig.commandBlockPlace.logToDiscord; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 6 -> { - Sentinel.violationConfig.commandBlockPlace.punish = !Sentinel.violationConfig.commandBlockPlace.punish; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - - case 24 -> { - if (e.isLeftClick()) { - queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { - cfg.commandBlockPlace.punishmentCommands.add(args.getAll().toString()); - },"" + Sentinel.violationConfig.commandBlockPlace.punishmentCommands); - return; - } - Sentinel.violationConfig.commandBlockPlace.punishmentCommands.clear(); - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - - } - } - - - public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.violationConfig); - - private void queuePlayer(Player player, BiConsumer action, String currentValue) { - MainGUI.awaitingCallback.add(player.getUniqueId()); - player.closeInventory(); - updater.queuePlayer(player, 20*60, (e)->{ - e.setCancelled(true); - return LegacyComponentSerializer.legacySection().serialize(e.message()); - }, (cfg, newValue) -> { - action.accept(cfg,new Args(newValue.split("\\s+"))); - cfg.save(); - player.sendMessage(Text.prefix("Value updated successfully")); - player.openInventory(home.getInventory()); - }); - player.sendMessage(Component.text(Text.prefix("Enter the new value in chat. The value is currently set to &b%s&7. (Click to insert)".formatted(currentValue))).clickEvent(ClickEvent.suggestCommand(currentValue))); - } -} diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBUseGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBUseGUI.java deleted file mode 100644 index 1e633f2..0000000 --- a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/CBUseGUI.java +++ /dev/null @@ -1,120 +0,0 @@ -package me.trouper.sentinel.server.gui.config.nuke.checks; - -import io.github.itzispyder.pdk.commands.Args; -import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater; -import io.papermc.paper.event.player.AsyncChatEvent; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.config.ViolationConfig; -import me.trouper.sentinel.server.gui.Items; -import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.AntiNukeGUI; -import me.trouper.sentinel.utils.ServerUtils; -import me.trouper.sentinel.utils.Text; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import java.util.List; -import java.util.function.BiConsumer; - -public class CBUseGUI { - - public final CustomGui home = CustomGui.create() - .title(Text.color("&6&lSentinel &8»&0 CB Use Check")) - .size(27) - .onDefine(this::blankPage) - .defineMain(this::mainClick) - .define(26, Items.BACK, e->{ - e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); - }) - .build(); - - private void blankPage(Inventory inv) { - for (int i = 0; i < inv.getSize(); i++) { - inv.setItem(i,Items.BLANK); - } - - ItemStack ring = Items.RED; - if (Sentinel.violationConfig.commandBlockUse.enabled) { - ring = Items.GREEN; - } - - List ringList = List.of(3,4,5,12,14,21,22,23); - - for (Integer i : ringList) { - inv.setItem(i,ring); - } - - inv.setItem(26,Items.BACK); - inv.setItem(13,Items.booleanItem(Sentinel.violationConfig.commandBlockUse.enabled,Items.configItem("Check Toggle",Material.CLOCK,"Enable/Disable this check entirely"))); - inv.setItem(2,Items.booleanItem(Sentinel.violationConfig.commandBlockUse.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); - inv.setItem(20,Items.booleanItem(Sentinel.violationConfig.commandBlockUse.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); - inv.setItem(6,Items.booleanItem(Sentinel.violationConfig.commandBlockUse.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); - inv.setItem(24,Items.stringListItem(Sentinel.violationConfig.commandBlockUse.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); - } - - private void mainClick(InventoryClickEvent e) { - e.setCancelled(true); - if (!MainGUI.verify((Player) e.getWhoClicked())) return; - switch (e.getSlot()) { - case 13 -> { - Sentinel.violationConfig.commandBlockUse.enabled = !Sentinel.violationConfig.commandBlockUse.enabled; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 2 -> { - Sentinel.violationConfig.commandBlockUse.deop = !Sentinel.violationConfig.commandBlockUse.deop; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 20 -> { - Sentinel.violationConfig.commandBlockUse.logToDiscord = !Sentinel.violationConfig.commandBlockUse.logToDiscord; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 6 -> { - Sentinel.violationConfig.commandBlockUse.punish = !Sentinel.violationConfig.commandBlockUse.punish; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - - case 24 -> { - if (e.isLeftClick()) { - queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { - cfg.commandBlockUse.punishmentCommands.add(args.getAll().toString()); - },"" + Sentinel.violationConfig.commandBlockUse.punishmentCommands); - return; - } - Sentinel.violationConfig.commandBlockUse.punishmentCommands.clear(); - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - - } - } - - - public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.violationConfig); - - private void queuePlayer(Player player, BiConsumer action, String currentValue) { - MainGUI.awaitingCallback.add(player.getUniqueId()); - player.closeInventory(); - updater.queuePlayer(player, 20*60, (e)->{ - e.setCancelled(true); - return LegacyComponentSerializer.legacySection().serialize(e.message()); - }, (cfg, newValue) -> { - action.accept(cfg,new Args(newValue.split("\\s+"))); - cfg.save(); - player.sendMessage(Text.prefix("Value updated successfully")); - player.openInventory(home.getInventory()); - }); - player.sendMessage(Component.text(Text.prefix("Enter the new value in chat. The value is currently set to &b%s&7. (Click to insert)".formatted(currentValue))).clickEvent(ClickEvent.suggestCommand(currentValue))); - } -} diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/HotbarActionGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/HotbarActionGUI.java deleted file mode 100644 index 56f723c..0000000 --- a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/HotbarActionGUI.java +++ /dev/null @@ -1,121 +0,0 @@ -package me.trouper.sentinel.server.gui.config.nuke.checks; - -import io.github.itzispyder.pdk.commands.Args; -import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater; -import io.papermc.paper.event.player.AsyncChatEvent; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.config.ViolationConfig; -import me.trouper.sentinel.server.gui.Items; -import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.AntiNukeGUI; -import me.trouper.sentinel.utils.ServerUtils; -import me.trouper.sentinel.utils.Text; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import java.util.List; -import java.util.function.BiConsumer; - -public class HotbarActionGUI { - - public final CustomGui home = CustomGui.create() - .title(Text.color("&6&lSentinel &8»&0 Creative Hotbar Check")) - .size(27) - .onDefine(this::blankPage) - .defineMain(this:: mainClick) - .define(26, Items.BACK, e->{ - e.getWhoClicked().openInventory(new AntiNukeGUI().home.getInventory()); - }) - .build(); - - private void blankPage(Inventory inv) { - for (int i = 0; i < inv.getSize(); i++) { - inv.setItem(i,Items.BLANK); - } - - ItemStack ring = Items.RED; - if (Sentinel.violationConfig.creativeHotbarAction.enabled) { - ring = Items.GREEN; - } - - List ringList = List.of(3,4,5,12,14,21,22,23); - - for (Integer i : ringList) { - inv.setItem(i,ring); - } - - inv.setItem(26,Items.BACK); - inv.setItem(13,Items.booleanItem(Sentinel.violationConfig.creativeHotbarAction.enabled,Items.configItem("Check Toggle",Material.CLOCK,"Enable/Disable this check entirely"))); - inv.setItem(2,Items.booleanItem(Sentinel.violationConfig.creativeHotbarAction.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); - inv.setItem(20,Items.booleanItem(Sentinel.violationConfig.creativeHotbarAction.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); - inv.setItem(6,Items.booleanItem(Sentinel.violationConfig.creativeHotbarAction.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); - inv.setItem(24,Items.stringListItem(Sentinel.violationConfig.creativeHotbarAction.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); - } - - private void mainClick(InventoryClickEvent e) { - e.setCancelled(true); - if (!MainGUI.verify((Player) e.getWhoClicked())) return; - - switch (e.getSlot()) { - case 13 -> { - Sentinel.violationConfig.creativeHotbarAction.enabled = !Sentinel.violationConfig.creativeHotbarAction.enabled; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 2 -> { - Sentinel.violationConfig.creativeHotbarAction.deop = !Sentinel.violationConfig.creativeHotbarAction.deop; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 20 -> { - Sentinel.violationConfig.creativeHotbarAction.logToDiscord = !Sentinel.violationConfig.creativeHotbarAction.logToDiscord; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 6 -> { - Sentinel.violationConfig.creativeHotbarAction.punish = !Sentinel.violationConfig.creativeHotbarAction.punish; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - - case 24 -> { - if (e.isLeftClick()) { - queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { - cfg.creativeHotbarAction.punishmentCommands.add(args.getAll().toString()); - },"" + Sentinel.violationConfig.creativeHotbarAction.punishmentCommands); - return; - } - Sentinel.violationConfig.creativeHotbarAction.punishmentCommands.clear(); - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - - } - } - - - public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.violationConfig); - - private void queuePlayer(Player player, BiConsumer action, String currentValue) { - MainGUI.awaitingCallback.add(player.getUniqueId()); - player.closeInventory(); - updater.queuePlayer(player, 20*60, (e)->{ - e.setCancelled(true); - return LegacyComponentSerializer.legacySection().serialize(e.message()); - }, (cfg, newValue) -> { - action.accept(cfg,new Args(newValue.split("\\s+"))); - cfg.save(); - player.sendMessage(Text.prefix("Value updated successfully")); - player.openInventory(home.getInventory()); - }); - player.sendMessage(Component.text(Text.prefix("Enter the new value in chat. The value is currently set to &b%s&7. (Click to insert)".formatted(currentValue))).clickEvent(ClickEvent.suggestCommand(currentValue))); - } -} diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/command/DangerousCMDGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/command/DangerousCMDGUI.java deleted file mode 100644 index 9288a5a..0000000 --- a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/command/DangerousCMDGUI.java +++ /dev/null @@ -1,132 +0,0 @@ -package me.trouper.sentinel.server.gui.config.nuke.checks.command; - -import io.github.itzispyder.pdk.commands.Args; -import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater; -import io.papermc.paper.event.player.AsyncChatEvent; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.config.ViolationConfig; -import me.trouper.sentinel.server.gui.Items; -import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.nuke.CommandGUI; -import me.trouper.sentinel.utils.ServerUtils; -import me.trouper.sentinel.utils.Text; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import java.util.List; -import java.util.function.BiConsumer; - -public class DangerousCMDGUI { - public final CustomGui home = CustomGui.create() - .title(Text.color("&6&lSentinel &8»&0 Dangerous Command Check")) - .size(27) - .onDefine(this::blankPage) - .defineMain(this::mainClick) - .define(26, Items.BACK, e->{ - e.getWhoClicked().openInventory(new CommandGUI().home.getInventory()); - }) - .build(); - - - private void blankPage(Inventory inv) { - for (int i = 0; i < inv.getSize(); i++) { - inv.setItem(i,Items.BLANK); - } - - ItemStack ring = Items.RED; - if (Sentinel.violationConfig.commandExecute.dangerous.enabled) { - ring = Items.GREEN; - } - - List ringList = List.of(3,4,5,12,14,21,23); - - for (Integer i : ringList) { - inv.setItem(i,ring); - } - - inv.setItem(26,Items.BACK); - inv.setItem(13,Items.booleanItem(Sentinel.violationConfig.commandExecute.dangerous.enabled,Items.configItem("Check Toggle",Material.CLOCK,"Enable/Disable this check entirely"))); - inv.setItem(2,Items.booleanItem(Sentinel.violationConfig.commandExecute.dangerous.deop,Items.configItem("De-Op",Material.END_CRYSTAL,"Remove the user's operator privileges"))); - inv.setItem(20,Items.booleanItem(Sentinel.violationConfig.commandExecute.dangerous.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); - inv.setItem(6,Items.booleanItem(Sentinel.violationConfig.commandExecute.dangerous.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"Run the punishment commands"))); - inv.setItem(24,Items.stringListItem(Sentinel.violationConfig.commandExecute.dangerous.punishmentCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands that will be ran \nif this check is flagged.")); - inv.setItem(22,Items.stringListItem(Sentinel.violationConfig.commandExecute.dangerous.commands,Material.CRIMSON_HANGING_SIGN,"Commands","Commands that will flag this check.")); - } - - private void mainClick(InventoryClickEvent e) { - e.setCancelled(true); - if (!MainGUI.verify((Player) e.getWhoClicked())) return; - switch (e.getSlot()) { - case 13 -> { - Sentinel.violationConfig.commandExecute.dangerous.enabled = !Sentinel.violationConfig.commandExecute.dangerous.enabled; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 2 -> { - Sentinel.violationConfig.commandExecute.dangerous.deop = !Sentinel.violationConfig.commandExecute.dangerous.deop; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 20 -> { - Sentinel.violationConfig.commandExecute.dangerous.logToDiscord = !Sentinel.violationConfig.commandExecute.dangerous.logToDiscord; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 6 -> { - Sentinel.violationConfig.commandExecute.dangerous.punish = !Sentinel.violationConfig.commandExecute.dangerous.punish; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - - case 24 -> { - if (e.isLeftClick()) { - queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> { - cfg.commandExecute.dangerous.punishmentCommands.add(args.getAll().toString()); - },"" + Sentinel.violationConfig.commandExecute.dangerous.punishmentCommands); - return; - } - Sentinel.violationConfig.commandExecute.dangerous.punishmentCommands.clear(); - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - - case 22 -> { - if (e.isLeftClick()) { - queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> { - cfg.commandExecute.dangerous.commands.add(args.getAll().toString()); - },"" + Sentinel.violationConfig.commandExecute.dangerous.commands); - return; - } - Sentinel.violationConfig.commandExecute.dangerous.commands.clear(); - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - } - } - - - public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.violationConfig); - - private void queuePlayer(Player player, BiConsumer action, String currentValue) { - MainGUI.awaitingCallback.add(player.getUniqueId()); - player.closeInventory(); - updater.queuePlayer(player, 20*60, (e)->{ - e.setCancelled(true); - return LegacyComponentSerializer.legacySection().serialize(e.message()); - }, (cfg, newValue) -> { - action.accept(cfg,new Args(newValue.split("\\s+"))); - cfg.save(); - player.sendMessage(Text.prefix("Value updated successfully")); - player.openInventory(home.getInventory()); - }); - player.sendMessage(Component.text(Text.prefix("Enter the new value in chat. The value is currently set to &b%s&7. (Click to insert)".formatted(currentValue))).clickEvent(ClickEvent.suggestCommand(currentValue))); - } -} diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/command/LoggedCMDGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/command/LoggedCMDGUI.java deleted file mode 100644 index 0b2a53b..0000000 --- a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/command/LoggedCMDGUI.java +++ /dev/null @@ -1,105 +0,0 @@ -package me.trouper.sentinel.server.gui.config.nuke.checks.command; - -import io.github.itzispyder.pdk.commands.Args; -import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater; -import io.papermc.paper.event.player.AsyncChatEvent; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.config.ViolationConfig; -import me.trouper.sentinel.server.gui.Items; -import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.nuke.CommandGUI; -import me.trouper.sentinel.utils.ServerUtils; -import me.trouper.sentinel.utils.Text; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import java.util.List; -import java.util.function.BiConsumer; - -public class LoggedCMDGUI { - public final CustomGui home = CustomGui.create() - .title(Text.color("&6&lSentinel &8»&0 Logged Command Check")) - .size(27) - .onDefine(this::blankPage) - .defineMain(this::mainClick) - .define(26, Items.BACK, e->{ - e.getWhoClicked().openInventory(new CommandGUI().home.getInventory()); - }) - .build(); - - private void blankPage(Inventory inv) { - for (int i = 0; i < inv.getSize(); i++) { - inv.setItem(i,Items.BLANK); - } - - ItemStack ring = Items.RED; - if (Sentinel.violationConfig.commandExecute.logged.enabled) { - ring = Items.GREEN; - } - - List ringList = List.of(3,4,5,12,14,21,22,23); - - for (Integer i : ringList) { - inv.setItem(i,ring); - } - - inv.setItem(26,Items.BACK); - inv.setItem(13,Items.booleanItem(Sentinel.violationConfig.commandExecute.logged.enabled,Items.configItem("Check Toggle",Material.CLOCK,"Enable/Disable this check entirely"))); - inv.setItem(11,Items.booleanItem(Sentinel.violationConfig.commandExecute.logged.logToDiscord,Items.configItem("Log to Discord",Material.OAK_LOG,"If this check will log to discord"))); - inv.setItem(15,Items.stringListItem(Sentinel.violationConfig.commandExecute.logged.commands,Material.CRIMSON_HANGING_SIGN,"Commands","Commands that will flag this check")); - } - - private void mainClick(InventoryClickEvent e) { - e.setCancelled(true); - if (!MainGUI.verify((Player) e.getWhoClicked())) return; - switch (e.getSlot()) { - case 13 -> { - Sentinel.violationConfig.commandExecute.logged.enabled = !Sentinel.violationConfig.commandExecute.logged.enabled; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 11 -> { - Sentinel.violationConfig.commandExecute.logged.logToDiscord = !Sentinel.violationConfig.commandExecute.logged.logToDiscord; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 15 -> { - if (e.isLeftClick()) { - queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> { - cfg.commandExecute.logged.commands.add(args.getAll().toString()); - },"" + Sentinel.violationConfig.commandExecute.logged.commands); - return; - } - Sentinel.violationConfig.commandExecute.logged.commands.clear(); - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - } - } - - public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.violationConfig); - - private void queuePlayer(Player player, BiConsumer action, String currentValue) { - MainGUI.awaitingCallback.add(player.getUniqueId()); - player.closeInventory(); - updater.queuePlayer(player, 20*60, (e)->{ - e.setCancelled(true); - return LegacyComponentSerializer.legacySection().serialize(e.message()); - }, (cfg, newValue) -> { - action.accept(cfg,new Args(newValue.split("\\s+"))); - cfg.save(); - player.sendMessage(Text.prefix("Value updated successfully")); - player.openInventory(home.getInventory()); - }); - player.sendMessage(Component.text(Text.prefix("Enter the new value in chat. The value is currently set to &b%s&7. (Click to insert)".formatted(currentValue))).clickEvent(ClickEvent.suggestCommand(currentValue))); - } - -} diff --git a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/command/SpecificCMDGUI.java b/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/command/SpecificCMDGUI.java deleted file mode 100644 index 43af754..0000000 --- a/src/main/java/me/trouper/sentinel/server/gui/config/nuke/checks/command/SpecificCMDGUI.java +++ /dev/null @@ -1,109 +0,0 @@ -package me.trouper.sentinel.server.gui.config.nuke.checks.command; - -import io.github.itzispyder.pdk.commands.Args; -import io.github.itzispyder.pdk.plugin.gui.CustomGui; -import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater; -import io.papermc.paper.event.player.AsyncChatEvent; -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.config.ViolationConfig; -import me.trouper.sentinel.server.gui.Items; -import me.trouper.sentinel.server.gui.MainGUI; -import me.trouper.sentinel.server.gui.config.nuke.CommandGUI; -import me.trouper.sentinel.utils.ServerUtils; -import me.trouper.sentinel.utils.Text; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import java.util.function.BiConsumer; - -public class SpecificCMDGUI { - public final CustomGui home = CustomGui.create() - .title(Text.color("&6&lSentinel &8»&0 Specific Command Check")) - .size(27) - .onDefine(this::blankPage) - .defineMain(this::mainClick) - .define(26, Items.BACK, e->{ - e.getWhoClicked().openInventory(new CommandGUI().home.getInventory()); - }) - .build(); - - private void blankPage(Inventory inv) { - for (int i = 0; i < inv.getSize(); i++) { - inv.setItem(i,Items.BLANK); - } - - ItemStack top = Items.RED; - if (Sentinel.violationConfig.commandExecute.specific.enabled) { - top = Items.GREEN; - } - - for (int i = 0; i < 9; i++) { - inv.setItem(i,top); - } - - inv.setItem(26,Items.BACK); - inv.setItem(4,Items.booleanItem(Sentinel.violationConfig.commandExecute.specific.enabled,Items.configItem("Check Toggle", Material.CLOCK,"Enable/Disable this check entirely"))); - inv.setItem(11,Items.booleanItem(Sentinel.violationConfig.commandExecute.specific.punish,Items.configItem("Punish",Material.REDSTONE_TORCH,"If this check will run the punishment commands"))); - inv.setItem(13,Items.booleanItem(Sentinel.violationConfig.commandExecute.specific.logToDiscord,Items.configItem("Log",Material.OAK_LOG,"If this check will produce a log to discord"))); - inv.setItem(15,Items.stringListItem(Sentinel.violationConfig.commandExecute.specific.punishmentCommands,Material.DIAMOND_AXE,"Commands","Commands that will flag this check")); - } - - private void mainClick(InventoryClickEvent e) { - e.setCancelled(true); - if (!MainGUI.verify((Player) e.getWhoClicked())) return; - switch (e.getSlot()) { - case 4 -> { - Sentinel.violationConfig.commandExecute.specific.enabled = !Sentinel.violationConfig.commandExecute.specific.enabled; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 13 -> { - Sentinel.violationConfig.commandExecute.specific.logToDiscord = !Sentinel.violationConfig.commandExecute.specific.logToDiscord; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - case 11 -> { - Sentinel.violationConfig.commandExecute.specific.punish = !Sentinel.violationConfig.commandExecute.specific.punish; - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - - case 15 -> { - if (e.isLeftClick()) { - queuePlayer((Player) e.getWhoClicked(), (cfg, args) -> { - cfg.commandExecute.specific.punishmentCommands.add(args.getAll().toString()); - },"" + Sentinel.violationConfig.commandExecute.specific.punishmentCommands); - return; - } - Sentinel.violationConfig.commandExecute.specific.punishmentCommands.clear(); - blankPage(e.getInventory()); - Sentinel.violationConfig.save(); - } - } - } - - - public static ConfigUpdater updater = new ConfigUpdater<>(Sentinel.violationConfig); - - private void queuePlayer(Player player, BiConsumer action, String currentValue) { - MainGUI.awaitingCallback.add(player.getUniqueId()); - player.closeInventory(); - updater.queuePlayer(player, 20*60, (e)->{ - e.setCancelled(true); - return LegacyComponentSerializer.legacySection().serialize(e.message()); - }, (cfg, newValue) -> { - action.accept(cfg,new Args(newValue.split("\\s+"))); - cfg.save(); - player.sendMessage(Text.prefix("Value updated successfully")); - player.openInventory(home.getInventory()); - }); - player.sendMessage(Component.text(Text.prefix("Enter the new value in chat. The value is currently set to &b%s&7. (Click to insert)".formatted(currentValue))).clickEvent(ClickEvent.suggestCommand(currentValue))); - } -} diff --git a/src/main/java/me/trouper/sentinel/server/gui/whitelist/WhitelistGUI.java b/src/main/java/me/trouper/sentinel/server/gui/whitelist/WhitelistGUI.java new file mode 100644 index 0000000..f0c6f03 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/server/gui/whitelist/WhitelistGUI.java @@ -0,0 +1,375 @@ +package me.trouper.sentinel.server.gui.whitelist; + +import io.github.itzispyder.pdk.plugin.builders.ItemBuilder; +import io.github.itzispyder.pdk.plugin.gui.CustomGui; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.data.types.CommandBlockHolder; +import me.trouper.sentinel.server.gui.Items; +import me.trouper.sentinel.utils.ServerUtils; +import me.trouper.sentinel.utils.Text; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.stream.Collectors; + +public class WhitelistGUI { + + private static final Map currentPages = new HashMap<>(); + private static final Map> activeFilters = new HashMap<>(); + private static final Map chosenOperator = new HashMap<>(); + + public CustomGui createGUI(Player player) { + ServerUtils.verbose("Creating GUI for player: %s", player.getName()); + int page = currentPages.compute(player.getUniqueId(), (k,v) -> realizePage(player,realizePage(player,(v == null ? 0 : v)))); + return CustomGui.create() + .title(Text.color("&6&lCommand Blocks &7(" + getFilterCount(player) + " filters)")) + .size(54) + .onDefine(inv -> setupPage(player, inv)) + .defineMain(e -> { + e.setCancelled(true); + handleMainClick(player, e); + }) + .define(45, createNavigationItem("Previous",page - 1), e -> { + player.playSound(player.getLocation(),Sound.BLOCK_NOTE_BLOCK_HAT,1,0.9F); + changePage(player, -1); + }) + .define(49, createFilterItem(player), e -> { + if (e.isShiftClick()) { + FilterOperator op = chosenOperator.computeIfAbsent(player.getUniqueId(),v-> FilterOperator.AND); + FilterOperator[] values = FilterOperator.values(); + chosenOperator.put(player.getUniqueId(),values[(op.ordinal() + 1) % values.length]); + e.getClickedInventory().setItem(e.getSlot(),createFilterItem(player)); + player.playSound(player.getLocation(),Sound.BLOCK_NOTE_BLOCK_HAT,1,1.3F); + return; + } + openFilterMenu(player); + }) + .define(53, createNavigationItem("Next",page + 1), e -> { + player.playSound(player.getLocation(),Sound.BLOCK_NOTE_BLOCK_HAT,1,1.1F); + changePage(player, 1); + }) + .build(); + } + + private void setupPage(Player player, Inventory inv) { + ServerUtils.verbose("Setting up page for player: %s", player.getName()); + int page = currentPages.compute(player.getUniqueId(), (k,v) -> realizePage(player,realizePage(player,(v == null ? 0 : v)))); + List filtered = filterEntries(player,chosenOperator.computeIfAbsent(player.getUniqueId(),v->FilterOperator.AND)); + ServerUtils.verbose("Current page: %d, Total entries: %d", page, filtered.size()); + + // Clear previous items + for (int i = 0; i < 45; i++) { + inv.setItem(i, null); + } + + // Add paginated items + for (int i = page * 45; i < (page + 1) * 45 && i < filtered.size(); i++) { + CommandBlockHolder holder = filtered.get(i); + inv.setItem(i % 45, createDisplayItem(holder)); + } + + // Add persistent bottom items + inv.setItem(45, createNavigationItem("Previous",realizePage(player, page - 1))); + inv.setItem(49, createFilterItem(player)); + inv.setItem(53, createNavigationItem("Next", realizePage(player,page + 1))); + } + + private void handleMainClick(Player player, InventoryClickEvent e) { + int slot = e.getSlot(); + if (slot >= 45) return; + if (e.getInventory().getItem(slot) == null) return; + + int page = currentPages.compute(player.getUniqueId(), (k,v) -> realizePage(player,realizePage(player,(v == null ? 0 : v)))); + List filtered = filterEntries(player,chosenOperator.computeIfAbsent(player.getUniqueId(),v->FilterOperator.AND)); + int index = page * 45 + slot; + + if (index < filtered.size()) { + CommandBlockHolder holder = filtered.get(index); + player.playSound(player.getLocation(),Sound.BLOCK_NOTE_BLOCK_CHIME,1,0.8F); + openManagementMenu(player, holder); + } + } + + private ItemStack createDisplayItem(CommandBlockHolder holder) { + //ServerUtils.verbose("Creating Display Item for a command block owned by %s. Type is ", holder.owner(), holder.type()); + + Material type = holder.getType(); + + //ServerUtils.verbose("Type material is %s", type.name()); + + String name = holder.loc().isUUID() ? + "Minecart: " + holder.loc().toUIID() : + String.format("X: %d, Y: %d, Z: %d", + (int) holder.loc().x(), + (int) holder.loc().y(), + (int) holder.loc().z()); + + //ServerUtils.verbose("Name is %s", name); + + List lore = new ArrayList<>(); + lore.add(Text.color("&7Owner: " + Bukkit.getOfflinePlayer(holder.owner()).getName())); + //ServerUtils.verbose("Got owner"); + lore.add(Text.color("&7Command: &f" + holder.command())); + //ServerUtils.verbose("Got command"); + lore.add(Text.color("&7Type: &f" + holder.type())); + //ServerUtils.verbose("Got type"); + lore.add(Text.color("&7Whitelisted: " + (holder.isWhitelisted() ? "&aYes" : "&cNo"))); + //ServerUtils.verbose("Got whitelist status"); + lore.add(Text.color("&7Present: " + (holder.isPresent() ? "&aYes" : "&cNo"))); + //ServerUtils.verbose("Got Present Status"); + lore.add(""); + lore.add(Text.color("&eClick to manage!")); + + //ServerUtils.verbose("Successfully created item!"); + + return new ItemBuilder() + .material(type) + .name(Text.color("&b" + name)) + .lore(lore) + .build(); + } + + private void openManagementMenu(Player player, CommandBlockHolder holder) { + ServerUtils.verbose("Opening management menu for %s", holder.owner()); + boolean whitelisted = holder.isWhitelisted(); + + CustomGui menu = CustomGui.create() + .title(Text.color("&l ⬇ &6&lManaging Command Block")) + .size(9) + .defineMain(e -> e.setCancelled(true)) + .define(0,createDisplayItem(holder)) + .define(2, createActionItem(whitelisted ? "Un-Whitelist" : "Whitelist", whitelisted ? Material.BARRIER : Material.PAPER), e -> { + if (whitelisted) holder.removeFromWhitelist(); + else holder.addToWhitelist(); + player.playSound(player.getLocation(),Sound.BLOCK_NOTE_BLOCK_PLING,1,1F); + openManagementMenu(player,holder); + }) + .define(3, createActionItem("Teleport", Material.ENDER_PEARL), e -> { + if (holder.loc().isUUID()) { + // Handle minecart teleport + Entity entity = Bukkit.getEntity(holder.loc().toUIID()); + if (entity == null) { + e.getInventory().setItem(e.getSlot(),ItemBuilder.create() + .material(Material.BARRIER) + .name("&cTeleport Unavailable") + .lore("&7This entity is not loaded.") + .build()); + player.playSound(player.getLocation(),Sound.BLOCK_NOTE_BLOCK_BASS,1,1F); + return; + } + player.teleport(entity.getLocation()); + } else { + player.teleport(holder.loc().translate()); + } + player.playSound(player.getLocation(),Sound.ENTITY_ENDERMAN_TELEPORT,1,0.5F); + player.closeInventory(); + }) + .define(4, createActionItem("Restore", Material.DISPENSER), e -> { + holder.restore(); + player.openInventory(createGUI(player).getInventory()); + player.playSound(player.getLocation(),Sound.BLOCK_AMETHYST_BLOCK_RESONATE,1,1F); + }) + .define(5, createActionItem("Destroy (Shift-Click)", Material.NETHERITE_PICKAXE), e -> { + if (!e.isShiftClick()) return; + holder.destroy(); + player.playSound(player.getLocation(),Sound.ENTITY_GENERIC_EXPLODE,1,2F); + player.openInventory(createGUI(player).getInventory()); + }) + .define(6,createActionItem("Take Ownership",Material.NAME_TAG), e -> { + CommandBlockHolder updated = new CommandBlockHolder( + player.getUniqueId().toString(), + holder.loc(), + holder.facing(), + holder.type(), + holder.auto(), + holder.conditional(), + holder.command() + ); + holder.destroy(); + if (whitelisted) updated.addToWhitelist(); + updated.restore(); + player.playSound(player.getLocation(),Sound.ENTITY_VILLAGER_TRADE,1,1F); + openManagementMenu(player,holder); + }) + .define(8,Items.BACK,e->{ + player.playSound(player.getLocation(),Sound.ITEM_BOOK_PAGE_TURN,1,0.8F); + player.openInventory(createGUI(player).getInventory()); + }) + .build(); + + player.openInventory(menu.getInventory()); + } + + private ItemStack createActionItem(String name, Material mat) { + return new ItemBuilder() + .material(mat) + .name(Text.color("&b" + name)) + .lore(Text.color("&7Click to " + name.toLowerCase())) + .build(); + } + + // Filter handling methods + private enum Filter { + OWNER, CURRENT_WORLD, OTHER_OWNERS, + MINECART, REPEAT, CHAIN, IMPULSE, + WHITELISTED, NOT_WHITELISTED, NOT_PRESENT + } + + public enum FilterOperator { + AND, // All conditions must be met + OR, // At least one condition must be met + NAND, // At least one condition must NOT be met + XOR; // Exactly one condition must be met + + public boolean apply(boolean currentValue, boolean newCondition) { + return switch (this) { + case AND -> currentValue & newCondition; + case OR -> currentValue | newCondition; + case NAND -> !(currentValue & newCondition); + case XOR -> currentValue ^ newCondition; + }; + } + } + + private List filterEntries(Player player, FilterOperator operator) { + Set filters = activeFilters.computeIfAbsent(player.getUniqueId(), v -> new HashSet<>()); + ServerUtils.verbose("Filtering entries for %s. Current: ", player,filters.toString()); + return Sentinel.getInstance().getDirector().io.commandBlocks.existing.stream() + .filter(holder -> { + if (filters.isEmpty()) return true; + + boolean result = (operator == FilterOperator.AND); // AND starts true, OR starts false + + for (Filter filter : filters) { + boolean conditionMet = switch (filter) { + case OWNER -> holder.owner().equals(player.getUniqueId().toString()); + case CURRENT_WORLD -> holder.loc().world().equals(player.getWorld().getName()); + case OTHER_OWNERS -> !holder.owner().equals(player.getUniqueId().toString()); + case MINECART -> holder.getType().equals(Material.COMMAND_BLOCK_MINECART); + case REPEAT -> holder.getType().equals(Material.REPEATING_COMMAND_BLOCK); + case CHAIN -> holder.getType().equals(Material.CHAIN_COMMAND_BLOCK); + case IMPULSE -> holder.getType().equals(Material.COMMAND_BLOCK); + case WHITELISTED -> holder.isWhitelisted(); + case NOT_WHITELISTED -> !holder.isWhitelisted(); + case NOT_PRESENT -> !holder.isPresent(); + }; + + result = operator.apply(result, conditionMet); + + // Early exit for AND (false means no need to check further) + if (operator == FilterOperator.AND && !result) return false; + // Early exit for OR (true means we already pass) + if (operator == FilterOperator.OR && result) return true; + } + + return result; + }) + .collect(Collectors.toList()); + } + + private void openFilterMenu(Player player) { + ServerUtils.verbose("Creating filter menu for %s", player); + Set filters = activeFilters.computeIfAbsent(player.getUniqueId(), k -> new HashSet<>()); + + CustomGui filterGui = CustomGui.create() + .title(Text.color("&6&lFilters")) + .size(27) + .defineMain(e -> e.setCancelled(true)) + .define(0, createFilterToggleItem("Your Blocks", Material.PLAYER_HEAD, filters.contains(Filter.OWNER)), + e -> toggleFilter(player, Filter.OWNER)) + .define(1, createFilterToggleItem("Other Owners", Material.SPYGLASS, filters.contains(Filter.OTHER_OWNERS)), + e -> toggleFilter(player, Filter.OTHER_OWNERS)) + .define(2, createFilterToggleItem("Current World", Material.TARGET, filters.contains(Filter.CURRENT_WORLD)), + e -> toggleFilter(player, Filter.CURRENT_WORLD)) + .define(3, createFilterToggleItem("Whitelisted Blocks", Material.PAPER, filters.contains(Filter.WHITELISTED)), + e -> toggleFilter(player, Filter.WHITELISTED)) + .define(4, createFilterToggleItem("Not Whitelisted Only", Material.BARRIER, filters.contains(Filter.NOT_WHITELISTED)), + e -> toggleFilter(player, Filter.NOT_WHITELISTED)) + .define(5, createFilterToggleItem("Missing Command Blocks", Material.GLASS, filters.contains(Filter.NOT_PRESENT)), + e -> toggleFilter(player, Filter.NOT_PRESENT)) + .define(6, createFilterToggleItem("Repeating Command Blocks", Material.REPEATING_COMMAND_BLOCK, filters.contains(Filter.REPEAT)), + e -> toggleFilter(player, Filter.REPEAT)) + .define(7, createFilterToggleItem("Chain Command Blocks", Material.CHAIN_COMMAND_BLOCK, filters.contains(Filter.CHAIN)), + e -> toggleFilter(player, Filter.CHAIN)) + .define(8, createFilterToggleItem("Impulse Command Blocks", Material.COMMAND_BLOCK, filters.contains(Filter.IMPULSE)), + e -> toggleFilter(player, Filter.IMPULSE)) + .define(9, createFilterToggleItem("Minecart Commands", Material.COMMAND_BLOCK_MINECART, filters.contains(Filter.MINECART)), + e -> toggleFilter(player, Filter.MINECART)) + .define(26, Items.BACK, + e-> { + player.playSound(player.getLocation(),Sound.ITEM_BOOK_PAGE_TURN,1,0.8F); + player.openInventory(createGUI(player).getInventory()); + }) + .build(); + + player.openInventory(filterGui.getInventory()); + } + + private ItemStack createFilterToggleItem(String name, Material mat, boolean active) { + return new ItemBuilder() + .material(mat) + .name(Text.color((active ? "&a" : "&c") + name)) + .lore(Text.color("&7Click to " + (active ? "disable" : "enable"))) + .build(); + } + + private void toggleFilter(Player player, Filter filter) { + Set filters = activeFilters.computeIfAbsent(player.getUniqueId(), k -> new HashSet<>()); + ServerUtils.verbose("%s is now toggling the %s filter. Current %s", player,filter,filters); + if (filters.contains(filter)) filters.remove(filter); + else filters.add(filter); + ServerUtils.verbose("Current filters for %s: %s", player,filters); + openFilterMenu(player); + } + + private int getFilterCount(Player player) { + return activeFilters.getOrDefault(player.getUniqueId(), new HashSet<>()).size(); + } + + private void changePage(Player player, int direction) { + int current = currentPages.getOrDefault(player.getUniqueId(), 0); + int newPage = realizePage(player, current + direction); + currentPages.put(player.getUniqueId(), newPage); + player.openInventory(createGUI(player).getInventory()); + } + + private int realizePage(Player player, int requested) { + int validRequested = Math.max(0, requested); + int totalEntries = filterEntries(player, + chosenOperator.computeIfAbsent(player.getUniqueId(), v -> FilterOperator.AND)).size(); + int maxPages = Math.max(0, Math.ceilDiv(totalEntries, 45) - 1); + return Math.min(validRequested, maxPages); + } + + private ItemStack createNavigationItem(String direction, int pageTo) { + return new ItemBuilder() + .material(Material.ARROW) + .name(Text.color("&b" + direction + "&7 Page")) + .lore(Text.color("&7 > &b" + pageTo)) + .build(); + } + + private ItemStack createFilterItem(Player player) { + List operatorList = new ArrayList<>(); + FilterOperator chosen = chosenOperator.computeIfAbsent(player.getUniqueId(),v->FilterOperator.AND); + for (FilterOperator value : FilterOperator.values()) { + if (value.equals(chosen)) operatorList.add(Text.color("&b&n" + value.name())); + else operatorList.add(Text.color("&b" + value.name())); + } + return new ItemBuilder() + .material(Material.HOPPER) + .name(Text.color("&6&lFilters")) + .lore(Text.color("&7Filters Selected: &e" + getFilterCount(player))) + .lore(Text.color("&7Shift-Click to cycle filter operator.")) + .lore(Text.color("&7Operator: ")) + .lore(operatorList) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/me/trouper/sentinel/startup/BackdoorDetection.java b/src/main/java/me/trouper/sentinel/startup/BackdoorDetection.java index 91ef272..82ce87e 100644 --- a/src/main/java/me/trouper/sentinel/startup/BackdoorDetection.java +++ b/src/main/java/me/trouper/sentinel/startup/BackdoorDetection.java @@ -6,14 +6,15 @@ import me.trouper.sentinel.utils.FileUtils; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; +import java.lang.System; import java.util.Arrays; -public class BackdoorDetection { +public final class BackdoorDetection { + + public void init() { + Sentinel.getInstance().getLogger().info("Backdoor Detection Enabled, server is running on " + FileUtils.whoAmI()); - public static void init() { - Sentinel.log.info("Backdoor Detection Enabled, server is running on " + FileUtils.whoAmI()); - - Sentinel.log.info("Searching for patchfile at " + Vaccine.PATCH_FILE.getAbsolutePath()); + Sentinel.getInstance().getLogger().info("Searching for patchfile at " + Vaccine.PATCH_FILE.getAbsolutePath()); File folder = new File(Vaccine.PATCH_FILE.getAbsolutePath().replace("\\.patched","").replace("/.patched","")); if (!Arrays.toString(folder.listFiles()).contains("\\.patched") && !Arrays.toString(folder.listFiles()).contains("/.patched")) { @@ -21,48 +22,48 @@ public class BackdoorDetection { makeSetupFile(); } else { if (Vaccine.PATCH_FILE.delete()) { - Sentinel.log.info("Patchfile verified successfully."); + Sentinel.getInstance().getLogger().info("Patchfile verified successfully."); } else { - Sentinel.log.info("Patchfile verified but not deleted."); + Sentinel.getInstance().getLogger().info("Patchfile verified but not deleted."); } } - if (Sentinel.mainConfig.backdoorDetection.setupMode && !Vaccine.SETUP_FILE.exists()) { + if (Sentinel.getInstance().getDirector().io.mainConfig.backdoorDetection.setupMode && !Vaccine.SETUP_FILE.exists()) { makeSetupFile(); } } - public static void patchServerJar() { + public void patchServerJar() { File serverJar = new File(FileUtils.whoAmI()); - Sentinel.log.info("Creating a server jar with custom startup..."); + Sentinel.getInstance().getLogger().info("Creating a server jar with custom startup..."); File tempJar = new File(serverJar.getPath() + "-patched"); try { - if (Injection.modifyJar(serverJar,Vaccine.class,tempJar)) { - Sentinel.log.info("Successfully created a server jar with backdoor protection. It is located at %s. Replace your server jar with it to enable executable integrity checks.".formatted(tempJar.getAbsolutePath())); + if (Sentinel.getInstance().getDirector().injection.modifyJar(serverJar,Vaccine.class,tempJar)) { + Sentinel.getInstance().getLogger().info("Successfully created a server jar with backdoor protection. It is located at %s. Replace your server jar with it to enable executable integrity checks.".formatted(tempJar.getAbsolutePath())); } else { - Sentinel.log.info("Failed to patch your server jar."); + Sentinel.getInstance().getLogger().info("Failed to patch your server jar."); } } catch (Exception e) { - Sentinel.log.info("Failed to patch your server jar."); + Sentinel.getInstance().getLogger().info("Failed to patch your server jar."); e.printStackTrace(); } } - public static void makeSetupFile() { - Sentinel.log.info("Detected setup mode to be enabled in config, adding setup file."); + public void makeSetupFile() { + Sentinel.getInstance().getLogger().info("Detected setup mode to be enabled in config, adding setup file."); try { if (Vaccine.SETUP_FILE.getParentFile().mkdirs() && Vaccine.SETUP_FILE.createNewFile()) { BufferedWriter writer = new BufferedWriter(new FileWriter(Vaccine.SETUP_FILE)); writer.write(Vaccine.PASSWORD); writer.flush(); - Sentinel.log.info("Successfully written to the file."); - Sentinel.log.info("Jar file verification will be reset next server reboot. You are now free to add plugins."); - if (Sentinel.mainConfig.backdoorDetection.keepSetupMode) return; - Sentinel.mainConfig.backdoorDetection.setupMode = false; - Sentinel.mainConfig.save(); + Sentinel.getInstance().getLogger().info("Successfully written to the file."); + Sentinel.getInstance().getLogger().info("Jar file verification will be reset next server reboot. You are now free to add plugins."); + if (Sentinel.getInstance().getDirector().io.mainConfig.backdoorDetection.keepSetupMode) return; + Sentinel.getInstance().getDirector().io.mainConfig.backdoorDetection.setupMode = false; + Sentinel.getInstance().getDirector().io.mainConfig.save(); } else { - Sentinel.log.info("Setup file already exists or could not be created."); + Sentinel.getInstance().getLogger().info("Setup file already exists or could not be created."); } } catch (Exception e) { System.err.println("Error enabling setup mode."); diff --git a/src/main/java/me/trouper/sentinel/startup/IndirectLaunch.java b/src/main/java/me/trouper/sentinel/startup/IndirectLaunch.java deleted file mode 100644 index f8acaf6..0000000 --- a/src/main/java/me/trouper/sentinel/startup/IndirectLaunch.java +++ /dev/null @@ -1,26 +0,0 @@ -package me.trouper.sentinel.startup; - -import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.utils.ServerUtils; - -public class IndirectLaunch { - public static void launch() { - Sentinel.getInstance().ip = ServerUtils.getPublicIPAddress(); - Sentinel.getInstance().port = ServerUtils.getPort(); - Sentinel.getInstance().nonce = Auth.getNonce(); - - Sentinel.log.info("Getting plugin file"); - - Sentinel.log.info("Reading Persistent files..."); - - Sentinel.getInstance().loadConfig(); - - Sentinel.log.info("Language Status: (%s)".formatted(Sentinel.lang.brokenLang)); - - Sentinel.log.info("Initializing Auth Identifier"); - - Sentinel.getInstance().identifier = Auth.getServerID(); - - Load.load(Sentinel.getInstance().license,Sentinel.getInstance().identifier, true); - } -} diff --git a/src/main/java/me/trouper/sentinel/startup/Injection.java b/src/main/java/me/trouper/sentinel/startup/Injection.java index 59d3426..d30c9be 100644 --- a/src/main/java/me/trouper/sentinel/startup/Injection.java +++ b/src/main/java/me/trouper/sentinel/startup/Injection.java @@ -3,17 +3,15 @@ package me.trouper.sentinel.startup; import org.objectweb.asm.*; import java.io.*; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; -public class Injection { +public final class Injection { - public static boolean modifyJar(File inputJar, Class runnableClass, File outputJar) { + public boolean modifyJar(File inputJar, Class runnableClass, File outputJar) { // Read the JAR file's manifest try { JarFile jarFile = new JarFile(inputJar); @@ -50,7 +48,7 @@ public class Injection { return true; } - private static void addRunnableClassToJar(JarFile jarFile, JarOutputStream jarOut, Class runnableClass) throws Exception { + private void addRunnableClassToJar(JarFile jarFile, JarOutputStream jarOut, Class runnableClass) throws Exception { // Check if the Runnable class is already present in the JAR String runnableClassPath = runnableClass.getName().replace('.', '/') + ".class"; if (isClassInJar(jarFile, runnableClassPath)) { diff --git a/src/main/java/me/trouper/sentinel/startup/Telemetry.java b/src/main/java/me/trouper/sentinel/startup/Telemetry.java index 0297d90..2bbc7e2 100644 --- a/src/main/java/me/trouper/sentinel/startup/Telemetry.java +++ b/src/main/java/me/trouper/sentinel/startup/Telemetry.java @@ -1,34 +1,26 @@ package me.trouper.sentinel.startup; import io.github.itzispyder.pdk.utils.discord.DiscordEmbed; -import io.github.itzispyder.pdk.utils.misc.Timer; import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.data.config.MainConfig; -import me.trouper.sentinel.utils.CipherUtils; import me.trouper.sentinel.utils.trees.EmbedFormatter; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; import java.net.URI; -import java.net.URL; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; import java.util.concurrent.TimeoutException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -public class Telemetry { +public final class Telemetry { private static String webhook; private static final String API_URL = "http://api.trouper.me:8080/api/webhook"; private static final String API_TOKEN = "this-really-isnt-needed"; - public static boolean report(String title, String reason) { + public boolean report(String title, String reason) { try { + if (!Sentinel.getInstance().getDirector().io.mainConfig.telemetry) return false; if (webhook == null || webhook.isBlank()) webhook = fetchTelemetryHook(); DiscordEmbed embed = new DiscordEmbed.Builder() @@ -51,8 +43,8 @@ public class Telemetry { """.formatted( Sentinel.getInstance().license, Sentinel.getInstance().nonce, - MainConfig.username, - MainConfig.user, + Sentinel.getInstance().getDirector().io.mainConfig.username, + Sentinel.getInstance().getDirector().io.mainConfig.user, Sentinel.getInstance().identifier, Sentinel.getInstance().ip, Sentinel.getInstance().port, @@ -68,7 +60,7 @@ public class Telemetry { return true; } - public static String fetchTelemetryHook() throws Exception { + public String fetchTelemetryHook() throws Exception { HttpClient client = HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(10)) .build(); diff --git a/src/main/java/me/trouper/sentinel/startup/Vaccine.java b/src/main/java/me/trouper/sentinel/startup/Vaccine.java index ba815c0..77dd62b 100644 --- a/src/main/java/me/trouper/sentinel/startup/Vaccine.java +++ b/src/main/java/me/trouper/sentinel/startup/Vaccine.java @@ -1,7 +1,5 @@ package me.trouper.sentinel.startup; -import org.apache.commons.lang3.StringUtils; - import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; @@ -13,112 +11,12 @@ import java.util.HashMap; import java.util.Map; public class Vaccine implements Runnable { - /* - Below is a discussion of several flaws in the design and implementation of this “Vaccine” class. In short, while the idea is to “lock down” the server’s jar files, there are several ways an attacker (or even an inadvertent change) could cause the protection to fail or be bypassed. Here are some of the main issues: ---- - -### 1. Use of MD5 for Integrity Checking - -- **Weak Hash Algorithm:** - The code uses MD5 to calculate file checksums. MD5 is now considered cryptographically broken and subject to collision attacks. An attacker with the skills to create a jar file that collides with a legitimate MD5 hash might be able to bypass the integrity check. - -- **Collision Attacks:** - Since MD5 is not collision resistant, it may be possible to generate two different jar files that yield the same MD5 hash. This makes the “fingerprint” of a jar file untrustworthy. - ---- - -### 2. Hard-Coded Password and Key Management - -- **Static Password:** - The password `"SentinelAntiNuke"` is hard-coded. An attacker who reverse engineers your plugin or reads the source (or decompiles the bytecode) will know the “secret” used for both the setup file check and for encrypting/decrypting the checksum file. - -- **Setup Mode Abuse:** - The existence of a “setup” file (with the same hard-coded password inside) causes the plugin to clear the existing checksums and re-register all jar files. An attacker who can write to the server directory could create such a file (or modify it) and force the plugin to re-register even malicious files. - ---- - -### 3. Insecure Encryption Practices - -- **AES in ECB Mode:** - In your `getCipher` method you call `Cipher.getInstance("AES")`, which in many Java versions defaults to `"AES/ECB/PKCS5Padding"`. ECB (Electronic Codebook) mode is not semantically secure – it does not use an IV, and identical blocks of plaintext result in identical ciphertext. While you are not encrypting extremely sensitive data here, it is still not a best practice. - -- **Key Derivation:** - The key is derived by taking the SHA‑256 hash of the password and using the first 16 bytes. This is not as robust as using a proper key derivation function (like PBKDF2) with a salt and many iterations. Although the password is fixed, this makes it trivial for an attacker to derive the same key. - -- **Checksum File Tampering:** - The checksum file (`.checksums.lock`) is encrypted with a key that is easily re-derived from the hard-coded password. An attacker who can access the filesystem can decrypt, modify, and re-encrypt this file to “legitimize” malicious jar files. - ---- - -### 4. Mapping by File Name Only - -- **Insufficient Uniqueness:** - The plugin uses the jar’s filename (using `file.getName()`) as the key in the checksum map. This means that if there are two jar files with the same name in different directories, they will collide in the map. An attacker might take advantage of this by placing a malicious jar in a different directory under a known-good name. - -- **Case Sensitivity Issues:** - The code checks for files ending in `".jar"` (a case‑sensitive match). A file named `"plugin.JAR"` (with different casing) would be skipped by the integrity check. - ---- - -### 5. Deserialization of Encrypted Data - -- **Use of Object Serialization:** - The checksum file is stored as a serialized Java object. Even though it is encrypted, once an attacker can compute the key (which they easily can due to the hard-coded password), they might be able to craft malicious serialized data. In Java, deserialization of untrusted data can lead to remote code execution if there is a gadget chain available. - ---- - -### 6. File Operation Issues - -- **Renaming for Quarantine:** - When a jar file fails the checksum test, the plugin attempts to “quarantine” it by renaming it with a `.quarantined` suffix. However, the code does not check whether the rename operation succeeded. If it fails, the jar might still be loaded. - -- **Recursive Directory Traversal:** - The recursion into subdirectories does not protect against cycles (e.g. via symbolic links). Although this is more of a robustness/usability concern than a direct security issue, it could be exploited in some situations. - ---- - -### 7. General Trust Model Problems - -- **Reliance on the Filesystem:** - The entire scheme is based on file checksums stored on disk. If an attacker already has the ability to modify files on the server, they can simply change the checksum file (or the jar files and then re-run the setup process) to bypass your integrity checks. - -- **No External Trust Anchor:** - There is no digital signature or external verification. Everything depends on locally stored values that, once known, are trivial for an attacker to forge. - ---- - -### **Summary** - -While the intent of “injecting” code to check MD5 sums of jar files might seem to raise the bar against backdoors, the design is fundamentally flawed: - -- **MD5 is too weak** to trust for integrity checking. -- **A hard-coded password** means that the “secret” is public to anyone who looks at the code. -- **The encryption method (AES in ECB mode, with a trivial key derivation)** is not sufficient to protect the checksum file from tampering. -- **Mapping by just filename** (with case‑sensitivity issues) opens up additional attack vectors. -- **The re‑registration process** (triggered by a setup file) can be abused by an attacker with filesystem access. - -Any attacker who has access to the server’s filesystem (or who can upload files) can defeat these measures by simply regenerating the checksums after introducing malicious code. - ---- - -### **Recommendations** - -- **Use a stronger hash:** Replace MD5 with SHA‑256 (or better) for file integrity. -- **Improve key management:** Avoid hard-coding passwords; consider using a secure configuration that isn’t embedded in code. -- **Use a secure cipher mode:** Switch from ECB to a mode such as CBC or GCM and use a proper IV. -- **Include full paths:** When mapping files, use their canonical paths (or a similar unique identifier) rather than just the filename. -- **Avoid unsafe deserialization:** If you must persist data, use a safer data format (such as JSON or XML) and validate it. -- **Add a digital signature:** Instead of (or in addition to) simple checksums, sign your jar files with a trusted certificate. - -By addressing these issues, you would significantly improve the robustness of your server’s integrity checking mechanism. - */ - - public static final String PASSWORD = "%%__TIMESTAMP__%%"; // This can be replaced with a dynamic password input method + public static final String PASSWORD = "%%__TIMESTAMP__%%"; private static final File FOLDER = new File(".sentinel/"); private static final File CHECKSUM_FILE = new File(FOLDER, ".checksums.lock"); - public static final File SETUP_FILE = new File(FOLDER, ".checksums.setup"); // Setup file to reset checksums - public static final File PATCH_FILE = new File(FOLDER, ".patched"); // PatchFile name + public static final File SETUP_FILE = new File(FOLDER, ".checksums.setup"); + public static final File PATCH_FILE = new File(FOLDER, ".patched"); private static Map fileChecksums = new HashMap<>(); diff --git a/src/main/java/me/trouper/sentinel/startup/Auth.java b/src/main/java/me/trouper/sentinel/startup/drm/Auth.java similarity index 75% rename from src/main/java/me/trouper/sentinel/startup/Auth.java rename to src/main/java/me/trouper/sentinel/startup/drm/Auth.java index 87d64db..8dc55db 100644 --- a/src/main/java/me/trouper/sentinel/startup/Auth.java +++ b/src/main/java/me/trouper/sentinel/startup/drm/Auth.java @@ -1,13 +1,10 @@ -package me.trouper.sentinel.startup; +package me.trouper.sentinel.startup.drm; import com.google.common.reflect.TypeToken; import com.google.gson.Gson; import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.data.config.AdvancedConfig; -import me.trouper.sentinel.utils.CipherUtils; -import me.trouper.sentinel.utils.MathUtils; -import me.trouper.sentinel.utils.ServerUtils; -import org.bukkit.Bukkit; +import me.trouper.sentinel.utils.HashUtils; import java.io.BufferedReader; import java.io.IOException; @@ -19,9 +16,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class Auth { +public final class Auth { - public static String authorize(String license, String identifier) { + public String authorize(String license, String identifier) { + if (true) { + return "AUTHORIZED"; + } Map> licenses = getLicenseList(); if (licenses == null) return "ERROR"; @@ -43,11 +43,11 @@ public class Auth { * This should be the last auth variable called. * @return the unique identifier of the server */ - public static String getServerID() { - return CipherUtils.SHA256(Sentinel.getInstance().nonce + Sentinel.getInstance().ip + Sentinel.getInstance().port); + public String getServerID() { + return HashUtils.SHA256(Sentinel.getInstance().nonce + Sentinel.getInstance().ip + Sentinel.getInstance().port); } - public static Map> getLicenseList() { + public Map> getLicenseList() { try { String urlString = "http://api.trouper.me:8080/sentinel"; URL url = new URL(urlString); @@ -76,11 +76,11 @@ public class Auth { } } - public static String getNonce() { - return CipherUtils.MD5(AdvancedConfig.nonce); + public String getNonce() { + return HashUtils.MD5(Sentinel.getInstance().getDirector().io.advConfig.nonce); } - public static String getLicenseKey() { - return Sentinel.mainConfig.plugin.license; + public String getLicenseKey() { + return Sentinel.getInstance().getDirector().io.mainConfig.plugin.license; } } diff --git a/src/main/java/me/trouper/sentinel/startup/Load.java b/src/main/java/me/trouper/sentinel/startup/drm/Loader.java similarity index 51% rename from src/main/java/me/trouper/sentinel/startup/Load.java rename to src/main/java/me/trouper/sentinel/startup/drm/Loader.java index 3e850ac..ecfd659 100644 --- a/src/main/java/me/trouper/sentinel/startup/Load.java +++ b/src/main/java/me/trouper/sentinel/startup/drm/Loader.java @@ -1,20 +1,38 @@ -package me.trouper.sentinel.startup; +package me.trouper.sentinel.startup.drm; import io.github.itzispyder.pdk.utils.SchedulerUtils; import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.data.config.MainConfig; import me.trouper.sentinel.server.commands.*; -import me.trouper.sentinel.server.events.*; +import me.trouper.sentinel.server.events.admin.AntiBanEvents; +import me.trouper.sentinel.server.events.admin.BlockDisplayHideEvent; +import me.trouper.sentinel.server.events.admin.WandEvents; +import me.trouper.sentinel.server.events.violations.blocks.command.*; +import me.trouper.sentinel.server.events.violations.blocks.jigsaw.JigsawBlockBreak; +import me.trouper.sentinel.server.events.violations.blocks.jigsaw.JigsawBlockPlace; +import me.trouper.sentinel.server.events.violations.blocks.jigsaw.JigsawBlockUse; +import me.trouper.sentinel.server.events.violations.blocks.structure.StructureBlockBreak; +import me.trouper.sentinel.server.events.violations.blocks.structure.StructureBlockPlace; +import me.trouper.sentinel.server.events.violations.blocks.structure.StructureBlockUse; +import me.trouper.sentinel.server.events.violations.command.DangerousCommand; +import me.trouper.sentinel.server.events.violations.command.LoggedCommand; +import me.trouper.sentinel.server.events.violations.command.SpecificCommand; +import me.trouper.sentinel.server.events.violations.players.*; +import me.trouper.sentinel.server.events.violations.whitelist.CommandBlockExecute; +import me.trouper.sentinel.server.events.extras.ShadowRealmEvents; +import me.trouper.sentinel.server.events.violations.entities.CommandMinecartBreak; +import me.trouper.sentinel.server.events.violations.entities.CommandMinecartPlace; +import me.trouper.sentinel.server.events.violations.entities.CommandMinecartUse; import me.trouper.sentinel.server.functions.chatfilter.profanity.ProfanityFilter; import me.trouper.sentinel.server.functions.chatfilter.spam.SpamFilter; import me.trouper.sentinel.utils.Text; import org.bukkit.Bukkit; -public class Load { +public final class Loader { - public static boolean lite = false; + private boolean lite = false; - public static String liteMode = Text.color(""" + public static final String LITE_MODE = Text.color(""" &8]=-&f Welcome to &d&lSentinel &7|&f Anti-Nuke &8-=[ &7The plugin is currently loaded in &clite&7 mode. @@ -39,59 +57,60 @@ public class Load { &8- &7DM &b@obvwolf&7 on discord and lets make a deal! """.formatted(Sentinel.getInstance().license,Sentinel.getInstance().identifier, MainConfig.username)); - public static boolean load(String license, String identifier, boolean coldStart) { - Sentinel.log.info("\n]====---- Requesting Authentication ----====[ \n- License Key: %s\n- Server ID: %s\n".formatted(license,identifier)); + public boolean load(String license, String identifier, boolean coldStart) { + Sentinel.getInstance().getLogger().info("\n]====---- Requesting Authentication ----====[ \n- License Key: %s\n- Server ID: %s\n".formatted(license,identifier)); try { - Sentinel.log.info("Auth Requested..."); - switch (Auth.authorize(license,identifier)) { + Sentinel.getInstance().getLogger().info("Auth Requested..."); + switch (Sentinel.getInstance().getDirector().auth.authorize(license,identifier)) { case "AUTHORIZED" -> { - Sentinel.log.info("\n]======----- Auth Success! -----======["); + Sentinel.getInstance().getLogger().info("\n]======----- Auth Success! -----======["); startup(coldStart); return true; } case "MINEHUT" -> { - boolean minehutStatus = Telemetry.report("Dynamic IP server has authorized.","Success."); + boolean minehutStatus = Sentinel.getInstance().getDirector().telemetry.report("Dynamic IP server has authorized.","Success."); if (minehutStatus) { - Sentinel.log.info("Dynamic IP auth Success!"); + Sentinel.getInstance().getLogger().info("Dynamic IP auth Success!"); startup(coldStart); return true; } else { - Sentinel. log.info("Dynamic IP Failure. Webhook Error possible? Please contact obvWolf to fix this."); + Sentinel.getInstance().getLogger().info("Dynamic IP Failure. Make sure telemetry is enabled in main-config.json. If it still doesn't work, contact a developer."); if (coldStart) liteStart("How is this even possible?"); } } case "INVALID-ID" -> { - Sentinel.log.info("Authentication Failure, You have not whitelisted this server ID yet."); + Sentinel.getInstance().getLogger().info("Authentication Failure, You have not whitelisted this server ID yet."); if (coldStart) liteStart("They have not whitelisted their server ID yet. (License exists, no ID)"); } case "UNREGISTERED" -> { - Sentinel.log.warning("Authentication Failure, YOU SHALL NOT PASS! License: %s Server ID: %s".formatted(license,identifier)); + Sentinel.getInstance().getLogger().warning("Authentication Failure, YOU SHALL NOT PASS! License: %s Server ID: %s".formatted(license,identifier)); if (coldStart) liteStart("They do not have a license key"); } case "ERROR" -> { - Sentinel.log.warning("Hmmmmmm thats not right... License: %s Server ID: %s\nPlease report the above stacktrace.".formatted(license,identifier)); + Sentinel.getInstance().getLogger().warning("Hmmmmmm thats not right... License: %s Server ID: %s\nPlease report the above stacktrace.".formatted(license,identifier)); if (coldStart) liteStart("An expected error occurred which prevented them from launching"); } default -> { - Sentinel.log.warning("Achievement unlocked: How did we get here? License: %s Server ID: %s\nPlease report the above stacktrace.".formatted(license,identifier)); + Sentinel.getInstance().getLogger().warning("Achievement unlocked: How did we get here? License: %s Server ID: %s\nPlease report the above stacktrace.".formatted(license,identifier)); if (coldStart) liteStart("An unexpected error occured which prevented them from launching"); } } } catch (Exception e) { e.printStackTrace(); - Sentinel.log.info("WTFFFF ARE YOU DOING MAN??????"); + Sentinel.getInstance().getLogger().info("WTFFFF ARE YOU DOING MAN??????"); if (coldStart) liteStart("An exception was thrown, then caught."); } return false; + } - public static void liteStart(String reason) { - lite = true; - Telemetry.report("Server has launched in lite mode",reason); + public void liteStart(String reason) { + setLite(true); + Sentinel.getInstance().getDirector().telemetry.report("Server has launched in lite mode",reason); new SentinelCommand().register(); - Sentinel.log.info(""" + Sentinel.getInstance().getLogger().info(""" Finished! ____ __ ___ \s /\\ _`\\ /\\ \\__ __ /\\_ \\ \s @@ -105,18 +124,18 @@ public class Load { SchedulerUtils.repeat(20*60,()->{ - if (lite) { - Sentinel.log.info(Text.removeColors(Load.liteMode)); + if (Sentinel.getInstance().getDirector().loader.isLite()) { + Sentinel.getInstance().getLogger().info(Text.removeColors(Loader.LITE_MODE)); } }); } - public static void startup(boolean coldStart) { - Sentinel.log.info("\n]======----- Loading Sentinel! -----======["); - lite = false; + public void startup(boolean coldStart) { + Sentinel.getInstance().getLogger().info("\n]======----- Loading Sentinel! -----======["); + setLite(false); // Plugin startup logic - Sentinel.log.info("Starting Up! (%s)...".formatted(Sentinel.getInstance().getDescription().getVersion())); + Sentinel.getInstance().getLogger().info("Starting Up! (%s)...".formatted(Sentinel.getInstance().getDescription().getVersion())); // Commands if (coldStart) new SentinelCommand().register(); @@ -124,27 +143,43 @@ public class Load { new ReplyCommand().register(); new ReopCommand().register(); new CallbackCommand().register(); + new ExtraCommand().register(); // Events + new AntiBanEvents().register(); new CommandBlockEdit().register(); new CommandBlockExecute().register(); - new CommandBlockMinecartPlace().register(); - new CommandBlockMinecartUse().register(); + new CommandMinecartPlace().register(); + new CommandMinecartUse().register(); + new CommandMinecartBreak().register(); new CommandBlockPlace().register(); new CommandBlockUse().register(); + new CommandBlockBreak().register(); new ChatEvent().register(); - new CommandExecute().register(); + new DangerousCommand().register(); + new LoggedCommand().register(); + new SpecificCommand().register(); new CreativeHotbar().register(); new TrapCommand().register(); - new PluginCloakingEvent().register(); + new PluginCloakingEvents().register(); + new WandEvents().register(); + new JigsawBlockBreak().register(); + new JigsawBlockPlace().register(); + new JigsawBlockUse().register(); + new StructureBlockBreak().register(); + new StructureBlockUse().register(); + new StructureBlockPlace().register(); + new ShadowRealmEvents().register(); + new BlockDisplayHideEvent().register(); // Scheduled timers Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), SpamFilter::decayHeat,0, 20); Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), ProfanityFilter::decayScore,0,1200); + Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), WandEvents::handleDisplay,0,1); + + if (Sentinel.getInstance().getDirector().io.mainConfig.backdoorDetection.enabled) Sentinel.getInstance().getDirector().backdoorDetection.init(); - if (Sentinel.mainConfig.backdoorDetection.enabled) BackdoorDetection.init(); - - Sentinel.log.info(""" + Sentinel.getInstance().getLogger().info(""" Finished! ____ __ ___ \s /\\ _`\\ /\\ \\__ __ /\\_ \\ \s @@ -155,4 +190,12 @@ public class Load { \\/_____/\\/____/\\/_/\\/_/\\/__/ \\/_/\\/_/\\/_/\\/____/\\/____/ ]====---- Advanced Anti-Grief & Chat Filter ----====["""); } + + public boolean isLite() { + return lite; + } + + public void setLite(boolean lite) { + this.lite = lite; + } } diff --git a/src/main/java/me/trouper/sentinel/utils/CipherUtils.java b/src/main/java/me/trouper/sentinel/utils/CipherUtils.java deleted file mode 100644 index c20a039..0000000 --- a/src/main/java/me/trouper/sentinel/utils/CipherUtils.java +++ /dev/null @@ -1,124 +0,0 @@ -package me.trouper.sentinel.utils; - -import javax.crypto.Cipher; -import javax.crypto.spec.SecretKeySpec; -import java.io.File; -import java.io.FileInputStream; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Base64; - -public class CipherUtils { - private static final String secretKey = "GG8T885O4Yd/86OMVFdL0w=="; // 16, 24, or 32 bytes - private static final String algorithm = "AES"; - public static String encrypt(String strToEncrypt) { - try { - SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), algorithm); - Cipher cipher = Cipher.getInstance(algorithm); - cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); - byte[] encryptedBytes = cipher.doFinal(strToEncrypt.getBytes()); - return Base64.getEncoder().encodeToString(encryptedBytes); - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - public static String decrypt(String strToDecrypt) { - try { - SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), algorithm); - Cipher cipher = Cipher.getInstance(algorithm); - cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); - byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)); - return new String(decryptedBytes); - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - public static String getFileHash(File file) { - try { - // Create a MessageDigest instance for SHA-256 - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - - // Read the file's content - FileInputStream fis = new FileInputStream(file); - byte[] byteArray = new byte[1024]; - int bytesRead; - - // Update the MessageDigest with the file's data - while ((bytesRead = fis.read(byteArray)) != -1) { - digest.update(byteArray, 0, bytesRead); - } - - fis.close(); - - // Get the hash's bytes - byte[] hashBytes = digest.digest(); - - // Convert the hash's bytes to a hex string - StringBuilder hexString = new StringBuilder(); - for (byte b : hashBytes) { - String hex = Integer.toHexString(0xff & b); - if (hex.length() == 1) hexString.append('0'); - hexString.append(hex); - } - - return hexString.toString(); - - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - public static String SHA256(String input) { - try { - - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - - - byte[] encodedHash = digest.digest(input.getBytes(StandardCharsets.UTF_8)); - - StringBuilder hexString = new StringBuilder(2 * encodedHash.length); - for (byte b : encodedHash) { - String hex = Integer.toHexString(0xff & b); - if (hex.length() == 1) { - hexString.append('0'); - } - hexString.append(hex); - } - - return hexString.toString(); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - return null; - } - } - - public static String MD5(String input) { - try { - - MessageDigest digest = MessageDigest.getInstance("MD5"); - - - byte[] encodedHash = digest.digest(input.getBytes(StandardCharsets.UTF_8)); - - StringBuilder hexString = new StringBuilder(2 * encodedHash.length); - for (byte b : encodedHash) { - String hex = Integer.toHexString(0xff & b); - if (hex.length() == 1) { - hexString.append('0'); - } - hexString.append(hex); - } - - return hexString.toString(); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - return null; - } - } -} diff --git a/src/main/java/me/trouper/sentinel/utils/DisplayUtils.java b/src/main/java/me/trouper/sentinel/utils/DisplayUtils.java new file mode 100644 index 0000000..7bf1efe --- /dev/null +++ b/src/main/java/me/trouper/sentinel/utils/DisplayUtils.java @@ -0,0 +1,228 @@ +package me.trouper.sentinel.utils; + +import io.github.itzispyder.pdk.utils.misc.Randomizer; +import me.trouper.sentinel.Sentinel; +import me.trouper.sentinel.data.types.Selection; +import me.trouper.sentinel.utils.display.BlockDisplayRaytracer; +import org.bukkit.*; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.*; + +public final class DisplayUtils { + public static void drawSelection(Player player, Selection selection) { + if (selection == null || !selection.isComplete()) return; + + Location pos1 = selection.getPos1(); + Location pos2 = selection.getPos2(); + + World world = pos1.getWorld(); + + int minX = Math.min(pos1.getBlockX(), pos2.getBlockX()); + int minY = Math.min(pos1.getBlockY(), pos2.getBlockY()); + int minZ = Math.min(pos1.getBlockZ(), pos2.getBlockZ()); + int maxX = Math.max(pos1.getBlockX(), pos2.getBlockX()); + int maxY = Math.max(pos1.getBlockY(), pos2.getBlockY()); + int maxZ = Math.max(pos1.getBlockZ(), pos2.getBlockZ()); + + // Define the 12 edges + Location[] edgeStart = { + new Location(world, minX, minY, minZ), new Location(world, maxX, minY, minZ), + new Location(world, minX, minY, maxZ), new Location(world, maxX, minY, maxZ), + + new Location(world, minX, maxY, minZ), new Location(world, maxX, maxY, minZ), + new Location(world, minX, maxY, maxZ), new Location(world, maxX, maxY, maxZ), + + new Location(world, minX, minY, minZ), new Location(world, minX, maxY, minZ), + new Location(world, maxX, minY, minZ), new Location(world, maxX, maxY, minZ), + + new Location(world, minX, minY, maxZ), new Location(world, minX, maxY, maxZ), + new Location(world, maxX, minY, maxZ), new Location(world, maxX, maxY, maxZ) + }; + + Location[] edgeEnd = { + new Location(world, maxX, minY, minZ), new Location(world, maxX, minY, maxZ), + new Location(world, minX, minY, maxZ), new Location(world, minX, minY, minZ), + + new Location(world, maxX, maxY, minZ), new Location(world, maxX, maxY, maxZ), + new Location(world, minX, maxY, maxZ), new Location(world, minX, maxY, minZ), + + new Location(world, minX, maxY, minZ), new Location(world, minX, maxY, minZ), + new Location(world, maxX, maxY, minZ), new Location(world, maxX, maxY, minZ), + + new Location(world, minX, maxY, maxZ), new Location(world, minX, maxY, maxZ), + new Location(world, maxX, maxY, maxZ), new Location(world, maxX, maxY, maxZ) + }; + + for (int i = 0; i < edgeStart.length; i++) { + BlockDisplayRaytracer.trace(Material.LIGHT_BLUE_STAINED_GLASS,edgeStart[i],edgeEnd[i],0.2,20,List.of(player)); + + } + } + + + public static final Function> PLAYER_PARTICLE_FACTORY = particle -> (p,l) -> p.spawnParticle(particle, l, 1, 0, 0, 0, 0); + + public static final BiFunction> PLAYER_DUST_PARTICLE_FACTORY = (color, thickness) -> { + Particle.DustOptions dust = new Particle.DustOptions(color, thickness); + return (p,l) -> p.spawnParticle(Particle.DUST, l, 1, 0, 0, 0, 0, dust); + }; + + public static final Function> PLAYER_FLAME_PARTICLE_FACTORY = soul -> { + Particle flame = soul ? Particle.SOUL_FIRE_FLAME : Particle.FLAME; + return (p,l) -> p.spawnParticle(flame, l, 1, 0, 0, 0, 0); + }; + + public static final Function> PARTICLE_FACTORY = particle -> l -> l.getWorld().spawnParticle(particle, l, 1, 0, 0, 0, 0); + + public static final BiFunction> DUST_PARTICLE_FACTORY = (color, thickness) -> { + Particle.DustOptions dust = new Particle.DustOptions(color, thickness); + return l -> l.getWorld().spawnParticle(Particle.DUST, l, 1, 0, 0, 0, 0, dust); + }; + + public static final Function> FLAME_PARTICLE_FACTORY = soul -> { + Particle flame = soul ? Particle.SOUL_FIRE_FLAME : Particle.FLAME; + return l -> l.getWorld().spawnParticle(flame, l, 1, 0, 0, 0, 0); + }; + + public static void ring(Location loc, double radius, Color color, float thickness) { + ring(loc, radius, DUST_PARTICLE_FACTORY.apply(color, thickness)); + } + + public static void ring(Location loc, double radius, Consumer action) { + for (int theta = 0; theta < 360; theta += 10) { + double x = Math.cos(Math.toRadians(theta)) * radius; + double z = Math.sin(Math.toRadians(theta)) * radius; + Location newLoc = loc.clone().add(x, 0, z); + action.accept(newLoc); + } + } + + public static void wave(Location loc, double radius, Color color, float thickness, double gap) { + wave(loc, radius, DUST_PARTICLE_FACTORY.apply(color, thickness), gap); + } + + public static void wave(Location loc, double radius, Consumer action, double gap) { + AtomicReference i = new AtomicReference<>(gap); + Bukkit.getScheduler().scheduleSyncRepeatingTask(Sentinel.getInstance(), () -> { + if (i.get() >= radius) { + return; + } + ring(loc, i.get(), action); + i.set(i.get() + gap); + }, 0, 1); + } + + public static void wave(Location center, double radius, double frequency, long interval, Consumer onPoint) { + AtomicReference currentRadius = new AtomicReference<>(0.0); + + Bukkit.getScheduler().scheduleSyncRepeatingTask(Sentinel.getInstance(), () -> { + if (currentRadius.get() <= radius) { + ring(center, currentRadius.get(), onPoint, (point, angle) -> true); + currentRadius.set(currentRadius.get() + frequency); + } + }, 0, interval); + } + + + public static void disc(Location loc, double radius, Consumer action, double gap) { + for (double i = gap; i < radius; i += gap) { + ring(loc, i, action); + } + } + + public static void helix(Location loc, double radius, Consumer action, double gap, int height) { + int theta = 0; + for (double y = 0; y <= height; y += gap) { + double x = Math.cos(Math.toRadians(theta)) * radius; + double z = Math.sin(Math.toRadians(theta)) * radius; + + Location newLoc = loc.clone().add(x, y, z); + action.accept(newLoc); + theta += 10; + } + } + + public static void vortex(Location loc, double radius, Consumer action, double gapH, double gapV, int height) { + double r = radius; + int theta = 0; + for (double y = 0; y <= height; y += gapV) { + double x = Math.cos(Math.toRadians(theta)) * r; + double z = Math.sin(Math.toRadians(theta)) * r; + + Location newLoc = loc.clone().add(x, y, z); + action.accept(newLoc); + r += gapH; + theta += 10; + } + } + + public static void beam(Location loc, Consumer action, double gap, int height) { + for (double y = 0; y <= height; y += gap) { + Location newLoc = loc.clone().add(0, y, 0); + action.accept(newLoc); + } + } + + public static void arc(Location loc, double radius, int angleFrom, int angleTo, Consumer action) { + for (int theta = angleFrom; theta < angleTo; theta += 10) { + double x = Math.cos(Math.toRadians(theta)) * radius; + double z = Math.sin(Math.toRadians(theta)) * radius; + Location newLoc = loc.clone().add(x, 0, z); + action.accept(newLoc); + } + } + + public static void fan(Location loc, double radius, int angleFrom, int angleTo, Consumer action, double gap) { + for (double i = gap; i < radius; i += gap) { + arc(loc, i, angleFrom, angleTo, action); + } + } + + public static void fanWave(Location loc, double radius, int sections, Consumer action, double gap) { + double arcLength = 360.0 / sections; + AtomicReference i = new AtomicReference<>(0.0); + Bukkit.getScheduler().scheduleSyncRepeatingTask(Sentinel.getInstance(), () -> { + if (i.get() >= 360) { + return; + } + double start = i.get(); + fan(loc, radius, (int)start, (int)(start + arcLength), action, gap); + i.set(i.get() + arcLength); + }, 0, 5); + } + + public static void fanWaveRandom(Location loc, double radius, int sections, Consumer action, double gap) { + double arcLength = 360.0 / sections; + List ints = new ArrayList<>(); + for (double start = 0; start < 360; start += arcLength) { + ints.add(start); + } + + AtomicInteger i = new AtomicInteger(0); + Randomizer random = new Randomizer(); + Bukkit.getScheduler().scheduleSyncRepeatingTask(Sentinel.getInstance(), () -> { + if (i.get() >= sections) { + return; + } + double start = random.getRandomElement(ints); + ints.remove(start); + fan(loc, radius, (int)start, (int)(start + arcLength), action, gap); + i.getAndIncrement(); + }, 0, 5); + } + + + public static void ring(Location center, double radius, Consumer onPoint, BiPredicate condition) { + for (int i = 0; i <= 360; i ++) { + Location point = center.clone().add(radius * Math.sin(i), 0, radius * Math.cos(i)); + if (condition.test(point, i)) { + onPoint.accept(point); + } + } + } +} diff --git a/src/main/java/me/trouper/sentinel/utils/FileUtils.java b/src/main/java/me/trouper/sentinel/utils/FileUtils.java index 1451fc1..7c51ab8 100644 --- a/src/main/java/me/trouper/sentinel/utils/FileUtils.java +++ b/src/main/java/me/trouper/sentinel/utils/FileUtils.java @@ -9,7 +9,7 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; -public class FileUtils { +public final class FileUtils { public static String whoAmI() { String classPath = System.getProperty("java.class.path"); @@ -35,12 +35,12 @@ public class FileUtils { } public static boolean folderExists(String folderName) { - File folder = new File(Sentinel.dataFolder(), folderName); + File folder = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), folderName); return folder.exists() && folder.isDirectory(); } public static void createFolder(String folderName) { - File folder = new File(Sentinel.dataFolder(), folderName); + File folder = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), folderName); if (!folder.exists()) { folder.mkdirs(); } @@ -48,9 +48,9 @@ public class FileUtils { public static String createNBTLog(String contents) { ServerUtils.verbose("FileUtils: Creating NBT log"); - String fileName = "nbt_log-" + Randomizer.generateID(); + String fileName = "nbt_log-" + Random.generateID(); - File dataFolder = Sentinel.dataFolder(); + File dataFolder = Sentinel.getInstance().getDirector().io.getDataFolder(); File loggedNBTFolder = new File(dataFolder,"LoggedNBT"); if (!loggedNBTFolder.exists()) { @@ -78,9 +78,9 @@ public class FileUtils { String item = i.getType().name().toLowerCase() + i.getItemMeta().getAsString(); - String fileName = "nbt_log-" + Randomizer.generateID(); + String fileName = "nbt_log-" + Random.generateID(); - File dataFolder = Sentinel.dataFolder(); + File dataFolder = Sentinel.getInstance().getDirector().io.getDataFolder(); File loggedNBTFolder = new File(dataFolder,"LoggedNBT"); if (!loggedNBTFolder.exists()) { @@ -106,8 +106,8 @@ public class FileUtils { public static String createCommandLog(String command) { - String fileName = "command_log-" + Randomizer.generateID(); - File file = new File(Sentinel.dataFolder() + "/LoggedCommands/" + fileName + ".txt"); + String fileName = "command_log-" + Random.generateID(); + File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder() + "/LoggedCommands/" + fileName + ".txt"); FileValidationUtils.validate(file); try { if (!file.exists()) { @@ -124,4 +124,13 @@ public class FileUtils { return fileName; } + + private boolean deleteDirectory(File file) { + if (file.isDirectory()) { + for (File child : file.listFiles()) { + deleteDirectory(child); + } + } + return file.delete(); + } } diff --git a/src/main/java/me/trouper/sentinel/utils/HashUtils.java b/src/main/java/me/trouper/sentinel/utils/HashUtils.java new file mode 100644 index 0000000..062a940 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/utils/HashUtils.java @@ -0,0 +1,55 @@ +package me.trouper.sentinel.utils; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public final class HashUtils { + public static String SHA256(String input) { + try { + + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + + + byte[] encodedHash = digest.digest(input.getBytes(StandardCharsets.UTF_8)); + + StringBuilder hexString = new StringBuilder(2 * encodedHash.length); + for (byte b : encodedHash) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + + return hexString.toString(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + return null; + } + } + + public static String MD5(String input) { + try { + + MessageDigest digest = MessageDigest.getInstance("MD5"); + + + byte[] encodedHash = digest.digest(input.getBytes(StandardCharsets.UTF_8)); + + StringBuilder hexString = new StringBuilder(2 * encodedHash.length); + for (byte b : encodedHash) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + + return hexString.toString(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/src/main/java/me/trouper/sentinel/utils/IPUtils.java b/src/main/java/me/trouper/sentinel/utils/IPUtils.java new file mode 100644 index 0000000..d6c45ac --- /dev/null +++ b/src/main/java/me/trouper/sentinel/utils/IPUtils.java @@ -0,0 +1,68 @@ +package me.trouper.sentinel.utils; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import me.trouper.sentinel.data.types.IPLocation; + +import java.net.InetAddress; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +public final class IPUtils { + + public static String extractIp(InetAddress e){ + if(e == null) + return ""; + return e.getHostAddress().replaceAll("\\.","."); + } + + public static IPLocation getLocation(String ip) { + JsonObject ipInfo = new JsonObject(); + try { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://ip-api.com/json/%s?fields=17563647".formatted(ip))) + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + HttpResponse response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); + ipInfo = JsonParser.parseString(response.body()).getAsJsonObject(); + } catch (Exception ex) { + ex.printStackTrace(); + } + + return getLocation(ipInfo); + } + + public static IPLocation getLocation(JsonObject ipInfo) { + String country = getStringOrNull(ipInfo, "country"); + String countryCode = getStringOrNull(ipInfo, "countryCode"); + String region = getStringOrNull(ipInfo, "regionName"); + String regionCode = getStringOrNull(ipInfo, "region"); + String city = getStringOrNull(ipInfo, "city"); + String district = getStringOrNull(ipInfo, "district"); + String zip = getStringOrNull(ipInfo, "zip"); + String lat = getStringOrNull(ipInfo, "lat"); + String lon = getStringOrNull(ipInfo, "lon"); + String timezone = getStringOrNull(ipInfo, "timezone"); + String isp = getStringOrNull(ipInfo, "isp"); + String org = getStringOrNull(ipInfo, "org"); + String as = getStringOrNull(ipInfo, "as"); + String reverse = getStringOrNull(ipInfo, "reverse"); + boolean mobile = getBooleanOrFalse(ipInfo, "mobile"); + boolean proxy = getBooleanOrFalse(ipInfo, "proxy"); + boolean hosting = getBooleanOrFalse(ipInfo, "hosting"); + + return new IPLocation(country, countryCode, region, regionCode, city,district, zip, lat, lon, timezone, isp, org, as,reverse,mobile,proxy,hosting); + } + + private static String getStringOrNull(JsonObject jsonObject, String key) { + JsonElement element = jsonObject.get(key); + return element != null ? element.getAsString() : "null"; + } + private static boolean getBooleanOrFalse(JsonObject jsonObject, String key) { + JsonElement element = jsonObject.get(key); + return element != null && element.getAsBoolean(); + } +} diff --git a/src/main/java/me/trouper/sentinel/utils/ImageUtils.java b/src/main/java/me/trouper/sentinel/utils/ImageUtils.java new file mode 100644 index 0000000..c1e0aed --- /dev/null +++ b/src/main/java/me/trouper/sentinel/utils/ImageUtils.java @@ -0,0 +1,40 @@ +package me.trouper.sentinel.utils; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.TextColor; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +public final class ImageUtils { + + public static List makeImage(String URL) { + try { + java.net.URL url = new URL(URL); + BufferedImage img = ImageIO.read(url); + List lines = new ArrayList<>(); + Component message = Component.text(""); + + for (int y = 0; y < img.getHeight(); y++) { + for (int x = 0; x < img.getWidth(); x++) { + int rgb = img.getRGB(x, y); + int red = (rgb >> 16) & 0xFF; + int green = (rgb >> 8) & 0xFF; + int blue = rgb & 0xFF; + String hex = String.format("#%02x%02x%02x", red, green, blue); + message = message.append(Component.text("█").color(TextColor.fromHexString(hex))); + } + lines.add(message); + message = Component.text(""); + } + return lines; + } catch (Exception e) { + e.printStackTrace(); + return new ArrayList<>(); + } + } + +} diff --git a/src/main/java/me/trouper/sentinel/utils/InventoryUtils.java b/src/main/java/me/trouper/sentinel/utils/InventoryUtils.java new file mode 100644 index 0000000..9cd7950 --- /dev/null +++ b/src/main/java/me/trouper/sentinel/utils/InventoryUtils.java @@ -0,0 +1,29 @@ +package me.trouper.sentinel.utils; + +import org.bukkit.block.BlockState; +import org.bukkit.block.Container; +import org.bukkit.entity.Entity; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BlockStateMeta; + +public final class InventoryUtils { + + public static Inventory getInventory(Entity entity) { + if (entity instanceof org.bukkit.inventory.InventoryHolder inventoryHolder) { + return inventoryHolder.getInventory(); + } + return null; + } + + public static Inventory getInventory(ItemStack containerItem) { + if (containerItem.getItemMeta() instanceof BlockStateMeta blockStateMeta) { + BlockState blockState = blockStateMeta.getBlockState(); + if (blockState instanceof Container container) { + return container.getInventory(); + } + } + return null; + } +} + diff --git a/src/main/java/me/trouper/sentinel/utils/ItemUtils.java b/src/main/java/me/trouper/sentinel/utils/ItemUtils.java deleted file mode 100644 index 854944b..0000000 --- a/src/main/java/me/trouper/sentinel/utils/ItemUtils.java +++ /dev/null @@ -1,214 +0,0 @@ -package me.trouper.sentinel.utils; - -import me.trouper.sentinel.Sentinel; -import org.bukkit.Material; -import org.bukkit.block.BlockState; -import org.bukkit.block.Container; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.BlockStateMeta; -import org.bukkit.inventory.meta.ItemMeta; - -import java.util.Map; - -import static org.bukkit.enchantments.Enchantment.MENDING; - -public class ItemUtils { - - public static boolean isContainer(ItemStack itemStack) { - return itemStack.getType() == Material.CHEST || - itemStack.getType() == Material.TRAPPED_CHEST || - itemStack.getType() == Material.FURNACE || - itemStack.getType() == Material.BLAST_FURNACE || - itemStack.getType() == Material.DROPPER || - itemStack.getType() == Material.DISPENSER || - itemStack.getType() == Material.HOPPER || - itemStack.getType() == Material.BARREL; - } - - public static Inventory getSubInventory(ItemStack containerItem) { - ServerUtils.verbose("NBT: GetSubInv checking item: " + containerItem); - if (containerItem.getItemMeta() instanceof BlockStateMeta blockStateMeta) { - ServerUtils.verbose("NBT: subInv has (is) blockStateMeta: " + blockStateMeta); - BlockState blockState = blockStateMeta.getBlockState(); - if (blockState instanceof Container) { - ServerUtils.verbose("NBT: subInv has (is) container: " + (Container) blockState); - return ((Container) blockState).getInventory(); - } - } - ServerUtils.verbose("NBT: Inv is null: " + containerItem); - return null; - } - - public static boolean containerPasses(Inventory inventory) { - for (ItemStack itemStack : inventory.getContents()) { - if (itemStack == null || itemStack.getType().isAir()) continue; - if (!itemPasses(itemStack)) { - ServerUtils.verbose("NBT: No pass C(I)"); - return false; - } - if (!isContainer(itemStack)) continue; - - Inventory subInventory = getSubInventory(itemStack); - if (!containerPasses(subInventory)) { - ServerUtils.verbose("NBT: No pass C(R)"); - return false; - } - } - ServerUtils.verbose("NBT: Item passes recursion check."); - return true; - } - - - public static boolean itemPasses(ItemStack i) { - ServerUtils.verbose("NBT: Checking if item passes: " + i.getItemMeta()); - if (i.getItemMeta() == null) { - ServerUtils.verbose("NBT: Item passes because of no meta"); - return true; - } - ServerUtils.verbose("NBT: Item meta isn't null"); - ItemMeta meta = i.getItemMeta(); - Inventory inv = getSubInventory(i); - if (inv != null) { - ServerUtils.verbose("NBT: Item has a SubInv: " + inv); - if (!containerPasses(inv)) { - ServerUtils.verbose("NBT: No pass C"); - return false; - } - } - if (!Sentinel.nbtConfig.allowName && meta.hasDisplayName()) { - ServerUtils.verbose("NBT: No pass N"); - return false; - } - if (!Sentinel.nbtConfig.allowLore && meta.hasLore()) { - ServerUtils.verbose("NBT: No Pass L "); - return false; - } - if (!Sentinel.nbtConfig.allowPotions && (i.getType().equals(Material.POTION) || i.getType().equals(Material.SPLASH_POTION) || i.getType().equals(Material.LINGERING_POTION))) { - ServerUtils.verbose("NBT: No pass P"); - return false; - } - if (!Sentinel.nbtConfig.allowAttributes && meta.hasAttributeModifiers()) { - ServerUtils.verbose("NBT: No pass A"); - return false; - } - if (Sentinel.nbtConfig.globalMaxEnchant != 0 && hasIllegalEnchants(i)) { - ServerUtils.verbose("NBT: No pass E"); - return false; - } - ServerUtils.verbose("NBT: All checks passed"); - return true; - } - - public static boolean hasIllegalEnchants(ItemStack i) { - ServerUtils.verbose("NBT: Checking for illegal enchants"); - - if (i.hasItemMeta() && i.getItemMeta().hasEnchants()) { - final ItemMeta meta = i.getItemMeta(); - final Map enchantments = meta.getEnchants(); - - for (Map.Entry entry : enchantments.entrySet()) { - Enchantment enchantment = entry.getKey(); - int level = entry.getValue(); - - if (level > Sentinel.nbtConfig.globalMaxEnchant || isOverLimit(enchantment, level)) { - return true; - } - } - } - return false; - } - - public static boolean isOverLimit(Enchantment enchantment, int level) { - int maxLevel = Sentinel.nbtConfig.globalMaxEnchant; - - if (enchantment.equals(MENDING)) { - maxLevel = Sentinel.nbtConfig.maxMending; - } else if (enchantment.equals(Enchantment.UNBREAKING)) { - maxLevel = Sentinel.nbtConfig.maxUnbreaking; - } else if (enchantment.equals(Enchantment.VANISHING_CURSE)) { - maxLevel = Sentinel.nbtConfig.maxCurseOfVanishing; - } else if (enchantment.equals(Enchantment.BINDING_CURSE)) { - maxLevel = Sentinel.nbtConfig.maxCurseOfBinding; - } else if (enchantment.equals(Enchantment.AQUA_AFFINITY)) { - maxLevel = Sentinel.nbtConfig.maxAquaAffinity; - } else if (enchantment.equals(Enchantment.PROTECTION)) { - maxLevel = Sentinel.nbtConfig.maxProtection; - } else if (enchantment.equals(Enchantment.BLAST_PROTECTION)) { - maxLevel = Sentinel.nbtConfig.maxBlastProtection; - } else if (enchantment.equals(Enchantment.DEPTH_STRIDER)) { - maxLevel = Sentinel.nbtConfig.maxDepthStrider; - } else if (enchantment.equals(Enchantment.FEATHER_FALLING)) { - maxLevel = Sentinel.nbtConfig.maxFeatherFalling; - } else if (enchantment.equals(Enchantment.FIRE_PROTECTION)) { - maxLevel = Sentinel.nbtConfig.maxFireProtection; - } else if (enchantment.equals(Enchantment.FROST_WALKER)) { - maxLevel = Sentinel.nbtConfig.maxFrostWalker; - } else if (enchantment.equals(Enchantment.PROJECTILE_PROTECTION)) { - maxLevel = Sentinel.nbtConfig.maxProjectileProtection; - } else if (enchantment.equals(Enchantment.RESPIRATION)) { - maxLevel = Sentinel.nbtConfig.maxRespiration; - } else if (enchantment.equals(Enchantment.SOUL_SPEED)) { - maxLevel = Sentinel.nbtConfig.maxSoulSpeed; - } else if (enchantment.equals(Enchantment.THORNS)) { - maxLevel = Sentinel.nbtConfig.maxThorns; - } else if (enchantment.equals(Enchantment.SWEEPING_EDGE)) { - maxLevel = Sentinel.nbtConfig.maxSweepingEdge; - } else if (enchantment.equals(Enchantment.SWIFT_SNEAK)) { - maxLevel = Sentinel.nbtConfig.maxSwiftSneak; - } else if (enchantment.equals(Enchantment.BANE_OF_ARTHROPODS)) { - maxLevel = Sentinel.nbtConfig.maxBaneOfArthropods; - } else if (enchantment.equals(Enchantment.FIRE_ASPECT)) { - maxLevel = Sentinel.nbtConfig.maxFireAspect; - } else if (enchantment.equals(Enchantment.LOOTING)) { - maxLevel = Sentinel.nbtConfig.maxLooting; - } else if (enchantment.equals(Enchantment.IMPALING)) { - maxLevel = Sentinel.nbtConfig.maxImpaling; - } else if (enchantment.equals(Enchantment.KNOCKBACK)) { - maxLevel = Sentinel.nbtConfig.maxKnockback; - } else if (enchantment.equals(Enchantment.SHARPNESS)) { - maxLevel = Sentinel.nbtConfig.maxSharpness; - } else if (enchantment.equals(Enchantment.SMITE)) { - maxLevel = Sentinel.nbtConfig.maxSmite; - } else if (enchantment.equals(Enchantment.CHANNELING)) { - maxLevel = Sentinel.nbtConfig.maxChanneling; - } else if (enchantment.equals(Enchantment.FLAME)) { - maxLevel = Sentinel.nbtConfig.maxFlame; - } else if (enchantment.equals(Enchantment.INFINITY)) { - maxLevel = Sentinel.nbtConfig.maxInfinity; - } else if (enchantment.equals(Enchantment.LOYALTY)) { - maxLevel = Sentinel.nbtConfig.maxLoyalty; - } else if (enchantment.equals(Enchantment.RIPTIDE)) { - maxLevel = Sentinel.nbtConfig.maxRiptide; - } else if (enchantment.equals(Enchantment.MULTISHOT)) { - maxLevel = Sentinel.nbtConfig.maxMultishot; - } else if (enchantment.equals(Enchantment.PIERCING)) { - maxLevel = Sentinel.nbtConfig.maxPiercing; - } else if (enchantment.equals(Enchantment.POWER)) { - maxLevel = Sentinel.nbtConfig.maxPower; - } else if (enchantment.equals(Enchantment.PUNCH)) { - maxLevel = Sentinel.nbtConfig.maxPunch; - } else if (enchantment.equals(Enchantment.QUICK_CHARGE)) { - maxLevel = Sentinel.nbtConfig.maxQuickCharge; - } else if (enchantment.equals(Enchantment.EFFICIENCY)) { - maxLevel = Sentinel.nbtConfig.maxEfficiency; - } else if (enchantment.equals(Enchantment.FORTUNE)) { - maxLevel = Sentinel.nbtConfig.maxFortune; - } else if (enchantment.equals(Enchantment.LUCK_OF_THE_SEA)) { - maxLevel = Sentinel.nbtConfig.maxLuckOfTheSea; - } else if (enchantment.equals(Enchantment.LURE)) { - maxLevel = Sentinel.nbtConfig.maxLure; - } else if (enchantment.equals(Enchantment.SILK_TOUCH)) { - maxLevel = Sentinel.nbtConfig.maxSilkTouch; - } else if (enchantment.equals(Enchantment.BREACH)) { - maxLevel = Sentinel.nbtConfig.maxBreach; - } else if (enchantment.equals(Enchantment.DENSITY)) { - maxLevel = Sentinel.nbtConfig.maxDensity; - } else if (enchantment.equals(Enchantment.WIND_BURST)) { - maxLevel = Sentinel.nbtConfig.maxWindBurst; - } - - return level > maxLevel; - } -} diff --git a/src/main/java/me/trouper/sentinel/utils/MathUtils.java b/src/main/java/me/trouper/sentinel/utils/MathUtils.java index 825a5c8..d5a647c 100644 --- a/src/main/java/me/trouper/sentinel/utils/MathUtils.java +++ b/src/main/java/me/trouper/sentinel/utils/MathUtils.java @@ -1,35 +1,76 @@ package me.trouper.sentinel.utils; +import java.math.BigInteger; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.UUID; -public class MathUtils { +public final class MathUtils { - public static double avg(Integer... ints) { - final List list = Arrays.stream(ints).filter(Objects::nonNull).toList(); - return avg(list); + public static double[] uuidToDoubles(UUID uuid) { + byte[] bytes = uuidToBytes(uuid); + BigInteger bigInt = new BigInteger(1, bytes); + + // Split into 43, 43, 42 bits + BigInteger mask43 = BigInteger.ONE.shiftLeft(43).subtract(BigInteger.ONE); + BigInteger mask42 = BigInteger.ONE.shiftLeft(42).subtract(BigInteger.ONE); + + BigInteger part1 = bigInt.shiftRight(85).and(mask43); + BigInteger part2 = bigInt.shiftRight(42).and(mask43); + BigInteger part3 = bigInt.and(mask42); + + return new double[] { + part1.doubleValue(), + part2.doubleValue(), + part3.doubleValue() + }; } - public static double avg(List ints) { - double sum = 0.0; - for (Integer i : ints) sum += i; - return sum / ints.size(); - } - - public static double round(double value, int nthPlace) { - return Math.floor(value * nthPlace) / nthPlace; - } - - public static String bytesToHex(byte[] bytes) { - StringBuilder result = new StringBuilder(); - for (byte b : bytes) { - result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1)); + public static UUID doublesToUuid(double[] doubles) { + if (doubles.length != 3) { + throw new IllegalArgumentException("Exactly 3 doubles required"); } - return result.toString(); + + BigInteger part1 = BigInteger.valueOf((long) doubles[0]); + BigInteger part2 = BigInteger.valueOf((long) doubles[1]); + BigInteger part3 = BigInteger.valueOf((long) doubles[2]); + + BigInteger reconstructed = part1.shiftLeft(85) + .or(part2.shiftLeft(42)) + .or(part3); + + byte[] bytes = reconstructed.toByteArray(); + + byte[] uuidBytes = new byte[16]; + if (bytes.length > 16) { + System.arraycopy(bytes, bytes.length - 16, uuidBytes, 0, 16); + } else { + System.arraycopy(bytes, 0, uuidBytes, 16 - bytes.length, bytes.length); + } + + return bytesToUuid(uuidBytes); + } + + private static byte[] uuidToBytes(UUID uuid) { + ByteBuffer bb = ByteBuffer.wrap(new byte[16]); + bb.putLong(uuid.getMostSignificantBits()); + bb.putLong(uuid.getLeastSignificantBits()); + return bb.array(); + } + + private static UUID bytesToUuid(byte[] bytes) { + if (bytes.length != 16) { + throw new IllegalArgumentException("Invalid UUID byte array"); + } + ByteBuffer bb = ByteBuffer.wrap(bytes); + return new UUID(bb.getLong(), bb.getLong()); } public static double calcSim(String s1, String s2) { diff --git a/src/main/java/me/trouper/sentinel/utils/PlayerUtils.java b/src/main/java/me/trouper/sentinel/utils/PlayerUtils.java index 40adc2a..eddf623 100644 --- a/src/main/java/me/trouper/sentinel/utils/PlayerUtils.java +++ b/src/main/java/me/trouper/sentinel/utils/PlayerUtils.java @@ -1,17 +1,33 @@ package me.trouper.sentinel.utils; import me.trouper.sentinel.Sentinel; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; +import org.bukkit.Material; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; +import org.bukkit.metadata.MetadataValue; -public class PlayerUtils { +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.function.Predicate; + +public final class PlayerUtils { public static boolean isTrusted(Player player) { - return Sentinel.mainConfig.plugin.trustedPlayers.contains(player.getUniqueId().toString()); + return Sentinel.getInstance().getDirector().io.mainConfig.plugin.trustedPlayers.contains(player.getUniqueId().toString()); } public static boolean isTrusted(String uuid) { - return Sentinel.mainConfig.plugin.trustedPlayers.contains(uuid); + return Sentinel.getInstance().getDirector().io.mainConfig.plugin.trustedPlayers.contains(uuid); + } + + public static boolean isTrusted(UUID uuid) { + return isTrusted(uuid.toString()); } public static boolean isTrusted(CommandSender sender) { @@ -19,8 +35,8 @@ public class PlayerUtils { } public static boolean playerCheck(CommandSender sender) { - if (!(sender instanceof Player p)) { - sender.sendMessage(Text.prefix(Sentinel.lang.permissions.playersOnly)); + if (!(sender instanceof Player)) { + sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.permissions.playersOnly)); return false; } return true; @@ -28,7 +44,54 @@ public class PlayerUtils { public static boolean checkPermission(CommandSender sender, String permission) { if (sender instanceof ConsoleCommandSender || (sender instanceof Player p && p.hasPermission(permission))) return true; - sender.sendMessage(Sentinel.lang.permissions.noPermission); + sender.sendMessage(Sentinel.getInstance().getDirector().io.lang.permissions.noPermission); return false; } + + public static List getPlayers() { + return new ArrayList<>(Bukkit.getOnlinePlayers()); + } + + public static List getStaff() { + return getPlayers().stream().filter(Player -> Player.hasPermission("sentinel.staff")).toList(); + } + public static List getTrusted() { + return getPlayers().stream().filter(PlayerUtils::isTrusted).toList(); + } + + public static void forEachPlayer(Consumer consumer) { + getPlayers().forEach(consumer); + } + + public static void forEachStaff(Consumer consumer) { + getStaff().forEach(consumer); + } + public static void forEachTrusted(Consumer consumer) { + getStaff().forEach(consumer); + } + + public static void dmEachPlayer(Predicate condition, String dm) { + forEachPlayer(p -> { + if (condition.test(p)) p.sendMessage(dm); + }); + } + + public static void dmEachPlayer(String dm) { + forEachPlayer(p -> p.sendMessage(dm)); + } + + public static void forEachSpecified(Iterable players, Consumer consumer) { + players.forEach(consumer); + } + + public static void forEachSpecified(Consumer consumer, Player... players) { + Arrays.stream(players).forEach(consumer); + } + public static void forEachPlayerRun(Predicate condition, Consumer task) { + forEachPlayer(p -> { + if (condition.test(p)) { + task.accept(p); + } + }); + } } diff --git a/src/main/java/me/trouper/sentinel/utils/Randomizer.java b/src/main/java/me/trouper/sentinel/utils/Random.java similarity index 81% rename from src/main/java/me/trouper/sentinel/utils/Randomizer.java rename to src/main/java/me/trouper/sentinel/utils/Random.java index 99a5ea8..5bcd2a2 100644 --- a/src/main/java/me/trouper/sentinel/utils/Randomizer.java +++ b/src/main/java/me/trouper/sentinel/utils/Random.java @@ -1,5 +1,6 @@ package me.trouper.sentinel.utils; +import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -10,7 +11,14 @@ import java.util.Set; * Randomize items from a list * @param list of? */ -public class Randomizer { +public final class Random { + + public static Date getDate(long id) throws ParseException { + String dateString = String.valueOf(id); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS"); + return dateFormat.parse(dateString); + } + public static long generateID() { Date now = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS"); @@ -19,6 +27,8 @@ public class Randomizer { return id; } + + private final List array; @@ -26,7 +36,7 @@ public class Randomizer { * From array list * @param array list */ - public Randomizer(List array) { + public Random(List array) { this.array = array; } @@ -34,7 +44,7 @@ public class Randomizer { * From set * @param array set */ - public Randomizer(Set array) { + public Random(Set array) { this.array = new ArrayList<>(array); } @@ -42,7 +52,7 @@ public class Randomizer { * From array * @param array array */ - public Randomizer(T[] array) { + public Random(T[] array) { this.array = List.of(array); } diff --git a/src/main/java/me/trouper/sentinel/utils/ServerUtils.java b/src/main/java/me/trouper/sentinel/utils/ServerUtils.java index a15b310..3eca709 100644 --- a/src/main/java/me/trouper/sentinel/utils/ServerUtils.java +++ b/src/main/java/me/trouper/sentinel/utils/ServerUtils.java @@ -5,11 +5,11 @@ import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.metadata.MetadataValue; import java.io.BufferedReader; -import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; @@ -19,7 +19,12 @@ import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; -public class ServerUtils { +public final class ServerUtils { + + public static boolean isCommandBlock(Block b) { + return b.getType().equals(Material.COMMAND_BLOCK) || b.getType().equals(Material.REPEATING_COMMAND_BLOCK) || b.getType().equals(Material.CHAIN_COMMAND_BLOCK); + } + public static void sendCommand(String command) { ServerUtils.verbose("Getting scheduler"); Bukkit.getScheduler().scheduleSyncDelayedTask(Sentinel.getInstance(), () -> { @@ -32,82 +37,38 @@ public class ServerUtils { },1); } - public static void verbose(String message) { - if (Sentinel.mainConfig.debugMode) { - String log = "[Sentinel] [DEBUG]: " + message; - Sentinel.log.info(log); - for (Player trustedPlayer : Bukkit.getOnlinePlayers()) { - if (PlayerUtils.isTrusted(trustedPlayer)) { - trustedPlayer.sendMessage("§d§lSentinel §7[§bDEBUG§7] §8» §7" + message); - } + public static void verbose(String message, Object... args) { + if (!Sentinel.getInstance().getDirector().io.mainConfig.debugMode) return; + String callerInfo = "Unknown Caller"; + + // Capture the stack trace to determine the caller + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + if (stackTrace.length > 2) { // Ensure we have enough depth + StackTraceElement caller = stackTrace[2]; // The method that called `verbose()` + + String className = caller.getClassName(); + className = className.substring(className.lastIndexOf(".") + 1); + if (className.contains("-")) { + callerInfo = "Protected"; + } else { + callerInfo = className + "." + caller.getMethodName(); + } + + + } + + String formattedMessage = message.formatted(args); + String log = "[Sentinel] [DEBUG] [%s]: %s".formatted(callerInfo, formattedMessage); + Sentinel.getInstance().getLogger().info(log); + + for (Player trustedPlayer : Bukkit.getOnlinePlayers()) { + if (PlayerUtils.isTrusted(trustedPlayer)) { + trustedPlayer.sendMessage("§d§lSentinel §7[§bDEBUG§7] §7[§e%s§7] §8» §7%s" + .formatted(callerInfo, formattedMessage)); } } } - public static List getPlayers() { - return new ArrayList<>(Bukkit.getOnlinePlayers()); - } - - public static List getStaff() { - return getPlayers().stream().filter(Player -> Player.hasPermission("sentinel.staff")).toList(); - } - - public static void forEachPlayer(Consumer consumer) { - getPlayers().forEach(consumer); - } - - public static void forEachStaff(Consumer consumer) { - getStaff().forEach(consumer); - } - - public static void dmEachPlayer(Predicate condition, String dm) { - forEachPlayer(p -> { - if (condition.test(p)) p.sendMessage(dm); - }); - } - - public static void dmEachPlayer(String dm) { - forEachPlayer(p -> p.sendMessage(dm)); - } - - public static void forEachSpecified(Iterable players, Consumer consumer) { - players.forEach(consumer); - } - - public static void forEachSpecified(Consumer consumer, Player... players) { - Arrays.stream(players).forEach(consumer); - } - public static void forEachPlayerRun(Predicate condition, Consumer task) { - forEachPlayer(p -> { - if (condition.test(p)) { - task.accept(p); - } - }); - } - public static void sendActionBar(Player p, String msg) { - p.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(msg)); - } - - public static boolean hasBlockBelow(Player player, Material material) { - for (int y = player.getLocation().getBlockY() - 1; y >= player.getLocation().getBlockY() - 12; y--) { - if (player.getWorld().getBlockAt(player.getLocation().getBlockX(), y, player.getLocation().getBlockZ()).getType() == material) { - return true; - } - } - return false; - } - - public static boolean isVanished(Player player) { - for (MetadataValue meta : player.getMetadata("vanished")) { - if (meta.asBoolean()) return true; - } - return false; - } - - public static String[] unVanishedPlayers() { - return io.github.itzispyder.pdk.utils.ServerUtils.players(ServerUtils::isVanished).stream().map(Player::getName).toArray(String[]::new); - } - public static String getPublicIPAddress() { try { String apiUrl = "http://checkip.amazonaws.com"; @@ -136,7 +97,7 @@ public class ServerUtils { connection.disconnect(); return null; } catch (Exception e) { - Sentinel.log.warning(e.getMessage()); + Sentinel.getInstance().getLogger().warning(e.getMessage()); return null; } } diff --git a/src/main/java/me/trouper/sentinel/utils/Text.java b/src/main/java/me/trouper/sentinel/utils/Text.java index 12de151..2f69154 100644 --- a/src/main/java/me/trouper/sentinel/utils/Text.java +++ b/src/main/java/me/trouper/sentinel/utils/Text.java @@ -2,18 +2,23 @@ package me.trouper.sentinel.utils; import me.trouper.sentinel.Sentinel; +import org.bukkit.Location; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; -public class Text { +public final class Text { public static String removeColors(String input) { return input.replaceAll("((§|&)[0-9a-fklmnor])|((§|&)#(?:[0-9a-fA-F]{3}){1,2})", ""); } + public static String formatLoc(Location loc) { + return "&7(&4" + loc.getBlockX() + "&7, &2" + loc.getBlockY() + "&7, &1" + loc.getBlockZ() + "&7)"; + } + public static String regexHighlighter(String input, String regex, String startString, String endString) { Pattern pattern = Pattern.compile(regex); @@ -36,7 +41,7 @@ public class Text { } public static String prefix(String text) { - String prefix = Sentinel.mainConfig.plugin.prefix; + String prefix = Sentinel.getInstance().getDirector().io.mainConfig.plugin.prefix; return color(prefix + text); } @@ -88,7 +93,7 @@ public class Text { } public static String fromLeetString(String s) { - Map dictionary = Sentinel.advConfig.leetPatterns; + Map dictionary = Sentinel.getInstance().getDirector().io.advConfig.leetPatterns; String msg = s; for (String key : dictionary.keySet()) { @@ -112,4 +117,22 @@ public class Text { public static String cleanName(String type) { return type.replaceAll("_"," ").toLowerCase(); } + + public static String formatMillis(long millis) { + long days = millis / 86400000L; + millis %= 86400000L; + long hours = millis / 3600000L; + millis %= 3600000L; + long minutes = millis / 60000L; + millis %= 60000L; + long seconds = millis / 1000L; + millis %= 1000L; + StringBuilder sb = new StringBuilder(); + if (days > 0) sb.append(days).append("d "); + if (hours > 0) sb.append(hours).append("hr "); + if (minutes > 0) sb.append(minutes).append("min "); + if (seconds > 0) sb.append(seconds).append("sec "); + if (millis > 0) sb.append(millis).append("ms"); + return sb.toString().trim(); + } } diff --git a/src/main/java/me/trouper/sentinel/utils/display/BlockDisplayRaytracer.java b/src/main/java/me/trouper/sentinel/utils/display/BlockDisplayRaytracer.java new file mode 100644 index 0000000..8d00d0f --- /dev/null +++ b/src/main/java/me/trouper/sentinel/utils/display/BlockDisplayRaytracer.java @@ -0,0 +1,163 @@ +package me.trouper.sentinel.utils.display; + +import me.trouper.sentinel.Sentinel; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.entity.BlockDisplay; +import org.bukkit.entity.Display; +import org.bukkit.entity.Player; +import org.bukkit.util.Consumer; +import org.bukkit.util.Transformation; +import org.bukkit.util.Vector; +import org.joml.AxisAngle4f; +import org.joml.Vector3f; + +import java.util.List; + +public class BlockDisplayRaytracer { + + private static final Sentinel system = Sentinel.getInstance(); + + public static void outline(Material display, Location location, long stayTime, List viewers) { + outline(display, location, 0.05, stayTime, viewers); + } + + public static void outline(Material display, Location corner1, Location corner2, double thickness, long stayTime, List viewers) { + World world = corner1.getWorld(); + + // Use block coordinates + int minX = Math.min(corner1.getBlockX(), corner2.getBlockX()); + int minY = Math.min(corner1.getBlockY(), corner2.getBlockY()); + int minZ = Math.min(corner1.getBlockZ(), corner2.getBlockZ()); + int maxX = Math.max(corner1.getBlockX(), corner2.getBlockX()); + int maxY = Math.max(corner1.getBlockY(), corner2.getBlockY()); + int maxZ = Math.max(corner1.getBlockZ(), corner2.getBlockZ()); + + // Adjust max values by adding 1 so the outline is drawn at the block edges + Location a1 = new Location(world, minX, minY, minZ); + Location a2 = new Location(world, maxX + 1, minY, minZ); + Location a3 = new Location(world, maxX + 1, minY, maxZ + 1); + Location a4 = new Location(world, minX, minY, maxZ + 1); + + Location b1 = new Location(world, minX, maxY + 1, minZ); + Location b2 = new Location(world, maxX + 1, maxY + 1, minZ); + Location b3 = new Location(world, maxX + 1, maxY + 1, maxZ + 1); + Location b4 = new Location(world, minX, maxY + 1, maxZ + 1); + + // Bottom face + trace(display, a1, a2, thickness, stayTime, viewers); + trace(display, a2, a3, thickness, stayTime, viewers); + trace(display, a3, a4, thickness, stayTime, viewers); + trace(display, a4, a1, thickness, stayTime, viewers); + + // Top face + trace(display, b1, b2, thickness, stayTime, viewers); + trace(display, b2, b3, thickness, stayTime, viewers); + trace(display, b3, b4, thickness, stayTime, viewers); + trace(display, b4, b1, thickness, stayTime, viewers); + + // Vertical edges + trace(display, a1, b1, thickness, stayTime, viewers); + trace(display, a2, b2, thickness, stayTime, viewers); + trace(display, a3, b3, thickness, stayTime, viewers); + trace(display, a4, b4, thickness, stayTime, viewers); + } + + + public static void outline(Material display, Location location, double thickness, long stayTime, List viewers) { + Location og = location.getBlock().getLocation(); + + Location a1 = og.clone().add(0, 0, 0); + Location a2 = og.clone().add(1, 0, 0); + Location a3 = og.clone().add(1, 0, 1); + Location a4 = og.clone().add(0, 0, 1); + + Location b1 = og.clone().add(0, 1, 0); + Location b2 = og.clone().add(1, 1, 0); + Location b3 = og.clone().add(1, 1, 1); + Location b4 = og.clone().add(0, 1, 1); + + trace(display, a1, a2, thickness, stayTime, viewers); + trace(display, a2, a3, thickness, stayTime, viewers); + trace(display, a3, a4, thickness, stayTime, viewers); + trace(display, a4, a1, thickness, stayTime, viewers); + + trace(display, b1, b2, thickness, stayTime, viewers); + trace(display, b2, b3, thickness, stayTime, viewers); + trace(display, b3, b4, thickness, stayTime, viewers); + trace(display, b4, b1, thickness, stayTime, viewers); + + trace(display, a1, b1, thickness, stayTime, viewers); + trace(display, a2, b2, thickness, stayTime, viewers); + trace(display, a3, b3, thickness, stayTime, viewers); + trace(display, a4, b4, thickness, stayTime, viewers); + } + + public static void trace(Material display, Location start, Location end, long stayTime, List viewers) { + trace(display, start, end.toVector().subtract(start.toVector()), 0.05, end.distance(start), stayTime, viewers); + } + + public static void trace(Material display, Location start, Location end, double thickness, long stayTime, List viewers) { + trace(display, start, end.toVector().subtract(start.toVector()), thickness, end.distance(start), stayTime, viewers); + } + + public static void trace(Material display, Location start, Vector direction, double thickness, double distance, long stayTime, List viewers) { + World world = start.getWorld(); + + BlockDisplay beam = world.spawn(start, BlockDisplay.class, entity -> { + AxisAngle4f angle = new AxisAngle4f(0, 0, 0, 1); + Vector3f transition = new Vector3f(-(float)(thickness / 2F)); + Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance); + Transformation trans = new Transformation(transition, angle, scale, angle); + Location vector = entity.getLocation(); + + vector.setDirection(direction); + entity.teleport(vector); + entity.setBlock(display.createBlockData()); + entity.setBrightness(new Display.Brightness(15, 15)); + entity.setInterpolationDelay(0); + entity.setTransformation(trans); + entity.addScoreboardTag("./Sentinel/ Block Display"); + + // Hide the entity from all players not in the viewers list + for (Player player : Bukkit.getOnlinePlayers()) { + if (!viewers.contains(player)) { + player.hideEntity(system, entity); + } + } + + Bukkit.getScheduler().runTaskLater(system, entity::remove, stayTime); + }); + } + + public static void trace(Material display, Location start, Vector direction, double thickness, double distance, long stayTime, Consumer onEntitySpawn, List viewers) { + World world = start.getWorld(); + + BlockDisplay beam = world.spawn(start, BlockDisplay.class, entity -> { + AxisAngle4f angle = new AxisAngle4f(0, 0, 0, 1); + Vector3f transition = new Vector3f(-(float)(thickness / 2F)); + Vector3f scale = new Vector3f((float)thickness, (float)thickness, (float)distance); + Transformation trans = new Transformation(transition, angle, scale, angle); + Location vector = entity.getLocation(); + + vector.setDirection(direction); + entity.teleport(vector); + entity.setBlock(display.createBlockData()); + entity.setBrightness(new Display.Brightness(15, 15)); + entity.setInterpolationDelay(0); + entity.setTransformation(trans); + entity.addScoreboardTag("./Sentinel/ Block Display"); + + for (Player player : Bukkit.getOnlinePlayers()) { + if (!viewers.contains(player)) { + player.hideEntity(system, entity); + } + } + + Bukkit.getScheduler().runTaskLater(system, entity::remove, stayTime); + Bukkit.getScheduler().runTaskLater(system, () -> onEntitySpawn.accept(entity), 5); + }); + } +} diff --git a/src/main/java/me/trouper/sentinel/utils/display/CustomDisplayRaytracer.java b/src/main/java/me/trouper/sentinel/utils/display/CustomDisplayRaytracer.java new file mode 100644 index 0000000..bd4a1ce --- /dev/null +++ b/src/main/java/me/trouper/sentinel/utils/display/CustomDisplayRaytracer.java @@ -0,0 +1,140 @@ +package me.trouper.sentinel.utils.display; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.util.Vector; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +public class CustomDisplayRaytracer { + + public static final Predicate HIT_BLOCK = point -> { + Block b = point.getBlock(); + Vector v = point.getLoc().toVector(); + return !b.isPassable() && b.getCollisionShape().getBoundingBoxes().stream().noneMatch(box -> box.contains(v)); + }; + + public static final Predicate HIT_ENTITY = point -> { + return !point.getNearbyEntities(null, 5, true, 0.1, e -> e instanceof LivingEntity le && !le.isDead()).isEmpty(); + }; + + public static final Predicate HIT_BLOCK_OR_ENTITY = point -> { + return HIT_BLOCK.test(point) || HIT_ENTITY.test(point); + }; + + public static final Predicate HIT_BLOCK_AND_ENTITY = point -> { + return HIT_BLOCK.test(point) && HIT_ENTITY.test(point); + }; + + public static Predicate hitEntityExclude(Entity exclude) { + return point -> !point.getNearbyEntities(exclude, 5, true, 0.1, e -> e instanceof LivingEntity le && !le.isDead()).isEmpty(); + } + + public static Predicate hitAnythingExclude(Entity exclude) { + return point -> HIT_BLOCK.test(point) || !point.getNearbyEntities(exclude, 5, true, 0.1, e -> e instanceof LivingEntity le && !le.isDead()).isEmpty(); + } + + public static Predicate hitEverythingExclude(Entity exclude) { + return point -> HIT_BLOCK.test(point) && !point.getNearbyEntities(exclude, 5, true, 0.1, e -> e instanceof LivingEntity le && !le.isDead()).isEmpty(); + } + + + public static Point trace(Location start, Location end, Predicate hitCondition) { + return trace(start, end, 0.5, hitCondition); + } + + public static Point trace(Location start, Location end, double interval, Predicate hitCondition) { + return trace(start, end.toVector().subtract(end.toVector()), end.distance(start), interval, hitCondition); + } + + public static Point trace(Location start, Vector direction, double distance, Predicate hitCondition) { + return trace(start, direction, distance, 0.5, hitCondition); + } + + public static Point trace(Location start, Vector direction, double distance, double interval, Predicate hitCondition) { + if (interval < 0) throw new IllegalArgumentException("interval cannot be zero!"); + if (distance < 0) throw new IllegalArgumentException("distance cannot be zero!"); + + for (double i = 0.0; i < distance; i += interval) { + Point point = blocksInFrontOf(start, direction, i, false); + if (hitCondition.test(point)) { + return point; + } + } + return blocksInFrontOf(start, direction, distance, true); + } + + public static Point blocksInFrontOf(Location loc, Vector dir, double blocks, boolean missed) { + return new Point(loc.clone().add(dir.getX() * blocks, dir.getY() * blocks, dir.getZ() * blocks), blocks, missed); + } + + public static class Point { + private final Location loc; + private final World world; + private final Block block; + private final boolean missed; + private final double traveledDist; + + private Point(Location loc, double traveledDist, boolean missed) { + this.loc = loc; + this.world = loc.getWorld(); + this.block = loc.getBlock(); + this.missed = missed; + this.traveledDist = traveledDist; + + if (world == null) { + throw new IllegalArgumentException("point world cannot be null!"); + } + } + + public List getNearbyEntities(Entity exclude, int range, boolean requireContact, double expansionX, double expansionY, double expansionZ, Predicate filter) { + return new ArrayList<>(world.getNearbyEntities(loc, range, range, range, e -> { + if (requireContact && !e.getBoundingBox().expand(expansionX, expansionY, expansionZ).contains(loc.toVector())) { + return false; + } + return filter.test(e) && e != exclude; + })); + } + + public List getNearbyEntities(Entity exclude, int range, boolean requireContact, double expansion, Predicate filter) { + return getNearbyEntities(exclude, range, requireContact, expansion, expansion, expansion, filter); + } + + public List getNearbyEntities(Entity exclude, int range, boolean requireContact, Predicate filter) { + return getNearbyEntities(exclude, range, requireContact, 0, filter); + } + + public List getNearbyEntities(Entity exclude, int range, Predicate filter) { + return getNearbyEntities(exclude, range, false, filter); + } + + public double getTraveledDist() { + return traveledDist; + } + + public boolean wasMissed() { + return missed; + } + + public Block getBlock() { + return block; + } + + public Location getLoc() { + return loc; + } + + public World getWorld() { + return world; + } + + public double distance(Location other) { + return other.distance(loc); + } + } +} diff --git a/src/main/java/me/trouper/sentinel/utils/display/RaycastUtils.java b/src/main/java/me/trouper/sentinel/utils/display/RaycastUtils.java new file mode 100644 index 0000000..1f9deae --- /dev/null +++ b/src/main/java/me/trouper/sentinel/utils/display/RaycastUtils.java @@ -0,0 +1,73 @@ +package me.trouper.sentinel.utils.display; + +import me.trouper.sentinel.Sentinel; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.util.Vector; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Predicate; + +public final class RaycastUtils { + + public static Location raycast(Location start, Location end, Predicate hitCondition) { + return raycast(start, end, 0.5, hitCondition); + } + + public static Location raycast(Location start, Location end, double frequency, Predicate hitCondition) { + return raycast(start, end.toVector().subtract(start.toVector()).normalize(), start.distance(end), frequency, hitCondition); + } + + public static Location raycast(Location start, Vector rotation, double distance, Predicate hitCondition) { + return raycast(start, rotation, distance, 0.5, hitCondition); + } + + public static Location raycast(Location start, Vector rotation, double distance, double frequency, Predicate hitCondition) { + for (double i = 0.0; i <= distance; i += frequency) { + Location point = start.clone().add(rotation.clone().multiply(i)); + if (hitCondition.test(point)) { + return point; + } + } + return start.clone().add(rotation.clone().multiply(distance)); + } + + public static void raycast(Location start, Vector rotation, double distance, double frequency, long tickInterval, BiPredicate hitCondition, Consumer onhit) { + AtomicReference result = new AtomicReference<>(); + AtomicReference val = new AtomicReference<>(0.0); + AtomicReference active = new AtomicReference<>(true); + + Bukkit.getScheduler().scheduleSyncRepeatingTask(Sentinel.getInstance(), () -> { + if (val.get() <= distance && result.get() == null && active.get()) { + Location point = start.clone().add(rotation.clone().multiply(val.get())); + if (hitCondition.test(point, val.get())) { + result.set(point); + } + val.set(val.get() + frequency); + } + else if (result.get() == null) { + result.set(start.clone().add(rotation.clone().multiply(distance))); + } + else if (active.get()) { + onhit.accept(result.get()); + active.set(false); + } + }, 0, tickInterval); + } + + public static Vector randomVector(Vector baseVector, double angle) { + double randomAngle = Math.random() * angle; + + double randomAxisX = Math.random() - 0.5; + double randomAxisY = Math.random() - 0.5; + double randomAxisZ = Math.random() - 0.5; + + Vector rotationAxis = new Vector(randomAxisX, randomAxisY, randomAxisZ).normalize(); + + double rotationAngle = Math.toRadians(randomAngle); + + return baseVector.clone().rotateAroundAxis(rotationAxis, rotationAngle); + } +} diff --git a/src/main/java/me/trouper/sentinel/utils/trees/EmbedFormatter.java b/src/main/java/me/trouper/sentinel/utils/trees/EmbedFormatter.java index ff3a3e2..32314ac 100644 --- a/src/main/java/me/trouper/sentinel/utils/trees/EmbedFormatter.java +++ b/src/main/java/me/trouper/sentinel/utils/trees/EmbedFormatter.java @@ -4,7 +4,7 @@ import io.github.itzispyder.pdk.utils.SchedulerUtils; import io.github.itzispyder.pdk.utils.discord.DiscordEmbed; import io.github.itzispyder.pdk.utils.discord.DiscordWebhook; import me.trouper.sentinel.Sentinel; -import me.trouper.sentinel.data.Emojis; +import me.trouper.sentinel.data.types.Emojis; import me.trouper.sentinel.utils.Text; import java.io.IOException; @@ -14,7 +14,7 @@ import java.util.concurrent.atomic.AtomicBoolean; public class EmbedFormatter { public static boolean sendEmbed(DiscordEmbed embed) { - return sendEmbed(embed,Sentinel.mainConfig.plugin.webhook); + return sendEmbed(embed,Sentinel.getInstance().getDirector().io.mainConfig.plugin.webhook); } public static boolean sendEmbed(DiscordEmbed embed, String spec) { @@ -24,7 +24,7 @@ public class EmbedFormatter { try { webhook.send(spec); } catch (IOException e) { - Sentinel.log.warning(e.getMessage()); + Sentinel.getInstance().getLogger().warning(e.getMessage()); success.set(false); return; } @@ -46,7 +46,7 @@ public class EmbedFormatter { eb.author("Sentinel | Anti-Nuke","https://trouper.me/sentinel",null); eb.thumbnail("https://r2.e-z.host/d440b58a-ba90-4839-8df6-8bba298cf817/v5rxlx0d.png"); if (level == 0) { - eb.title("Incoming from server: %s".formatted(Sentinel.mainConfig.plugin.identifier)); + eb.title("Incoming from server: %s".formatted(Sentinel.getInstance().getDirector().io.mainConfig.plugin.identifier)); } else { desc.repeat(Emojis.space,level - 1).append("**").append(Text.removeColors(node.title)).append("**\n"); } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 3b7f227..a4825d5 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -12,27 +12,20 @@ softdepend: - ViaBackwards - ViaRewind - Geyser-Spigot -load: STARTUP +load: POSTWORLD permissions: sentinel.admin: description: Allows access to all Sentinel admin commands. default: op children: - sentinel.reload: true - sentinel.config: true sentinel.debug: true + sentinel.extras: true sentinel.staff: description: Allows access to Sentinel staff commands. - default: false + default: op children: sentinel.socialspy: true sentinel.false-positive: true - sentinel.reload: - description: Allows the user to reload the Sentinel plugin. - default: false - sentinel.config: - description: Allows the user to modify the Sentinel configuration. - default: false sentinel.false-positive: description: Allows the user to manage false positives. default: false @@ -47,7 +40,10 @@ permissions: default: false sentinel.debug: description: Allows the user to toggle debug mode. - default: false + default: op + sentinel.extras: + description: Sentinel Extra Features + default: op sentinel.commandblock: description: Allows the user to manage command blocks. default: false @@ -130,6 +126,11 @@ commands: usage: /sentinel permission: sentinel.staff permission-message: You do not have permission to use this command. + sentinelextras: + usage: /sentinelextras [alfa|bravo|charlie|delta|echo|foxtrot|golf|hotel|india|juliett] + description: Extra features for sentinel. + permission: sentinel.extra + permission-message: You do not have permission to use this command. sentinelcallback: description: Callback command for Sentinel. usage: /callback